diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h index 6b2a37ea8c..b8d0d1896f 100644 --- a/src/video_core/buffer_cache/buffer_base.h +++ b/src/video_core/buffer_cache/buffer_base.h @@ -137,6 +137,22 @@ public: content_serial = ++next_content_serial; } + [[nodiscard]] bool HasDrawHazard(u64 pass, u64 wfi, DAddr addr, u64 size) const noexcept { + return draw_write_pass == pass && draw_write_wfi < wfi && addr < draw_write_end && + addr + size > draw_write_begin; + } + + void MarkDrawWrite(u64 pass, u64 wfi, DAddr addr, u64 size) noexcept { + if (draw_write_pass != pass) { + draw_write_pass = pass; + draw_write_begin = addr; + draw_write_end = addr + size; + } + draw_write_wfi = wfi; + draw_write_begin = (std::min)(draw_write_begin, addr); + draw_write_end = (std::max)(draw_write_end, addr + size); + } + private: static inline u64 next_content_serial = 0; @@ -147,6 +163,10 @@ private: size_t size_bytes = 0; u64 write_tick = 0; u64 content_serial = ++next_content_serial; + u64 draw_write_pass = 0; + u64 draw_write_wfi = 0; + DAddr draw_write_begin = 0; + DAddr draw_write_end = 0; }; } // namespace VideoCommon diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 89d0003207..d4c8a61eb7 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -366,6 +366,13 @@ void BufferCache

::DisableGraphicsUniformBuffer(size_t stage, u32 index) { template void BufferCache

::UpdateGraphicsBuffers(bool is_indexed) { + if constexpr (!IS_OPENGL) { + draw_writes.clear(); + draw_pass = runtime.RenderPassSerial(); + draw_wfi = runtime.WaitForIdleSerial(); + draw_hazard = false; + recording_draw = true; + } ReclaimInline(); do { channel_state->has_deleted_buffers = false; @@ -373,6 +380,24 @@ void BufferCache

::UpdateGraphicsBuffers(bool is_indexed) { } while (channel_state->has_deleted_buffers); } +template +bool BufferCache

::TakeDrawHazard() noexcept { + return std::exchange(draw_hazard, false); +} + +template +void BufferCache

::CommitDrawWrites() { + recording_draw = false; + if (draw_writes.empty()) { + return; + } + const u64 pass = runtime.RenderPassSerial(); + for (const DrawWrite& write : draw_writes) { + slot_buffers[write.buffer_id].MarkDrawWrite(pass, draw_wfi, write.device_addr, write.size); + } + runtime.MarkRenderPassWrites(); +} + template void BufferCache

::UpdateComputeBuffers() { ReclaimInline(); @@ -1550,6 +1575,9 @@ void BufferCache

::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u3 Buffer& buffer = slot_buffers[buffer_id]; buffer.setWriteTick(runtime.CurrentTick()); buffer.MarkContentModified(); + if (recording_draw) { + draw_writes.push_back({buffer_id, device_addr, size}); + } } memory_tracker.MarkRegionAsGpuModified(device_addr, size); gpu_modified_ranges.Add(device_addr, size); @@ -1770,6 +1798,9 @@ void BufferCache

::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept { template bool BufferCache

::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 size) { + if constexpr (!IS_OPENGL) { + draw_hazard |= buffer.HasDrawHazard(draw_pass, draw_wfi, device_addr, size); + } upload_copies.clear(); u64 total_size_bytes = 0; u64 largest_copy = 0; diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index 80a0b2c7eb..6730291831 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -260,6 +260,10 @@ public: void UpdateComputeBuffers(); + [[nodiscard]] bool TakeDrawHazard() noexcept; + + void CommitDrawWrites(); + void BindHostGeometryBuffers(bool is_indexed); void BindHostStageBuffers(size_t stage); @@ -520,6 +524,17 @@ private: boost::container::small_vector upload_copies; + struct DrawWrite { + BufferId buffer_id; + DAddr device_addr; + u32 size; + }; + boost::container::small_vector draw_writes; + u64 draw_pass = 0; + u64 draw_wfi = 0; + bool draw_hazard = false; + bool recording_draw = false; + MemoryTracker memory_tracker; Common::RangeSet uncommitted_gpu_modified_ranges; Common::RangeSet gpu_modified_ranges; diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index d3d3d7a437..57494d9649 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -440,6 +440,18 @@ u64 BufferCacheRuntime::CurrentTick() { return scheduler.GetMasterSemaphore().CurrentTick(); } +u64 BufferCacheRuntime::RenderPassSerial() const noexcept { + return scheduler.ActiveRenderPassSerial(); +} + +u64 BufferCacheRuntime::WaitForIdleSerial() const noexcept { + return scheduler.WaitForIdleSerial(); +} + +void BufferCacheRuntime::MarkRenderPassWrites() noexcept { + scheduler.MarkRenderPassWrites(); +} + u64 BufferCacheRuntime::KnownGpuTick() { return scheduler.GetMasterSemaphore().KnownGpuTick(); } diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index f83696f0de..1a8629aee8 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -112,6 +112,12 @@ public: u64 CurrentTick(); + u64 RenderPassSerial() const noexcept; + + u64 WaitForIdleSerial() const noexcept; + + void MarkRenderPassWrites() noexcept; + u64 KnownGpuTick(); void Wait(u64 buffer_tick); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 101fab301e..767099eea2 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -595,7 +595,11 @@ bool GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, } } + if (buffer_cache.TakeDrawHazard()) { + scheduler.RequestOutsideRenderPassOperationContext(); + } scheduler.RequestRenderpass(texture_cache.GetFramebuffer()); + buffer_cache.CommitDrawWrites(); if (!is_built.load(std::memory_order::relaxed)) { // Wait for the pipeline to be built scheduler.Record([this](vk::CommandBuffer) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 4ba8c2abbc..2562ff1ad3 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -894,6 +894,7 @@ void RasterizerVulkan::FlushAndInvalidateRegion(DAddr addr, u64 size, void RasterizerVulkan::WaitForIdle() { query_cache.NotifyWFI(); + scheduler.NotifyWaitForIdle(); fence_manager.SignalOrdering(); } diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 33b03ea8f2..4cde9c8651 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -98,6 +98,7 @@ void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass state.renderpass = renderpass; state.framebuffer = framebuffer_handle; state.render_area = render_area; + ++renderpass_serial; if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { const std::string render_pass_info = @@ -417,8 +418,17 @@ void Scheduler::EndRenderPass() Record([num_images = num_renderpass_images, images = renderpass_images, ranges = renderpass_image_ranges, - has_transform_feedback = device.IsExtTransformFeedbackSupported()]( + 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]; @@ -459,20 +469,8 @@ void Scheduler::EndRenderPass() }; } cmdbuf.EndRenderPass(); - cmdbuf.PipelineBarrier(0, {}, {}, vk::Span(barriers.data(), num_images)); - if (has_transform_feedback) { - static constexpr VkMemoryBarrier2 XFB_OUTPUT_BARRIER{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .pNext = nullptr, - .srcStageMask = VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT, - .srcAccessMask = VK_ACCESS_2_TRANSFORM_FEEDBACK_WRITE_BIT_EXT, - .dstStageMask = VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT - | VK_PIPELINE_STAGE_2_TRANSFER_BIT, - .dstAccessMask = VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT - | VK_ACCESS_2_TRANSFER_READ_BIT, - }; - cmdbuf.PipelineBarrier(XFB_OUTPUT_BARRIER); - } + cmdbuf.PipelineBarrier(0, vk::Span(&WRITE_BARRIER, num_memory_barriers), {}, + vk::Span(barriers.data(), num_images)); }); state.renderpass = VkRenderPass{}; diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index e82b0414fc..587ed27f5b 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -77,6 +78,25 @@ public: return state.renderpass != VK_NULL_HANDLE; } + u64 ActiveRenderPassSerial() const noexcept { + if (state.renderpass) { + return renderpass_serial; + } + return (std::numeric_limits::max)(); + } + + u64 WaitForIdleSerial() const noexcept { + return wfi_serial; + } + + void NotifyWaitForIdle() noexcept { + ++wfi_serial; + } + + void MarkRenderPassWrites() noexcept { + renderpass_writes = true; + } + /// Update the pipeline to the current execution context. bool UpdateGraphicsPipeline(GraphicsPipeline* pipeline); @@ -311,6 +331,9 @@ private: State state; + u64 renderpass_serial = 0; + u64 wfi_serial = 0; + bool renderpass_writes = false; u32 num_renderpass_images = 0; std::array renderpass_images{}; std::array renderpass_image_ranges{};