From b65d5cf18362244df13d2fd67ed1dfac5bc5d19f Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Sun, 23 Nov 2025 02:39:56 +0100 Subject: [PATCH] adding base Image and ICD file generation --- build.zig | 18 +++++ build.zig.zon | 8 ++- src/soft/Executor.zig | 30 ++++++-- src/soft/SoftCommandBuffer.zig | 9 +++ src/soft/SoftDevice.zig | 17 +++-- src/soft/SoftImage.zig | 40 +++++++++++ src/soft/SoftPhysicalDevice.zig | 120 ++++++++++++++++++++++++++++++-- src/soft/SoftQueue.zig | 2 +- src/soft/lib.zig | 1 + src/vulkan/CommandBuffer.zig | 30 ++++++-- src/vulkan/Device.zig | 10 ++- src/vulkan/Image.zig | 71 +++++++++++++++++++ src/vulkan/commands.zig | 2 +- src/vulkan/error_set.zig | 4 ++ src/vulkan/lib.zig | 4 +- src/vulkan/lib_vulkan.zig | 74 ++++++++++++++++++-- test/c/main.c | 46 ++++++++---- 17 files changed, 438 insertions(+), 48 deletions(-) create mode 100644 src/soft/SoftImage.zig create mode 100644 src/vulkan/Image.zig diff --git a/build.zig b/build.zig index e1fb996..e37a132 100644 --- a/build.zig +++ b/build.zig @@ -5,6 +5,7 @@ const zcc = @import("compile_commands"); const ImplementationDesc = struct { name: []const u8, root_source_file: []const u8, + vulkan_version: std.SemanticVersion, custom: ?*const fn (*std.Build, *std.Build.Module) anyerror!void = null, }; @@ -12,6 +13,7 @@ const implementations = [_]ImplementationDesc{ .{ .name = "soft", .root_source_file = "src/soft/lib.zig", + .vulkan_version = .{ .major = 1, .minor = 0, .patch = 0 }, .custom = customSoft, }, }; @@ -29,6 +31,7 @@ pub fn build(b: *std.Build) !void { const zdt = b.dependency("zdt", .{}).module("zdt"); const vulkan_headers = b.dependency("vulkan_headers", .{}); + const vulkan_utility_libraries = b.dependency("vulkan_utility_libraries", .{}); const vulkan = b.dependency("vulkan_zig", .{ .registry = vulkan_headers.path("registry/vk.xml"), @@ -37,6 +40,7 @@ pub fn build(b: *std.Build) !void { base_mod.addImport("zdt", zdt); base_mod.addImport("vulkan", vulkan); base_mod.addSystemIncludePath(vulkan_headers.path("include")); + base_mod.addSystemIncludePath(vulkan_utility_libraries.path("include")); for (implementations) |impl| { var targets = std.ArrayList(*std.Build.Step.Compile){}; @@ -64,6 +68,20 @@ pub fn build(b: *std.Build) !void { .linkage = .dynamic, .use_llvm = true, // Fixes some random bugs happenning with custom backend. Investigations needed }); + + const icd_file = b.addWriteFile(b.getInstallPath(.lib, b.fmt("vk_stroll_{s}.json", .{impl.name})), b.fmt( + \\{{ + \\ "file_format_version": "1.0.1", + \\ "ICD": {{ + \\ "library_path": "{s}", + \\ "api_version": "{}.{}.{}", + \\ "library_arch": "64", + \\ "is_portability_driver": false + \\ }} + \\}} + , .{ lib.out_lib_filename, impl.vulkan_version.major, impl.vulkan_version.minor, impl.vulkan_version.patch })); + + lib.step.dependOn(&icd_file.step); const lib_install = b.addInstallArtifact(lib, .{}); const lib_tests = b.addTest(.{ .root_module = lib_mod }); diff --git a/build.zig.zon b/build.zig.zon index 41847a1..f9fc355 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -17,6 +17,10 @@ .url = "git+https://github.com/catmeow72/vulkan-zig/#8961518db28f88d2cf09ea68e146923de2cfa7f0", .hash = "vulkan-0.0.0-r7Ytx6hBAwD8X_TN32qlkzul4riK6vFvjtK9fZfRvALg", }, + .vulkan_utility_libraries = .{ + .url = "git+https://github.com/KhronosGroup/Vulkan-Utility-Libraries.git#ba452bad58bb4d4c64d7fbd872bf69f70141510e", + .hash = "N-V-__8AAE42fwC1FFw26LNZ8AaSuGMdgG4vfYkfV_227sET", + }, .zdt = .{ .url = "git+https://github.com/FObersteiner/zdt/?ref=v0.8.1#8b551a0a3e5ae64a32b5bad0e6a93119787b43af", .hash = "zdt-0.8.1-xr0_vAxUDwCJRDh9pcAS_mdZBIsvcGTtN-K8JJSWY4I6", @@ -32,8 +36,8 @@ .lazy = true, }, .kvf = .{ - .url = "git+https://github.com/Kbz-8/KVF#f633db3070033db1a9358fd4b5cae7b35245ed9d", - .hash = "N-V-__8AAPaHAgCFo6mTsjpxuOdPZSpYtFZlDOlRXeu499mJ", + .url = "git+https://github.com/Kbz-8/KVF#98b845f876bea94f7bf1b9d30588cf617bf93452", + .hash = "N-V-__8AAEGKAgC2cGDnxmAIFKkaICxS_ogfVYWH83Re29zN", .lazy = true, }, }, diff --git a/src/soft/Executor.zig b/src/soft/Executor.zig index 2e8aaa1..589753c 100644 --- a/src/soft/Executor.zig +++ b/src/soft/Executor.zig @@ -1,7 +1,9 @@ const std = @import("std"); const vk = @import("vulkan"); +const base = @import("base"); -const cmd = @import("base").commands; +const cmd = base.commands; +const VkError = base.VkError; const Self = @This(); @@ -13,17 +15,33 @@ pub fn deinit(self: *Self) void { _ = self; } -pub fn dispatch(self: *Self, command: *const cmd.Command) void { +pub fn dispatch(self: *Self, command: *const cmd.Command) VkError!void { _ = self; switch (command.*) { - .FillBuffer => |data| fillBuffer(&data), + .CopyBuffer => |data| try copyBuffer(&data), + .FillBuffer => |data| try fillBuffer(&data), else => {}, } } -fn fillBuffer(data: *const cmd.CommandFillBuffer) void { - const memory = if (data.buffer.memory) |memory| memory else unreachable; - const raw_memory_map: [*]u32 = @ptrCast(@alignCast(memory.map(data.offset, data.size) catch unreachable)); +fn copyBuffer(data: *const cmd.CommandCopyBuffer) VkError!void { + for (data.regions) |region| { + const src_memory = if (data.src.memory) |memory| memory else return VkError.ValidationFailed; + const dst_memory = if (data.dst.memory) |memory| memory else return VkError.ValidationFailed; + + const src_map: []u8 = @as([*]u8, @ptrCast(try src_memory.map(region.src_offset, region.size)))[0..region.size]; + const dst_map: []u8 = @as([*]u8, @ptrCast(try dst_memory.map(region.dst_offset, region.size)))[0..region.size]; + + @memcpy(dst_map, src_map); + + src_memory.unmap(); + dst_memory.unmap(); + } +} + +fn fillBuffer(data: *const cmd.CommandFillBuffer) VkError!void { + const memory = if (data.buffer.memory) |memory| memory else return VkError.ValidationFailed; + const raw_memory_map: [*]u32 = @ptrCast(@alignCast(try memory.map(data.offset, data.size))); var memory_map: []u32 = raw_memory_map[0..data.size]; for (0..@divExact(data.size, @sizeOf(u32))) |i| { diff --git a/src/soft/SoftCommandBuffer.zig b/src/soft/SoftCommandBuffer.zig index c388f51..dd44a93 100644 --- a/src/soft/SoftCommandBuffer.zig +++ b/src/soft/SoftCommandBuffer.zig @@ -22,6 +22,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v interface.dispatch_table = &.{ .begin = begin, + .copyBuffer = copyBuffer, .end = end, .fillBuffer = fillBuffer, .reset = reset, @@ -65,3 +66,11 @@ pub fn fillBuffer(interface: *Interface, buffer: *base.Buffer, offset: vk.Device _ = size; _ = data; } + +pub fn copyBuffer(interface: *Interface, src: *base.Buffer, dst: *base.Buffer, regions: []const vk.BufferCopy) VkError!void { + // No-op + _ = interface; + _ = src; + _ = dst; + _ = regions; +} diff --git a/src/soft/SoftDevice.zig b/src/soft/SoftDevice.zig index aaa1566..8622c22 100644 --- a/src/soft/SoftDevice.zig +++ b/src/soft/SoftDevice.zig @@ -11,10 +11,9 @@ const SoftQueue = @import("SoftQueue.zig"); const SoftBuffer = @import("SoftBuffer.zig"); const SoftDeviceMemory = @import("SoftDeviceMemory.zig"); const SoftFence = @import("SoftFence.zig"); +const SoftImage = @import("SoftImage.zig"); const VkError = base.VkError; -const Dispatchable = base.Dispatchable; -const NonDispatchable = base.NonDispatchable; const Self = @This(); pub const Interface = base.Device; @@ -41,6 +40,7 @@ pub fn create(physical_device: *base.PhysicalDevice, allocator: std.mem.Allocato .createBuffer = createBuffer, .createCommandPool = createCommandPool, .createFence = createFence, + .createImage = createImage, .destroy = destroy, }; @@ -73,6 +73,12 @@ pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) VkError!void allocator.destroy(self); } +pub fn allocateMemory(interface: *Interface, allocator: std.mem.Allocator, info: *const vk.MemoryAllocateInfo) VkError!*base.DeviceMemory { + const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); + const device_memory = try SoftDeviceMemory.create(self, allocator, info.allocation_size, info.memory_type_index); + return &device_memory.interface; +} + pub fn createBuffer(interface: *Interface, allocator: std.mem.Allocator, info: *const vk.BufferCreateInfo) VkError!*base.Buffer { const buffer = try SoftBuffer.create(interface, allocator, info); return &buffer.interface; @@ -88,8 +94,7 @@ pub fn createCommandPool(interface: *Interface, allocator: std.mem.Allocator, in return &pool.interface; } -pub fn allocateMemory(interface: *Interface, allocator: std.mem.Allocator, info: *const vk.MemoryAllocateInfo) VkError!*base.DeviceMemory { - const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); - const device_memory = try SoftDeviceMemory.create(self, allocator, info.allocation_size, info.memory_type_index); - return &device_memory.interface; +pub fn createImage(interface: *Interface, allocator: std.mem.Allocator, info: *const vk.ImageCreateInfo) VkError!*base.Image { + const image = try SoftImage.create(interface, allocator, info); + return &image.interface; } diff --git a/src/soft/SoftImage.zig b/src/soft/SoftImage.zig new file mode 100644 index 0000000..8ca46b7 --- /dev/null +++ b/src/soft/SoftImage.zig @@ -0,0 +1,40 @@ +const std = @import("std"); +const vk = @import("vulkan"); +const base = @import("base"); + +const lib = @import("lib.zig"); + +const VkError = base.VkError; +const Device = base.Device; + +const Self = @This(); +pub const Interface = base.Image; + +interface: Interface, + +pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const vk.ImageCreateInfo) VkError!*Self { + const self = allocator.create(Self) catch return VkError.OutOfHostMemory; + errdefer allocator.destroy(self); + + var interface = try Interface.init(device, allocator, info); + + interface.vtable = &.{ + .destroy = destroy, + .getMemoryRequirements = getMemoryRequirements, + }; + + self.* = .{ + .interface = interface, + }; + return self; +} + +pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void { + const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); + allocator.destroy(self); +} + +pub fn getMemoryRequirements(interface: *Interface, requirements: *vk.MemoryRequirements) void { + _ = interface; + requirements.alignment = lib.MEMORY_REQUIREMENTS_ALIGNMENT; +} diff --git a/src/soft/SoftPhysicalDevice.zig b/src/soft/SoftPhysicalDevice.zig index 6e24824..e83fad4 100644 --- a/src/soft/SoftPhysicalDevice.zig +++ b/src/soft/SoftPhysicalDevice.zig @@ -35,8 +35,114 @@ pub fn create(allocator: std.mem.Allocator, instance: *const base.Instance) VkEr interface.props.device_id = root.DEVICE_ID; interface.props.device_type = .cpu; - interface.props.limits.max_bound_descriptor_sets = 1024; // tmp - interface.props.limits.max_memory_allocation_count = 1024; + interface.props.limits = .{ + .max_image_dimension_1d = 4096, + .max_image_dimension_2d = 4096, + .max_image_dimension_3d = 256, + .max_image_dimension_cube = 4096, + .max_image_array_layers = 256, + .max_texel_buffer_elements = 65536, + .max_uniform_buffer_range = 16384, + .max_storage_buffer_range = 134217728, + .max_push_constants_size = 128, + .max_memory_allocation_count = std.math.maxInt(u32), + .max_sampler_allocation_count = 4096, + .buffer_image_granularity = 131072, + .sparse_address_space_size = 0, + .max_bound_descriptor_sets = 4, + .max_per_stage_descriptor_samplers = 16, + .max_per_stage_descriptor_uniform_buffers = 12, + .max_per_stage_descriptor_storage_buffers = 4, + .max_per_stage_descriptor_sampled_images = 16, + .max_per_stage_descriptor_storage_images = 4, + .max_per_stage_descriptor_input_attachments = 4, + .max_per_stage_resources = 128, + .max_descriptor_set_samplers = 96, + .max_descriptor_set_uniform_buffers = 72, + .max_descriptor_set_uniform_buffers_dynamic = 8, + .max_descriptor_set_storage_buffers = 24, + .max_descriptor_set_storage_buffers_dynamic = 4, + .max_descriptor_set_sampled_images = 96, + .max_descriptor_set_storage_images = 24, + .max_descriptor_set_input_attachments = 4, + .max_vertex_input_attributes = 16, + .max_vertex_input_bindings = 16, + .max_vertex_input_attribute_offset = 2047, + .max_vertex_input_binding_stride = 2048, + .max_vertex_output_components = 64, + .max_tessellation_generation_level = 0, + .max_tessellation_patch_size = 0, + .max_tessellation_control_per_vertex_input_components = 0, + .max_tessellation_control_per_vertex_output_components = 0, + .max_tessellation_control_per_patch_output_components = 0, + .max_tessellation_control_total_output_components = 0, + .max_tessellation_evaluation_input_components = 0, + .max_tessellation_evaluation_output_components = 0, + .max_geometry_shader_invocations = 0, + .max_geometry_input_components = 0, + .max_geometry_output_components = 0, + .max_geometry_output_vertices = 0, + .max_geometry_total_output_components = 0, + .max_fragment_input_components = 64, + .max_fragment_output_attachments = 4, + .max_fragment_dual_src_attachments = 0, + .max_fragment_combined_output_resources = 4, + .max_compute_shared_memory_size = 16384, + .max_compute_work_group_count = .{ 65535, 65535, 65535 }, + .max_compute_work_group_invocations = 128, + .max_compute_work_group_size = .{ 128, 128, 64 }, + .sub_pixel_precision_bits = 4, + .sub_texel_precision_bits = 4, + .mipmap_precision_bits = 4, + .max_draw_indexed_index_value = 4294967295, + .max_draw_indirect_count = 65535, + .max_sampler_lod_bias = 2.0, + .max_sampler_anisotropy = 1.0, + .max_viewports = 1, + .max_viewport_dimensions = .{ 4096, 4096 }, + .viewport_bounds_range = .{ -8192.0, 8191.0 }, + .viewport_sub_pixel_bits = 0, + .min_memory_map_alignment = 64, + .min_texel_buffer_offset_alignment = 256, + .min_uniform_buffer_offset_alignment = 256, + .min_storage_buffer_offset_alignment = 256, + .min_texel_offset = -8, + .max_texel_offset = 7, + .min_texel_gather_offset = 0, + .max_texel_gather_offset = 0, + .min_interpolation_offset = 0.0, + .max_interpolation_offset = 0.0, + .sub_pixel_interpolation_offset_bits = 0, + .max_framebuffer_width = 4096, + .max_framebuffer_height = 4096, + .max_framebuffer_layers = 256, + .framebuffer_color_sample_counts = .{ .@"1_bit" = true, .@"4_bit" = true }, + .framebuffer_depth_sample_counts = .{ .@"1_bit" = true, .@"4_bit" = true }, + .framebuffer_stencil_sample_counts = .{ .@"1_bit" = true, .@"4_bit" = true }, + .framebuffer_no_attachments_sample_counts = .{ .@"1_bit" = true, .@"4_bit" = true }, + .max_color_attachments = 4, + .sampled_image_color_sample_counts = .{ .@"1_bit" = true, .@"4_bit" = true }, + .sampled_image_integer_sample_counts = .{ .@"1_bit" = true, .@"4_bit" = true }, + .sampled_image_depth_sample_counts = .{ .@"1_bit" = true, .@"4_bit" = true }, + .sampled_image_stencil_sample_counts = .{ .@"1_bit" = true, .@"4_bit" = true }, + .storage_image_sample_counts = .{ .@"1_bit" = true, .@"4_bit" = true }, + .max_sample_mask_words = 1, + .timestamp_compute_and_graphics = .false, + .timestamp_period = 1.0, + .max_clip_distances = 0, + .max_cull_distances = 0, + .max_combined_clip_and_cull_distances = 0, + .discrete_queue_priorities = 2, + .point_size_range = .{ 1.0, 1.0 }, + .line_width_range = .{ 1.0, 1.0 }, + .point_size_granularity = 0.0, + .line_width_granularity = 0.0, + .strict_lines = .false, + .standard_sample_locations = .true, + .optimal_buffer_copy_offset_alignment = 1, + .optimal_buffer_copy_row_pitch_alignment = 1, + .non_coherent_atom_size = 256, + }; interface.mem_props.memory_type_count = 1; interface.mem_props.memory_types[0] = .{ @@ -122,7 +228,13 @@ pub fn getImageFormatProperties( _ = tiling; _ = usage; _ = flags; - return VkError.FormatNotSupported; + return .{ + .max_extent = undefined, + .max_mip_levels = 1, + .max_array_layers = 6, + .sample_counts = .{ .@"1_bit" = true, .@"4_bit" = true }, + .max_resource_size = 0, + }; } pub fn getSparseImageFormatProperties( @@ -141,7 +253,7 @@ pub fn getSparseImageFormatProperties( _ = tiling; _ = usage; _ = flags; - return VkError.FormatNotSupported; + return undefined; } pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) VkError!void { diff --git a/src/soft/SoftQueue.zig b/src/soft/SoftQueue.zig index ffbf32e..195edc3 100644 --- a/src/soft/SoftQueue.zig +++ b/src/soft/SoftQueue.zig @@ -89,7 +89,7 @@ fn taskRunner(self: *Self, info: Interface.SubmitInfo, p_fence: ?*base.Fence) vo loop: for (info.command_buffers.items) |command_buffer| { command_buffer.submit() catch continue :loop; for (command_buffer.commands.items) |command| { - executor.dispatch(&command); + executor.dispatch(&command) catch |err| base.errors.errorLoggerContext(err, "the software command dispatcher"); } } diff --git a/src/soft/lib.zig b/src/soft/lib.zig index e36f229..f8a844b 100644 --- a/src/soft/lib.zig +++ b/src/soft/lib.zig @@ -14,6 +14,7 @@ pub const SoftCommandBuffer = @import("SoftCommandBuffer.zig"); pub const SoftCommandPool = @import("SoftCommandPool.zig"); pub const SoftDeviceMemory = @import("SoftDeviceMemory.zig"); pub const SoftFence = @import("SoftFence.zig"); +pub const SoftImage = @import("SoftImage.zig"); pub const Instance = SoftInstance; diff --git a/src/vulkan/CommandBuffer.zig b/src/vulkan/CommandBuffer.zig index 8654416..9ca19ba 100644 --- a/src/vulkan/CommandBuffer.zig +++ b/src/vulkan/CommandBuffer.zig @@ -38,6 +38,7 @@ dispatch_table: *const DispatchTable, pub const DispatchTable = struct { begin: *const fn (*Self, *const vk.CommandBufferBeginInfo) VkError!void, + copyBuffer: *const fn (*Self, *Buffer, *Buffer, []const vk.BufferCopy) VkError!void, end: *const fn (*Self) VkError!void, fillBuffer: *const fn (*Self, *Buffer, vk.DeviceSize, vk.DeviceSize, u32) VkError!void, reset: *const fn (*Self, vk.CommandBufferResetFlags) VkError!void, @@ -71,6 +72,7 @@ inline fn transitionState(self: *Self, target: State, from_allowed: []const Stat } pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void { + self.cleanCommandList(); self.commands.deinit(allocator); self.vtable.destroy(self, allocator); } @@ -94,6 +96,8 @@ pub inline fn reset(self: *Self, flags: vk.CommandBufferResetFlags) VkError!void if (!self.pool.flags.reset_command_buffer_bit) { return VkError.ValidationFailed; } + defer self.cleanCommandList(); + self.transitionState(.Initial, &.{ .Initial, .Recording, .Executable, .Invalid }) catch return VkError.ValidationFailed; try self.dispatch_table.reset(self, flags); } @@ -107,15 +111,19 @@ pub inline fn submit(self: *Self) VkError!void { self.transitionState(.Pending, &.{ .Pending, .Executable }) catch return VkError.ValidationFailed; } +fn cleanCommandList(self: *Self) void { + const allocator = self.host_allocator.allocator(); + _ = allocator; + for (self.commands.items) |command| { + switch (command) { + else => {}, + } + } +} + // Commands ==================================================================================================== pub inline fn fillBuffer(self: *Self, buffer: *Buffer, offset: vk.DeviceSize, size: vk.DeviceSize, data: u32) VkError!void { - if (offset >= buffer.size) return VkError.ValidationFailed; - if (size != vk.WHOLE_SIZE and (size == 0 or size > offset + buffer.size)) return VkError.ValidationFailed; - if ((size != vk.WHOLE_SIZE and @mod(size, 4) != 0) or @mod(offset, 4) != 0) return VkError.ValidationFailed; - if (!buffer.usage.transfer_dst_bit) return VkError.ValidationFailed; - if (buffer.memory == null) return VkError.ValidationFailed; - const allocator = self.host_allocator.allocator(); self.commands.append(allocator, .{ .FillBuffer = .{ .buffer = buffer, @@ -125,3 +133,13 @@ pub inline fn fillBuffer(self: *Self, buffer: *Buffer, offset: vk.DeviceSize, si } }) catch return VkError.OutOfHostMemory; try self.dispatch_table.fillBuffer(self, buffer, offset, size, data); } + +pub inline fn copyBuffer(self: *Self, src: *Buffer, dst: *Buffer, regions: []const vk.BufferCopy) VkError!void { + const allocator = self.host_allocator.allocator(); + self.commands.append(allocator, .{ .CopyBuffer = .{ + .src = src, + .dst = dst, + .regions = regions, + } }) catch return VkError.OutOfHostMemory; + try self.dispatch_table.copyBuffer(self, src, dst, regions); +} diff --git a/src/vulkan/Device.zig b/src/vulkan/Device.zig index 0e45cd8..5f0d511 100644 --- a/src/vulkan/Device.zig +++ b/src/vulkan/Device.zig @@ -14,6 +14,7 @@ const CommandBuffer = @import("CommandBuffer.zig"); const CommandPool = @import("CommandPool.zig"); const DeviceMemory = @import("DeviceMemory.zig"); const Fence = @import("Fence.zig"); +const Image = @import("Image.zig"); const Self = @This(); pub const ObjectType: vk.ObjectType = .device; @@ -35,6 +36,7 @@ pub const DispatchTable = struct { createBuffer: *const fn (*Self, std.mem.Allocator, *const vk.BufferCreateInfo) VkError!*Buffer, createCommandPool: *const fn (*Self, std.mem.Allocator, *const vk.CommandPoolCreateInfo) VkError!*CommandPool, createFence: *const fn (*Self, std.mem.Allocator, *const vk.FenceCreateInfo) VkError!*Fence, + createImage: *const fn (*Self, std.mem.Allocator, *const vk.ImageCreateInfo) VkError!*Image, destroy: *const fn (*Self, std.mem.Allocator) VkError!void, }; @@ -84,6 +86,10 @@ pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) VkError!void { try self.dispatch_table.destroy(self, allocator); } +pub inline fn allocateMemory(self: *Self, allocator: std.mem.Allocator, info: *const vk.MemoryAllocateInfo) VkError!*DeviceMemory { + return self.dispatch_table.allocateMemory(self, allocator, info); +} + pub inline fn createBuffer(self: *Self, allocator: std.mem.Allocator, info: *const vk.BufferCreateInfo) VkError!*Buffer { return self.dispatch_table.createBuffer(self, allocator, info); } @@ -96,6 +102,6 @@ pub inline fn createCommandPool(self: *Self, allocator: std.mem.Allocator, info: return self.dispatch_table.createCommandPool(self, allocator, info); } -pub inline fn allocateMemory(self: *Self, allocator: std.mem.Allocator, info: *const vk.MemoryAllocateInfo) VkError!*DeviceMemory { - return self.dispatch_table.allocateMemory(self, allocator, info); +pub inline fn createImage(self: *Self, allocator: std.mem.Allocator, info: *const vk.ImageCreateInfo) VkError!*Image { + return self.dispatch_table.createImage(self, allocator, info); } diff --git a/src/vulkan/Image.zig b/src/vulkan/Image.zig new file mode 100644 index 0000000..fa1c690 --- /dev/null +++ b/src/vulkan/Image.zig @@ -0,0 +1,71 @@ +const std = @import("std"); +const vk = @import("vulkan"); +const vku = @cImport({ + @cInclude("vulkan/utility/vk_format_utils.h"); +}); + +const VkError = @import("error_set.zig").VkError; +const DeviceMemory = @import("DeviceMemory.zig"); +const Device = @import("Device.zig"); + +const Self = @This(); +pub const ObjectType: vk.ObjectType = .image; + +owner: *Device, +image_type: vk.ImageType, +format: vk.Format, +extent: vk.Extent3D, +mip_levels: u32, +array_layers: u32, +samples: vk.SampleCountFlags, +tiling: vk.ImageTiling, +usage: vk.ImageUsageFlags, +memory: ?*DeviceMemory, +memory_offset: vk.DeviceSize, +allowed_memory_types: std.bit_set.IntegerBitSet(32), + +vtable: *const VTable, + +pub const VTable = struct { + destroy: *const fn (*Self, std.mem.Allocator) void, + getMemoryRequirements: *const fn (*Self, *vk.MemoryRequirements) void, +}; + +pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.ImageCreateInfo) VkError!Self { + _ = allocator; + return .{ + .owner = device, + .image_type = info.image_type, + .format = info.format, + .extent = info.extent, + .mip_levels = info.mip_levels, + .array_layers = info.array_layers, + .samples = info.samples, + .tiling = info.tiling, + .usage = info.usage, + .memory = null, + .memory_offset = 0, + .allowed_memory_types = std.bit_set.IntegerBitSet(32).initFull(), + .vtable = undefined, + }; +} + +pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void { + self.vtable.destroy(self, allocator); +} + +pub inline fn bindMemory(self: *Self, memory: *DeviceMemory, offset: vk.DeviceSize) VkError!void { + if (offset >= self.size or !self.allowed_memory_types.isSet(memory.memory_type_index)) { + return VkError.ValidationFailed; + } + self.memory = memory; + self.offset = offset; +} + +pub inline fn getMemoryRequirements(self: *Self, requirements: *vk.MemoryRequirements) void { + const pixel_size = vku.vkuFormatTexelBlockSize(@intCast(@intFromEnum(self.format))); + + requirements.size = self.extent.width * self.extent.height * self.extent.depth * pixel_size; + requirements.memory_type_bits = self.allowed_memory_types.mask; + self.vtable.getMemoryRequirements(self, requirements); +} diff --git a/src/vulkan/commands.zig b/src/vulkan/commands.zig index 73acfa0..f34e4ae 100644 --- a/src/vulkan/commands.zig +++ b/src/vulkan/commands.zig @@ -17,7 +17,7 @@ pub const CommandType = enum { pub const CommandCopyBuffer = struct { src: *Buffer, dst: *Buffer, - regions: []*const vk.BufferCopy, + regions: []const vk.BufferCopy, }; pub const CommandFillBuffer = struct { diff --git a/src/vulkan/error_set.zig b/src/vulkan/error_set.zig index 3bca5f6..7ee0f59 100644 --- a/src/vulkan/error_set.zig +++ b/src/vulkan/error_set.zig @@ -56,6 +56,10 @@ pub inline fn errorLogger(err: VkError) void { std.log.scoped(.errorLogger).err("Error logger catched a '{s}'", .{@errorName(err)}); } +pub inline fn errorLoggerContext(err: VkError, context: []const u8) void { + std.log.scoped(.errorLogger).err("Error logger catched a '{s}' in {s}", .{ @errorName(err), context }); +} + pub inline fn toVkResult(err: VkError) vk.Result { errorLogger(err); return switch (err) { diff --git a/src/vulkan/lib.zig b/src/vulkan/lib.zig index 9ca716f..fe46faa 100644 --- a/src/vulkan/lib.zig +++ b/src/vulkan/lib.zig @@ -4,10 +4,11 @@ const vk = @import("vulkan"); pub const commands = @import("commands.zig"); pub const lib_vulkan = @import("lib_vulkan.zig"); pub const logger = @import("logger.zig"); +pub const errors = @import("error_set.zig"); pub const Dispatchable = @import("Dispatchable.zig").Dispatchable; pub const NonDispatchable = @import("NonDispatchable.zig").NonDispatchable; -pub const VkError = @import("error_set.zig").VkError; +pub const VkError = errors.VkError; pub const VulkanAllocator = @import("VulkanAllocator.zig"); pub const Instance = @import("Instance.zig"); @@ -20,6 +21,7 @@ pub const CommandBuffer = @import("CommandBuffer.zig"); pub const CommandPool = @import("CommandPool.zig"); pub const DeviceMemory = @import("DeviceMemory.zig"); pub const Fence = @import("Fence.zig"); +pub const Image = @import("Image.zig"); pub const VULKAN_VENDOR_ID = @typeInfo(vk.VendorId).@"enum".fields[@typeInfo(vk.VendorId).@"enum".fields.len - 1].value + 1; diff --git a/src/vulkan/lib_vulkan.zig b/src/vulkan/lib_vulkan.zig index 071b188..ece4944 100644 --- a/src/vulkan/lib_vulkan.zig +++ b/src/vulkan/lib_vulkan.zig @@ -27,6 +27,7 @@ const CommandBuffer = @import("CommandBuffer.zig"); const CommandPool = @import("CommandPool.zig"); const DeviceMemory = @import("DeviceMemory.zig"); const Fence = @import("Fence.zig"); +const Image = @import("Image.zig"); fn entryPointBeginLogTrace(comptime scope: @Type(.enum_literal)) void { std.log.scoped(scope).debug("Calling {s}...", .{@tagName(scope)}); @@ -43,6 +44,7 @@ fn entryPointNotFoundErrorLog(comptime scope: @Type(.enum_literal), name: []cons } fn functionMapEntryPoint(comptime name: []const u8) struct { []const u8, vk.PfnVoidFunction } { + // Mapping 'vkFnName' to 'strollFnName' const stroll_name = std.fmt.comptimePrint("stroll{s}", .{name[2..]}); return if (std.meta.hasFn(@This(), name)) @@ -89,20 +91,24 @@ const device_pfn_map = std.StaticStringMap(vk.PfnVoidFunction).initComptime(.{ functionMapEntryPoint("vkAllocateMemory"), functionMapEntryPoint("vkBeginCommandBuffer"), functionMapEntryPoint("vkBindBufferMemory"), + functionMapEntryPoint("vkCmdCopyBuffer"), functionMapEntryPoint("vkCmdFillBuffer"), functionMapEntryPoint("vkCreateCommandPool"), functionMapEntryPoint("vkCreateBuffer"), functionMapEntryPoint("vkCreateFence"), + functionMapEntryPoint("vkCreateImage"), functionMapEntryPoint("vkDestroyBuffer"), functionMapEntryPoint("vkDestroyCommandPool"), - functionMapEntryPoint("vkDestroyFence"), functionMapEntryPoint("vkDestroyDevice"), + functionMapEntryPoint("vkDestroyFence"), + functionMapEntryPoint("vkDestroyImage"), functionMapEntryPoint("vkEndCommandBuffer"), functionMapEntryPoint("vkFreeCommandBuffers"), functionMapEntryPoint("vkFreeMemory"), + functionMapEntryPoint("vkGetBufferMemoryRequirements"), functionMapEntryPoint("vkGetDeviceQueue"), functionMapEntryPoint("vkGetFenceStatus"), - functionMapEntryPoint("vkGetBufferMemoryRequirements"), + functionMapEntryPoint("vkGetImageMemoryRequirements"), functionMapEntryPoint("vkMapMemory"), functionMapEntryPoint("vkUnmapMemory"), functionMapEntryPoint("vkResetCommandBuffer"), @@ -302,7 +308,15 @@ pub export fn strollGetPhysicalDeviceFeatures(p_physical_device: vk.PhysicalDevi features.* = physical_device.features; } -pub export fn strollGetPhysicalDeviceImageFormatProperties(p_physical_device: vk.PhysicalDevice, format: vk.Format, image_type: vk.ImageType, tiling: vk.ImageTiling, usage: vk.ImageUsageFlags, flags: vk.ImageCreateFlags, properties: *vk.ImageFormatProperties) callconv(vk.vulkan_call_conv) vk.Result { +pub export fn strollGetPhysicalDeviceImageFormatProperties( + p_physical_device: vk.PhysicalDevice, + format: vk.Format, + image_type: vk.ImageType, + tiling: vk.ImageTiling, + usage: vk.ImageUsageFlags, + flags: vk.ImageCreateFlags, + properties: *vk.ImageFormatProperties, +) callconv(vk.vulkan_call_conv) vk.Result { entryPointBeginLogTrace(.vkGetPhysicalDeviceImageFormatProperties); defer entryPointEndLogTrace(); @@ -491,6 +505,21 @@ pub export fn strollCreateFence(p_device: vk.Device, p_info: ?*const vk.FenceCre return .success; } +pub export fn strollCreateImage(p_device: vk.Device, p_info: ?*const vk.ImageCreateInfo, callbacks: ?*const vk.AllocationCallbacks, p_image: *vk.Image) callconv(vk.vulkan_call_conv) vk.Result { + entryPointBeginLogTrace(.vkCreateImage); + defer entryPointEndLogTrace(); + + const info = p_info orelse return .error_validation_failed; + if (info.s_type != .image_create_info) { + return .error_validation_failed; + } + const allocator = VulkanAllocator.init(callbacks, .object).allocator(); + const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err); + const image = device.createImage(allocator, info) catch |err| return toVkResult(err); + p_image.* = (NonDispatchable(Image).wrap(allocator, image) catch |err| return toVkResult(err)).toVkHandle(vk.Image); + return .success; +} + pub export fn strollDestroyBuffer(p_device: vk.Device, p_buffer: vk.Buffer, callbacks: ?*const vk.AllocationCallbacks) callconv(vk.vulkan_call_conv) void { entryPointBeginLogTrace(.vkDestroyBuffer); defer entryPointEndLogTrace(); @@ -537,6 +566,17 @@ pub export fn strollDestroyFence(p_device: vk.Device, p_fence: vk.Fence, callbac non_dispatchable.intrusiveDestroy(allocator); } +pub export fn strollDestroyImage(p_device: vk.Device, p_image: vk.Image, callbacks: ?*const vk.AllocationCallbacks) callconv(vk.vulkan_call_conv) void { + entryPointBeginLogTrace(.vkDestroyImage); + defer entryPointEndLogTrace(); + + Dispatchable(Device).checkHandleValidity(p_device) catch |err| return errorLogger(err); + + const allocator = VulkanAllocator.init(callbacks, .object).allocator(); + const non_dispatchable = NonDispatchable(Image).fromHandle(p_image) catch |err| return errorLogger(err); + non_dispatchable.intrusiveDestroy(allocator); +} + pub export fn strollFreeCommandBuffers(p_device: vk.Device, p_pool: vk.CommandPool, count: u32, p_cmds: [*]const vk.CommandBuffer) callconv(vk.vulkan_call_conv) void { entryPointBeginLogTrace(.vkFreeCommandBuffers); defer entryPointEndLogTrace(); @@ -559,6 +599,16 @@ pub export fn strollFreeMemory(p_device: vk.Device, p_memory: vk.DeviceMemory, c non_dispatchable.intrusiveDestroy(allocator); } +pub export fn strollGetBufferMemoryRequirements(p_device: vk.Device, p_buffer: vk.Buffer, requirements: *vk.MemoryRequirements) callconv(vk.vulkan_call_conv) void { + entryPointBeginLogTrace(.vkGetBufferMemoryRequirements); + defer entryPointEndLogTrace(); + + Dispatchable(Device).checkHandleValidity(p_device) catch |err| return errorLogger(err); + + const buffer = NonDispatchable(Buffer).fromHandleObject(p_buffer) catch |err| return errorLogger(err); + buffer.getMemoryRequirements(requirements); +} + pub export fn strollGetDeviceProcAddr(p_device: vk.Device, p_name: ?[*:0]const u8) callconv(vk.vulkan_call_conv) vk.PfnVoidFunction { if (lib.getLogVerboseLevel() == .TooMuch) { entryPointBeginLogTrace(.vkGetDeviceProcAddr); @@ -605,14 +655,14 @@ pub export fn strollGetFenceStatus(p_device: vk.Device, p_fence: vk.Fence) callc return .success; } -pub export fn strollGetBufferMemoryRequirements(p_device: vk.Device, p_buffer: vk.Buffer, requirements: *vk.MemoryRequirements) callconv(vk.vulkan_call_conv) void { - entryPointBeginLogTrace(.vkGetBufferMemoryRequirements); +pub export fn strollGetImageMemoryRequirements(p_device: vk.Device, p_image: vk.Image, requirements: *vk.MemoryRequirements) callconv(vk.vulkan_call_conv) void { + entryPointBeginLogTrace(.vkGetImageMemoryRequirements); defer entryPointEndLogTrace(); Dispatchable(Device).checkHandleValidity(p_device) catch |err| return errorLogger(err); - const buffer = NonDispatchable(Buffer).fromHandleObject(p_buffer) catch |err| return errorLogger(err); - buffer.getMemoryRequirements(requirements); + const image = NonDispatchable(Image).fromHandleObject(p_image) catch |err| return errorLogger(err); + image.getMemoryRequirements(requirements); } pub export fn strollMapMemory(p_device: vk.Device, p_memory: vk.DeviceMemory, offset: vk.DeviceSize, size: vk.DeviceSize, _: vk.MemoryMapFlags, pp_data: *?*anyopaque) callconv(vk.vulkan_call_conv) vk.Result { @@ -678,6 +728,16 @@ pub export fn strollBeginCommandBuffer(p_cmd: vk.CommandBuffer, p_info: ?*const return .success; } +pub export fn strollCmdCopyBuffer(p_cmd: vk.CommandBuffer, p_src: vk.Buffer, p_dst: vk.Buffer, count: u32, regions: [*]const vk.BufferCopy) callconv(vk.vulkan_call_conv) void { + entryPointBeginLogTrace(.vkCmdCopyBuffer); + defer entryPointEndLogTrace(); + + const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err); + const src = NonDispatchable(Buffer).fromHandleObject(p_src) catch |err| return errorLogger(err); + const dst = NonDispatchable(Buffer).fromHandleObject(p_dst) catch |err| return errorLogger(err); + cmd.copyBuffer(src, dst, regions[0..count]) catch |err| return errorLogger(err); +} + pub export fn strollCmdFillBuffer(p_cmd: vk.CommandBuffer, p_buffer: vk.Buffer, offset: vk.DeviceSize, size: vk.DeviceSize, data: u32) callconv(vk.vulkan_call_conv) void { entryPointBeginLogTrace(.vkCmdFillBuffer); defer entryPointEndLogTrace(); diff --git a/test/c/main.c b/test/c/main.c index 9cf0496..a5f48c9 100644 --- a/test/c/main.c +++ b/test/c/main.c @@ -20,6 +20,19 @@ #define KVF_NO_KHR #include +void CreateAndBindMemoryToBuffer(VkPhysicalDevice physical_device, VkDevice device, VkBuffer buffer, VkDeviceMemory* memory , VkDeviceSize size, VkMemoryPropertyFlags props) +{ + VkMemoryRequirements requirements; + vkGetBufferMemoryRequirements(device, buffer, &requirements); + + VkMemoryAllocateInfo alloc_info = {0}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = size; + alloc_info.memoryTypeIndex = kvfFindMemoryType(physical_device, requirements.memoryTypeBits, props); + kvfCheckVk(vkAllocateMemory(device, &alloc_info, NULL, memory)); + kvfCheckVk(vkBindBufferMemory(device, buffer, *memory, 0)); +} + int main(void) { volkInitialize(); @@ -52,16 +65,15 @@ int main(void) VkDevice device = kvfCreateDevice(physical_device, NULL, 0, NULL); volkLoadDevice(device); - VkBuffer buffer = kvfCreateBuffer(device, VK_BUFFER_USAGE_TRANSFER_DST_BIT, 256); + VkBuffer buffer = kvfCreateBuffer(device, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, 256); + VkDeviceMemory memory; + CreateAndBindMemoryToBuffer(physical_device, device, buffer, &memory, 256, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VkDeviceMemory memory = VK_NULL_HANDLE; - VkMemoryAllocateInfo alloc_info = {0}; - alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - alloc_info.allocationSize = 256; - alloc_info.memoryTypeIndex = 0; - kvfCheckVk(vkAllocateMemory(device, &alloc_info, NULL, &memory)); + VkBuffer buffer2 = kvfCreateBuffer(device, VK_BUFFER_USAGE_TRANSFER_DST_BIT, 256); + VkDeviceMemory memory2; + CreateAndBindMemoryToBuffer(physical_device, device, buffer2, &memory2, 256, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); - kvfCheckVk(vkBindBufferMemory(device, buffer, memory, 0)); + VkImage image = kvfCreateImage(device, 256, 256, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, KVF_IMAGE_COLOR); VkQueue queue = kvfGetDeviceQueue(device, KVF_GRAPHICS_QUEUE); VkFence fence = kvfCreateFence(device); @@ -71,7 +83,13 @@ int main(void) kvfBeginCommandBuffer(cmd, 0); { - vkCmdFillBuffer(cmd, buffer, 0, VK_WHOLE_SIZE, 0x12ABCDEF); + vkCmdFillBuffer(cmd, buffer, 0, VK_WHOLE_SIZE, 0x600DCAFE); + + VkBufferCopy region = {0}; + region.srcOffset = 0; + region.dstOffset = 0; + region.size = 256; + vkCmdCopyBuffer(cmd, buffer, buffer2, 1, ®ion); } kvfEndCommandBuffer(cmd); @@ -79,15 +97,19 @@ int main(void) kvfWaitForFence(device, fence); uint32_t* map = NULL; - kvfCheckVk(vkMapMemory(device, memory, 0, VK_WHOLE_SIZE, 0, (void**)&map)); + kvfCheckVk(vkMapMemory(device, memory2, 0, VK_WHOLE_SIZE, 0, (void**)&map)); for(size_t i = 0; i < 64; i++) - printf("0x%X%s", map[i], (i + 1 == 64 ? "" : " - ")); + printf("0x%X ", map[i]); puts(""); - vkUnmapMemory(device, memory); + vkUnmapMemory(device, memory2); kvfDestroyFence(device, fence); kvfDestroyBuffer(device, buffer); vkFreeMemory(device, memory, NULL); + kvfDestroyBuffer(device, buffer2); + vkFreeMemory(device, memory2, NULL); + + kvfDestroyImage(device, image); kvfDestroyDevice(device); kvfDestroyInstance(instance);