From 40084fc530d8500fdfaceff638470c7dc1fa7512 Mon Sep 17 00:00:00 2001 From: MichaelFisher1997 Date: Tue, 10 Feb 2026 22:30:55 +0000 Subject: [PATCH] fix(vulkan): hotfix descriptor hazards and MSAA attachment wiring --- .../graphics/vulkan/post_process_system.zig | 37 ++++++++-------- .../vulkan/rhi_frame_orchestration.zig | 4 +- .../graphics/vulkan/rhi_init_deinit.zig | 4 +- .../vulkan/rhi_pass_orchestration.zig | 4 +- .../vulkan/rhi_resource_lifecycle.zig | 42 +++++++++++++++++++ .../graphics/vulkan/rhi_resource_setup.zig | 4 +- src/engine/graphics/vulkan/taa_system.zig | 29 +++++++++---- 7 files changed, 88 insertions(+), 36 deletions(-) diff --git a/src/engine/graphics/vulkan/post_process_system.zig b/src/engine/graphics/vulkan/post_process_system.zig index 1e87c7e..8175516 100644 --- a/src/engine/graphics/vulkan/post_process_system.zig +++ b/src/engine/graphics/vulkan/post_process_system.zig @@ -198,25 +198,24 @@ pub const PostProcessSystem = struct { } } - pub fn updateSourceDescriptors(self: *PostProcessSystem, vk: c.VkDevice, source_view: c.VkImageView, source_sampler: c.VkSampler) void { - for (0..rhi.MAX_FRAMES_IN_FLIGHT) |i| { - if (self.descriptor_sets[i] == null) continue; - - var source_image_info = std.mem.zeroes(c.VkDescriptorImageInfo); - source_image_info.imageLayout = c.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - source_image_info.imageView = source_view; - source_image_info.sampler = source_sampler; - - var write = std.mem.zeroes(c.VkWriteDescriptorSet); - write.sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - write.dstSet = self.descriptor_sets[i]; - write.dstBinding = 0; - write.descriptorType = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - write.descriptorCount = 1; - write.pImageInfo = &source_image_info; - - c.vkUpdateDescriptorSets(vk, 1, &write, 0, null); - } + pub fn updateSourceDescriptor(self: *PostProcessSystem, vk: c.VkDevice, frame_index: usize, source_view: c.VkImageView, source_sampler: c.VkSampler) void { + if (frame_index >= rhi.MAX_FRAMES_IN_FLIGHT) return; + if (self.descriptor_sets[frame_index] == null) return; + + var source_image_info = std.mem.zeroes(c.VkDescriptorImageInfo); + source_image_info.imageLayout = c.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + source_image_info.imageView = source_view; + source_image_info.sampler = source_sampler; + + var write = std.mem.zeroes(c.VkWriteDescriptorSet); + write.sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write.dstSet = self.descriptor_sets[frame_index]; + write.dstBinding = 0; + write.descriptorType = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write.descriptorCount = 1; + write.pImageInfo = &source_image_info; + + c.vkUpdateDescriptorSets(vk, 1, &write, 0, null); } pub fn updateLUTDescriptor(self: *PostProcessSystem, vk: c.VkDevice, lut_view: c.VkImageView, lut_sampler: c.VkSampler) void { diff --git a/src/engine/graphics/vulkan/rhi_frame_orchestration.zig b/src/engine/graphics/vulkan/rhi_frame_orchestration.zig index c943027..75399db 100644 --- a/src/engine/graphics/vulkan/rhi_frame_orchestration.zig +++ b/src/engine/graphics/vulkan/rhi_frame_orchestration.zig @@ -48,11 +48,11 @@ pub fn recreateSwapchainInternal(ctx: anytype) void { std.log.err("Failed to recreate TAA resources: {}", .{err}); return; }; - ctx.render_pass_manager.createMainRenderPass(ctx.vulkan_device.vk_device, ctx.swapchain.getExtent(), 1) catch |err| { + ctx.render_pass_manager.createMainRenderPass(ctx.vulkan_device.vk_device, ctx.swapchain.getExtent(), ctx.options.msaa_samples) catch |err| { std.log.err("Failed to recreate render pass: {}", .{err}); return; }; - ctx.pipeline_manager.createMainPipelines(ctx.allocator, ctx.vulkan_device.vk_device, ctx.render_pass_manager.hdr_render_pass, ctx.render_pass_manager.g_render_pass, 1) catch |err| { + ctx.pipeline_manager.createMainPipelines(ctx.allocator, ctx.vulkan_device.vk_device, ctx.render_pass_manager.hdr_render_pass, ctx.render_pass_manager.g_render_pass, ctx.options.msaa_samples) catch |err| { std.log.err("Failed to recreate pipelines: {}", .{err}); return; }; diff --git a/src/engine/graphics/vulkan/rhi_init_deinit.zig b/src/engine/graphics/vulkan/rhi_init_deinit.zig index 47eda8d..fa38fe4 100644 --- a/src/engine/graphics/vulkan/rhi_init_deinit.zig +++ b/src/engine/graphics/vulkan/rhi_init_deinit.zig @@ -82,7 +82,7 @@ pub fn initContext(ctx: anytype, allocator: std.mem.Allocator, render_device: ?* try ctx.render_pass_manager.createMainRenderPass( ctx.vulkan_device.vk_device, ctx.swapchain.getExtent(), - 1, + ctx.options.msaa_samples, ); try ctx.pipeline_manager.createMainPipelines( @@ -90,7 +90,7 @@ pub fn initContext(ctx: anytype, allocator: std.mem.Allocator, render_device: ?* ctx.vulkan_device.vk_device, ctx.render_pass_manager.hdr_render_pass, ctx.render_pass_manager.g_render_pass, - 1, + ctx.options.msaa_samples, ); try setup.createPostProcessResources(ctx); diff --git a/src/engine/graphics/vulkan/rhi_pass_orchestration.zig b/src/engine/graphics/vulkan/rhi_pass_orchestration.zig index 5c41f66..14e37dc 100644 --- a/src/engine/graphics/vulkan/rhi_pass_orchestration.zig +++ b/src/engine/graphics/vulkan/rhi_pass_orchestration.zig @@ -190,7 +190,7 @@ pub fn beginMainPassInternal(ctx: anytype) void { if (ctx.swapchain.getExtent().width == 0 or ctx.swapchain.getExtent().height == 0) return; if (ctx.render_pass_manager.hdr_render_pass == null) { - ctx.render_pass_manager.createMainRenderPass(ctx.vulkan_device.vk_device, ctx.swapchain.getExtent(), 1) catch |err| { + ctx.render_pass_manager.createMainRenderPass(ctx.vulkan_device.vk_device, ctx.swapchain.getExtent(), ctx.options.msaa_samples) catch |err| { std.log.err("beginMainPass: failed to recreate render pass: {}", .{err}); return; }; @@ -315,7 +315,7 @@ pub fn beginPostProcessPassInternal(ctx: anytype) void { source_sampler = tex.sampler; } } - ctx.post_process.updateSourceDescriptors(ctx.vulkan_device.vk_device, source_view, source_sampler); + ctx.post_process.updateSourceDescriptor(ctx.vulkan_device.vk_device, ctx.frames.current_frame, source_view, source_sampler); const pp_ds = ctx.post_process.descriptor_sets[ctx.frames.current_frame]; if (pp_ds == null) { diff --git a/src/engine/graphics/vulkan/rhi_resource_lifecycle.zig b/src/engine/graphics/vulkan/rhi_resource_lifecycle.zig index 4f133f7..1cc9d06 100644 --- a/src/engine/graphics/vulkan/rhi_resource_lifecycle.zig +++ b/src/engine/graphics/vulkan/rhi_resource_lifecycle.zig @@ -199,6 +199,13 @@ pub fn transitionImagesToShaderRead(ctx: anytype, images: []const c.VkImage, is_ pub fn createHDRResources(ctx: anytype) !void { const extent = ctx.swapchain.getExtent(); const format = c.VK_FORMAT_R16G16B16A16_SFLOAT; + const sample_count: c_uint = @intCast(switch (ctx.options.msaa_samples) { + 1 => c.VK_SAMPLE_COUNT_1_BIT, + 2 => c.VK_SAMPLE_COUNT_2_BIT, + 4 => c.VK_SAMPLE_COUNT_4_BIT, + 8 => c.VK_SAMPLE_COUNT_8_BIT, + else => c.VK_SAMPLE_COUNT_1_BIT, + }); var image_info = std.mem.zeroes(c.VkImageCreateInfo); image_info.sType = c.VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; @@ -231,4 +238,39 @@ pub fn createHDRResources(ctx: anytype) !void { view_info.format = format; view_info.subresourceRange = .{ .aspectMask = c.VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1 }; try Utils.checkVk(c.vkCreateImageView(ctx.vulkan_device.vk_device, &view_info, null, &ctx.hdr.hdr_view)); + + if (ctx.options.msaa_samples > 1) { + var msaa_info = std.mem.zeroes(c.VkImageCreateInfo); + msaa_info.sType = c.VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + msaa_info.imageType = c.VK_IMAGE_TYPE_2D; + msaa_info.extent = .{ .width = extent.width, .height = extent.height, .depth = 1 }; + msaa_info.mipLevels = 1; + msaa_info.arrayLayers = 1; + msaa_info.format = format; + msaa_info.tiling = c.VK_IMAGE_TILING_OPTIMAL; + msaa_info.initialLayout = c.VK_IMAGE_LAYOUT_UNDEFINED; + msaa_info.usage = c.VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | c.VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + msaa_info.samples = sample_count; + msaa_info.sharingMode = c.VK_SHARING_MODE_EXCLUSIVE; + + try Utils.checkVk(c.vkCreateImage(ctx.vulkan_device.vk_device, &msaa_info, null, &ctx.hdr.hdr_msaa_image)); + + var msaa_reqs: c.VkMemoryRequirements = undefined; + c.vkGetImageMemoryRequirements(ctx.vulkan_device.vk_device, ctx.hdr.hdr_msaa_image, &msaa_reqs); + var msaa_alloc = std.mem.zeroes(c.VkMemoryAllocateInfo); + msaa_alloc.sType = c.VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + msaa_alloc.allocationSize = msaa_reqs.size; + msaa_alloc.memoryTypeIndex = Utils.findMemoryType(ctx.vulkan_device.physical_device, msaa_reqs.memoryTypeBits, c.VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT | c.VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) catch + try Utils.findMemoryType(ctx.vulkan_device.physical_device, msaa_reqs.memoryTypeBits, c.VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + try Utils.checkVk(c.vkAllocateMemory(ctx.vulkan_device.vk_device, &msaa_alloc, null, &ctx.hdr.hdr_msaa_memory)); + try Utils.checkVk(c.vkBindImageMemory(ctx.vulkan_device.vk_device, ctx.hdr.hdr_msaa_image, ctx.hdr.hdr_msaa_memory, 0)); + + var msaa_view_info = std.mem.zeroes(c.VkImageViewCreateInfo); + msaa_view_info.sType = c.VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + msaa_view_info.image = ctx.hdr.hdr_msaa_image; + msaa_view_info.viewType = c.VK_IMAGE_VIEW_TYPE_2D; + msaa_view_info.format = format; + msaa_view_info.subresourceRange = .{ .aspectMask = c.VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1 }; + try Utils.checkVk(c.vkCreateImageView(ctx.vulkan_device.vk_device, &msaa_view_info, null, &ctx.hdr.hdr_msaa_view)); + } } diff --git a/src/engine/graphics/vulkan/rhi_resource_setup.zig b/src/engine/graphics/vulkan/rhi_resource_setup.zig index 50a7c5a..b27472f 100644 --- a/src/engine/graphics/vulkan/rhi_resource_setup.zig +++ b/src/engine/graphics/vulkan/rhi_resource_setup.zig @@ -463,9 +463,9 @@ pub fn createMainFramebuffers(ctx: anytype) !void { ctx.vulkan_device.vk_device, ctx.swapchain.getExtent(), ctx.hdr.hdr_view, - null, + ctx.hdr.hdr_msaa_view, ctx.swapchain.swapchain.depth_image_view, - 1, + ctx.options.msaa_samples, ); } diff --git a/src/engine/graphics/vulkan/taa_system.zig b/src/engine/graphics/vulkan/taa_system.zig index 05ff5f7..38cd3f1 100644 --- a/src/engine/graphics/vulkan/taa_system.zig +++ b/src/engine/graphics/vulkan/taa_system.zig @@ -11,6 +11,9 @@ pub const TAAPushConstants = extern struct { _pad: f32, }; +const DESCRIPTOR_SETS_PER_FRAME: usize = 2; +const TAA_DESCRIPTOR_SET_COUNT: usize = rhi.MAX_FRAMES_IN_FLIGHT * DESCRIPTOR_SETS_PER_FRAME; + pub const TAASystem = struct { enabled: bool = true, pass_active: bool = false, @@ -23,7 +26,7 @@ pub const TAASystem = struct { pipeline: c.VkPipeline = null, pipeline_layout: c.VkPipelineLayout = null, descriptor_set_layout: c.VkDescriptorSetLayout = null, - descriptor_sets: [rhi.MAX_FRAMES_IN_FLIGHT]c.VkDescriptorSet = .{null} ** rhi.MAX_FRAMES_IN_FLIGHT, + descriptor_sets: [TAA_DESCRIPTOR_SET_COUNT]c.VkDescriptorSet = .{null} ** TAA_DESCRIPTOR_SET_COUNT, sampler: c.VkSampler = null, history_textures: [2]rhi.TextureHandle = .{ 0, 0 }, @@ -228,20 +231,24 @@ pub const TAASystem = struct { } if (self.descriptor_sets[0] == null) { - var layouts: [rhi.MAX_FRAMES_IN_FLIGHT]c.VkDescriptorSetLayout = undefined; - for (0..rhi.MAX_FRAMES_IN_FLIGHT) |i| { + var layouts: [TAA_DESCRIPTOR_SET_COUNT]c.VkDescriptorSetLayout = undefined; + for (0..TAA_DESCRIPTOR_SET_COUNT) |i| { layouts[i] = self.descriptor_set_layout; } var alloc_info = std.mem.zeroes(c.VkDescriptorSetAllocateInfo); alloc_info.sType = c.VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; alloc_info.descriptorPool = descriptor_pool; - alloc_info.descriptorSetCount = rhi.MAX_FRAMES_IN_FLIGHT; + alloc_info.descriptorSetCount = TAA_DESCRIPTOR_SET_COUNT; alloc_info.pSetLayouts = &layouts[0]; try Utils.checkVk(c.vkAllocateDescriptorSets(vk, &alloc_info, &self.descriptor_sets[0])); } } + fn descriptorSetIndex(frame_index: usize, read_idx: usize) usize { + return frame_index * DESCRIPTOR_SETS_PER_FRAME + read_idx; + } + fn createFramebuffers(self: *TAASystem, vk: c.VkDevice, resources: anytype, extent: c.VkExtent2D) !void { errdefer self.destroyFramebuffers(vk); @@ -301,6 +308,10 @@ pub const TAASystem = struct { const history_tex = resources.textures.get(self.history_textures[read_idx]) orelse return; + const set_index = descriptorSetIndex(frame_index, read_idx); + const descriptor_set = self.descriptor_sets[set_index]; + if (descriptor_set == null) return; + var image_infos = [_]c.VkDescriptorImageInfo{ .{ .sampler = self.sampler, .imageView = hdr_view, .imageLayout = c.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }, .{ .sampler = self.sampler, .imageView = history_tex.view, .imageLayout = c.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }, @@ -308,9 +319,9 @@ pub const TAASystem = struct { }; var writes = [_]c.VkWriteDescriptorSet{ - .{ .sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = self.descriptor_sets[frame_index], .dstBinding = 0, .descriptorType = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = 1, .pImageInfo = &image_infos[0] }, - .{ .sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = self.descriptor_sets[frame_index], .dstBinding = 1, .descriptorType = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = 1, .pImageInfo = &image_infos[1] }, - .{ .sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = self.descriptor_sets[frame_index], .dstBinding = 2, .descriptorType = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = 1, .pImageInfo = &image_infos[2] }, + .{ .sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = descriptor_set, .dstBinding = 0, .descriptorType = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = 1, .pImageInfo = &image_infos[0] }, + .{ .sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = descriptor_set, .dstBinding = 1, .descriptorType = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = 1, .pImageInfo = &image_infos[1] }, + .{ .sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = descriptor_set, .dstBinding = 2, .descriptorType = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = 1, .pImageInfo = &image_infos[2] }, }; c.vkUpdateDescriptorSets(vk, writes.len, &writes[0], 0, null); @@ -342,7 +353,7 @@ pub const TAASystem = struct { c.vkCmdSetScissor(command_buffer, 0, 1, &scissor); c.vkCmdBindPipeline(command_buffer, c.VK_PIPELINE_BIND_POINT_GRAPHICS, self.pipeline); - c.vkCmdBindDescriptorSets(command_buffer, c.VK_PIPELINE_BIND_POINT_GRAPHICS, self.pipeline_layout, 0, 1, &self.descriptor_sets[frame_index], 0, null); + c.vkCmdBindDescriptorSets(command_buffer, c.VK_PIPELINE_BIND_POINT_GRAPHICS, self.pipeline_layout, 0, 1, &descriptor_set, 0, null); const push = TAAPushConstants{ .blend_factor = self.blend_factor, @@ -369,7 +380,7 @@ pub const TAASystem = struct { self.destroyHistoryTextures(resources); if (descriptor_pool != null) { - for (0..rhi.MAX_FRAMES_IN_FLIGHT) |i| { + for (0..TAA_DESCRIPTOR_SET_COUNT) |i| { if (self.descriptor_sets[i] != null) { _ = c.vkFreeDescriptorSets(vk, descriptor_pool, 1, &self.descriptor_sets[i]); self.descriptor_sets[i] = null;