From 92535cc66e440a080d94e2e8c73485a9c4180627 Mon Sep 17 00:00:00 2001 From: johnnyboi12 <54293281+JohnSmoit@users.noreply.github.com> Date: Wed, 6 Aug 2025 16:13:11 -0700 Subject: [PATCH 1/2] feat(vulkan): minor adjustments to keep internal functions in-line with the new API --- src/api/depth.zig | 6 ++---- src/api/graphics_pipeline.zig | 4 ++-- src/api/image.zig | 12 ++++++------ src/api/index_buffer.zig | 24 +++++++++--------------- src/api/vertex_buffer.zig | 24 ++++++++---------------- 5 files changed, 27 insertions(+), 43 deletions(-) diff --git a/src/api/depth.zig b/src/api/depth.zig index 50a6da4..9c076b2 100644 --- a/src/api/depth.zig +++ b/src/api/depth.zig @@ -17,11 +17,9 @@ dev: *const DeviceHandler, pub fn init(ctx: *const Context, dimensions: vk.Extent2D) !Self { const dev: *const DeviceHandler = ctx.env(.dev); log.debug("Chosen depth format: {s}", .{@tagName(try dev.findDepthFormat())}); - const image = try Image.init(ctx, &.{ + const image = try Image.init(ctx, .{ .format = try dev.findDepthFormat(), - .height = dimensions.height, - .width = dimensions.width, - + .extent = dimensions, .mem_flags = .{ .device_local_bit = true }, .tiling = .optimal, .usage = .{ .depth_stencil_attachment_bit = true }, diff --git a/src/api/graphics_pipeline.zig b/src/api/graphics_pipeline.zig index 34ff985..8ee7a1c 100644 --- a/src/api/graphics_pipeline.zig +++ b/src/api/graphics_pipeline.zig @@ -72,9 +72,9 @@ pub const FixedFunctionState = struct { self.vertex_input = vk.PipelineVertexInputStateCreateInfo{ .vertex_binding_description_count = if (config.vertex_binding != null) 1 else 0, - .p_vertex_binding_descriptions = if (config.vertex_binding) |vb| util.asManyPtr( + .p_vertex_binding_descriptions = if (config.vertex_binding) |*vb| util.asManyPtr( vk.VertexInputBindingDescription, - &vb, + vb, ) else null, .vertex_attribute_description_count = @intCast(config.vertex_attribs.len), .p_vertex_attribute_descriptions = config.vertex_attribs.ptr, diff --git a/src/api/image.zig b/src/api/image.zig index cd245cd..a485795 100644 --- a/src/api/image.zig +++ b/src/api/image.zig @@ -432,7 +432,7 @@ pub fn init(ctx: *const Context, config: Config) !Self { /// creates an image from an image file. /// Image parameters will be tuned for usage as a texture -/// more so than a general purpose image for now... +/// more so than a general purpose image pub fn fromFile(ctx: *const Context, allocator: Allocator, path: []const u8) !Self { var image_data = rsh.loadImageFile(path, allocator) catch |err| { log.err("Failed to load image: {!}", .{err}); @@ -446,13 +446,13 @@ pub fn fromFile(ctx: *const Context, allocator: Allocator, path: []const u8) !Se try staging_buffer.buffer().setData(image_data.pixels.asBytes().ptr); defer staging_buffer.deinit(); - const image = try Self.init(ctx, &.{ + const image = try Self.init(ctx, .{ .format = .r8g8b8a8_srgb, .tiling = .optimal, - - .height = @intCast(image_data.height), - .width = @intCast(image_data.width), - + .extent = .{ + .height = @intCast(image_data.height), + .width = @intCast(image_data.width), + }, .usage = .{ .transfer_dst_bit = true, .sampled_bit = true }, .mem_flags = .{ .device_local_bit = true }, .staging_buf = &staging_buffer, diff --git a/src/api/index_buffer.zig b/src/api/index_buffer.zig index fb98dd6..4f1b18b 100644 --- a/src/api/index_buffer.zig +++ b/src/api/index_buffer.zig @@ -1,14 +1,15 @@ const vk = @import("vulkan"); -const buf = @import("buffer.zig"); +const buf_api = @import("buffer.zig"); -const GenericBuffer = buf.GenericBuffer; +const GenericBuffer = buf_api.GenericBuffer; const CommandBuffer = @import("command_buffer.zig"); const DeviceHandler = @import("base.zig").DeviceHandler; const Context = @import("../context.zig"); -const AnyBuffer = buf.AnyBuffer; +const AnyBuffer = buf_api.AnyBuffer; fn getIndexType(T: type) vk.IndexType { return switch (T) { + u8 => .uint8, u16 => .uint16, u32 => .uint32, else => @compileError("Invalid index format (TODO: Better deduction)"), @@ -37,8 +38,7 @@ pub fn IndexBuffer(T: type) type { }; } - pub fn setData(ctx: *anyopaque, data: *const anyopaque) !void { - const self: *Self = @ptrCast(@alignCast(ctx)); + pub fn setData(self: *Self, data: *const anyopaque) !void { const elem: []const T = @as([*]const T, @ptrCast(@alignCast(data)))[0..self.buf.size]; var staging = try self.buf.createStaging(); @@ -49,16 +49,14 @@ pub fn IndexBuffer(T: type) type { @memcpy(staging_mem, elem); - try buf.copy(staging.buffer(), self.buffer(), self.buf.dev); + try buf_api.copy(staging.buffer(), self.buffer(), self.buf.dev); } - pub fn deinit(ctx: *anyopaque) void { - const self: *Self = @ptrCast(@alignCast(ctx)); + pub fn deinit(self: *Self) void { self.buf.deinit(); } - pub fn bind(ctx: *anyopaque, cmd_buf: *const CommandBuffer) void { - const self: *Self = @ptrCast(@alignCast(ctx)); + pub fn bind(self: *Self, cmd_buf: *const CommandBuffer) void { self.buf.dev.pr_dev.cmdBindIndexBuffer(cmd_buf.h_cmd_buffer, self.buf.h_buf, 0, index_type); } @@ -68,11 +66,7 @@ pub fn IndexBuffer(T: type) type { .handle = self.buf.h_buf, .ptr = self, .size = self.buf.bytesSize(), - .vtable = &.{ - .bind = bind, - .setData = setData, - .deinit = deinit, - }, + .vtable = buf_api.AutoVTable(Self), }; } }; diff --git a/src/api/vertex_buffer.zig b/src/api/vertex_buffer.zig index 02ad17f..bf73073 100644 --- a/src/api/vertex_buffer.zig +++ b/src/api/vertex_buffer.zig @@ -6,7 +6,7 @@ const vk = @import("vulkan"); const meth = @import("../math.zig"); const util = @import("../util.zig"); -const buf = @import("buffer.zig"); +const buf_api = @import("buffer.zig"); const Layout = union(enum) { Struct: StructInfo, @@ -16,7 +16,7 @@ const Layout = union(enum) { const DeviceHandler = @import("base.zig").DeviceHandler; const CommandBuffer = @import("command_buffer.zig"); const Context = @import("../context.zig"); -const AnyBuffer = buf.AnyBuffer; +const AnyBuffer = buf_api.AnyBuffer; fn validateType(comptime T: type) Layout { const info = @typeInfo(T); @@ -30,7 +30,6 @@ fn validateType(comptime T: type) Layout { }; } -// This is extremely jank when taking index buffers into account FIX once we get to uniform buffers... fn getBindingDescription(T: type) vk.VertexInputBindingDescription { _ = validateType(T); @@ -86,7 +85,7 @@ pub fn VertexInputDescription(T: type) type { pub fn VertexBuffer(T: type) type { return struct { const Self = @This(); - const Inner = buf.GenericBuffer(T, .{ + const Inner = buf_api.GenericBuffer(T, .{ .memory = .{ .device_local_bit = true }, .usage = .{ .transfer_dst_bit = true, @@ -106,8 +105,7 @@ pub fn VertexBuffer(T: type) type { }; } - pub fn setData(ctx: *anyopaque, data: *const anyopaque) !void { - const self: *Self = @ptrCast(@alignCast(ctx)); + pub fn setData(self: *Self, data: *const anyopaque) !void { const elem: []const T = @as([*]const T, @ptrCast(@alignCast(data)))[0..self.buf.size]; var staging = try self.buf.createStaging(); @@ -118,11 +116,10 @@ pub fn VertexBuffer(T: type) type { @memcpy(staging_mem, elem); - try buf.copy(staging.buffer(), self.buffer(), self.buf.dev); + try buf_api.copy(staging.buffer(), self.buffer(), self.buf.dev); } - pub fn bind(ctx: *anyopaque, cmd_buf: *const CommandBuffer) void { - const self: *Self = @ptrCast(@alignCast(ctx)); + pub fn bind(self: *Self, cmd_buf: *const CommandBuffer) void { self.buf.dev.pr_dev.cmdBindVertexBuffers( cmd_buf.h_cmd_buffer, 0, @@ -138,16 +135,11 @@ pub fn VertexBuffer(T: type) type { .handle = self.buf.h_buf, .ptr = self, .size = self.buf.bytesSize(), - .vtable = &.{ - .bind = bind, - .setData = setData, - .deinit = deinit, - }, + .vtable = buf_api.AutoVTable(Self), }; } - pub fn deinit(ctx: *anyopaque) void { - const self: *Self = @ptrCast(@alignCast(ctx)); + pub fn deinit(self: *Self) void { self.buf.deinit(); } }; From 1b73d91299976b28e90c21ae1584c4ae2ead07f1 Mon Sep 17 00:00:00 2001 From: johnnyboi12 <54293281+JohnSmoit@users.noreply.github.com> Date: Wed, 6 Aug 2025 16:13:40 -0700 Subject: [PATCH 2/2] refactor(sample): refactor basic planes to make use of the new vulkan API --- samples/basic_planes.zig | 157 ++++++++++++++++----------------------- 1 file changed, 66 insertions(+), 91 deletions(-) diff --git a/samples/basic_planes.zig b/samples/basic_planes.zig index d66c596..485da4b 100644 --- a/samples/basic_planes.zig +++ b/samples/basic_planes.zig @@ -33,8 +33,6 @@ var external_extensions: ?[][*:0]const u8 = null; var context: *Context = undefined; -var graphics_queue: api.GraphicsQueue = undefined; -var present_queue: api.PresentQueue = undefined; var swapchain: api.Swapchain = undefined; var window_handle: *glfw.Window = undefined; @@ -52,9 +50,9 @@ var command_buffer: api.CommandBuffer = undefined; const validation_layers: [1][*:0]const u8 = .{"VK_LAYER_KHRONOS_validation"}; const device_extensions = [_][*:0]const u8{api.extensions.khr_swapchain.name}; -var render_finished_semaphore: api.Semaphore = .null_handle; -var image_finished_semaphore: api.Semaphore = .null_handle; -var present_finished_fence: api.Fence = .null_handle; +var render_finished_semaphore: api.Semaphore = undefined; +var image_finished_semaphore: api.Semaphore = undefined; +var present_finished_fence: api.Fence = undefined; var dev: *const api.DeviceHandler = undefined; var pr_dev: *const api.DeviceInterface = undefined; @@ -81,11 +79,9 @@ var vertex_buffer: VertexBuffer = undefined; var index_buffer: IndexBuffer = undefined; var uniform_buffer: UniformBuffer = undefined; -var vb_interface: api.BufInterface = undefined; -var ib_interface: api.BufInterface = undefined; - var test_descriptor: api.Descriptor = undefined; -var test_tex: api.TexImage = undefined; +var test_tex: api.Image = undefined; +var test_tex_view: api.Image.View = undefined; fn glfwErrorCallback(code: c_int, desc: [*c]const u8) callconv(.c) void { glfw_log.err("error code {d} -- Message: {s}", .{ code, desc }); @@ -120,12 +116,6 @@ fn init(allocator: Allocator) !void { dev = context.env(.dev); pr_dev = context.env(.di); - graphics_queue = try api.GraphicsQueue.init(context); - errdefer graphics_queue.deinit(); - - present_queue = try api.PresentQueue.init(context); - errdefer present_queue.deinit(); - swapchain = try api.Swapchain.init(context, allocator, .{ .requested_present_mode = .mailbox_khr, .requested_format = .{ @@ -139,16 +129,20 @@ fn init(allocator: Allocator) !void { }); errdefer swapchain.deinit(); - const vert_shader_module = try api.ShaderModule.fromSourceFile(context, allocator, .{ - .stage = .Vertex, - .filename = "shaders/shader.vert", - }); + const vert_shader_module = try api.ShaderModule.fromSourceFile( + context, + allocator, + "shaders/shader.vert", + .Vertex, + ); defer vert_shader_module.deinit(); - const frag_shader_module = try api.ShaderModule.fromSourceFile(context, allocator, .{ - .stage = .Fragment, - .filename = "shaders/shader.frag", - }); + const frag_shader_module = try api.ShaderModule.fromSourceFile( + context, + allocator, + "shaders/shader.frag", + .Fragment, + ); defer frag_shader_module.deinit(); const dynamic_states = [_]api.DynamicState{ @@ -156,25 +150,29 @@ fn init(allocator: Allocator) !void { .scissor, }; - test_tex = try api.TexImage.fromFile(context, allocator, "textures/shrek.png"); + test_tex = try api.Image.fromFile(context, allocator, "textures/shrek.png"); errdefer test_tex.deinit(); + test_tex_view = try test_tex.createView(.{ .color_bit = true }); + errdefer test_tex_view.deinit(); + uniform_buffer = try UniformBuffer.create(context); - errdefer uniform_buffer.buffer().deinit(); + errdefer uniform_buffer.deinit(); - const desc_bindings = [_]api.ResolvedDescriptorBinding{ .{ + test_descriptor = try api.Descriptor.init(context, allocator, .{ .bindings = &.{ .{ .stages = .{ .vertex_bit = true }, .data = .{ .Uniform = uniform_buffer.buffer() }, }, .{ .stages = .{ .fragment_bit = true }, - .data = .{ .Sampler = &test_tex }, - } }; - - test_descriptor = try api.Descriptor.init(context, allocator, .{ - .bindings = desc_bindings[0..], - }); + .data = .{ .Sampler = .{ + .view = test_tex_view.h_view, + .sampler = try test_tex.getSampler(.{}), + } }, + } } }); errdefer test_descriptor.deinit(); + root_log.debug("vertex stride: {d}", .{VertexBuffer.Description.vertex_desc.stride}); + var fixed_function_state = api.FixedFunctionState{}; fixed_function_state.init_self(context, &.{ .viewport = .{ .Swapchain = &swapchain }, @@ -182,11 +180,11 @@ fn init(allocator: Allocator) !void { .deez_nuts = true, .vertex_binding = VertexBuffer.Description.vertex_desc, .vertex_attribs = VertexBuffer.Description.attrib_desc, - .descriptors = &[_]api.vk.DescriptorSetLayout{test_descriptor.h_desc_layout}, + .descriptors = &.{test_descriptor.h_desc_layout}, }); defer fixed_function_state.deinit(); - renderpass = try api.RenderPass.initAlloc(context, scratch, &[_]api.RenderPass.ConfigEntry{ + renderpass = try api.RenderPass.initAlloc(context, scratch, &.{ .{ .attachment = .{ .format = swapchain.surface_format.format, @@ -216,37 +214,33 @@ fn init(allocator: Allocator) !void { }); errdefer renderpass.deinit(); - graphics_pipeline = try api.GraphicsPipeline.init(context, &.{ + graphics_pipeline = try api.GraphicsPipeline.init(context, .{ .renderpass = &renderpass, .fixed_functions = &fixed_function_state, - .shader_stages = &[_]api.ShaderModule{ vert_shader_module, frag_shader_module }, + .shader_stages = &.{ vert_shader_module, frag_shader_module }, }, scratch); errdefer graphics_pipeline.deinit(); depth_image = try api.DepthImage.init(context, swapchain.extent); errdefer depth_image.deinit(); - framebuffers = try api.FrameBuffer.initAlloc(context, allocator, &.{ + framebuffers = try api.FrameBuffer.initAlloc(context, allocator, .{ .renderpass = &renderpass, - .image_views = swapchain.images, + .swapchain = &swapchain, .depth_view = depth_image.view.h_view, - .extent = api.vk.Rect2D{ - .extent = swapchain.extent, - .offset = .{ .x = 0, .y = 0 }, - }, }); errdefer framebuffers.deinit(); // create a bunch of stupid synchronization objects and stuff // I didn't really integrate these into the wrapper structs cuz i kinda don't really // know where to put them - render_finished_semaphore = try pr_dev.createSemaphore(&.{}, null); - image_finished_semaphore = try pr_dev.createSemaphore(&.{}, null); - present_finished_fence = try pr_dev.createFence(&.{ - .flags = .{ .signaled_bit = true }, - }, null); + render_finished_semaphore = try api.Semaphore.init(context); + image_finished_semaphore = try api.Semaphore.init(context); + present_finished_fence = try api.Fence.init(context, true); - command_buffer = try api.CommandBuffer.init(context); + command_buffer = try api.CommandBuffer.init(context, .{ + .src_queue_family = .Graphics, + }); const vertex_data = [_]TestVertexInput{ .{ .position = math.vec(.{ -0.5, 0.0, -0.5 }), .color = math.vec(.{ 1.0, 0.0, 0.0 }), .uv = math.vec(.{ 1.0, 0.0 }) }, @@ -265,10 +259,7 @@ fn init(allocator: Allocator) !void { return err; }; - vb_interface = vertex_buffer.buffer(); - errdefer vb_interface.deinit(); - - vb_interface.setData(vertex_data[0..]) catch |err| { + vertex_buffer.setData(vertex_data[0..]) catch |err| { root_log.err("Failed to load vertex data: {!}", .{err}); return err; }; @@ -280,10 +271,7 @@ fn init(allocator: Allocator) !void { return err; }; - ib_interface = index_buffer.buffer(); - errdefer ib_interface.deinit(); - - ib_interface.setData(index_data[0..]) catch |err| { + index_buffer.setData(index_data[0..]) catch |err| { root_log.err("Failed to load index buffer data: {!}", .{err}); return err; }; @@ -326,32 +314,23 @@ fn updateUniforms() !void { } fn mainLoop() !void { - _ = pr_dev.waitForFences( - 1, - util.asManyPtr(api.Fence, &present_finished_fence), - vk_true, - std.math.maxInt(u64), - ) catch {}; // fuck rendering errors + try present_finished_fence.wait(); + try present_finished_fence.reset(); - pr_dev.resetFences( - 1, - util.asManyPtr(api.Fence, &present_finished_fence), - ) catch {}; - - const current_image = try swapchain.getNextImage(image_finished_semaphore, null); + _ = try swapchain.getNextImage(image_finished_semaphore.h_sem, null); try command_buffer.reset(); try command_buffer.begin(); - renderpass.begin(&command_buffer, &framebuffers, current_image); + renderpass.begin(&command_buffer, &framebuffers, swapchain.image_index); try updateUniforms(); graphics_pipeline.bind(&command_buffer); - vb_interface.bind(&command_buffer); - ib_interface.bind(&command_buffer); - test_descriptor.bind(&command_buffer, graphics_pipeline.h_pipeline_layout); + vertex_buffer.bind(&command_buffer); + index_buffer.bind(&command_buffer); + test_descriptor.bind(&command_buffer, graphics_pipeline.h_pipeline_layout, .{}); dev.drawIndexed(&command_buffer, @intCast(index_buffer.buf.size), 1, 0, 0, 0); @@ -359,18 +338,15 @@ fn mainLoop() !void { try command_buffer.end(); - try graphics_queue.submit( - &command_buffer, - image_finished_semaphore, - render_finished_semaphore, - present_finished_fence, - ); + try command_buffer.submit(.Graphics, .{ + .sem_sig = render_finished_semaphore.h_sem, + .sem_wait = image_finished_semaphore.h_sem, + .fence_wait = present_finished_fence.h_fence, + }); - try present_queue.present( - &swapchain, - current_image, - render_finished_semaphore, - ); + try context.presentFrame(&swapchain, .{ + .sem_wait = render_finished_semaphore.h_sem, + }); } fn deinit() void { @@ -378,16 +354,17 @@ fn deinit() void { root_log.err("Failed to wait on device", .{}); }; // destroy synchronization objects - dev.pr_dev.destroySemaphore(render_finished_semaphore, null); - dev.pr_dev.destroySemaphore(image_finished_semaphore, null); - dev.pr_dev.destroyFence(present_finished_fence, null); + render_finished_semaphore.deinit(); + image_finished_semaphore.deinit(); + present_finished_fence.deinit(); + test_tex_view.deinit(); test_tex.deinit(); - ib_interface.deinit(); - vb_interface.deinit(); + index_buffer.deinit(); + vertex_buffer.deinit(); - uniform_buffer.buffer().deinit(); + uniform_buffer.deinit(); depth_image.deinit(); framebuffers.deinit(); @@ -395,8 +372,6 @@ fn deinit() void { test_descriptor.deinit(); renderpass.deinit(); swapchain.deinit(); - graphics_queue.deinit(); - present_queue.deinit(); context.deinit(); }