From 8f19b161308850e1048df0729e40f379d2324855 Mon Sep 17 00:00:00 2001 From: Szymon Nowakowski Date: Sat, 20 Dec 2025 18:11:24 +0100 Subject: [PATCH] Transient CB only, validation fixes --- src/Game.zig | 81 ++++++++++++++++++++++++------------ src/engine/CommandBuffer.zig | 18 +++----- src/engine/Engine.zig | 81 +++++++++++++----------------------- src/engine/GenericBuffer.zig | 4 +- src/engine/Skybox.zig | 46 ++++++++++---------- src/engine/Texture.zig | 4 +- src/engine/Transient.zig | 4 -- 7 files changed, 116 insertions(+), 122 deletions(-) delete mode 100644 src/engine/Transient.zig diff --git a/src/Game.zig b/src/Game.zig index db89684..a025718 100644 --- a/src/Game.zig +++ b/src/Game.zig @@ -41,11 +41,11 @@ index_buffer: shaders.IndexBuffer, global_uniforms: shaders.GlobalUniformsBuffer, global_uniforms_staging_buffer: StagingBuffer, -global_uniforms_transfer_command_buffer: CommandBuffer, global_uniforms_transfer_semaphores: []vk.Semaphore, point_lights: shaders.PointLightBuffer, directional_lights: shaders.DirectionalLightBuffer, sampler: vk.Sampler, +deferred_command_buffers: std.ArrayList(CommandBuffer), blocks: Blocks, materials: Materials, @@ -232,23 +232,6 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain }); errdefer global_uniforms_staging_buffer.deinit(engine); - var global_uniforms_transfer_command_buffer: CommandBuffer = try .init(engine, .transfer, .persistent); - errdefer global_uniforms_transfer_command_buffer.deinit(engine); - - try global_uniforms_transfer_command_buffer.beginCommandBuffer(); - global_uniforms_transfer_command_buffer.copyBuffer( - global_uniforms_staging_buffer.buffer, - global_uniforms.buffer, - &.{ - .{ - .src_offset = 0, - .dst_offset = 0, - .size = @sizeOf(shaders.GlobalUniforms), - }, - }, - ); - try global_uniforms_transfer_command_buffer.endCommandBuffer(); - const global_uniforms_transfer_semaphores = blk: { var semaphores: std.ArrayList(vk.Semaphore) = try .initCapacity(allocator, swapchain.swapchain_images.len); errdefer semaphores.deinit(allocator); @@ -367,8 +350,24 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain .depth_compare_op = .greater, .depth_bounds_test_enable = .false, .stencil_test_enable = .false, - .front = undefined, - .back = undefined, + .front = .{ + .fail_op = .keep, + .pass_op = .keep, + .depth_fail_op = .keep, + .compare_op = .never, + .compare_mask = 0, + .write_mask = 0, + .reference = 0, + }, + .back = .{ + .fail_op = .keep, + .pass_op = .keep, + .depth_fail_op = .keep, + .compare_op = .never, + .compare_mask = 0, + .write_mask = 0, + .reference = 0, + }, .min_depth_bounds = 0, .max_depth_bounds = 1, }, @@ -536,8 +535,8 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain const world_seed = engine.random.int(u64); std.log.info("Using world seed 0x{x:0<16}", .{world_seed}); var it = Iterator2(i16).init(.{ - .min = .{ -12, -12 }, - .max = .{ 12, 12 }, + .min = .{ -8, -8 }, + .max = .{ 8, 8 }, }); while (it.next()) |chunk_coords2| { const chunk_coords3 = chunk_coords2 ++ [_]i16{0}; @@ -648,11 +647,11 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain .global_uniforms = global_uniforms, .global_uniforms_staging_buffer = global_uniforms_staging_buffer, - .global_uniforms_transfer_command_buffer = global_uniforms_transfer_command_buffer, .global_uniforms_transfer_semaphores = global_uniforms_transfer_semaphores, .point_lights = point_lights, .directional_lights = directional_lights, .sampler = sampler, + .deferred_command_buffers = try .initCapacity(allocator, 4), .blocks = blocks, .materials = materials, @@ -667,6 +666,11 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain pub fn deinit(self: *Game) void { std.log.scoped(.deinit).debug("Deinitializing {*}", .{self}); + for (self.deferred_command_buffers.items) |*command_buffer| { + command_buffer.deinit(self.engine); + } + self.deferred_command_buffers.deinit(self.allocator); + self.vertex_buffer.deinit(self.engine); self.index_buffer.deinit(self.engine); @@ -679,7 +683,6 @@ pub fn deinit(self: *Game) void { self.global_uniforms.deinit(self.engine); self.global_uniforms_staging_buffer.deinit(self.engine); - self.global_uniforms_transfer_command_buffer.deinit(self.engine); self.point_lights.deinit(self.engine); self.directional_lights.deinit(self.engine); @@ -755,6 +758,11 @@ fn render(self: *Game) !void { else => return err, }; + for (self.deferred_command_buffers.items) |*deferred_command_buffer| { + deferred_command_buffer.deinit(self.engine); + } + self.deferred_command_buffers.clearRetainingCapacity(); + const extent = self.swapchain.extent; const framebuffer_size = Vector2.init( @@ -800,12 +808,31 @@ fn render(self: *Game) !void { @memcpy(staging_memory, std.mem.asBytes(&global_uniforms_data)); self.global_uniforms_staging_buffer.unmap(self.engine); - try self.global_uniforms_transfer_command_buffer.submit(self.engine, .{ + try self.deferred_command_buffers.ensureUnusedCapacity(self.allocator, 1); + const global_uniforms_transfer_command_buffer = try CommandBuffer.init(self.engine, .transfer); + self.deferred_command_buffers.appendAssumeCapacity(global_uniforms_transfer_command_buffer); + + try global_uniforms_transfer_command_buffer.beginCommandBuffer(); + global_uniforms_transfer_command_buffer.copyBuffer( + self.global_uniforms_staging_buffer.buffer, + self.global_uniforms.buffer, + &.{ + .{ + .src_offset = 0, + .dst_offset = 0, + .size = @sizeOf(shaders.GlobalUniforms), + }, + }, + ); + try global_uniforms_transfer_command_buffer.endCommandBuffer(); + + try global_uniforms_transfer_command_buffer.submit(self.engine, .{ .signal_semaphores = &.{self.global_uniforms_transfer_semaphores[self.swapchain.image_index.?]}, }); - var command_buffer: CommandBuffer = try .init(self.engine, .graphics, .transient); - defer command_buffer.deinit(self.engine); + try self.deferred_command_buffers.ensureUnusedCapacity(self.allocator, 1); + const command_buffer: CommandBuffer = try .init(self.engine, .graphics); + self.deferred_command_buffers.appendAssumeCapacity(command_buffer); try command_buffer.beginCommandBuffer(); { diff --git a/src/engine/CommandBuffer.zig b/src/engine/CommandBuffer.zig index af92dee..7ac5322 100644 --- a/src/engine/CommandBuffer.zig +++ b/src/engine/CommandBuffer.zig @@ -2,9 +2,9 @@ //! arguments by turning pointer-length pairs into slices and integrates with //! different queues from `Engine` struct. //! -//! The wrapper retains information about the queue type and whether its -//! transient, such that it can submit to the correct queue preallocated in the -//! `Engine` struct when calling `CommandBuffer.submit`. +//! The wrapper retains information about the queue type, such that it can +//! submit to the correct queue preallocated in the `Engine` struct when calling +//! `CommandBuffer.submit`. const CommandBuffer = @This(); const std = @import("std"); @@ -13,17 +13,14 @@ const vk = @import("vulkan"); const Engine = @import("Engine.zig"); const QueueType = @import("QueueType.zig").QueueType; -const Transient = @import("Transient.zig").Transient; proxy: vk.CommandBufferProxy, allocator: std.mem.Allocator, queue_type: QueueType, -transient: Transient, -pub fn init(engine: *Engine, queue_type: QueueType, transient: Transient) !CommandBuffer { +pub fn init(engine: *Engine, queue_type: QueueType) !CommandBuffer { const handle = try engine.allocateCommandBuffer(.{ .queue_type = queue_type, - .transient = transient, .level = .primary, }); const proxy: vk.CommandBufferProxy = .init(handle, engine.device.wrapper); @@ -32,14 +29,12 @@ pub fn init(engine: *Engine, queue_type: QueueType, transient: Transient) !Comma .proxy = proxy, .allocator = allocator, .queue_type = queue_type, - .transient = transient, }; } pub fn deinit(self: *CommandBuffer, engine: *Engine) void { engine.freeCommandBuffer(.{ .queue_type = self.queue_type, - .transient = self.transient, .command_buffer = self.proxy.handle, }); self.* = undefined; @@ -83,10 +78,7 @@ pub const VertexBufferBinding = struct { pub fn beginCommandBuffer(self: CommandBuffer) !void { try self.proxy.beginCommandBuffer(&.{ .flags = .{ - .one_time_submit_bit = switch (self.transient) { - .persistent => true, - .transient => false, - }, + .one_time_submit_bit = true, }, }); } diff --git a/src/engine/Engine.zig b/src/engine/Engine.zig index 5d4de3e..483425b 100644 --- a/src/engine/Engine.zig +++ b/src/engine/Engine.zig @@ -7,7 +7,6 @@ const vk = @import("vulkan"); const Queue = @import("Queue.zig"); const QueueType = @import("QueueType.zig").QueueType; -const Transient = @import("Transient.zig").Transient; const VkAllocator = @import("VkAllocator.zig"); const WaitSemaphore = @import("WaitSemaphore.zig"); @@ -96,10 +95,6 @@ graphics_command_pool: vk.CommandPool, compute_command_pool: vk.CommandPool, transfer_command_pool: vk.CommandPool, -transient_graphics_command_pool: vk.CommandPool, -transient_compute_command_pool: vk.CommandPool, -transient_transfer_command_pool: vk.CommandPool, - prng_ptr: *std.Random.Pcg, random: std.Random, @@ -110,7 +105,7 @@ const vk_application_info: vk.ApplicationInfo = .{ .application_version = @bitCast(vk_version), .p_engine_name = c.app_name, .engine_version = @bitCast(vk_version), - .api_version = @bitCast(vk.API_VERSION_1_2), + .api_version = @bitCast(vk.API_VERSION_1_4), }; const vk_debug_utils_messenger_create_info: vk.DebugUtilsMessengerCreateInfoEXT = .{ @@ -163,7 +158,7 @@ pub fn init(allocator: std.mem.Allocator, maybe_window: ?*glfw.Window) !Engine { break :blk try base.createInstance(&.{ .p_application_info = &vk_application_info, - .enabled_layer_count = 0, //enabled_layers.len, + .enabled_layer_count = enabled_layers.len, .pp_enabled_layer_names = enabled_layers.ptr, .enabled_extension_count = @intCast(enabled_extensions.items.len), .pp_enabled_extension_names = enabled_extensions.items.ptr, @@ -291,14 +286,32 @@ pub fn init(allocator: std.mem.Allocator, maybe_window: ?*glfw.Window) !Engine { var enabled_features_vulkan12: vk.PhysicalDeviceVulkan12Features = .{ .p_next = &enabled_features_vulkan11, + .descriptor_indexing = .true, .descriptor_binding_partially_bound = .true, .descriptor_binding_variable_descriptor_count = .true, .runtime_descriptor_array = .true, .scalar_block_layout = .true, + .imageless_framebuffer = .true, + .timeline_semaphore = .true, + }; + + var enabled_features_vulkan13: vk.PhysicalDeviceVulkan13Features = .{ + .p_next = &enabled_features_vulkan12, + .robust_image_access = .true, + .inline_uniform_block = .true, + .synchronization_2 = .true, + .dynamic_rendering = .true, + .maintenance_4 = .true, + }; + + var enabled_features_vulkan14: vk.PhysicalDeviceVulkan14Features = .{ + .p_next = &enabled_features_vulkan13, + .maintenance_5 = .true, + .maintenance_6 = .true, }; break :blk try instance.createDevice(physical_device, &.{ - .p_next = &enabled_features_vulkan12, + .p_next = &enabled_features_vulkan14, .queue_create_info_count = @intCast(queue_create_info.items.len), .p_queue_create_infos = queue_create_info.items.ptr, .enabled_extension_count = @intCast(enabled_extensions.items.len), @@ -317,38 +330,23 @@ pub fn init(allocator: std.mem.Allocator, maybe_window: ?*glfw.Window) !Engine { // --- CREATE COMMAND POOLS, GET QUEUES ------------------------------------ const graphics_command_pool = try device.createCommandPool(&.{ + .flags = .{ .transient_bit = true }, .queue_family_index = queue_allocations.graphics_queue.family, }, &vk_allocator.interface); errdefer device.destroyCommandPool(graphics_command_pool, &vk_allocator.interface); const compute_command_pool = try device.createCommandPool(&.{ + .flags = .{ .transient_bit = true }, .queue_family_index = queue_allocations.compute_queue.family, }, &vk_allocator.interface); errdefer device.destroyCommandPool(compute_command_pool, &vk_allocator.interface); const transfer_command_pool = try device.createCommandPool(&.{ + .flags = .{ .transient_bit = true }, .queue_family_index = queue_allocations.transfer_queue.family, }, &vk_allocator.interface); errdefer device.destroyCommandPool(transfer_command_pool, &vk_allocator.interface); - const transient_graphics_command_pool = try device.createCommandPool(&.{ - .flags = .{ .transient_bit = true }, - .queue_family_index = queue_allocations.graphics_queue.family, - }, &vk_allocator.interface); - errdefer device.destroyCommandPool(transient_graphics_command_pool, &vk_allocator.interface); - - const transient_compute_command_pool = try device.createCommandPool(&.{ - .flags = .{ .transient_bit = true }, - .queue_family_index = queue_allocations.compute_queue.family, - }, &vk_allocator.interface); - errdefer device.destroyCommandPool(transient_compute_command_pool, &vk_allocator.interface); - - const transient_transfer_command_pool = try device.createCommandPool(&.{ - .flags = .{ .transient_bit = true }, - .queue_family_index = queue_allocations.transfer_queue.family, - }, &vk_allocator.interface); - errdefer device.destroyCommandPool(transient_transfer_command_pool, &vk_allocator.interface); - const graphics_queue: Queue = .init( device.getDeviceQueue( queue_allocations.graphics_queue.family, @@ -414,10 +412,6 @@ pub fn init(allocator: std.mem.Allocator, maybe_window: ?*glfw.Window) !Engine { .compute_command_pool = compute_command_pool, .transfer_command_pool = transfer_command_pool, - .transient_graphics_command_pool = transient_graphics_command_pool, - .transient_compute_command_pool = transient_compute_command_pool, - .transient_transfer_command_pool = transient_transfer_command_pool, - .prng_ptr = prng_ptr, .random = random, }; @@ -434,10 +428,6 @@ pub fn deinit(self: *Engine) void { self.device.destroyCommandPool(self.compute_command_pool, &self.vk_allocator.interface); self.device.destroyCommandPool(self.transfer_command_pool, &self.vk_allocator.interface); - self.device.destroyCommandPool(self.transient_graphics_command_pool, &self.vk_allocator.interface); - self.device.destroyCommandPool(self.transient_compute_command_pool, &self.vk_allocator.interface); - self.device.destroyCommandPool(self.transient_transfer_command_pool, &self.vk_allocator.interface); - self.device.destroyDevice(&self.vk_allocator.interface); allocator.destroy(self.device.wrapper); @@ -702,20 +692,11 @@ fn findPresentationQueueFamily(queue_families_properties: []const vk.QueueFamily return null; } -fn resolveCommandPool(self: *const Engine, queue_type: QueueType, transient: Transient) vk.CommandPool { +fn resolveCommandPool(self: *const Engine, queue_type: QueueType) vk.CommandPool { return switch (queue_type) { - .graphics => switch (transient) { - .persistent => self.graphics_command_pool, - .transient => self.transient_graphics_command_pool, - }, - .compute => switch (transient) { - .persistent => self.compute_command_pool, - .transient => self.transient_compute_command_pool, - }, - .transfer => switch (transient) { - .persistent => self.transfer_command_pool, - .transient => self.transient_transfer_command_pool, - }, + .graphics => self.graphics_command_pool, + .compute => self.compute_command_pool, + .transfer => self.transfer_command_pool, }; } @@ -736,13 +717,11 @@ pub const BufferCreateInfo = struct { pub const CommandBufferAllocateInfo = struct { queue_type: QueueType, - transient: Transient, level: vk.CommandBufferLevel, }; pub const CommandBufferFreeInfo = struct { queue_type: QueueType, - transient: Transient, command_buffer: vk.CommandBuffer, }; @@ -944,7 +923,7 @@ pub fn acquireNextImage(self: *Engine, swapchain: vk.SwapchainKHR, acquire_info: } pub fn allocateCommandBuffer(self: *Engine, allocate_info: CommandBufferAllocateInfo) !vk.CommandBuffer { - const command_pool = self.resolveCommandPool(allocate_info.queue_type, allocate_info.transient); + const command_pool = self.resolveCommandPool(allocate_info.queue_type); var command_buffers: [1]vk.CommandBuffer = undefined; try self.device.allocateCommandBuffers(&.{ .command_pool = command_pool, @@ -1372,7 +1351,7 @@ pub fn deviceWaitIdle(self: *Engine) !void { } pub fn freeCommandBuffer(self: *Engine, free_info: CommandBufferFreeInfo) void { - const command_pool = self.resolveCommandPool(free_info.queue_type, free_info.transient); + const command_pool = self.resolveCommandPool(free_info.queue_type); const command_buffers = [_]vk.CommandBuffer{free_info.command_buffer}; self.device.freeCommandBuffers(command_pool, command_buffers.len, &command_buffers); } diff --git a/src/engine/GenericBuffer.zig b/src/engine/GenericBuffer.zig index 7c5cd90..58a9c19 100644 --- a/src/engine/GenericBuffer.zig +++ b/src/engine/GenericBuffer.zig @@ -154,7 +154,7 @@ pub fn GenericBuffer(comptime Header: type, comptime Element: type) type { @memcpy(staging_memory[array_write_offset..write_size], std.mem.sliceAsBytes(write_info.elements)); staging_buffer.unmap(engine); - var command_buffer: CommandBuffer = try .init(engine, .transfer, .transient); + var command_buffer: CommandBuffer = try .init(engine, .transfer); defer command_buffer.deinit(engine); try command_buffer.beginCommandBuffer(); @@ -185,7 +185,7 @@ pub fn GenericBuffer(comptime Header: type, comptime Element: type) type { @memcpy(staging_memory, data); staging_buffer.unmap(engine); - const command_buffer: CommandBuffer(.tranfer, .transient) = try .init(engine); + var command_buffer = try CommandBuffer.init(engine, .transfer); defer command_buffer.deinit(engine); try command_buffer.beginCommandBuffer(.{}); diff --git a/src/engine/Skybox.zig b/src/engine/Skybox.zig index b459ce1..ce6a85f 100644 --- a/src/engine/Skybox.zig +++ b/src/engine/Skybox.zig @@ -120,7 +120,7 @@ pub fn load(filename: []const u8, engine: *Engine, cube_size: u32, global_unifor // --- TRANSITION TO TRANSFER_DST_OPTIMAL AND COPY ------------------------- - var transfer_command_buffer = try CommandBuffer.init(engine, .transfer, .transient); + var transfer_command_buffer = try CommandBuffer.init(engine, .transfer); defer transfer_command_buffer.deinit(engine); try transfer_command_buffer.beginCommandBuffer(); @@ -174,7 +174,7 @@ pub fn load(filename: []const u8, engine: *Engine, cube_size: u32, global_unifor // --- TRANSITION TO SHADER_READ_ONLY_OPTIMAL ------------------------------ - var transition1_command_buffer = try CommandBuffer.init(engine, .graphics, .transient); + var transition1_command_buffer = try CommandBuffer.init(engine, .graphics); defer transition1_command_buffer.deinit(engine); try transition1_command_buffer.beginCommandBuffer(); @@ -386,7 +386,7 @@ pub fn load(filename: []const u8, engine: *Engine, cube_size: u32, global_unifor // 1. Run compute shader - var compute_command_buffer = try CommandBuffer.init(engine, .compute, .transient); + var compute_command_buffer = try CommandBuffer.init(engine, .compute); defer compute_command_buffer.deinit(engine); try compute_command_buffer.beginCommandBuffer(); @@ -394,22 +394,6 @@ pub fn load(filename: []const u8, engine: *Engine, cube_size: u32, global_unifor .src_stage_mask = .{ .top_of_pipe_bit = true }, .dst_stage_mask = .{ .compute_shader_bit = true }, .image_memory_barriers = &.{ - .{ - .src_access_mask = .{}, - .dst_access_mask = .{ .shader_read_bit = true }, - .old_layout = .preinitialized, - .new_layout = .shader_read_only_optimal, - .src_queue_family_index = vk.QUEUE_FAMILY_IGNORED, - .dst_queue_family_index = vk.QUEUE_FAMILY_IGNORED, - .image = equirect_image, - .subresource_range = .{ - .aspect_mask = .{ .color_bit = true }, - .base_mip_level = 0, - .level_count = 1, - .base_array_layer = 0, - .layer_count = 1, - }, - }, .{ .src_access_mask = .{}, .dst_access_mask = .{ .shader_write_bit = true }, @@ -445,13 +429,13 @@ pub fn load(filename: []const u8, engine: *Engine, cube_size: u32, global_unifor // 2. Transition cubemap - var transition2_command_buffer = try CommandBuffer.init(engine, .graphics, .transient); + var transition2_command_buffer = try CommandBuffer.init(engine, .graphics); defer transition2_command_buffer.deinit(engine); try transition2_command_buffer.beginCommandBuffer(); transition2_command_buffer.pipelineBarrier(.{ .src_stage_mask = .{ .compute_shader_bit = true }, - .dst_stage_mask = .{ .top_of_pipe_bit = true }, + .dst_stage_mask = .{ .fragment_shader_bit = true }, .image_memory_barriers = &.{ .{ .src_access_mask = .{ .shader_write_bit = true }, @@ -700,8 +684,24 @@ pub fn load(filename: []const u8, engine: *Engine, cube_size: u32, global_unifor .depth_compare_op = .equal, .depth_bounds_test_enable = .false, .stencil_test_enable = .false, - .front = undefined, - .back = undefined, + .front = .{ + .fail_op = .keep, + .pass_op = .keep, + .depth_fail_op = .keep, + .compare_op = .never, + .compare_mask = 0, + .write_mask = 0, + .reference = 0, + }, + .back = .{ + .fail_op = .keep, + .pass_op = .keep, + .depth_fail_op = .keep, + .compare_op = .never, + .compare_mask = 0, + .write_mask = 0, + .reference = 0, + }, .min_depth_bounds = 0, .max_depth_bounds = 1, }, diff --git a/src/engine/Texture.zig b/src/engine/Texture.zig index f30bedb..4554ab7 100644 --- a/src/engine/Texture.zig +++ b/src/engine/Texture.zig @@ -205,7 +205,7 @@ pub fn writeRaw(self: Texture, engine: *Engine, data: []const u8) !void { // --- TRANSITION TO TRANSFER_DST_OPTIMAL AND COPY ----------------- - var transfer_command_buffer: CommandBuffer = try .init(engine, .transfer, .transient); + var transfer_command_buffer: CommandBuffer = try .init(engine, .transfer); defer transfer_command_buffer.deinit(engine); try transfer_command_buffer.beginCommandBuffer(); @@ -265,7 +265,7 @@ pub fn writeRaw(self: Texture, engine: *Engine, data: []const u8) !void { // --- TRANSITION TO SHADER_READ_ONLY_OPTIMAL ---------------------- - var graphics_command_buffer: CommandBuffer = try .init(engine, .graphics, .transient); + var graphics_command_buffer: CommandBuffer = try .init(engine, .graphics); defer graphics_command_buffer.deinit(engine); try graphics_command_buffer.beginCommandBuffer(); diff --git a/src/engine/Transient.zig b/src/engine/Transient.zig deleted file mode 100644 index 6a9b7ba..0000000 --- a/src/engine/Transient.zig +++ /dev/null @@ -1,4 +0,0 @@ -pub const Transient = enum { - persistent, - transient, -};