From fd311b97e711ec45e500a61175e2948e1d4ea908 Mon Sep 17 00:00:00 2001 From: Szymon Nowakowski Date: Fri, 22 May 2026 23:34:25 +0200 Subject: [PATCH] Minor refactors --- src/Game.zig | 14 +-- src/engine/Atom.zig | 7 +- src/engine/Atoms.zig | 30 +++--- src/engine/CommandBuffer.zig | 204 ++++++++++++++++++++--------------- src/engine/Engine.zig | 2 +- src/engine/Swapchain.zig | 6 +- 6 files changed, 145 insertions(+), 118 deletions(-) diff --git a/src/Game.zig b/src/Game.zig index 0e6e87d..9c6fe77 100644 --- a/src/Game.zig +++ b/src/Game.zig @@ -899,7 +899,7 @@ fn render(self: *Game) !void { try command_buffer.beginCommandBuffer(); { - const swapchain_image = &swapchain.swapchain_images[swapchain.image_index.?]; + const swapchain_image = swapchain.getCurrentSwapchainImage(); command_buffer.pipelineBarrier(.{ .src_stage_mask = .{ .color_attachment_output_bit = true }, @@ -988,31 +988,25 @@ fn render(self: *Game) !void { }, }); - command_buffer.setViewport(0, &.{ - .{ + command_buffer.setViewport(0, .{ .x = 0, .y = 0, .width = @floatFromInt(extent.width), .height = @floatFromInt(extent.height), .min_depth = 0, .max_depth = 1, - }, }); - command_buffer.setScissor(0, &.{ - .{ + command_buffer.setScissor(0, .{ .offset = .{ .x = 0, .y = 0 }, .extent = extent, - }, }); // --- RENDER 3D SCENE --- command_buffer.bindPipeline(.graphics, self.pipeline); - try command_buffer.bindVertexBuffers(0, &.{ - .{ + try command_buffer.bindVertexBuffer(0, .{ .buffer = self.vertex_buffer.buffer, .offset = 0, - }, }); command_buffer.bindIndexBuffer(self.index_buffer.buffer, 0, .uint16); command_buffer.bindDescriptorSet(.graphics, self.pipeline_layout, 0, self.global_descriptor_set, null); diff --git a/src/engine/Atom.zig b/src/engine/Atom.zig index 6909ba9..7e17253 100644 --- a/src/engine/Atom.zig +++ b/src/engine/Atom.zig @@ -2,8 +2,13 @@ const std = @import("std"); const ctx = @import("../AppContext.zig"); +/// Interned string ID. A string can be converted to a stable integer constant, +/// called an *atom*. The value of an atom for a given string is guaranteed to +/// be stable throughout a program's runtime, but not across different runs. +/// There can be no more than 2¹⁶ atoms. pub const Atom = enum(u16) { - // VOLATILE Synchronize explicit values with `Atoms.init` implementation. + // VOLATILE When modifying the list of explicitly defined atoms (i.e. any + // explicit enum value), we need to update `Atoms.init` implementation. /// Atom representing an empty string, i.e. `""`. empty, diff --git a/src/engine/Atoms.zig b/src/engine/Atoms.zig index a545816..8ad410c 100644 --- a/src/engine/Atoms.zig +++ b/src/engine/Atoms.zig @@ -3,13 +3,13 @@ //! guaranteed to be stable throughout a program's runtime, but not across //! different runs. There can be no more than 2¹⁶ atoms. //! -//! Use this module to convert string IDs into numbers, so that they can be -//! compared more easily. The users of this module should instead import -//! `@import("Atom.zig").Atom` and use the methods available in the `Atom` type. +//! The users should instead import `@import("Atom.zig").Atom` and use the +//! methods available in the `Atom` type instead of accessing this module +//! directly. //! -//! This module is intended to be initialized once and persist until the end of -//! the whole application. Trying to use it in any other way will result in -//! weird behavior. +//! This module is intended to be initialized once and to persist until the end +//! of the whole program's runtime. Trying to use it in any other way will +//! result in weird behavior. const Atoms = @This(); const std = @import("std"); @@ -40,7 +40,9 @@ pub fn init() !*Atoms { .mutex = .init, }; - // VOLATILE Synchronize with explicit values on top of `Atom` type. + // VOLATILE The initial contents of `atoms.map` and `atoms.array` must + // correspond to explicitly defined values at the top of the `Atom` type. + try atoms.map.put(allocator_general, "", .empty); try atoms.array.append(allocator_general, ""); @@ -49,18 +51,14 @@ pub fn init() !*Atoms { pub fn deinit(self: *Atoms) void { const allocator_general = ctx.allocator_general; - const io = ctx.io; - { - // VOLATILE Unlock mutex before `self.* = undefined` line. - self.mutex.lockUncancelable(io); - defer self.mutex.unlock(io); + std.log.scoped(.deinit).debug("Deinitializing {*}", .{self}); - std.log.scoped(.deinit).debug("Deinitializing {*}", .{self}); + // No waiting; if atoms are in use while deinitializing, something is wrong. + std.debug.assert(self.mutex.tryLock()); - self.map.deinit(allocator_general); - self.array.deinit(allocator_general); - } + self.map.deinit(allocator_general); + self.array.deinit(allocator_general); self.* = undefined; } diff --git a/src/engine/CommandBuffer.zig b/src/engine/CommandBuffer.zig index 72447e0..defd710 100644 --- a/src/engine/CommandBuffer.zig +++ b/src/engine/CommandBuffer.zig @@ -21,10 +21,7 @@ queue_type: QueueType, pub fn init(queue_type: QueueType) !CommandBuffer { const engine = ctx.engine; - const handle = try engine.allocateCommandBuffer(.{ - .queue_type = queue_type, - .level = .primary, - }); + const handle = try engine.allocateCommandBuffer(.{ .queue_type = queue_type }); return .{ .proxy = .init(handle, engine.device.wrapper), .queue_type = queue_type, @@ -49,6 +46,40 @@ pub fn submit(self: CommandBuffer, submit_info: Engine.SubmitInfo) !void { // --- VULKAN WRAPPERS --------------------------------------------------------- +fn ToVkType(comptime T: type) type { + if (!std.meta.hasFn(T, "toVk")) { + @compileError("Type " ++ T ++ " has no toVk method."); + } + + const to_vk_type_info = @typeInfo(@TypeOf(@field(T, "toVk"))); + return to_vk_type_info.@"fn".return_type.?; +} + +fn toVkOptional(comptime T: type, maybe_value: ?T) !?*const ToVkType(T) { + const allocator_frame = ctx.allocator_frame; + + if (maybe_value) |value| { + const ptr = try allocator_frame.create(ToVkType(T)); + if (std.meta.hasFn(T, "toVk")) { + ptr.* = value.toVk(); + } + return ptr; + } else { + return null; + } +} + +fn toVkMulti(comptime T: type, values: []const T) ![*]const ToVkType(T) { + const allocator_frame = ctx.allocator_frame; + + const slice = try allocator_frame.alloc(ToVkType(T), values.len); + for (slice, values) |*y, x| { + y.* = x.toVk(); + } + + return slice.ptr; +} + pub const DrawIndexed = struct { index_count: u32, instance_count: u32 = 1, @@ -75,6 +106,19 @@ pub const RenderingAttachmentInfo = struct { load_op: vk.AttachmentLoadOp, store_op: vk.AttachmentStoreOp, clear_value: vk.ClearValue, + + pub fn toVk(self: RenderingAttachmentInfo) vk.RenderingAttachmentInfo { + return .{ + .image_view = self.image_view, + .image_layout = self.image_layout, + .resolve_mode = self.resolve_mode, + .resolve_image_view = self.resolve_image_view, + .resolve_image_layout = self.resolve_image_layout, + .load_op = self.load_op, + .store_op = self.store_op, + .clear_value = self.clear_value, + }; + } }; pub const RenderingInfo = struct { @@ -85,11 +129,40 @@ pub const RenderingInfo = struct { color_attachments: []const RenderingAttachmentInfo = &.{}, depth_attachment: ?RenderingAttachmentInfo = null, stencil_attachment: ?RenderingAttachmentInfo = null, + + pub fn toVk(self: RenderingInfo) !vk.RenderingInfo { + return .{ + .flags = self.flags, + .render_area = self.render_area, + .layer_count = self.layer_count, + .view_mask = self.view_mask, + .color_attachment_count = @intCast(self.color_attachments.len), + .p_color_attachments = try toVkMulti(RenderingAttachmentInfo, self.color_attachments), + .p_depth_attachment = try toVkOptional(RenderingAttachmentInfo, self.depth_attachment), + .p_stencil_attachment = try toVkOptional(RenderingAttachmentInfo, self.stencil_attachment), + }; + } }; pub const VertexBufferBinding = struct { buffer: vk.Buffer, offset: usize, + + pub fn transpose(vertex_buffer_bindings: []const VertexBufferBinding) !struct { []const vk.Buffer, []const usize } { + const allocator_frame = ctx.allocator_frame; + + var t: std.MultiArrayList(VertexBufferBinding) = .empty; + try t.ensureTotalCapacity(allocator_frame, vertex_buffer_bindings.len); + + for (vertex_buffer_bindings) |binding| { + t.appendAssumeCapacity(binding); + } + + return .{ + t.items(.buffer), + t.items(.offset), + }; + } }; pub fn beginCommandBuffer(self: CommandBuffer) !void { @@ -101,50 +174,8 @@ pub fn beginCommandBuffer(self: CommandBuffer) !void { } pub fn beginRendering(self: CommandBuffer, rendering_info: RenderingInfo) !void { - const allocator_frame = ctx.allocator_frame; - - const color_attachments = try allocator_frame.alloc(vk.RenderingAttachmentInfo, rendering_info.color_attachments.len); - for (color_attachments, rendering_info.color_attachments) |*x, color_attachment| { - x.* = .{ - .image_view = color_attachment.image_view, - .image_layout = color_attachment.image_layout, - .resolve_mode = color_attachment.resolve_mode, - .resolve_image_view = color_attachment.resolve_image_view, - .resolve_image_layout = color_attachment.resolve_image_layout, - .load_op = color_attachment.load_op, - .store_op = color_attachment.store_op, - .clear_value = color_attachment.clear_value, - }; - } - - self.proxy.beginRendering(&.{ - .flags = rendering_info.flags, - .render_area = rendering_info.render_area, - .layer_count = rendering_info.layer_count, - .view_mask = rendering_info.view_mask, - .color_attachment_count = @intCast(color_attachments.len), - .p_color_attachments = color_attachments.ptr, - .p_depth_attachment = if (rendering_info.depth_attachment) |x| &.{ - .image_view = x.image_view, - .image_layout = x.image_layout, - .resolve_mode = x.resolve_mode, - .resolve_image_view = x.resolve_image_view, - .resolve_image_layout = x.resolve_image_layout, - .load_op = x.load_op, - .store_op = x.store_op, - .clear_value = x.clear_value, - } else null, - .p_stencil_attachment = if (rendering_info.stencil_attachment) |x| &.{ - .image_view = x.image_view, - .image_layout = x.image_layout, - .resolve_mode = x.resolve_mode, - .resolve_image_view = x.resolve_image_view, - .resolve_image_layout = x.resolve_image_layout, - .load_op = x.load_op, - .store_op = x.store_op, - .clear_value = x.clear_value, - } else null, - }); + const rendering_info_vk = try rendering_info.toVk(); + self.proxy.beginRendering(&rendering_info_vk); } pub fn bindDescriptorSet( @@ -181,48 +212,21 @@ pub fn bindDescriptorSets( ); } -pub fn bindPipeline(self: CommandBuffer, pipeline_bind_point: vk.PipelineBindPoint, pipeline: vk.Pipeline) void { - self.proxy.bindPipeline(pipeline_bind_point, pipeline); -} - pub fn bindIndexBuffer(self: CommandBuffer, buffer: vk.Buffer, offset: u64, index_type: vk.IndexType) void { self.proxy.bindIndexBuffer(buffer, offset, index_type); } +pub fn bindPipeline(self: CommandBuffer, pipeline_bind_point: vk.PipelineBindPoint, pipeline: vk.Pipeline) void { + self.proxy.bindPipeline(pipeline_bind_point, pipeline); +} + +pub fn bindVertexBuffer(self: CommandBuffer, index: u32, binding: VertexBufferBinding) !void { + self.proxy.bindVertexBuffers(index, &.{binding.buffer}, &.{binding.offset}); +} + pub fn bindVertexBuffers(self: CommandBuffer, first_binding: u32, bindings: []const VertexBufferBinding) !void { - const allocator_frame = ctx.allocator_frame; - - var vertex_buffer_bindings = std.MultiArrayList(VertexBufferBinding){}; - defer vertex_buffer_bindings.deinit(allocator_frame); - try vertex_buffer_bindings.ensureTotalCapacity(allocator_frame, bindings.len); - - for (bindings) |binding| { - vertex_buffer_bindings.appendAssumeCapacity(binding); - } - - self.proxy.bindVertexBuffers( - first_binding, - vertex_buffer_bindings.items(.buffer), - vertex_buffer_bindings.items(.offset), - ); -} - -pub fn drawIndexed(self: CommandBuffer, draw_indexed: DrawIndexed) void { - self.proxy.drawIndexed( - draw_indexed.index_count, - draw_indexed.instance_count, - draw_indexed.first_index, - draw_indexed.vertex_offset, - draw_indexed.first_instance, - ); -} - -pub fn endCommandBuffer(self: CommandBuffer) !void { - try self.proxy.endCommandBuffer(); -} - -pub fn endRendering(self: CommandBuffer) void { - self.proxy.endRendering(); + const buffers, const offsets = try VertexBufferBinding.transpose(bindings); + self.proxy.bindVertexBuffers(first_binding, buffers, offsets); } pub fn copyBuffer( @@ -244,8 +248,22 @@ pub fn copyBufferToImage( self.proxy.copyBufferToImage(src_buffer, dst_image, dst_image_layout, regions); } -pub fn nextSubpass(self: CommandBuffer, contents: vk.SubpassContents) void { - self.proxy.nextSubpass(contents); +pub fn drawIndexed(self: CommandBuffer, draw_indexed: DrawIndexed) void { + self.proxy.drawIndexed( + draw_indexed.index_count, + draw_indexed.instance_count, + draw_indexed.first_index, + draw_indexed.vertex_offset, + draw_indexed.first_instance, + ); +} + +pub fn endCommandBuffer(self: CommandBuffer) !void { + try self.proxy.endCommandBuffer(); +} + +pub fn endRendering(self: CommandBuffer) void { + self.proxy.endRendering(); } pub fn pipelineBarrier(self: CommandBuffer, barrier: PipelineBarrier) void { @@ -259,10 +277,18 @@ pub fn pipelineBarrier(self: CommandBuffer, barrier: PipelineBarrier) void { ); } -pub fn setScissor(self: CommandBuffer, first_scissor: u32, scissors: []const vk.Rect2D) void { +pub fn setScissor(self: CommandBuffer, index: u32, scissor: vk.Rect2D) void { + self.proxy.setScissor(index, &.{scissor}); +} + +pub fn setScissors(self: CommandBuffer, first_scissor: u32, scissors: []const vk.Rect2D) void { self.proxy.setScissor(first_scissor, scissors); } -pub fn setViewport(self: CommandBuffer, first_viewport: u32, viewports: []const vk.Viewport) void { +pub fn setViewport(self: CommandBuffer, index: u32, viewports: vk.Viewport) void { + self.proxy.setViewport(index, &.{viewports}); +} + +pub fn setViewports(self: CommandBuffer, first_viewport: u32, viewports: []const vk.Viewport) void { self.proxy.setViewport(first_viewport, viewports); } diff --git a/src/engine/Engine.zig b/src/engine/Engine.zig index 4f5d371..0cc5d91 100644 --- a/src/engine/Engine.zig +++ b/src/engine/Engine.zig @@ -692,7 +692,7 @@ pub const BufferCreateInfo = struct { pub const CommandBufferAllocateInfo = struct { queue_type: QueueType, - level: vk.CommandBufferLevel, + level: vk.CommandBufferLevel = .primary, }; pub const CommandBufferFreeInfo = struct { diff --git a/src/engine/Swapchain.zig b/src/engine/Swapchain.zig index ab796a8..b4a1d64 100644 --- a/src/engine/Swapchain.zig +++ b/src/engine/Swapchain.zig @@ -218,6 +218,10 @@ pub fn acquire(self: *Swapchain) !void { self.image_index = res.image_index; } +pub fn getCurrentSwapchainImage(self: *Swapchain) *const SwapchainImage { + return &self.swapchain_images[self.image_index.?]; +} + pub const PresentInfo = struct { command_buffer: CommandBuffer, wait_semaphores: []const WaitSemaphore = &.{}, @@ -227,7 +231,7 @@ pub fn present(self: *Swapchain, present_info: PresentInfo) !void { const allocator_frame = ctx.allocator_frame; const engine = ctx.engine; - const current_swapchain_image = &self.swapchain_images[self.image_index.?]; + const current_swapchain_image = self.getCurrentSwapchainImage(); // --- SUBMIT COMMAND BUFFER -----------------------------------------------