From f3ce6acf530ba657475a9942eb50466c4cf1efcb Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 26 Sep 2026 17:21:32 -0400 Subject: [PATCH] Another intent on load store op none impl --- .../renderer_vulkan/vk_graphics_pipeline.cpp | 34 +++++++++- .../renderer_vulkan/vk_rasterizer.cpp | 3 - .../renderer_vulkan/vk_scheduler.cpp | 66 +++++++++++++++---- src/video_core/renderer_vulkan/vk_scheduler.h | 33 ++++++---- src/video_core/vulkan_common/vulkan_device.h | 6 ++ 5 files changed, 116 insertions(+), 26 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 5c7a88e81e..792e7d2e7c 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -136,6 +136,37 @@ RenderingFormats PipelineFormats(const FixedPipelineState& state, const Device& return MakeRenderingFormats(device, color_formats, depth_format); } +struct AttachmentAccess { + u32 touched; + u32 written; +}; + +AttachmentAccess MakeAttachmentAccess(const Maxwell& regs) { + AttachmentAccess access{}; + for (size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { + size_t mask_index = index; + if (regs.color_mask_common) { + mask_index = 0; + } + const auto& mask = regs.color_mask[mask_index]; + if (mask.R || mask.G || mask.B || mask.A) { + access.touched |= 1u << index; + access.written |= 1u << index; + } + } + if (regs.depth_test_enable || regs.depth_bounds_enable) { + access.touched |= Scheduler::DEPTH_ATTACHMENT_BIT; + } + if (regs.depth_test_enable && regs.depth_write_enabled) { + access.written |= Scheduler::DEPTH_ATTACHMENT_BIT; + } + if (regs.stencil_enable) { + access.touched |= Scheduler::STENCIL_ATTACHMENT_BIT; + access.written |= Scheduler::STENCIL_ATTACHMENT_BIT; + } + return access; +} + size_t NumAttachments(const FixedPipelineState& state) { size_t num{}; for (size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { @@ -586,7 +617,8 @@ bool GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling, if (buffer_cache.TakeDrawHazard()) { scheduler.RequestOutsideRenderPassOperationContext(); } - scheduler.RequestRenderpass(texture_cache.GetFramebuffer()); + const AttachmentAccess access = MakeAttachmentAccess(maxwell3d->regs); + scheduler.RequestRenderpass(texture_cache.GetFramebuffer(), access.touched, access.written); buffer_cache.CommitDrawWrites(); if (!is_built.load(std::memory_order::relaxed)) { // Wait for the pipeline to be built diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index ec30f2fa84..835aacf675 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -263,8 +263,6 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, bool skip_empty, Func&& draw pipeline->SetEngine(maxwell3d, gpu_memory); if (!pipeline->Configure(is_indexed)) return; - scheduler.MarkDepthWrites(maxwell3d->regs.depth_test_enable != 0 && - maxwell3d->regs.depth_write_enabled != 0); UpdateDynamicStates(pipeline->HasDynamicVertexInput()); @@ -584,7 +582,6 @@ void RasterizerVulkan::Clear(u32 layer_count) { if (aspect_flags == 0) { return; } - scheduler.MarkDepthWrites(!can_defer_clear && (aspect_flags & VK_IMAGE_ASPECT_DEPTH_BIT) != 0); if (use_stencil && framebuffer->HasAspectStencilBit() && regs.stencil_front_mask != 0xFF && regs.stencil_front_mask != 0) { diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index ec01fbfcae..024b75e584 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -27,6 +27,15 @@ #include "video_core/vulkan_common/vulkan_wrapper.h" namespace Vulkan { +namespace { +struct BeginRenderingCommand { + void operator()(vk::CommandBuffer cmdbuf, vk::CommandBuffer) const { + BeginRendering(cmdbuf, attachments); + } + + RenderingAttachments attachments; +}; +} // Anonymous namespace void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) { @@ -104,6 +113,7 @@ void Scheduler::DispatchWork() { work_queue.push(std::move(chunk)); } event_cv.notify_all(); + recorded_attachments = nullptr; AcquireNewChunk(); } } @@ -113,7 +123,8 @@ void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer, PublishComputeWrites(); state.framebuffer_id = framebuffer->Id(); ++renderpass_serial; - renderpass_depth_writes = false; + attachments_touched = 0; + attachments_written = 0; if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) { const VkExtent2D render_area = attachments.render_area.extent; @@ -123,7 +134,13 @@ void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer, GPU::Logging::GPULogger::GetInstance().LogRenderPassBegin(render_pass_info); } - Record([attachments](vk::CommandBuffer cmdbuf) { BeginRendering(cmdbuf, attachments); }); + BeginRenderingCommand command{attachments}; + BeginRenderingCommand* recorded = chunk->Record(command); + if (recorded == nullptr) { + DispatchWork(); + recorded = chunk->Record(command); + } + recorded_attachments = &recorded->attachments; num_renderpass_images = framebuffer->NumImages(); renderpass_images = framebuffer->Images(); renderpass_image_ranges = framebuffer->ImageRanges(); @@ -161,7 +178,9 @@ void Scheduler::RealizeDeferredClear() { } EndRenderPass(); BeginRenderPassImpl(dc.framebuffer, attachments); - renderpass_depth_writes = dc.depth_stencil; + if (dc.depth_stencil) { + attachments_written |= DEPTH_ATTACHMENT_BIT | STENCIL_ATTACHMENT_BIT; + } } bool Scheduler::DeferColorClear(const Framebuffer* framebuffer, u32 rt_slot, @@ -201,17 +220,16 @@ void Scheduler::FlushDeferredClear() { EndRenderPass(); } -void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) { +void Scheduler::RequestRenderpass(const Framebuffer* framebuffer, u32 touched, u32 written) { if (deferred_clear.framebuffer == framebuffer) { RealizeDeferredClear(); - return; + } else if (framebuffer->Id() != state.framebuffer_id) { + // Ends any active pass and realizes a deferred clear + EndRenderPass(); + BeginRenderPassImpl(framebuffer, framebuffer->Attachments()); } - if (framebuffer->Id() == state.framebuffer_id) { - return; - } - // Ends any active pass and realizes a deferred clear - EndRenderPass(); - BeginRenderPassImpl(framebuffer, framebuffer->Attachments()); + attachments_touched |= touched; + attachments_written |= written; } void Scheduler::RequestOutsideRenderPassOperationContext() { @@ -224,6 +242,28 @@ void Scheduler::RequestComputeDispatchContext() { compute_writes = true; } +void Scheduler::RelaxAttachmentOps(RenderingAttachments& attachments) const { + const bool load_op_none = device.IsLoadOpNoneSupported(); + const auto relax = [&](VkRenderingAttachmentInfo& attachment, u32 bit) { + if (attachment.imageView == VK_NULL_HANDLE || + attachment.resolveMode != VK_RESOLVE_MODE_NONE || + attachment.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR || + attachment.storeOp != VK_ATTACHMENT_STORE_OP_STORE || + (attachments_written & bit) != 0) { + return; + } + attachment.storeOp = VK_ATTACHMENT_STORE_OP_NONE; + if (load_op_none && (attachments_touched & bit) == 0) { + attachment.loadOp = VK_ATTACHMENT_LOAD_OP_NONE; + } + }; + for (u32 slot = 0; slot < attachments.num_colors; ++slot) { + relax(attachments.colors[slot], 1u << slot); + } + relax(attachments.depth, DEPTH_ATTACHMENT_BIT); + relax(attachments.stencil, STENCIL_ATTACHMENT_BIT); +} + void Scheduler::PublishComputeWrites() { if (!std::exchange(compute_writes, false)) { return; @@ -406,6 +446,10 @@ void Scheduler::EndRenderPass() if (state.framebuffer_id == 0) { return; } + if (recorded_attachments != nullptr) { + RelaxAttachmentOps(*recorded_attachments); + recorded_attachments = nullptr; + } query_cache->CounterClose(VideoCommon::QueryType::StreamingByteCount); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index e4fa647132..a4bd593f40 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -42,6 +42,10 @@ struct RenderingAttachments; /// OpenGL-like operations on Vulkan command buffers. class Scheduler { public: + static constexpr u32 DEPTH_ATTACHMENT_BIT = 1u << 8; + static constexpr u32 STENCIL_ATTACHMENT_BIT = 1u << 9; + static constexpr u32 ALL_ATTACHMENTS = 0x3FF; + explicit Scheduler(const Device& device, StateTracker& state_tracker); ~Scheduler(); @@ -59,7 +63,8 @@ public: void DispatchWork(); /// Requests to begin a renderpass. - void RequestRenderpass(const Framebuffer* framebuffer); + void RequestRenderpass(const Framebuffer* framebuffer, u32 touched = ALL_ATTACHMENTS, + u32 written = ALL_ATTACHMENTS); /// Defers a full-attachment color clear so it becomes the next render pass. bool DeferColorClear(const Framebuffer* framebuffer, u32 rt_slot, const VkClearValue& value); @@ -100,12 +105,8 @@ public: renderpass_writes = true; } - void MarkDepthWrites(bool writes) noexcept { - renderpass_depth_writes |= writes; - } - bool HasDepthWrites() const noexcept { - return renderpass_depth_writes; + return (attachments_written & DEPTH_ATTACHMENT_BIT) != 0; } /// Update the pipeline to the current execution context. @@ -235,6 +236,10 @@ private: command(cmdbuf, upload_cmdbuf); } + T& Get() noexcept { + return command; + } + private: T command; }; @@ -244,16 +249,18 @@ private: void ExecuteAll(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf); template - bool Record(T& command) { + T* Record(T& command) { using FuncType = TypedCommand; static_assert(sizeof(FuncType) < sizeof(data), "Lambda is too large"); command_offset = Common::AlignUp(command_offset, alignof(FuncType)); if (command_offset > sizeof(data) - sizeof(FuncType)) { - return false; + return nullptr; } Command* const current_last = last; - last = new (data.data() + command_offset) FuncType(std::move(command)); + FuncType* const recorded = + new (data.data() + command_offset) FuncType(std::move(command)); + last = recorded; if (current_last) { current_last->SetNext(last); @@ -261,7 +268,7 @@ private: first = last; } command_offset += sizeof(FuncType); - return true; + return &recorded->Get(); } void MarkSubmit() { @@ -322,6 +329,8 @@ private: void PublishComputeWrites(); + void RelaxAttachmentOps(RenderingAttachments& attachments) const; + void AcquireNewChunk(); const Device& device; @@ -345,8 +354,10 @@ private: u64 renderpass_serial = 0; u64 wfi_serial = 0; bool renderpass_writes = false; - bool renderpass_depth_writes = false; bool compute_writes = false; + u32 attachments_touched = 0; + u32 attachments_written = 0; + RenderingAttachments* recorded_attachments = nullptr; VkMemoryBarrier2 renderpass_write_barrier{}; VkMemoryBarrier2 compute_write_barrier{}; VkMemoryBarrier2 upload_write_barrier{}; diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index a23f9b6334..69f21a6f50 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -106,6 +106,8 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION(KHR, SWAPCHAIN, swapchain) \ EXTENSION(KHR, SWAPCHAIN_MUTABLE_FORMAT, swapchain_mutable_format) \ EXTENSION(KHR, IMAGE_FORMAT_LIST, image_format_list) \ + EXTENSION(KHR, LOAD_STORE_OP_NONE, load_store_op_none) \ + EXTENSION(EXT, LOAD_STORE_OP_NONE, load_store_op_none_ext) \ EXTENSION(NV, DEVICE_DIAGNOSTICS_CONFIG, device_diagnostics_config) \ EXTENSION(NV, GEOMETRY_SHADER_PASSTHROUGH, geometry_shader_passthrough) \ EXTENSION(NV, VIEWPORT_ARRAY2, viewport_array2) \ @@ -669,6 +671,10 @@ FN_MAX_LIMIT_LIST } /// Returns true if the device supports VK_EXT_depth_bias_control. + bool IsLoadOpNoneSupported() const { + return extensions.load_store_op_none || extensions.load_store_op_none_ext; + } + bool IsExtDepthBiasControlSupported() const { return extensions.depth_bias_control; }