From 70668fbd98b84e5374544d6d733d409770fe90a7 Mon Sep 17 00:00:00 2001 From: nmzik Date: Wed, 21 Oct 2026 01:33:10 +0200 Subject: [PATCH] gpu: drop global barriers for PM4 flush events and releases, ordering buffer-writing draws like dispatches --- .../command_processor/commandProcessor.h | 1 - .../command_processor/pm4Handlers.cpp | 28 ++-------------- src/graphics/guest_gpu/graphicsRun.cpp | 32 +++---------------- src/graphics/host_gpu/renderer/context.cpp | 8 ++++- .../pipeline/shaderResourceBarrier.cpp | 3 +- src/graphics/host_gpu/renderer/renderDraw.cpp | 23 +++++++------ 6 files changed, 31 insertions(+), 64 deletions(-) diff --git a/src/graphics/guest_gpu/command_processor/commandProcessor.h b/src/graphics/guest_gpu/command_processor/commandProcessor.h index c976bd8f6..844acf290 100644 --- a/src/graphics/guest_gpu/command_processor/commandProcessor.h +++ b/src/graphics/guest_gpu/command_processor/commandProcessor.h @@ -104,7 +104,6 @@ public: uint32_t value); void PrepareCpuFlip(uint64_t request_id); void SynchronizeGpu(); - void EmitGlobalBarrier(); void TriggerEopEventAtEndOfPipe(uint32_t interrupt_context_id); void DispatchDirect(uint32_t thread_group_x, uint32_t thread_group_y, uint32_t thread_group_z, uint32_t mode); diff --git a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp index 19034daaf..736e77d7f 100644 --- a/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp +++ b/src/graphics/guest_gpu/command_processor/pm4Handlers.cpp @@ -71,12 +71,6 @@ constexpr uint32_t NormalizeRegisterOffset(uint32_t raw_offset) { return raw_offset & ~RegisterSelectorMask; } -bool ReleaseMemGcrNeedsBarrier(uint32_t eop_event_type, uint32_t gcr_cntl) { - return eop_event_type != 0x28u || - (gcr_cntl & (GcrGl2MetadataInvalidate | GcrGl0VectorInvalidate | GcrGl1Invalidate | - GcrGl2Invalidate | GcrGl2Writeback)) != 0; -} - uint32_t ReleaseMemCacheActionFromGcr(uint32_t gcr_cntl) { return ((gcr_cntl & GcrGl2Writeback) != 0 ? 0x38u : 0x00u); } @@ -2259,30 +2253,14 @@ KYTY_CP_OP_PARSER(CpOpReleaseMem) { } }; - if (data_sel == 0 || interrupt_selector == 4) { - if (eop_event_type != 0x28 || gcr_cntl != 0) { - cp.EmitGlobalBarrier(); - } - + // Cache actions only order GPU work, which the renderer's resource barriers already order. + if (data_sel == 0 || interrupt_selector == 4 || + (release_dst == ReleaseMemDstMemory && dst_gpu_addr == nullptr)) { trigger_interrupt(); return 7; } - if (release_dst == ReleaseMemDstMemory && dst_gpu_addr == nullptr) { - if (eop_event_type != 0x28 || gcr_cntl != 0) { - cp.EmitGlobalBarrier(); - } - - trigger_interrupt(); - - return 7; - } - - if (ReleaseMemGcrNeedsBarrier(eop_event_type, gcr_cntl)) { - cp.EmitGlobalBarrier(); - } - auto cache_action = ReleaseMemCacheActionFromGcr(gcr_cntl); cache_policy = 0; diff --git a/src/graphics/guest_gpu/graphicsRun.cpp b/src/graphics/guest_gpu/graphicsRun.cpp index 41aeee369..19e270c6b 100644 --- a/src/graphics/guest_gpu/graphicsRun.cpp +++ b/src/graphics/guest_gpu/graphicsRun.cpp @@ -607,7 +607,6 @@ bool GuestGpu::Process(Submission& submission) { case SubmissionType::SuspendPoint: // Age caches at the frame boundary, not once per command list. m_renderer.RunGarbageCollector(); - cp.EmitGlobalBarrier(); m_renderer.GetCommandScheduler().DeferPriorityOperation( [ready = m_suspend_point_ready] { ready->release(); }); cp.BufferFlush(); @@ -1222,22 +1221,6 @@ void CommandProcessor::WriteAtEndOfPipe64(uint32_t cache_policy, uint32_t event_ interrupt_context_id); } -void CommandProcessor::EmitGlobalBarrier() { - Common::LockGuard lock(m_renderer.GetMutex()); - - vk::MemoryBarrier2 barrier {}; - barrier.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands; - barrier.srcAccessMask = vk::AccessFlagBits2::eMemoryWrite; - barrier.dstStageMask = vk::PipelineStageFlagBits2::eAllCommands; - barrier.dstAccessMask = vk::AccessFlagBits2::eMemoryRead | vk::AccessFlagBits2::eMemoryWrite; - - vk::DependencyInfo dependency {}; - dependency.memoryBarrierCount = 1; - dependency.pMemoryBarriers = &barrier; - GetScheduler().EndRendering(); - CurrentBuffer().Handle().pipelineBarrier2(dependency); -} - void CommandProcessor::TriggerEopEventAtEndOfPipe(uint32_t interrupt_context_id) { (void)CurrentBuffer().Handle(); GetScheduler().DeferPriorityOperation( @@ -1257,21 +1240,17 @@ void CommandProcessor::TriggerEvent(uint32_t event_type, uint32_t event_index, } const auto valid_cache_event_index = event_index == 0x00000000 || event_index == 0x00000007; + // Partial flushes and cache writebacks only order GPU work, which the renderer's resource + // barriers already order. switch (event_type) { // CsPartialFlush, GsPartialFlush, PsPartialFlush. case 0x00000007: case 0x0000000f: - case 0x00000010: EmitGlobalBarrier(); break; - // CbDbDataWritebackInvalidate, CbDataWritebackInvalidate. + case 0x00000010: break; + // CbDbDataWritebackInvalidate, CbDataWritebackInvalidate, DbDataWritebackInvalidate, + // DbMetadataWritebackInvalidate, CbMetadataWritebackInvalidate. case 0x00000016: case 0x00000031: - if (!valid_cache_event_index) { - EXIT("unknown event type: 0x%08" PRIx32 ", 0x%08" PRIx32 "\n", event_type, - event_index); - } - EmitGlobalBarrier(); - break; - // DbDataWritebackInvalidate, DbMetadataWritebackInvalidate, CbMetadataWritebackInvalidate. case 0x0000002a: case 0x0000002c: case 0x0000002e: @@ -1279,7 +1258,6 @@ void CommandProcessor::TriggerEvent(uint32_t event_type, uint32_t event_index, EXIT("unknown event type: 0x%08" PRIx32 ", 0x%08" PRIx32 "\n", event_type, event_index); } - EmitGlobalBarrier(); break; case 0x0000000d: case 0x0000000e: diff --git a/src/graphics/host_gpu/renderer/context.cpp b/src/graphics/host_gpu/renderer/context.cpp index d71929fb1..a5f25302d 100644 --- a/src/graphics/host_gpu/renderer/context.cpp +++ b/src/graphics/host_gpu/renderer/context.cpp @@ -104,8 +104,14 @@ void CommandBuffer::BeginRendering(const RenderState& state) const { rendering.pDepthAttachment = depth_stencil.has_depth ? &depth : nullptr; rendering.pStencilAttachment = depth_stencil.has_stencil ? &stencil : nullptr; Handle().beginRendering(rendering); + // The open pass holds the cleared contents, so a later clear begins a new pass. m_render_state = state; - m_rendering = true; + for (auto& color: m_render_state.color_attachments) { + color.is_clear = false; + } + m_render_state.depth_stencil_attachment.depth_clear = false; + m_render_state.depth_stencil_attachment.stencil_clear = false; + m_rendering = true; } void CommandBuffer::EndRendering() const { diff --git a/src/graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.cpp b/src/graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.cpp index f0f7d4557..4a9508886 100644 --- a/src/graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/shaderResourceBarrier.cpp @@ -92,7 +92,8 @@ bool HasShaderBufferWrites(const ShaderStageRuntime& runtime) { const auto& program = *runtime.program; const auto& resources = *runtime.resources; EXIT_IF(resources.buffers.size() != program.info.buffers.size()); - bool has_writes = false; + // Address writes reach memory outside the bound buffer descriptors. + bool has_writes = program.has_address_writes; for (uint32_t i = 0; i < program.info.buffers.size(); i++) { if (!program.info.buffers[i].written) { continue; diff --git a/src/graphics/host_gpu/renderer/renderDraw.cpp b/src/graphics/host_gpu/renderer/renderDraw.cpp index fada73b93..9071854d1 100644 --- a/src/graphics/host_gpu/renderer/renderDraw.cpp +++ b/src/graphics/host_gpu/renderer/renderDraw.cpp @@ -1227,6 +1227,20 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff dynamic_state.SetAttachmentFeedbackLoopAspects(feedback_aspects); dynamic_state.Commit(m_context.GetGraphics(), vk_buffer); + vk::PipelineStageFlags shader_write_stages = {}; + for (const auto& stage: vertex_stages) { + if (HasShaderBufferWrites(stage.stage)) { + shader_write_stages |= ShaderPipelineStages(NativeShaderStage(stage.logical_stage)); + } + } + if (state.ps_active && HasShaderBufferWrites(state.ps_input_info.stage)) { + shader_write_stages |= vk::PipelineStageFlagBits::eFragmentShader; + } + if (shader_write_stages) { + // As for dispatches, buffer writes wait for earlier accesses to the memory. + m_context.GetCommandScheduler().EndRendering(); + ShaderWriteHazardBarrier(vk_buffer, shader_write_stages); + } LogDrawPhase(draw.Name(), "BeginRendering"); if (!draw.IsIndexed()) { SetDrawDebugPhase(buffer, submit_id, draw, 0x400u); @@ -1247,15 +1261,6 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff if (!draw.IsIndexed()) { SetDrawDebugPhase(buffer, submit_id, draw, 0x600u); } - vk::PipelineStageFlags shader_write_stages = {}; - for (const auto& stage: vertex_stages) { - if (HasShaderBufferWrites(stage.stage)) { - shader_write_stages |= ShaderPipelineStages(NativeShaderStage(stage.logical_stage)); - } - } - if (state.ps_active && HasShaderBufferWrites(state.ps_input_info.stage)) { - shader_write_stages |= vk::PipelineStageFlagBits::eFragmentShader; - } if (shader_write_stages) { m_context.GetCommandScheduler().EndRendering(); ShaderWriteBarrier(vk_buffer, shader_write_stages);