From 9d4355064b7dbc8f72eddee2444a412cccef73e3 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 26 Sep 2026 02:31:24 -0400 Subject: [PATCH] Another try to make barriers more explicit --- .../renderer_vulkan/vk_buffer_cache.cpp | 66 +++++-------------- .../renderer_vulkan/vk_rasterizer.cpp | 26 ++++---- .../renderer_vulkan/vk_scheduler.cpp | 43 +++++++++--- src/video_core/renderer_vulkan/vk_scheduler.h | 14 ++++ src/video_core/vulkan_common/vulkan_wrapper.h | 25 +++++++ 5 files changed, 103 insertions(+), 71 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 57494d9649..30baf44e9d 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -36,6 +36,24 @@ struct VertexBindings { u32 count; }; +constexpr VkMemoryBarrier2 READ_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = vk::PIPELINE_STAGE_BUFFER_INPUTS | VK_PIPELINE_STAGE_2_TRANSFER_BIT, + .srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT | VK_ACCESS_2_TRANSFER_WRITE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, + .dstAccessMask = vk::ACCESS_TRANSFER, +}; + +constexpr VkMemoryBarrier2 WRITE_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, + .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, + .dstStageMask = vk::PIPELINE_STAGE_BUFFER_INPUTS, + .dstAccessMask = vk::ACCESS_BUFFER_INPUTS, +}; + VkBufferCopy MakeBufferCopy(const VideoCommon::BufferCopy& copy) { return VkBufferCopy{ .srcOffset = copy.src_offset, @@ -482,22 +500,6 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, if (dst_buffer == VK_NULL_HANDLE || src_buffer == VK_NULL_HANDLE) { return; } - static constexpr VkMemoryBarrier2 READ_BARRIER{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, - .srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT | VK_ACCESS_2_TRANSFER_WRITE_BIT, - }; - static constexpr VkMemoryBarrier2 WRITE_BARRIER{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, - .dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, - }; // Measuring a popular game, this number never exceeds the specified size once data is warmed up boost::container::small_vector vk_copies(copies.size()); @@ -523,14 +525,6 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, } void BufferCacheRuntime::PreCopyBarrier() { - static constexpr VkMemoryBarrier2 READ_BARRIER{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, - .srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT | VK_ACCESS_2_TRANSFER_WRITE_BIT, - }; scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([](vk::CommandBuffer cmdbuf) { cmdbuf.PipelineBarrier(READ_BARRIER); @@ -538,14 +532,6 @@ void BufferCacheRuntime::PreCopyBarrier() { } void BufferCacheRuntime::PostCopyBarrier() { - static constexpr VkMemoryBarrier2 WRITE_BARRIER{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, - .dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, - }; scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([](vk::CommandBuffer cmdbuf) { cmdbuf.PipelineBarrier(WRITE_BARRIER); @@ -556,22 +542,6 @@ void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t si if (dest_buffer == VK_NULL_HANDLE) { return; } - static constexpr VkMemoryBarrier2 READ_BARRIER{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, - .srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT | VK_ACCESS_2_TRANSFER_WRITE_BIT, - }; - static constexpr VkMemoryBarrier2 WRITE_BARRIER{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, - .dstStageMask = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, - .dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, - }; scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([dest_buffer, offset, size, value](vk::CommandBuffer cmdbuf) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index c77d032f94..488fe8019c 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -631,19 +631,19 @@ void RasterizerVulkan::DispatchCompute() { static constexpr VkMemoryBarrier2 READ_BARRIER{ .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, .pNext = nullptr, - .srcStageMask = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, - .srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT, - .dstStageMask = - VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, - .dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT, + .srcStageMask = vk::PIPELINE_STAGE_BUFFER_INPUTS, + .srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + .dstAccessMask = vk::ACCESS_SHADER_RESOURCES, }; - static constexpr VkMemoryBarrier2 WRITE_BARRIER{ + static constexpr VkMemoryBarrier2 INDIRECT_READ_BARRIER{ .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + .srcStageMask = vk::PIPELINE_STAGE_BUFFER_INPUTS, .srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT, - .dstStageMask = VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, + .dstStageMask = + VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + .dstAccessMask = VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT | vk::ACCESS_SHADER_RESOURCES, }; const auto& qmd{kepler_compute->launch_description}; auto indirect_address = kepler_compute->GetIndirectComputeAddress(); @@ -653,15 +653,14 @@ void RasterizerVulkan::DispatchCompute() { const auto post_op = VideoCommon::ObtainBufferOperation::DiscardWrite; const auto [buffer, offset] = buffer_cache.ObtainBuffer(*indirect_address, 12, sync_info, post_op); - scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.RequestComputeDispatchContext(); scheduler.Record([pipeline, indirect_buffer = buffer->Handle(), indirect_offset = offset](vk::CommandBuffer cmdbuf) { if (!pipeline->IsBound()) { return; } - cmdbuf.PipelineBarrier(READ_BARRIER); + cmdbuf.PipelineBarrier(INDIRECT_READ_BARRIER); cmdbuf.DispatchIndirect(indirect_buffer, indirect_offset); - cmdbuf.PipelineBarrier(WRITE_BARRIER); }); return; } @@ -670,14 +669,13 @@ void RasterizerVulkan::DispatchCompute() { if (dim[0] > max_dim[0] || dim[1] > max_dim[1] || dim[2] > max_dim[2]) { return; } - scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.RequestComputeDispatchContext(); scheduler.Record([pipeline, dim](vk::CommandBuffer cmdbuf) { if (!pipeline->IsBound()) { return; } cmdbuf.PipelineBarrier(READ_BARRIER); cmdbuf.Dispatch(dim[0], dim[1], dim[2]); - cmdbuf.PipelineBarrier(WRITE_BARRIER); }); // Log compute dispatch diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 457221b2b1..ed7cc1ed19 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -47,6 +47,12 @@ Scheduler::Scheduler(const Device& device_, StateTracker& state_tracker_) : device{device_}, state_tracker{state_tracker_}, master_semaphore{std::make_unique(device)}, command_pool{std::make_unique(*master_semaphore, device)} { + if (device.IsExtTransformFeedbackSupported()) { + renderpass_write_barrier.srcStageMask |= VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT; + renderpass_write_barrier.srcAccessMask |= VK_ACCESS_2_TRANSFORM_FEEDBACK_WRITE_BIT_EXT; + renderpass_write_barrier.dstStageMask |= VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT; + renderpass_write_barrier.dstAccessMask |= VK_ACCESS_2_TRANSFORM_FEEDBACK_WRITE_BIT_EXT; + } AcquireNewChunk(); AllocateWorkerCommandBuffer(); @@ -95,6 +101,7 @@ void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass const VkClearValue* clear_values, u32 clear_value_count) { const VkFramebuffer framebuffer_handle = framebuffer->Handle(); const VkExtent2D render_area = framebuffer->RenderArea(); + PublishComputeWrites(); state.renderpass = renderpass; state.framebuffer = framebuffer_handle; state.render_area = render_area; @@ -222,6 +229,31 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) { void Scheduler::RequestOutsideRenderPassOperationContext() { EndRenderPass(); + PublishComputeWrites(); +} + +void Scheduler::RequestComputeDispatchContext() { + EndRenderPass(); + compute_writes = true; +} + +void Scheduler::PublishComputeWrites() { + if (!std::exchange(compute_writes, false)) { + return; + } + Record([](vk::CommandBuffer cmdbuf) { + static constexpr VkMemoryBarrier2 COMPUTE_WRITE_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + .srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT, + .dstStageMask = vk::PIPELINE_STAGE_BUFFER_INPUTS | vk::PIPELINE_STAGE_ATTACHMENTS | + VK_PIPELINE_STAGE_2_TRANSFER_BIT, + .dstAccessMask = + vk::ACCESS_BUFFER_INPUTS | vk::ACCESS_ATTACHMENTS | vk::ACCESS_TRANSFER, + }; + cmdbuf.PipelineBarrier(COMPUTE_WRITE_BARRIER); + }); } bool Scheduler::UpdateGraphicsPipeline(GraphicsPipeline* pipeline) { @@ -420,17 +452,10 @@ void Scheduler::EndRenderPass() Record([num_images = num_renderpass_images, images = renderpass_images, ranges = renderpass_image_ranges, + write_barrier = &renderpass_write_barrier, num_memory_barriers = static_cast(std::exchange(renderpass_writes, false))]( vk::CommandBuffer cmdbuf) { - static constexpr VkMemoryBarrier2 WRITE_BARRIER{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT, - .srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT, - .dstStageMask = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, - .dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT, - }; std::array barriers; for (size_t i = 0; i < num_images; ++i) { const VkImageSubresourceRange& range = ranges[i]; @@ -471,7 +496,7 @@ void Scheduler::EndRenderPass() }; } cmdbuf.EndRenderPass(); - cmdbuf.PipelineBarrier(0, vk::Span(&WRITE_BARRIER, num_memory_barriers), {}, + cmdbuf.PipelineBarrier(0, vk::Span(write_barrier, num_memory_barriers), {}, vk::Span(barriers.data(), num_images)); }); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index c78faeecb5..53be37928b 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -73,6 +73,8 @@ public: /// of a renderpass. void RequestOutsideRenderPassOperationContext(); + void RequestComputeDispatchContext(); + /// Returns true when a render pass is currently active in the scheduler state. bool IsRenderPassActive() const { return state.renderpass != VK_NULL_HANDLE; @@ -319,6 +321,8 @@ private: void EndRenderPass(); + void PublishComputeWrites(); + void AcquireNewChunk(); const Device& device; @@ -343,6 +347,16 @@ private: u64 wfi_serial = 0; bool renderpass_writes = false; bool renderpass_depth_writes = false; + bool compute_writes = false; + VkMemoryBarrier2 renderpass_write_barrier{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + .pNext = nullptr, + .srcStageMask = vk::PIPELINE_STAGE_GRAPHICS_SHADERS, + .srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT, + .dstStageMask = vk::PIPELINE_STAGE_BUFFER_INPUTS | vk::PIPELINE_STAGE_ATTACHMENTS | + VK_PIPELINE_STAGE_2_TRANSFER_BIT, + .dstAccessMask = vk::ACCESS_BUFFER_INPUTS | vk::ACCESS_ATTACHMENTS | vk::ACCESS_TRANSFER, + }; u32 num_renderpass_images = 0; std::array renderpass_images{}; std::array renderpass_image_ranges{}; diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 7898886921..72403aff8b 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -158,6 +158,31 @@ inline constexpr VkPipelineStageFlags2 PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER_ inline constexpr VkPipelineStageFlags2 PIPELINE_STAGE_HOST = VK_PIPELINE_STAGE_2_HOST_BIT; +inline constexpr VkPipelineStageFlags2 PIPELINE_STAGE_GRAPHICS_SHADERS = + VK_PIPELINE_STAGE_2_PRE_RASTERIZATION_SHADERS_BIT | VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT; + +inline constexpr VkPipelineStageFlags2 PIPELINE_STAGE_BUFFER_INPUTS = + VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT | VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT | + PIPELINE_STAGE_GRAPHICS_SHADERS | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT; + +inline constexpr VkPipelineStageFlags2 PIPELINE_STAGE_ATTACHMENTS = + VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT; + +inline constexpr VkAccessFlags2 ACCESS_SHADER_RESOURCES = + VK_ACCESS_2_UNIFORM_READ_BIT | VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_WRITE_BIT; + +inline constexpr VkAccessFlags2 ACCESS_BUFFER_INPUTS = + VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT | VK_ACCESS_2_INDEX_READ_BIT | + VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT | ACCESS_SHADER_RESOURCES; + +inline constexpr VkAccessFlags2 ACCESS_ATTACHMENTS = + VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + +inline constexpr VkAccessFlags2 ACCESS_TRANSFER = + VK_ACCESS_2_TRANSFER_READ_BIT | VK_ACCESS_2_TRANSFER_WRITE_BIT; + /// Table holding Vulkan instance function pointers. struct InstanceDispatch {