From ff717b3ab99ea6ba66bcadf51523a764c8151efe Mon Sep 17 00:00:00 2001 From: Ryp Date: Sun, 28 Jan 2024 10:55:06 +0100 Subject: [PATCH] WIP vulkan: add MSAA support for visibility pass Vis will render at half the res but 4x MSAA which should essentially result in a no-op. Also add legacy path for devices with no depth write in compute. --- src/renderer/CMakeLists.txt | 3 + .../shader/vis_buffer/fill_gbuffer.comp.hlsl | 25 + .../shader/vis_buffer/fill_gbuffer.share.hlsl | 24 +- .../vis_buffer/fill_gbuffer_msaa.comp.hlsl | 2 + ..._gbuffer_msaa_with_depth_resolve.comp.hlsl | 3 + .../vis_buffer/resolve_depth_legacy.frag.hlsl | 24 + src/renderer/vulkan/Backend.cpp | 4 +- src/renderer/vulkan/Backend.h | 1 + src/renderer/vulkan/PhysicalDevice.cpp | 43 ++ src/renderer/vulkan/PhysicalDevice.h | 5 + src/renderer/vulkan/ShaderModules.cpp | 8 + src/renderer/vulkan/ShaderModules.h | 3 + src/renderer/vulkan/api/AssertHelper.h | 12 +- .../vulkan/renderpass/TestGraphics.cpp | 35 +- .../vulkan/renderpass/TiledLightingPass.cpp | 2 +- .../renderpass/VisibilityBufferPass.cpp | 535 +++++++++++++++--- .../vulkan/renderpass/VisibilityBufferPass.h | 62 +- src/vulkan_loader/SymbolHelper.inl | 1 + 18 files changed, 654 insertions(+), 138 deletions(-) create mode 100644 src/renderer/shader/vis_buffer/fill_gbuffer_msaa.comp.hlsl create mode 100644 src/renderer/shader/vis_buffer/fill_gbuffer_msaa_with_depth_resolve.comp.hlsl create mode 100644 src/renderer/shader/vis_buffer/resolve_depth_legacy.frag.hlsl diff --git a/src/renderer/CMakeLists.txt b/src/renderer/CMakeLists.txt index c437975e..6529bc3c 100644 --- a/src/renderer/CMakeLists.txt +++ b/src/renderer/CMakeLists.txt @@ -186,9 +186,12 @@ set(REAPER_SHADER_SRCS ${REAPER_SHADER_DIR}/tiled_lighting/tile_depth_downsample.comp.hlsl ${REAPER_SHADER_DIR}/tiled_lighting/tiled_lighting.comp.hlsl ${REAPER_SHADER_DIR}/tiled_lighting/tiled_lighting_debug.comp.hlsl + ${REAPER_SHADER_DIR}/vis_buffer/resolve_depth_legacy.frag.hlsl ${REAPER_SHADER_DIR}/vis_buffer/vis_buffer_raster.frag.hlsl ${REAPER_SHADER_DIR}/vis_buffer/vis_buffer_raster.vert.hlsl ${REAPER_SHADER_DIR}/vis_buffer/fill_gbuffer.comp.hlsl + ${REAPER_SHADER_DIR}/vis_buffer/fill_gbuffer_msaa.comp.hlsl + ${REAPER_SHADER_DIR}/vis_buffer/fill_gbuffer_msaa_with_depth_resolve.comp.hlsl ) # Shader compilation diff --git a/src/renderer/shader/vis_buffer/fill_gbuffer.comp.hlsl b/src/renderer/shader/vis_buffer/fill_gbuffer.comp.hlsl index 01d28662..1e05efb5 100644 --- a/src/renderer/shader/vis_buffer/fill_gbuffer.comp.hlsl +++ b/src/renderer/shader/vis_buffer/fill_gbuffer.comp.hlsl @@ -13,9 +13,23 @@ #include "vis_buffer.hlsl" #include "fill_gbuffer.share.hlsl" +#if defined(ENABLE_MSAA_DEPTH_RESOLVE) && !defined(ENABLE_MSAA_VIS_BUFFER) + #error +#endif + VK_PUSH_CONSTANT_HELPER(FillGBufferPushConstants) push; +#if defined(ENABLE_MSAA_VIS_BUFFER) +VK_BINDING(0, Slot_VisBuffer) Texture2DMS VisBufferMS; +#else VK_BINDING(0, Slot_VisBuffer) Texture2D VisBuffer; +#endif + +#if defined(ENABLE_MSAA_DEPTH_RESOLVE) +VK_BINDING(0, Slot_VisBufferDepthMS) Texture2DMS VisBufferDepthMS; +VK_BINDING(0, Slot_ResolvedDepth) RWTexture2D ResolvedDepth; +#endif + VK_BINDING(0, Slot_GBuffer0) RWTexture2D GBuffer0; VK_BINDING(0, Slot_GBuffer1) RWTexture2D GBuffer1; VK_BINDING(0, Slot_instance_params) StructuredBuffer instance_params; @@ -49,7 +63,18 @@ void main(uint3 gtid : SV_GroupThreadID, return; } +#if defined(ENABLE_MSAA_VIS_BUFFER) + uint sample_location = (position_ts.x & 1) + (position_ts.y & 1) * 2; + VisBufferRawType vis_buffer_raw = VisBufferMS.Load(uint3(position_ts / 2, 0), sample_location); + +#if defined(ENABLE_MSAA_DEPTH_RESOLVE) + // Resolve depth in this pass as well + const float depth = VisBufferDepthMS.Load(uint3(position_ts / 2, 0), sample_location); + ResolvedDepth[position_ts] = depth; +#endif +#else VisBufferRawType vis_buffer_raw = VisBuffer.Load(uint3(position_ts, 0)); +#endif if (!is_vis_buffer_valid(vis_buffer_raw)) { diff --git a/src/renderer/shader/vis_buffer/fill_gbuffer.share.hlsl b/src/renderer/shader/vis_buffer/fill_gbuffer.share.hlsl index 791dccb6..4429a561 100644 --- a/src/renderer/shader/vis_buffer/fill_gbuffer.share.hlsl +++ b/src/renderer/shader/vis_buffer/fill_gbuffer.share.hlsl @@ -11,17 +11,19 @@ #include "shared_types.hlsl" #define Slot_VisBuffer 0 -#define Slot_GBuffer0 1 -#define Slot_GBuffer1 2 -#define Slot_instance_params 3 -#define Slot_visible_index_buffer 4 -#define Slot_buffer_position_ms 5 -#define Slot_buffer_normal_ms 6 -#define Slot_buffer_tangent_ms 7 -#define Slot_buffer_uv 8 -#define Slot_visible_meshlets 9 -#define Slot_diffuse_map_sampler 10 -#define Slot_material_maps 11 +#define Slot_VisBufferDepthMS 1 +#define Slot_ResolvedDepth 2 +#define Slot_GBuffer0 3 +#define Slot_GBuffer1 4 +#define Slot_instance_params 5 +#define Slot_visible_index_buffer 6 +#define Slot_buffer_position_ms 7 +#define Slot_buffer_normal_ms 8 +#define Slot_buffer_tangent_ms 9 +#define Slot_buffer_uv 10 +#define Slot_visible_meshlets 11 +#define Slot_diffuse_map_sampler 12 +#define Slot_material_maps 13 static const hlsl_uint GBufferFillThreadCountX = 16; static const hlsl_uint GBufferFillThreadCountY = 16; diff --git a/src/renderer/shader/vis_buffer/fill_gbuffer_msaa.comp.hlsl b/src/renderer/shader/vis_buffer/fill_gbuffer_msaa.comp.hlsl new file mode 100644 index 00000000..7b240491 --- /dev/null +++ b/src/renderer/shader/vis_buffer/fill_gbuffer_msaa.comp.hlsl @@ -0,0 +1,2 @@ +#define ENABLE_MSAA_VIS_BUFFER +#include "fill_gbuffer.comp.hlsl" diff --git a/src/renderer/shader/vis_buffer/fill_gbuffer_msaa_with_depth_resolve.comp.hlsl b/src/renderer/shader/vis_buffer/fill_gbuffer_msaa_with_depth_resolve.comp.hlsl new file mode 100644 index 00000000..238d59ac --- /dev/null +++ b/src/renderer/shader/vis_buffer/fill_gbuffer_msaa_with_depth_resolve.comp.hlsl @@ -0,0 +1,3 @@ +#define ENABLE_MSAA_VIS_BUFFER +#define ENABLE_MSAA_DEPTH_RESOLVE +#include "fill_gbuffer.comp.hlsl" diff --git a/src/renderer/shader/vis_buffer/resolve_depth_legacy.frag.hlsl b/src/renderer/shader/vis_buffer/resolve_depth_legacy.frag.hlsl new file mode 100644 index 00000000..339216cd --- /dev/null +++ b/src/renderer/shader/vis_buffer/resolve_depth_legacy.frag.hlsl @@ -0,0 +1,24 @@ +#include "lib/base.hlsl" + +VK_BINDING(0, 0) Texture2DMS DepthMS; + +struct PS_INPUT +{ + float4 PositionCS : SV_Position; + float2 PositionUV : TEXCOORD0; +}; + +struct PS_OUTPUT +{ + float depth : SV_Depth; +}; + +void main(in PS_INPUT input, out PS_OUTPUT output) +{ + uint2 position_ts = input.PositionCS.xy; + uint sample_location = (position_ts.x & 1) + (position_ts.y & 1) * 2; + + const float depth = DepthMS.Load(uint3(position_ts / 2, 0), sample_location); + + output.depth = depth; +} diff --git a/src/renderer/vulkan/Backend.cpp b/src/renderer/vulkan/Backend.cpp index 1c1bb160..92b5cc4d 100644 --- a/src/renderer/vulkan/Backend.cpp +++ b/src/renderer/vulkan/Backend.cpp @@ -208,8 +208,8 @@ namespace vkGetDeviceQueue(backend.device, backend.physical_device.present_queue_family_index, 0, &backend.present_queue); } - void vulkan_check_physical_device_supported_extensions(VkPhysicalDevice physical_device, - std::span checked_extensions) + void vulkan_check_physical_device_supported_extensions( + VkPhysicalDevice physical_device, std::span checked_extensions) { if (checked_extensions.empty()) return; diff --git a/src/renderer/vulkan/Backend.h b/src/renderer/vulkan/Backend.h index 4769ac69..f9d15b02 100644 --- a/src/renderer/vulkan/Backend.h +++ b/src/renderer/vulkan/Backend.h @@ -84,6 +84,7 @@ struct REAPER_RENDERER_API VulkanBackend { bool freeze_meshlet_culling = false; // FIXME using the framegraph we can't have persistent resources yet bool enable_debug_tile_lighting = true; + bool enable_msaa_visibility = false; } options; BackendResources* resources = nullptr; diff --git a/src/renderer/vulkan/PhysicalDevice.cpp b/src/renderer/vulkan/PhysicalDevice.cpp index 71180d2e..ff5de468 100644 --- a/src/renderer/vulkan/PhysicalDevice.cpp +++ b/src/renderer/vulkan/PhysicalDevice.cpp @@ -123,6 +123,47 @@ namespace } } } + + bool check_support_for_compute_writable_depth_format(VkPhysicalDevice physical_device) + { + // FIXME This way of detection is a bit brittle. + // We're not exactly matching what the render pass code will do + // NOTE: Maybe we could have detection code per-renderpass instead. + const VkPhysicalDeviceImageFormatInfo2 compute_writable_depth_info = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, + .pNext = nullptr, + .format = VK_FORMAT_D16_UNORM, // Only checking one format here + .type = VK_IMAGE_TYPE_2D, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + .flags = VK_FLAGS_NONE, + }; + + VkImageFormatProperties2 image_properties; + image_properties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2; + image_properties.pNext = nullptr; + + const VkResult result = + vkGetPhysicalDeviceImageFormatProperties2(physical_device, &compute_writable_depth_info, &image_properties); + + switch (result) + { + case VK_SUCCESS: + return true; + case VK_ERROR_FORMAT_NOT_SUPPORTED: + return false; + default: + AssertVk(result); + return false; + } + } + + PhysicalDeviceInfo::MacroFeatures fill_physical_device_info_features(VkPhysicalDevice handle) + { + return PhysicalDeviceInfo::MacroFeatures{ + .compute_stores_to_depth = check_support_for_compute_writable_depth_format(handle), + }; + } } // namespace PhysicalDeviceInfo create_physical_device_info(VkPhysicalDevice handle, IWindow* window, @@ -136,6 +177,8 @@ PhysicalDeviceInfo create_physical_device_info(VkPhysicalDevice handle, IWindow* fill_physical_device_supported_features(physical_device, handle); fill_physical_device_supported_queues(physical_device, handle, window, presentationSurface); + physical_device.macro_features = fill_physical_device_info_features(handle); + return physical_device; } } // namespace Reaper diff --git a/src/renderer/vulkan/PhysicalDevice.h b/src/renderer/vulkan/PhysicalDevice.h index c312accc..115945d2 100644 --- a/src/renderer/vulkan/PhysicalDevice.h +++ b/src/renderer/vulkan/PhysicalDevice.h @@ -33,6 +33,11 @@ struct PhysicalDeviceInfo // These can point to the same object! uint32_t graphics_queue_family_index; uint32_t present_queue_family_index; + + struct MacroFeatures + { + bool compute_stores_to_depth = false; + } macro_features; }; class IWindow; diff --git a/src/renderer/vulkan/ShaderModules.cpp b/src/renderer/vulkan/ShaderModules.cpp index 83d5d2a2..084c039a 100644 --- a/src/renderer/vulkan/ShaderModules.cpp +++ b/src/renderer/vulkan/ShaderModules.cpp @@ -80,6 +80,11 @@ ShaderModules create_shader_modules(VulkanBackend& backend) modules.vis_buffer_raster_fs = create_shader_module(backend.device, "vis_buffer/vis_buffer_raster.frag.spv"); modules.vis_buffer_raster_vs = create_shader_module(backend.device, "vis_buffer/vis_buffer_raster.vert.spv"); modules.vis_fill_gbuffer_cs = create_shader_module(backend.device, "vis_buffer/fill_gbuffer.comp.spv"); + modules.vis_fill_gbuffer_msaa_cs = create_shader_module(backend.device, "vis_buffer/fill_gbuffer_msaa.comp.spv"); + modules.vis_fill_gbuffer_msaa_with_depth_resolve_cs = + create_shader_module(backend.device, "vis_buffer/fill_gbuffer_msaa_with_depth_resolve.comp.spv"); + modules.vis_resolve_depth_legacy_fs = + create_shader_module(backend.device, "vis_buffer/resolve_depth_legacy.frag.spv"); return modules; } @@ -116,5 +121,8 @@ void destroy_shader_modules(VulkanBackend& backend, ShaderModules& shader_module vkDestroyShaderModule(backend.device, shader_modules.vis_buffer_raster_fs, nullptr); vkDestroyShaderModule(backend.device, shader_modules.vis_buffer_raster_vs, nullptr); vkDestroyShaderModule(backend.device, shader_modules.vis_fill_gbuffer_cs, nullptr); + vkDestroyShaderModule(backend.device, shader_modules.vis_fill_gbuffer_msaa_cs, nullptr); + vkDestroyShaderModule(backend.device, shader_modules.vis_fill_gbuffer_msaa_with_depth_resolve_cs, nullptr); + vkDestroyShaderModule(backend.device, shader_modules.vis_resolve_depth_legacy_fs, nullptr); } } // namespace Reaper diff --git a/src/renderer/vulkan/ShaderModules.h b/src/renderer/vulkan/ShaderModules.h index 1eb7eb73..531e060d 100644 --- a/src/renderer/vulkan/ShaderModules.h +++ b/src/renderer/vulkan/ShaderModules.h @@ -43,6 +43,9 @@ struct ShaderModules VkShaderModule vis_buffer_raster_fs; VkShaderModule vis_buffer_raster_vs; VkShaderModule vis_fill_gbuffer_cs; + VkShaderModule vis_fill_gbuffer_msaa_cs; + VkShaderModule vis_fill_gbuffer_msaa_with_depth_resolve_cs; + VkShaderModule vis_resolve_depth_legacy_fs; }; struct VulkanBackend; diff --git a/src/renderer/vulkan/api/AssertHelper.h b/src/renderer/vulkan/api/AssertHelper.h index 54a758d6..59da7de0 100644 --- a/src/renderer/vulkan/api/AssertHelper.h +++ b/src/renderer/vulkan/api/AssertHelper.h @@ -13,10 +13,10 @@ #include "core/Assert.h" // NOTE: It's really important to evaluate 'vk_call' only once -#define AssertVk(vk_call) \ - do \ - { \ - const VkResult result = vk_call; \ - AssertImpl(__FILE__, __FUNCTION__, __LINE__, result == VK_SUCCESS, \ - fmt::format("vulkan call failed with result '{}'", vk_to_string(result))); \ +#define AssertVk(vk_call) \ + do \ + { \ + const VkResult vk_result = vk_call; \ + AssertImpl(__FILE__, __FUNCTION__, __LINE__, vk_result == VK_SUCCESS, \ + fmt::format("vulkan call failed with result '{}'", vk_to_string(vk_result))); \ } while (false) diff --git a/src/renderer/vulkan/renderpass/TestGraphics.cpp b/src/renderer/vulkan/renderpass/TestGraphics.cpp index 4d4b609a..cdb195c0 100644 --- a/src/renderer/vulkan/renderpass/TestGraphics.cpp +++ b/src/renderer/vulkan/renderpass/TestGraphics.cpp @@ -107,6 +107,7 @@ void backend_debug_ui(VulkanBackend& backend) { ImGui::Checkbox("Freeze culling [BROKEN]", &backend.options.freeze_meshlet_culling); // FIXME ImGui::Checkbox("Enable debug tile culling", &backend.options.enable_debug_tile_lighting); + ImGui::Checkbox("Enable MSAA-based visibility", &backend.options.enable_msaa_visibility); ImGui::SliderFloat("Tonemap min (nits)", &backend.presentInfo.tonemap_min_nits, 0.0001f, 1.f); ImGui::SliderFloat("Tonemap max (nits)", &backend.presentInfo.tonemap_max_nits, 80.f, 2000.f); ImGui::SliderFloat("SDR UI max brightness (nits)", &backend.presentInfo.sdr_ui_max_brightness_nits, 20.f, @@ -198,10 +199,11 @@ void backend_execute_frame(ReaperRoot& root, VulkanBackend& backend, CommandBuff const ShadowFrameGraphRecord shadow = create_shadow_map_pass_record(builder, meshlet_pass, prepared); const VisBufferFrameGraphRecord vis_buffer_record = - create_vis_buffer_pass_record(builder, meshlet_pass, render_extent); + create_vis_buffer_pass_record(builder, meshlet_pass, render_extent, backend.options.enable_msaa_visibility, + backend.physical_device.macro_features.compute_stores_to_depth); const HZBReduceFrameGraphRecord hzb_reduce = - create_hzb_pass_record(builder, vis_buffer_record.render.depth, tiled_lighting_frame); + create_hzb_pass_record(builder, vis_buffer_record.depth, tiled_lighting_frame); const LightRasterFrameGraphRecord light_raster_record = create_tiled_lighting_raster_pass_record( builder, tiled_lighting_frame, hzb_reduce.hzb_properties, hzb_reduce.hzb_texture); @@ -213,7 +215,7 @@ void backend_execute_frame(ReaperRoot& root, VulkanBackend& backend, CommandBuff create_tiled_lighting_debug_pass_record(builder, tiled_lighting, render_extent); const ForwardFrameGraphRecord forward = - create_forward_pass_record(builder, meshlet_pass, shadow, vis_buffer_record.render.depth, render_extent); + create_forward_pass_record(builder, meshlet_pass, shadow, vis_buffer_record.depth, render_extent); const GUIFrameGraphRecord gui = create_gui_pass_record(builder, backend.presentInfo.surface_extent); @@ -260,10 +262,11 @@ void backend_execute_frame(ReaperRoot& root, VulkanBackend& backend, CommandBuff update_shadow_map_resources(descriptor_write_helper, resources.frame_storage_allocator, prepared, resources.shadow_map_resources, resources.mesh_cache.vertexBufferPosition); - update_vis_buffer_pass_resources(framegraph, resources.framegraph_resources, vis_buffer_record, - descriptor_write_helper, resources.frame_storage_allocator, - resources.vis_buffer_pass_resources, prepared, resources.samplers_resources, - resources.material_resources, resources.mesh_cache); + update_vis_buffer_pass_resources( + framegraph, resources.framegraph_resources, vis_buffer_record, descriptor_write_helper, + resources.frame_storage_allocator, resources.vis_buffer_pass_resources, prepared, + resources.samplers_resources, resources.material_resources, resources.mesh_cache, + backend.options.enable_msaa_visibility, backend.physical_device.macro_features.compute_stores_to_depth); update_tiled_lighting_raster_pass_resources(framegraph, resources.framegraph_resources, light_raster_record, descriptor_write_helper, resources.frame_storage_allocator, @@ -420,8 +423,18 @@ void backend_execute_frame(ReaperRoot& root, VulkanBackend& backend, CommandBuff resources.shadow_map_resources); record_vis_buffer_pass_command_buffer(frame_graph_helper, vis_buffer_record.render, cmdBuffer, - resources.pipeline_factory, prepared, - resources.vis_buffer_pass_resources); + resources.pipeline_factory, prepared, resources.vis_buffer_pass_resources, + backend.options.enable_msaa_visibility); + + record_fill_gbuffer_pass_command_buffer(frame_graph_helper, vis_buffer_record.fill_gbuffer, cmdBuffer, + resources.pipeline_factory, resources.vis_buffer_pass_resources, + render_extent, backend.options.enable_msaa_visibility, + backend.physical_device.macro_features.compute_stores_to_depth); + + record_legacy_depth_resolve_pass_command_buffer( + frame_graph_helper, vis_buffer_record.legacy_depth_resolve, cmdBuffer, resources.pipeline_factory, + resources.vis_buffer_pass_resources, backend.options.enable_msaa_visibility, + backend.physical_device.macro_features.compute_stores_to_depth); record_hzb_command_buffer( frame_graph_helper, hzb_reduce, cmdBuffer, resources.pipeline_factory, resources.hzb_pass_resources, @@ -429,10 +442,6 @@ void backend_execute_frame(ReaperRoot& root, VulkanBackend& backend, CommandBuff .height = vis_buffer_record.scene_depth_properties.height}, VkExtent2D{.width = hzb_reduce.hzb_properties.width, .height = hzb_reduce.hzb_properties.height}); - record_fill_gbuffer_pass_command_buffer(frame_graph_helper, vis_buffer_record.fill_gbuffer, cmdBuffer, - resources.pipeline_factory, resources.vis_buffer_pass_resources, - render_extent); - record_depth_copy(frame_graph_helper, light_raster_record.tile_depth_copy, cmdBuffer, resources.pipeline_factory, resources.tiled_raster_resources); diff --git a/src/renderer/vulkan/renderpass/TiledLightingPass.cpp b/src/renderer/vulkan/renderpass/TiledLightingPass.cpp index c9e2d63f..624af790 100644 --- a/src/renderer/vulkan/renderpass/TiledLightingPass.cpp +++ b/src/renderer/vulkan/renderpass/TiledLightingPass.cpp @@ -183,7 +183,7 @@ TiledLightingFrameGraphRecord create_tiled_lighting_pass_record(FrameGraph::Buil VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL}); tiled_lighting.depth = - builder.read_texture(tiled_lighting.pass_handle, vis_buffer_record.render.depth, + builder.read_texture(tiled_lighting.pass_handle, vis_buffer_record.depth, GPUTextureAccess{VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT, VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL}); diff --git a/src/renderer/vulkan/renderpass/VisibilityBufferPass.cpp b/src/renderer/vulkan/renderpass/VisibilityBufferPass.cpp index f9eda54d..beaaaa03 100644 --- a/src/renderer/vulkan/renderpass/VisibilityBufferPass.cpp +++ b/src/renderer/vulkan/renderpass/VisibilityBufferPass.cpp @@ -42,6 +42,21 @@ namespace Reaper { +static const u32 MSAASamples = 4; + +// NOTE: origin is in the top-left corner +static const std::array MSAA4XGridSampleLocations = { + VkSampleLocationEXT{0.25f, 0.25f}, + VkSampleLocationEXT{0.75f, 0.25f}, + VkSampleLocationEXT{0.25f, 0.75f}, + VkSampleLocationEXT{0.75f, 0.75f}, +}; +// VkSampleLocationEXT{0.5f, 0.5f}, +// VkSampleLocationEXT{0.5f, 0.5f}, +// VkSampleLocationEXT{0.5f, 0.5f}, +// VkSampleLocationEXT{0.5f, 0.5f}, +// }; + namespace Render { enum BindingIndex @@ -65,6 +80,8 @@ namespace FillGBuffer enum BindingIndex { VisBuffer, + VisBufferDepthMS, // MSAA-only + ResolvedDepth, // MSAA-only GBuffer0, GBuffer1, instance_params, @@ -84,6 +101,14 @@ namespace FillGBuffer .count = 1, .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, .stage_mask = VK_SHADER_STAGE_COMPUTE_BIT}, + {.slot = Slot_VisBufferDepthMS, + .count = 1, + .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .stage_mask = VK_SHADER_STAGE_COMPUTE_BIT}, + {.slot = Slot_ResolvedDepth, + .count = 1, + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .stage_mask = VK_SHADER_STAGE_COMPUTE_BIT}, {.slot = Slot_GBuffer0, .count = 1, .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, @@ -134,7 +159,7 @@ namespace FillGBuffer namespace { VkPipeline create_vis_buffer_pipeline(VkDevice device, const ShaderModules& shader_modules, - VkPipelineLayout pipeline_layout) + VkPipelineLayout pipeline_layout, bool enable_msaa) { std::vector shader_stages = { default_pipeline_shader_stage_create_info(VK_SHADER_STAGE_VERTEX_BIT, shader_modules.vis_buffer_raster_vs), @@ -171,6 +196,27 @@ namespace pipeline_properties.pipeline_rendering.pColorAttachmentFormats = color_formats.data(); pipeline_properties.pipeline_rendering.depthAttachmentFormat = depth_format; + const VkPipelineSampleLocationsStateCreateInfoEXT sample_location_info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT, + .pNext = nullptr, + .sampleLocationsEnable = true, + .sampleLocationsInfo = + { + .sType = VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT, + .pNext = nullptr, + .sampleLocationsPerPixel = SampleCountToVulkan(MSAASamples), + .sampleLocationGridSize = VkExtent2D{1, 1}, + .sampleLocationsCount = static_cast(MSAA4XGridSampleLocations.size()), + .pSampleLocations = MSAA4XGridSampleLocations.data(), + }, + }; + + if (enable_msaa) + { + pipeline_properties.multisample.rasterizationSamples = SampleCountToVulkan(MSAASamples); + pipeline_properties.multisample.pNext = &sample_location_info; + } + std::vector dynamic_states = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; VkPipeline pipeline = create_graphics_pipeline(device, shader_stages, pipeline_properties, dynamic_states); @@ -181,11 +227,58 @@ namespace return pipeline; } + VkPipeline create_vis_buffer_pipeline_non_msaa(VkDevice device, const ShaderModules& shader_modules, + VkPipelineLayout pipeline_layout) + { + return create_vis_buffer_pipeline(device, shader_modules, pipeline_layout, false); + } + + VkPipeline create_vis_buffer_pipeline_msaa(VkDevice device, const ShaderModules& shader_modules, + VkPipelineLayout pipeline_layout) + { + return create_vis_buffer_pipeline(device, shader_modules, pipeline_layout, true); + } + VkPipeline create_vis_buffer_fill_pipeline(VkDevice device, const ShaderModules& shader_modules, VkPipelineLayout pipeline_layout) { return create_compute_pipeline(device, pipeline_layout, shader_modules.vis_fill_gbuffer_cs); - ; + } + + VkPipeline create_vis_buffer_fill_msaa_pipeline(VkDevice device, const ShaderModules& shader_modules, + VkPipelineLayout pipeline_layout) + { + return create_compute_pipeline(device, pipeline_layout, shader_modules.vis_fill_gbuffer_msaa_cs); + } + + VkPipeline create_vis_buffer_fill_msaa_with_resolve_pipeline(VkDevice device, const ShaderModules& shader_modules, + VkPipelineLayout pipeline_layout) + { + return create_compute_pipeline(device, pipeline_layout, + shader_modules.vis_fill_gbuffer_msaa_with_depth_resolve_cs); + } + + VkPipeline create_legacy_depth_resolve_pipeline(VkDevice device, const ShaderModules& shader_modules, + VkPipelineLayout pipeline_layout) + { + std::vector shader_stages = { + default_pipeline_shader_stage_create_info(VK_SHADER_STAGE_VERTEX_BIT, + shader_modules.fullscreen_triangle_vs), + default_pipeline_shader_stage_create_info(VK_SHADER_STAGE_FRAGMENT_BIT, + shader_modules.vis_resolve_depth_legacy_fs), + }; + + GraphicsPipelineProperties pipeline_properties = default_graphics_pipeline_properties(); + pipeline_properties.depth_stencil.depthTestEnable = VK_TRUE; + pipeline_properties.depth_stencil.depthWriteEnable = VK_TRUE; + pipeline_properties.depth_stencil.depthCompareOp = VK_COMPARE_OP_ALWAYS; + pipeline_properties.pipeline_layout = pipeline_layout; + pipeline_properties.pipeline_rendering.depthAttachmentFormat = PixelFormatToVulkan(MainPassDepthFormat); + + const std::vector dynamic_states = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; + + VkPipeline pipeline = create_graphics_pipeline(device, shader_stages, pipeline_properties, dynamic_states); + return pipeline; } } // namespace @@ -214,7 +307,32 @@ VisibilityBufferPassResources create_vis_buffer_pass_resources(VulkanBackend& register_pipeline_creator(pipeline_factory, PipelineCreator{ .pipeline_layout = resources.pipe.pipelineLayout, - .pipeline_creation_function = &create_vis_buffer_pipeline, + .pipeline_creation_function = &create_vis_buffer_pipeline_non_msaa, + }); + } + + { + using namespace Render; + + std::vector layout_bindings(g_bindings.size()); + fill_layout_bindings(layout_bindings, g_bindings); + + const VkDescriptorSetLayout descriptor_set_layout = + create_descriptor_set_layout(backend.device, layout_bindings); + + resources.pipe_msaa.desc_set_layout = descriptor_set_layout; + + resources.pipe_msaa.pipelineLayout = + create_pipeline_layout(backend.device, std::span(&descriptor_set_layout, 1)); + + Assert(backend.physical_device.graphics_queue_family_index + == backend.physical_device.present_queue_family_index); + + resources.pipe_msaa.pipeline_index = + register_pipeline_creator(pipeline_factory, + PipelineCreator{ + .pipeline_layout = resources.pipe_msaa.pipelineLayout, + .pipeline_creation_function = &create_vis_buffer_pipeline_msaa, }); } @@ -246,12 +364,94 @@ VisibilityBufferPassResources create_vis_buffer_pass_resources(VulkanBackend& }); } + { + using namespace FillGBuffer; + + std::vector layout_bindings(g_bindings.size()); + fill_layout_bindings(layout_bindings, g_bindings); + + std::vector binding_flags(layout_bindings.size(), VK_FLAGS_NONE); + binding_flags.back() = VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT; + + VkDescriptorSetLayout descriptor_set_layout = + create_descriptor_set_layout(backend.device, layout_bindings, binding_flags); + + const VkPushConstantRange pushConstantRange = {VK_SHADER_STAGE_COMPUTE_BIT, 0, + sizeof(FillGBufferPushConstants)}; + + VkPipelineLayout pipelineLayout = create_pipeline_layout(backend.device, std::span(&descriptor_set_layout, 1), + std::span(&pushConstantRange, 1)); + + resources.fill_pipe_msaa.desc_set_layout = descriptor_set_layout; + resources.fill_pipe_msaa.pipelineLayout = pipelineLayout; + resources.fill_pipe_msaa.pipeline_index = + register_pipeline_creator(pipeline_factory, + PipelineCreator{ + .pipeline_layout = pipelineLayout, + .pipeline_creation_function = &create_vis_buffer_fill_msaa_pipeline, + }); + } + + { + using namespace FillGBuffer; + + std::vector layout_bindings(g_bindings.size()); + fill_layout_bindings(layout_bindings, g_bindings); + + std::vector binding_flags(layout_bindings.size(), VK_FLAGS_NONE); + binding_flags.back() = VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT; + + VkDescriptorSetLayout descriptor_set_layout = + create_descriptor_set_layout(backend.device, layout_bindings, binding_flags); + + const VkPushConstantRange pushConstantRange = {VK_SHADER_STAGE_COMPUTE_BIT, 0, + sizeof(FillGBufferPushConstants)}; + + VkPipelineLayout pipelineLayout = create_pipeline_layout(backend.device, std::span(&descriptor_set_layout, 1), + std::span(&pushConstantRange, 1)); + + resources.fill_pipe_msaa_with_resolve.desc_set_layout = descriptor_set_layout; + resources.fill_pipe_msaa_with_resolve.pipelineLayout = pipelineLayout; + resources.fill_pipe_msaa_with_resolve.pipeline_index = register_pipeline_creator( + pipeline_factory, + PipelineCreator{ + .pipeline_layout = pipelineLayout, + .pipeline_creation_function = &create_vis_buffer_fill_msaa_with_resolve_pipeline, + }); + } + + { + std::vector layout_bindings = { + {0, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}, + }; + + std::vector binding_flags(layout_bindings.size()); + + VkDescriptorSetLayout descriptor_set_layout = create_descriptor_set_layout(backend.device, layout_bindings); + + VkPipelineLayout pipelineLayout = create_pipeline_layout(backend.device, std::span(&descriptor_set_layout, 1)); + + resources.legacy_resolve_pipe.desc_set_layout = descriptor_set_layout; + resources.legacy_resolve_pipe.pipelineLayout = pipelineLayout; + resources.legacy_resolve_pipe.pipeline_index = + register_pipeline_creator(pipeline_factory, + PipelineCreator{ + .pipeline_layout = pipelineLayout, + .pipeline_creation_function = &create_legacy_depth_resolve_pipeline, + }); + } + allocate_descriptor_sets(backend.device, backend.global_descriptor_pool, std::span(&resources.pipe.desc_set_layout, 1), std::span(&resources.descriptor_set, 1)); allocate_descriptor_sets(backend.device, backend.global_descriptor_pool, std::span(&resources.fill_pipe.desc_set_layout, 1), std::span(&resources.descriptor_set_fill, 1)); + + allocate_descriptor_sets(backend.device, backend.global_descriptor_pool, + std::span(&resources.legacy_resolve_pipe.desc_set_layout, 1), + std::span(&resources.descriptor_set_legacy_resolve, 1)); + return resources; } @@ -260,96 +460,192 @@ void destroy_vis_buffer_pass_resources(VulkanBackend& backend, VisibilityBufferP vkDestroyPipelineLayout(backend.device, resources.pipe.pipelineLayout, nullptr); vkDestroyDescriptorSetLayout(backend.device, resources.pipe.desc_set_layout, nullptr); + vkDestroyPipelineLayout(backend.device, resources.pipe_msaa.pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(backend.device, resources.pipe_msaa.desc_set_layout, nullptr); + vkDestroyPipelineLayout(backend.device, resources.fill_pipe.pipelineLayout, nullptr); vkDestroyDescriptorSetLayout(backend.device, resources.fill_pipe.desc_set_layout, nullptr); + + vkDestroyPipelineLayout(backend.device, resources.fill_pipe_msaa.pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(backend.device, resources.fill_pipe_msaa.desc_set_layout, nullptr); + + vkDestroyPipelineLayout(backend.device, resources.fill_pipe_msaa_with_resolve.pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(backend.device, resources.fill_pipe_msaa_with_resolve.desc_set_layout, nullptr); + + vkDestroyPipelineLayout(backend.device, resources.legacy_resolve_pipe.pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(backend.device, resources.legacy_resolve_pipe.desc_set_layout, nullptr); } VisBufferFrameGraphRecord create_vis_buffer_pass_record(FrameGraph::Builder& builder, const CullMeshletsFrameGraphRecord& meshlet_pass, - VkExtent2D render_extent) + VkExtent2D render_extent, bool enable_msaa, + bool support_shader_stores_to_depth) { - VisBufferFrameGraphRecord vis_buffer_record; - VisBufferFrameGraphRecord::Render& visibility = vis_buffer_record.render; - VisBufferFrameGraphRecord::FillGBuffer& visibility_gbuffer = vis_buffer_record.fill_gbuffer; - - visibility.pass_handle = builder.create_render_pass("Visibility"); - - visibility.vis_buffer = builder.create_texture( - visibility.pass_handle, "Visibility Buffer", - default_texture_properties(render_extent.width, render_extent.height, VisibilityBufferFormat, - GPUTextureUsage::ColorAttachment | GPUTextureUsage::Sampled), - GPUTextureAccess{VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, - VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL}); + VisBufferFrameGraphRecord vis_buffer_record; - const GPUTextureProperties scene_depth_properties = + GPUTextureProperties scene_depth_properties = default_texture_properties(render_extent.width, render_extent.height, MainPassDepthFormat, GPUTextureUsage::DepthStencilAttachment | GPUTextureUsage::Sampled); - vis_buffer_record.scene_depth_properties = scene_depth_properties; + { + VisBufferFrameGraphRecord::Render& visibility = vis_buffer_record.render; + + visibility.pass_handle = builder.create_render_pass("Visibility"); + + GPUTextureProperties vis_buffer_properties = + default_texture_properties(render_extent.width, render_extent.height, VisibilityBufferFormat, + GPUTextureUsage::ColorAttachment | GPUTextureUsage::Sampled); + + if (enable_msaa) + { + // FIXME dragons be hiding when using even resolutions! + GPUTextureProperties vis_buffer_msaa_properties = vis_buffer_properties; + vis_buffer_msaa_properties.width /= 2; + vis_buffer_msaa_properties.height /= 2; + vis_buffer_msaa_properties.sample_count = MSAASamples; + + visibility.vis_buffer = builder.create_texture( + visibility.pass_handle, "Visibility Buffer MSAA", vis_buffer_msaa_properties, + GPUTextureAccess{VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL}); + } + else + { + visibility.vis_buffer = builder.create_texture( + visibility.pass_handle, "Visibility Buffer", vis_buffer_properties, + GPUTextureAccess{VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL}); + } + + if (enable_msaa) + { + // FIXME dragons be hiding when using even resolutions! + GPUTextureProperties vis_depth_msaa_properties = scene_depth_properties; + vis_depth_msaa_properties.width /= 2; + vis_depth_msaa_properties.height /= 2; + vis_depth_msaa_properties.sample_count = MSAASamples; + + visibility.depth = builder.create_texture( + visibility.pass_handle, "Visibility Depth MSAA", vis_depth_msaa_properties, + GPUTextureAccess{VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT + | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT, + VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL}); + } + else + { + visibility.depth = builder.create_texture( + visibility.pass_handle, "Main Depth", scene_depth_properties, + GPUTextureAccess{VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT + | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT, + VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL}); + + vis_buffer_record.depth = visibility.depth; // FIXME + } + + visibility.meshlet_counters = builder.read_buffer( + visibility.pass_handle, meshlet_pass.cull_triangles.meshlet_counters, + GPUBufferAccess{VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT, VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT}); + + visibility.meshlet_indirect_draw_commands = builder.read_buffer( + visibility.pass_handle, meshlet_pass.cull_triangles.meshlet_indirect_draw_commands, + GPUBufferAccess{VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT, VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT}); + + visibility.meshlet_visible_index_buffer = + builder.read_buffer(visibility.pass_handle, meshlet_pass.cull_triangles.meshlet_visible_index_buffer, + GPUBufferAccess{VK_PIPELINE_STAGE_2_INDEX_INPUT_BIT, VK_ACCESS_2_INDEX_READ_BIT}); + + visibility.visible_meshlet_buffer = + builder.read_buffer(visibility.pass_handle, meshlet_pass.cull_triangles.visible_meshlet_buffer, + GPUBufferAccess{VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT}); + } + + { + VisBufferFrameGraphRecord::FillGBuffer& visibility_gbuffer = vis_buffer_record.fill_gbuffer; + + visibility_gbuffer.pass_handle = builder.create_render_pass("Visibility Fill GBuffer"); + + visibility_gbuffer.vis_buffer = + builder.read_texture(visibility_gbuffer.pass_handle, vis_buffer_record.render.vis_buffer, + GPUTextureAccess{VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL}); - visibility.depth = builder.create_texture(visibility.pass_handle, "Main Depth", scene_depth_properties, - GPUTextureAccess{VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT, - VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL}); + if (enable_msaa && support_shader_stores_to_depth) + { + visibility_gbuffer.vis_buffer_depth_msaa = + builder.read_texture(visibility_gbuffer.pass_handle, vis_buffer_record.render.depth, + GPUTextureAccess{VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_2_SHADER_READ_BIT, VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL}); - visibility.meshlet_counters = builder.read_buffer( - visibility.pass_handle, meshlet_pass.cull_triangles.meshlet_counters, - GPUBufferAccess{VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT, VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT}); + scene_depth_properties.usage_flags |= GPUTextureUsage::Storage; - visibility.meshlet_indirect_draw_commands = builder.read_buffer( - visibility.pass_handle, meshlet_pass.cull_triangles.meshlet_indirect_draw_commands, - GPUBufferAccess{VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT, VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT}); + visibility_gbuffer.resolved_depth = + builder.create_texture(visibility_gbuffer.pass_handle, "Main Depth", scene_depth_properties, + GPUTextureAccess{VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_2_SHADER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL}); - visibility.meshlet_visible_index_buffer = - builder.read_buffer(visibility.pass_handle, meshlet_pass.cull_triangles.meshlet_visible_index_buffer, - GPUBufferAccess{VK_PIPELINE_STAGE_2_INDEX_INPUT_BIT, VK_ACCESS_2_INDEX_READ_BIT}); + vis_buffer_record.depth = visibility_gbuffer.resolved_depth; // FIXME + } + else + { + visibility_gbuffer.vis_buffer_depth_msaa = FrameGraph::InvalidResourceUsageHandle; + visibility_gbuffer.resolved_depth = FrameGraph::InvalidResourceUsageHandle; + } - visibility.visible_meshlet_buffer = - builder.read_buffer(visibility.pass_handle, meshlet_pass.cull_triangles.visible_meshlet_buffer, - GPUBufferAccess{VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT}); + visibility_gbuffer.gbuffer_rt0 = builder.create_texture( + visibility_gbuffer.pass_handle, "GBuffer RT0", + default_texture_properties(render_extent.width, render_extent.height, GBufferRT0Format, + GPUTextureUsage::Sampled | GPUTextureUsage::Storage), + GPUTextureAccess{VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_WRITE_BIT, + VK_IMAGE_LAYOUT_GENERAL}); + + visibility_gbuffer.gbuffer_rt1 = builder.create_texture( + visibility_gbuffer.pass_handle, "GBuffer RT1", + default_texture_properties(render_extent.width, render_extent.height, GBufferRT1Format, + GPUTextureUsage::Sampled | GPUTextureUsage::Storage), + GPUTextureAccess{VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_WRITE_BIT, + VK_IMAGE_LAYOUT_GENERAL}); + + visibility_gbuffer.meshlet_visible_index_buffer = builder.read_buffer( + visibility_gbuffer.pass_handle, meshlet_pass.cull_triangles.meshlet_visible_index_buffer, + GPUBufferAccess{VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT}); + + visibility_gbuffer.visible_meshlet_buffer = + builder.read_buffer(visibility_gbuffer.pass_handle, meshlet_pass.cull_triangles.visible_meshlet_buffer, + GPUBufferAccess{VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT}); + } - visibility_gbuffer.pass_handle = builder.create_render_pass("Visibility Fill GBuffer"); + if (enable_msaa && !support_shader_stores_to_depth) + { + VisBufferFrameGraphRecord::LegacyDepthResolve& legacy_depth_resolve = vis_buffer_record.legacy_depth_resolve; - visibility_gbuffer.vis_buffer = - builder.read_texture(visibility_gbuffer.pass_handle, visibility.vis_buffer, - GPUTextureAccess{VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT, - VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL}); + legacy_depth_resolve.pass_handle = builder.create_render_pass("Legacy Depth Resolve"); - visibility_gbuffer.gbuffer_rt0 = - builder.create_texture(visibility_gbuffer.pass_handle, "GBuffer RT0", - default_texture_properties(render_extent.width, render_extent.height, GBufferRT0Format, - GPUTextureUsage::Sampled | GPUTextureUsage::Storage), - GPUTextureAccess{VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_WRITE_BIT, - VK_IMAGE_LAYOUT_GENERAL}); + legacy_depth_resolve.vis_buffer_depth_msaa = + builder.read_texture(legacy_depth_resolve.pass_handle, vis_buffer_record.render.depth, + GPUTextureAccess{VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL}); - visibility_gbuffer.gbuffer_rt1 = - builder.create_texture(visibility_gbuffer.pass_handle, "GBuffer RT1", - default_texture_properties(render_extent.width, render_extent.height, GBufferRT1Format, - GPUTextureUsage::Sampled | GPUTextureUsage::Storage), - GPUTextureAccess{VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_WRITE_BIT, - VK_IMAGE_LAYOUT_GENERAL}); + legacy_depth_resolve.resolved_depth = builder.create_texture( + legacy_depth_resolve.pass_handle, "Main Depth", scene_depth_properties, + GPUTextureAccess{VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT, + VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL}); - visibility_gbuffer.meshlet_visible_index_buffer = - builder.read_buffer(visibility_gbuffer.pass_handle, meshlet_pass.cull_triangles.meshlet_visible_index_buffer, - GPUBufferAccess{VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT}); + vis_buffer_record.depth = legacy_depth_resolve.resolved_depth; // FIXME + } - visibility_gbuffer.visible_meshlet_buffer = - builder.read_buffer(visibility_gbuffer.pass_handle, meshlet_pass.cull_triangles.visible_meshlet_buffer, - GPUBufferAccess{VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, VK_ACCESS_2_SHADER_READ_BIT}); + vis_buffer_record.scene_depth_properties = scene_depth_properties; return vis_buffer_record; } -void update_vis_buffer_pass_resources(const FrameGraph::FrameGraph& frame_graph, - const FrameGraphResources& frame_graph_resources, - const VisBufferFrameGraphRecord& record, - DescriptorWriteHelper& write_helper, +void update_vis_buffer_pass_resources(const FrameGraph::FrameGraph& frame_graph, + const FrameGraphResources& frame_graph_resources, + const VisBufferFrameGraphRecord& record, DescriptorWriteHelper& write_helper, StorageBufferAllocator& frame_storage_allocator, - const VisibilityBufferPassResources& resources, - const PreparedData& prepared, - const SamplerResources& sampler_resources, - const MaterialResources& material_resources, - const MeshCache& mesh_cache) + const VisibilityBufferPassResources& resources, const PreparedData& prepared, + const SamplerResources& sampler_resources, + const MaterialResources& material_resources, const MeshCache& mesh_cache, + bool enable_msaa, bool support_shader_stores_to_depth) { REAPER_PROFILE_SCOPE_FUNC(); @@ -396,6 +692,20 @@ void update_vis_buffer_pass_resources(const FrameGraph::FrameGraph& frame write_helper.append(resources.descriptor_set_fill, g_bindings[VisBuffer], vis_buffer.default_view_handle, vis_buffer.image_layout); + + if (enable_msaa && support_shader_stores_to_depth) + { + const FrameGraphTexture vis_buffer_depth_msaa = + get_frame_graph_texture(frame_graph_resources, frame_graph, record.fill_gbuffer.vis_buffer_depth_msaa); + const FrameGraphTexture resolved_depth = + get_frame_graph_texture(frame_graph_resources, frame_graph, record.fill_gbuffer.resolved_depth); + + write_helper.append(resources.descriptor_set_fill, g_bindings[VisBufferDepthMS], + vis_buffer_depth_msaa.default_view_handle, vis_buffer_depth_msaa.image_layout); + write_helper.append(resources.descriptor_set_fill, g_bindings[ResolvedDepth], + resolved_depth.default_view_handle, resolved_depth.image_layout); + } + write_helper.append(resources.descriptor_set_fill, g_bindings[GBuffer0], gbuffer_rt0.default_view_handle, gbuffer_rt0.image_layout); write_helper.append(resources.descriptor_set_fill, g_bindings[GBuffer1], gbuffer_rt1.default_view_handle, @@ -433,14 +743,22 @@ void update_vis_buffer_pass_resources(const FrameGraph::FrameGraph& frame g_bindings[material_maps].type, albedo_image_infos)); } } + + if (enable_msaa && !support_shader_stores_to_depth) + { + const FrameGraphTexture vis_buffer_depth_msaa = get_frame_graph_texture( + frame_graph_resources, frame_graph, record.legacy_depth_resolve.vis_buffer_depth_msaa); + + write_helper.append(resources.descriptor_set_legacy_resolve, 0, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + vis_buffer_depth_msaa.default_view_handle, vis_buffer_depth_msaa.image_layout); + } } void record_vis_buffer_pass_command_buffer(const FrameGraphHelper& frame_graph_helper, const VisBufferFrameGraphRecord::Render& pass_record, - CommandBuffer& cmdBuffer, - const PipelineFactory& pipeline_factory, - const PreparedData& prepared, - const VisibilityBufferPassResources& pass_resources) + CommandBuffer& cmdBuffer, const PipelineFactory& pipeline_factory, + const PreparedData& prepared, + const VisibilityBufferPassResources& pass_resources, bool enable_msaa) { // FIXME should be moved out if (prepared.mesh_instances.empty()) @@ -450,6 +768,8 @@ void record_vis_buffer_pass_command_buffer(const FrameGraphHelper& const FrameGraphBarrierScope framegraph_barrier_scope(cmdBuffer, frame_graph_helper, pass_record.pass_handle); + const VisibilityBufferPipelineInfo& pipe = enable_msaa ? pass_resources.pipe_msaa : pass_resources.pipe; + const FrameGraphBuffer meshlet_counters = get_frame_graph_buffer( frame_graph_helper.resources, frame_graph_helper.frame_graph, pass_record.meshlet_counters); const FrameGraphBuffer meshlet_indirect_draw_commands = get_frame_graph_buffer( @@ -466,7 +786,7 @@ void record_vis_buffer_pass_command_buffer(const FrameGraphHelper& const VkViewport viewport = default_vk_viewport(pass_rect); vkCmdBindPipeline(cmdBuffer.handle, VK_PIPELINE_BIND_POINT_GRAPHICS, - get_pipeline(pipeline_factory, pass_resources.pipe.pipeline_index)); + get_pipeline(pipeline_factory, pipe.pipeline_index)); vkCmdSetViewport(cmdBuffer.handle, 0, 1, &viewport); vkCmdSetScissor(cmdBuffer.handle, 0, 1, &pass_rect); @@ -489,15 +809,11 @@ void record_vis_buffer_pass_command_buffer(const FrameGraphHelper& const MeshletDrawParams meshlet_draw = get_meshlet_draw_params(prepared.main_culling_pass_index); - std::vector pass_descriptors = { - pass_resources.descriptor_set, - }; - vkCmdBindIndexBuffer(cmdBuffer.handle, meshlet_visible_index_buffer.handle, meshlet_draw.index_buffer_offset, meshlet_draw.index_type); - vkCmdBindDescriptorSets(cmdBuffer.handle, VK_PIPELINE_BIND_POINT_GRAPHICS, pass_resources.pipe.pipelineLayout, 0, - static_cast(pass_descriptors.size()), pass_descriptors.data(), 0, nullptr); + vkCmdBindDescriptorSets(cmdBuffer.handle, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.pipelineLayout, 0, 1, + &pass_resources.descriptor_set, 0, nullptr); vkCmdDrawIndexedIndirectCount(cmdBuffer.handle, meshlet_indirect_draw_commands.handle, meshlet_draw.command_buffer_offset, meshlet_counters.handle, @@ -509,27 +825,31 @@ void record_vis_buffer_pass_command_buffer(const FrameGraphHelper& void record_fill_gbuffer_pass_command_buffer(const FrameGraphHelper& frame_graph_helper, const VisBufferFrameGraphRecord::FillGBuffer& pass_record, - CommandBuffer& cmdBuffer, - const PipelineFactory& pipeline_factory, - const VisibilityBufferPassResources& resources, - VkExtent2D render_extent) + CommandBuffer& cmdBuffer, const PipelineFactory& pipeline_factory, + const VisibilityBufferPassResources& resources, VkExtent2D render_extent, + bool enable_msaa, bool support_shader_stores_to_depth) { REAPER_GPU_SCOPE(cmdBuffer, "Visibility Fill GBuffer"); const FrameGraphBarrierScope framegraph_barrier_scope(cmdBuffer, frame_graph_helper, pass_record.pass_handle); + const VisibilityBufferPipelineInfo& pipe = + enable_msaa + ? (support_shader_stores_to_depth ? resources.fill_pipe_msaa_with_resolve : resources.fill_pipe_msaa) + : resources.fill_pipe; + vkCmdBindPipeline(cmdBuffer.handle, VK_PIPELINE_BIND_POINT_COMPUTE, - get_pipeline(pipeline_factory, resources.fill_pipe.pipeline_index)); + get_pipeline(pipeline_factory, pipe.pipeline_index)); FillGBufferPushConstants push_constants; push_constants.extent_ts = glm::uvec2(render_extent.width, render_extent.height); push_constants.extent_ts_inv = glm::fvec2(1.f / static_cast(render_extent.width), 1.f / static_cast(render_extent.height)); - vkCmdPushConstants(cmdBuffer.handle, resources.fill_pipe.pipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0, - sizeof(push_constants), &push_constants); + vkCmdPushConstants(cmdBuffer.handle, pipe.pipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(push_constants), + &push_constants); - vkCmdBindDescriptorSets(cmdBuffer.handle, VK_PIPELINE_BIND_POINT_COMPUTE, resources.fill_pipe.pipelineLayout, 0, 1, + vkCmdBindDescriptorSets(cmdBuffer.handle, VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipelineLayout, 0, 1, &resources.descriptor_set_fill, 0, nullptr); vkCmdDispatch(cmdBuffer.handle, @@ -537,4 +857,49 @@ void record_fill_gbuffer_pass_command_buffer(const FrameGraphHelper& div_round_up(render_extent.height, GBufferFillThreadCountY), 1); } + +void record_legacy_depth_resolve_pass_command_buffer(const FrameGraphHelper& frame_graph_helper, + const VisBufferFrameGraphRecord::LegacyDepthResolve& pass_record, + CommandBuffer& cmdBuffer, const PipelineFactory& pipeline_factory, + const VisibilityBufferPassResources& resources, bool enable_msaa, + bool support_shader_stores_to_depth) +{ + bool is_pass_active = enable_msaa && !support_shader_stores_to_depth; + + if (!is_pass_active) + return; + + REAPER_GPU_SCOPE(cmdBuffer, "Legacy Depth Resolve"); + + const FrameGraphBarrierScope framegraph_barrier_scope(cmdBuffer, frame_graph_helper, pass_record.pass_handle); + + FrameGraphTexture depth_dst = get_frame_graph_texture(frame_graph_helper.resources, frame_graph_helper.frame_graph, + pass_record.resolved_depth); + + const VkExtent2D depth_extent = {depth_dst.properties.width, depth_dst.properties.height}; + const VkRect2D pass_rect = default_vk_rect(depth_extent); + const VkViewport viewport = default_vk_viewport(pass_rect); + + vkCmdBindPipeline(cmdBuffer.handle, VK_PIPELINE_BIND_POINT_GRAPHICS, + get_pipeline(pipeline_factory, resources.legacy_resolve_pipe.pipeline_index)); + + vkCmdSetViewport(cmdBuffer.handle, 0, 1, &viewport); + vkCmdSetScissor(cmdBuffer.handle, 0, 1, &pass_rect); + + vkCmdBindDescriptorSets(cmdBuffer.handle, VK_PIPELINE_BIND_POINT_GRAPHICS, + resources.legacy_resolve_pipe.pipelineLayout, 0, 1, + &resources.descriptor_set_legacy_resolve, 0, nullptr); + + VkRenderingAttachmentInfo depth_attachment = + default_rendering_attachment_info(depth_dst.default_view_handle, depth_dst.image_layout); + depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + + const VkRenderingInfo rendering_info = default_rendering_info(pass_rect, nullptr, &depth_attachment); + + vkCmdBeginRendering(cmdBuffer.handle, &rendering_info); + + vkCmdDraw(cmdBuffer.handle, 3, 1, 0, 0); + + vkCmdEndRendering(cmdBuffer.handle); +} } // namespace Reaper diff --git a/src/renderer/vulkan/renderpass/VisibilityBufferPass.h b/src/renderer/vulkan/renderpass/VisibilityBufferPass.h index a45c0191..68f76069 100644 --- a/src/renderer/vulkan/renderpass/VisibilityBufferPass.h +++ b/src/renderer/vulkan/renderpass/VisibilityBufferPass.h @@ -30,10 +30,16 @@ struct VisibilityBufferPipelineInfo struct VisibilityBufferPassResources { VisibilityBufferPipelineInfo pipe; + VisibilityBufferPipelineInfo pipe_msaa; VkDescriptorSet descriptor_set; VisibilityBufferPipelineInfo fill_pipe; + VisibilityBufferPipelineInfo fill_pipe_msaa; + VisibilityBufferPipelineInfo fill_pipe_msaa_with_resolve; VkDescriptorSet descriptor_set_fill; + + VisibilityBufferPipelineInfo legacy_resolve_pipe; + VkDescriptorSet descriptor_set_legacy_resolve; }; struct VulkanBackend; @@ -51,8 +57,6 @@ namespace FrameGraph struct VisBufferFrameGraphRecord { - GPUTextureProperties scene_depth_properties; - struct Render { FrameGraph::RenderPassHandle pass_handle; @@ -68,11 +72,26 @@ struct VisBufferFrameGraphRecord { FrameGraph::RenderPassHandle pass_handle; FrameGraph::ResourceUsageHandle vis_buffer; + FrameGraph::ResourceUsageHandle vis_buffer_depth_msaa; + FrameGraph::ResourceUsageHandle resolved_depth; FrameGraph::ResourceUsageHandle gbuffer_rt0; FrameGraph::ResourceUsageHandle gbuffer_rt1; FrameGraph::ResourceUsageHandle meshlet_visible_index_buffer; FrameGraph::ResourceUsageHandle visible_meshlet_buffer; } fill_gbuffer; + + // Used when MSAA is on and there's no support for shader stores to depth + struct LegacyDepthResolve + { + FrameGraph::RenderPassHandle pass_handle; + FrameGraph::ResourceUsageHandle vis_buffer_depth_msaa; + FrameGraph::ResourceUsageHandle resolved_depth; + } legacy_depth_resolve; + + // NOTE: dynamically set to whoever produces the usable depth for further render passes + FrameGraph::ResourceUsageHandle depth; + + GPUTextureProperties scene_depth_properties; }; struct TiledLightingFrame; @@ -81,7 +100,8 @@ struct CullMeshletsFrameGraphRecord; VisBufferFrameGraphRecord create_vis_buffer_pass_record(FrameGraph::Builder& builder, const CullMeshletsFrameGraphRecord& meshlet_pass, - VkExtent2D render_extent); + VkExtent2D render_extent, bool enable_msaa, + bool support_shader_stores_to_depth); struct FrameGraphResources; struct MaterialResources; @@ -91,31 +111,33 @@ class DescriptorWriteHelper; struct PreparedData; struct StorageBufferAllocator; -void update_vis_buffer_pass_resources(const FrameGraph::FrameGraph& frame_graph, - const FrameGraphResources& frame_graph_resources, - const VisBufferFrameGraphRecord& record, - DescriptorWriteHelper& write_helper, +void update_vis_buffer_pass_resources(const FrameGraph::FrameGraph& frame_graph, + const FrameGraphResources& frame_graph_resources, + const VisBufferFrameGraphRecord& record, DescriptorWriteHelper& write_helper, StorageBufferAllocator& frame_storage_allocator, - const VisibilityBufferPassResources& resources, - const PreparedData& prepared, - const SamplerResources& sampler_resources, - const MaterialResources& material_resources, - const MeshCache& mesh_cache); + const VisibilityBufferPassResources& resources, const PreparedData& prepared, + const SamplerResources& sampler_resources, + const MaterialResources& material_resources, const MeshCache& mesh_cache, + bool enable_msaa, bool support_shader_stores_to_depth); struct FrameGraphHelper; struct CommandBuffer; void record_vis_buffer_pass_command_buffer(const FrameGraphHelper& frame_graph_helper, const VisBufferFrameGraphRecord::Render& pass_record, - CommandBuffer& cmdBuffer, - const PipelineFactory& pipeline_factory, - const PreparedData& prepared, - const VisibilityBufferPassResources& pass_resources); + CommandBuffer& cmdBuffer, const PipelineFactory& pipeline_factory, + const PreparedData& prepared, + const VisibilityBufferPassResources& pass_resources, bool enable_msaa); void record_fill_gbuffer_pass_command_buffer(const FrameGraphHelper& frame_graph_helper, const VisBufferFrameGraphRecord::FillGBuffer& pass_record, - CommandBuffer& cmdBuffer, - const PipelineFactory& pipeline_factory, - const VisibilityBufferPassResources& resources, - VkExtent2D render_extent); + CommandBuffer& cmdBuffer, const PipelineFactory& pipeline_factory, + const VisibilityBufferPassResources& resources, VkExtent2D render_extent, + bool enable_msaa, bool support_shader_stores_to_depth); + +void record_legacy_depth_resolve_pass_command_buffer(const FrameGraphHelper& frame_graph_helper, + const VisBufferFrameGraphRecord::LegacyDepthResolve& pass_record, + CommandBuffer& cmdBuffer, const PipelineFactory& pipeline_factory, + const VisibilityBufferPassResources& resources, bool enable_msaa, + bool support_shader_stores_to_depth); } // namespace Reaper diff --git a/src/vulkan_loader/SymbolHelper.inl b/src/vulkan_loader/SymbolHelper.inl index 0da0072f..dc45d52c 100644 --- a/src/vulkan_loader/SymbolHelper.inl +++ b/src/vulkan_loader/SymbolHelper.inl @@ -40,6 +40,7 @@ REAPER_VK_INSTANCE_LEVEL_FUNCTION(vkEnumerateDeviceExtensionProperties) REAPER_VK_INSTANCE_LEVEL_FUNCTION(vkGetPhysicalDeviceMemoryProperties) REAPER_VK_INSTANCE_LEVEL_FUNCTION(vkGetPhysicalDeviceMemoryProperties2) REAPER_VK_INSTANCE_LEVEL_FUNCTION(vkGetPhysicalDeviceFormatProperties) +REAPER_VK_INSTANCE_LEVEL_FUNCTION(vkGetPhysicalDeviceImageFormatProperties2) // Device level functions #ifndef REAPER_VK_DEVICE_LEVEL_FUNCTION