From 93452b24e32b48d6302cd809a52e30af931e4bb1 Mon Sep 17 00:00:00 2001 From: nmzik Date: Sat, 10 Oct 2026 00:28:51 +0200 Subject: [PATCH] renderer: cache dynamic graphics state --- .../host_gpu/renderer/commandScheduler.cpp | 1 + .../host_gpu/renderer/commandScheduler.h | 5 +- .../host_gpu/renderer/dynamicState.cpp | 150 ++++++++++ src/graphics/host_gpu/renderer/dynamicState.h | 258 ++++++++++++++++++ .../host_gpu/renderer/image/blitHelper.cpp | 2 + src/graphics/host_gpu/renderer/renderDraw.cpp | 58 ++-- 6 files changed, 442 insertions(+), 32 deletions(-) create mode 100644 src/graphics/host_gpu/renderer/dynamicState.cpp create mode 100644 src/graphics/host_gpu/renderer/dynamicState.h diff --git a/src/graphics/host_gpu/renderer/commandScheduler.cpp b/src/graphics/host_gpu/renderer/commandScheduler.cpp index 81e92156c..bb59bba6b 100644 --- a/src/graphics/host_gpu/renderer/commandScheduler.cpp +++ b/src/graphics/host_gpu/renderer/commandScheduler.cpp @@ -334,6 +334,7 @@ CommandBuffer& CommandScheduler::BeginCommand() { EXIT_IF(!m_command.IsInvalid()); m_command.m_buffer = m_command_pool.Commit(); m_command.Begin(); + m_dynamic_state.Invalidate(); return m_command; } diff --git a/src/graphics/host_gpu/renderer/commandScheduler.h b/src/graphics/host_gpu/renderer/commandScheduler.h index ebdaaa21f..6e418850d 100644 --- a/src/graphics/host_gpu/renderer/commandScheduler.h +++ b/src/graphics/host_gpu/renderer/commandScheduler.h @@ -3,14 +3,13 @@ #include "common/common.h" #include "common/uniqueFunction.h" +#include "graphics/host_gpu/renderer/dynamicState.h" #include "graphics/host_gpu/renderer/masterSemaphore.h" #include "graphics/host_gpu/renderer/render.h" #include #include - #include - #include #include @@ -50,6 +49,7 @@ public: [[nodiscard]] uint64_t CurrentTick() const noexcept { return m_master.CurrentTick(); } [[nodiscard]] bool IsFree(uint64_t tick); [[nodiscard]] MasterSemaphore& GetMasterSemaphore() noexcept { return m_master; } + [[nodiscard]] DynamicState& GetDynamicState() noexcept { return m_dynamic_state; } [[nodiscard]] RenderContext& Context() const noexcept { return m_context; } [[nodiscard]] GraphicContext& Graphics() const noexcept { return m_graphics; } @@ -92,6 +92,7 @@ private: GraphicContext& m_graphics; CommandPool m_command_pool; CommandBuffer m_command; + DynamicState m_dynamic_state; std::queue m_pending_operations; std::queue m_priority_operations; std::mutex m_operation_mutex; diff --git a/src/graphics/host_gpu/renderer/dynamicState.cpp b/src/graphics/host_gpu/renderer/dynamicState.cpp new file mode 100644 index 000000000..da9f69667 --- /dev/null +++ b/src/graphics/host_gpu/renderer/dynamicState.cpp @@ -0,0 +1,150 @@ +#include "graphics/host_gpu/renderer/dynamicState.h" + +#include "graphics/host_gpu/graphicContext.h" + +namespace Libs::Graphics { + +void DynamicState::Commit(const GraphicContext& graphics, vk::CommandBuffer cmdbuf) { + if (dirty_state.viewports) { + dirty_state.viewports = false; + cmdbuf.setViewportWithCount(num_viewports, viewports.data()); + } + if (dirty_state.scissors) { + dirty_state.scissors = false; + cmdbuf.setScissorWithCount(num_scissors, scissors.data()); + } + if (dirty_state.depth_test_enabled) { + dirty_state.depth_test_enabled = false; + cmdbuf.setDepthTestEnable(depth_test_enabled); + } + if (dirty_state.depth_write_enabled) { + dirty_state.depth_write_enabled = false; + // Note that this must be set in a command buffer even if depth test is disabled. + cmdbuf.setDepthWriteEnable(depth_write_enabled); + } + if (depth_test_enabled && dirty_state.depth_compare_op) { + dirty_state.depth_compare_op = false; + cmdbuf.setDepthCompareOp(depth_compare_op); + } + if (dirty_state.depth_bounds_test_enabled) { + dirty_state.depth_bounds_test_enabled = false; +#if !defined(__APPLE__) + cmdbuf.setDepthBoundsTestEnable(depth_bounds_test_enabled); +#endif + } + if (depth_bounds_test_enabled && dirty_state.depth_bounds) { + dirty_state.depth_bounds = false; +#if !defined(__APPLE__) + cmdbuf.setDepthBounds(depth_bounds_min, depth_bounds_max); +#endif + } + if (dirty_state.depth_bias_enabled) { + dirty_state.depth_bias_enabled = false; + cmdbuf.setDepthBiasEnable(depth_bias_enabled); + } + if (depth_bias_enabled && dirty_state.depth_bias) { + dirty_state.depth_bias = false; + cmdbuf.setDepthBias(depth_bias_constant, depth_bias_clamp, depth_bias_slope); + } + if (dirty_state.stencil_test_enabled) { + dirty_state.stencil_test_enabled = false; + cmdbuf.setStencilTestEnable(stencil_test_enabled); + } + if (stencil_test_enabled) { + if (dirty_state.stencil_front_ops && dirty_state.stencil_back_ops && + stencil_front_ops == stencil_back_ops) { + dirty_state.stencil_front_ops = false; + dirty_state.stencil_back_ops = false; + cmdbuf.setStencilOp(vk::StencilFaceFlagBits::eFrontAndBack, stencil_front_ops.fail_op, + stencil_front_ops.pass_op, stencil_front_ops.depth_fail_op, + stencil_front_ops.compare_op); + } else { + if (dirty_state.stencil_front_ops) { + dirty_state.stencil_front_ops = false; + cmdbuf.setStencilOp(vk::StencilFaceFlagBits::eFront, stencil_front_ops.fail_op, + stencil_front_ops.pass_op, stencil_front_ops.depth_fail_op, + stencil_front_ops.compare_op); + } + if (dirty_state.stencil_back_ops) { + dirty_state.stencil_back_ops = false; + cmdbuf.setStencilOp(vk::StencilFaceFlagBits::eBack, stencil_back_ops.fail_op, + stencil_back_ops.pass_op, stencil_back_ops.depth_fail_op, + stencil_back_ops.compare_op); + } + } + if (dirty_state.stencil_front_reference && dirty_state.stencil_back_reference && + stencil_front_reference == stencil_back_reference) { + dirty_state.stencil_front_reference = false; + dirty_state.stencil_back_reference = false; + cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eFrontAndBack, + stencil_front_reference); + } else { + if (dirty_state.stencil_front_reference) { + dirty_state.stencil_front_reference = false; + cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eFront, + stencil_front_reference); + } + if (dirty_state.stencil_back_reference) { + dirty_state.stencil_back_reference = false; + cmdbuf.setStencilReference(vk::StencilFaceFlagBits::eBack, stencil_back_reference); + } + } + if (dirty_state.stencil_front_write_mask && dirty_state.stencil_back_write_mask && + stencil_front_write_mask == stencil_back_write_mask) { + dirty_state.stencil_front_write_mask = false; + dirty_state.stencil_back_write_mask = false; + cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eFrontAndBack, + stencil_front_write_mask); + } else { + if (dirty_state.stencil_front_write_mask) { + dirty_state.stencil_front_write_mask = false; + cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eFront, + stencil_front_write_mask); + } + if (dirty_state.stencil_back_write_mask) { + dirty_state.stencil_back_write_mask = false; + cmdbuf.setStencilWriteMask(vk::StencilFaceFlagBits::eBack, stencil_back_write_mask); + } + } + if (dirty_state.stencil_front_compare_mask && dirty_state.stencil_back_compare_mask && + stencil_front_compare_mask == stencil_back_compare_mask) { + dirty_state.stencil_front_compare_mask = false; + dirty_state.stencil_back_compare_mask = false; + cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eFrontAndBack, + stencil_front_compare_mask); + } else { + if (dirty_state.stencil_front_compare_mask) { + dirty_state.stencil_front_compare_mask = false; + cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eFront, + stencil_front_compare_mask); + } + if (dirty_state.stencil_back_compare_mask) { + dirty_state.stencil_back_compare_mask = false; + cmdbuf.setStencilCompareMask(vk::StencilFaceFlagBits::eBack, + stencil_back_compare_mask); + } + } + } + if (dirty_state.blend_constants) { + dirty_state.blend_constants = false; + cmdbuf.setBlendConstants(blend_constants.data()); + } + if (dirty_state.color_write_enables) { + dirty_state.color_write_enables = false; +#if !defined(__APPLE__) + if (num_color_write_enables != 0) { + cmdbuf.setColorWriteEnableEXT(num_color_write_enables, color_write_enables.data()); + } +#endif + } + if (dirty_state.line_width) { + dirty_state.line_width = false; + cmdbuf.setLineWidth(line_width); + } + if (dirty_state.feedback_loop_aspects && graphics.attachment_feedback_loop_enabled) { + dirty_state.feedback_loop_aspects = false; + cmdbuf.setAttachmentFeedbackLoopEnableEXT(feedback_loop_aspects); + } +} + +} // namespace Libs::Graphics diff --git a/src/graphics/host_gpu/renderer/dynamicState.h b/src/graphics/host_gpu/renderer/dynamicState.h new file mode 100644 index 000000000..c243b20ef --- /dev/null +++ b/src/graphics/host_gpu/renderer/dynamicState.h @@ -0,0 +1,258 @@ +#ifndef EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_DYNAMICSTATE_H_ +#define EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_DYNAMICSTATE_H_ + +#include "common/assert.h" +#include "graphics/host_gpu/renderer/renderTarget.h" +#include "graphics/host_gpu/vulkanCommon.h" + +#include +#include +#include +#include +#include + +namespace Libs::Graphics { + +struct GraphicContext; + +struct StencilOps { + vk::StencilOp fail_op {}; + vk::StencilOp pass_op {}; + vk::StencilOp depth_fail_op {}; + vk::CompareOp compare_op {}; + + bool operator==(const StencilOps& other) const { + return fail_op == other.fail_op && pass_op == other.pass_op && + depth_fail_op == other.depth_fail_op && compare_op == other.compare_op; + } +}; + +struct DynamicState { + struct { + bool viewports : 1; + bool scissors : 1; + + bool depth_test_enabled : 1; + bool depth_write_enabled : 1; + bool depth_compare_op : 1; + + bool depth_bounds_test_enabled : 1; + bool depth_bounds : 1; + + bool depth_bias_enabled : 1; + bool depth_bias : 1; + + bool stencil_test_enabled : 1; + bool stencil_front_ops : 1; + bool stencil_front_reference : 1; + bool stencil_front_write_mask : 1; + bool stencil_front_compare_mask : 1; + bool stencil_back_ops : 1; + bool stencil_back_reference : 1; + bool stencil_back_write_mask : 1; + bool stencil_back_compare_mask : 1; + + bool blend_constants : 1; + bool color_write_enables : 1; + bool line_width : 1; + bool feedback_loop_aspects : 1; + } dirty_state {}; + + std::array viewports {}; + std::array scissors {}; + uint32_t num_viewports {}; + uint32_t num_scissors {}; + + bool depth_test_enabled {}; + bool depth_write_enabled {}; + vk::CompareOp depth_compare_op {}; + + bool depth_bounds_test_enabled {}; + float depth_bounds_min {}; + float depth_bounds_max {}; + + bool depth_bias_enabled {}; + float depth_bias_constant {}; + float depth_bias_clamp {}; + float depth_bias_slope {}; + + bool stencil_test_enabled {}; + StencilOps stencil_front_ops {}; + uint32_t stencil_front_reference {}; + uint32_t stencil_front_write_mask {}; + uint32_t stencil_front_compare_mask {}; + StencilOps stencil_back_ops {}; + uint32_t stencil_back_reference {}; + uint32_t stencil_back_write_mask {}; + uint32_t stencil_back_compare_mask {}; + + std::array blend_constants {}; + std::array color_write_enables {}; + uint32_t num_color_write_enables {}; + float line_width {}; + vk::ImageAspectFlags feedback_loop_aspects {}; + + /// Commits the dynamic state to the provided command buffer. + void Commit(const GraphicContext& graphics, vk::CommandBuffer cmdbuf); + + /// Invalidates all dynamic state to be flushed into the next command buffer. + void Invalidate() { std::memset(&dirty_state, 0xFF, sizeof(dirty_state)); } + + void SetViewports(std::span viewports_) { + EXIT_IF(viewports_.size() > viewports.size()); + if (!std::ranges::equal(std::span(viewports).first(num_viewports), viewports_)) { + std::ranges::copy(viewports_, viewports.begin()); + num_viewports = static_cast(viewports_.size()); + dirty_state.viewports = true; + } + } + + void SetScissors(std::span scissors_) { + EXIT_IF(scissors_.size() > scissors.size()); + if (!std::ranges::equal(std::span(scissors).first(num_scissors), scissors_)) { + std::ranges::copy(scissors_, scissors.begin()); + num_scissors = static_cast(scissors_.size()); + dirty_state.scissors = true; + } + } + + void SetDepthTestEnabled(const bool enabled) { + if (depth_test_enabled != enabled) { + depth_test_enabled = enabled; + dirty_state.depth_test_enabled = true; + } + } + + void SetDepthWriteEnabled(const bool enabled) { + if (depth_write_enabled != enabled) { + depth_write_enabled = enabled; + dirty_state.depth_write_enabled = true; + } + } + + void SetDepthCompareOp(const vk::CompareOp compare_op) { + if (depth_compare_op != compare_op) { + depth_compare_op = compare_op; + dirty_state.depth_compare_op = true; + } + } + + void SetDepthBoundsTestEnabled(const bool enabled) { + if (depth_bounds_test_enabled != enabled) { + depth_bounds_test_enabled = enabled; + dirty_state.depth_bounds_test_enabled = true; + } + } + + void SetDepthBounds(const float min, const float max) { + if (depth_bounds_min != min || depth_bounds_max != max) { + depth_bounds_min = min; + depth_bounds_max = max; + dirty_state.depth_bounds = true; + } + } + + void SetDepthBiasEnabled(const bool enabled) { + if (depth_bias_enabled != enabled) { + depth_bias_enabled = enabled; + dirty_state.depth_bias_enabled = true; + } + } + + void SetDepthBias(const float constant, const float clamp, const float slope) { + if (depth_bias_constant != constant || depth_bias_clamp != clamp || + depth_bias_slope != slope) { + depth_bias_constant = constant; + depth_bias_clamp = clamp; + depth_bias_slope = slope; + dirty_state.depth_bias = true; + } + } + + void SetStencilTestEnabled(const bool enabled) { + if (stencil_test_enabled != enabled) { + stencil_test_enabled = enabled; + dirty_state.stencil_test_enabled = true; + } + } + + void SetStencilOps(const StencilOps& front_ops, const StencilOps& back_ops) { + if (stencil_front_ops != front_ops) { + stencil_front_ops = front_ops; + dirty_state.stencil_front_ops = true; + } + if (stencil_back_ops != back_ops) { + stencil_back_ops = back_ops; + dirty_state.stencil_back_ops = true; + } + } + + void SetStencilReferences(const uint32_t front_reference, const uint32_t back_reference) { + if (stencil_front_reference != front_reference) { + stencil_front_reference = front_reference; + dirty_state.stencil_front_reference = true; + } + if (stencil_back_reference != back_reference) { + stencil_back_reference = back_reference; + dirty_state.stencil_back_reference = true; + } + } + + void SetStencilWriteMasks(const uint32_t front_write_mask, const uint32_t back_write_mask) { + if (stencil_front_write_mask != front_write_mask) { + stencil_front_write_mask = front_write_mask; + dirty_state.stencil_front_write_mask = true; + } + if (stencil_back_write_mask != back_write_mask) { + stencil_back_write_mask = back_write_mask; + dirty_state.stencil_back_write_mask = true; + } + } + + void SetStencilCompareMasks(const uint32_t front_compare_mask, + const uint32_t back_compare_mask) { + if (stencil_front_compare_mask != front_compare_mask) { + stencil_front_compare_mask = front_compare_mask; + dirty_state.stencil_front_compare_mask = true; + } + if (stencil_back_compare_mask != back_compare_mask) { + stencil_back_compare_mask = back_compare_mask; + dirty_state.stencil_back_compare_mask = true; + } + } + + void SetBlendConstants(const std::array blend_constants_) { + if (blend_constants != blend_constants_) { + blend_constants = blend_constants_; + dirty_state.blend_constants = true; + } + } + + void SetColorWriteEnables(std::span enables) { + EXIT_IF(enables.size() > color_write_enables.size()); + if (!std::ranges::equal(std::span(color_write_enables).first(num_color_write_enables), + enables)) { + std::ranges::copy(enables, color_write_enables.begin()); + num_color_write_enables = static_cast(enables.size()); + dirty_state.color_write_enables = true; + } + } + + void SetLineWidth(const float width) { + if (line_width != width) { + line_width = width; + dirty_state.line_width = true; + } + } + + void SetAttachmentFeedbackLoopAspects(const vk::ImageAspectFlags aspects) { + if (feedback_loop_aspects != aspects) { + feedback_loop_aspects = aspects; + dirty_state.feedback_loop_aspects = true; + } + } +}; + +} // namespace Libs::Graphics + +#endif // EMULATOR_SRC_GRAPHICS_HOST_GPU_RENDERER_DYNAMICSTATE_H_ diff --git a/src/graphics/host_gpu/renderer/image/blitHelper.cpp b/src/graphics/host_gpu/renderer/image/blitHelper.cpp index f5dbf2c7a..f029dfd34 100644 --- a/src/graphics/host_gpu/renderer/image/blitHelper.cpp +++ b/src/graphics/host_gpu/renderer/image/blitHelper.cpp @@ -193,6 +193,8 @@ void BlitHelper::ReinterpretColorAsMsDepth(Image& source, Image& destination) { command.setScissor(0, 1, &scissor); command.draw(3, 1, 0, 0); command.endRendering(); + // The helper pipeline has static state that invalidates the guest draw's dynamic state. + m_scheduler.GetDynamicState().Invalidate(); } } // namespace Libs::Graphics diff --git a/src/graphics/host_gpu/renderer/renderDraw.cpp b/src/graphics/host_gpu/renderer/renderDraw.cpp index 84d8c8138..f088fff49 100644 --- a/src/graphics/host_gpu/renderer/renderDraw.cpp +++ b/src/graphics/host_gpu/renderer/renderDraw.cpp @@ -312,9 +312,9 @@ static void LogDrawInputState(const CommandBuffer& buffer, const RenderColorInfo } } -static void SetGraphicsDynamicParams(const CommandBuffer& buffer, vk::CommandBuffer vk_buffer, - const ShaderVertexInputInfo& vs_input_info, - const RenderDepthInfo& depth, const RenderState& rendering) { +static void UpdateGraphicsDynamicState(const CommandBuffer& buffer, DynamicState& dynamic_state, + const ShaderVertexInputInfo& vs_input_info, + const RenderDepthInfo& depth, const RenderState& rendering) { KYTY_PROFILER_FUNCTION(); const auto& ctx = buffer.GetRegisters(); @@ -358,8 +358,8 @@ static void SetGraphicsDynamicParams(const CommandBuffer& buffer, vk::CommandBuf scissor.extent = {0, 0}; } } - vk_buffer.setViewportWithCount(viewport_count, viewports.data()); - vk_buffer.setScissorWithCount(viewport_count, scissors.data()); + dynamic_state.SetViewports(std::span {viewports}.first(viewport_count)); + dynamic_state.SetScissors(std::span {scissors}.first(viewport_count)); float line_width = ctx.GetLineWidth(); if (line_width != 1.0f) { @@ -372,17 +372,17 @@ static void SetGraphicsDynamicParams(const CommandBuffer& buffer, vk::CommandBuf } line_width = 1.0f; } - vk_buffer.setLineWidth(line_width); + dynamic_state.SetLineWidth(line_width); const auto& blend = ctx.GetBlendColor(); const std::array blend_constants {blend.red, blend.green, blend.blue, blend.alpha}; - vk_buffer.setBlendConstants(blend_constants.data()); - vk_buffer.setDepthTestEnable(depth.depth_test_enable ? VK_TRUE : VK_FALSE); - vk_buffer.setDepthWriteEnable(depth.depth_write_enable ? VK_TRUE : VK_FALSE); - vk_buffer.setDepthCompareOp(depth.depth_compare_op); + dynamic_state.SetBlendConstants(blend_constants); + dynamic_state.SetDepthTestEnabled(depth.depth_test_enable); + dynamic_state.SetDepthWriteEnabled(depth.depth_write_enable); + dynamic_state.SetDepthCompareOp(depth.depth_compare_op); #if !defined(__APPLE__) - vk_buffer.setDepthBoundsTestEnable(depth.depth_bounds_test_enable ? VK_TRUE : VK_FALSE); + dynamic_state.SetDepthBoundsTestEnabled(depth.depth_bounds_test_enable); if (depth.depth_bounds_test_enable) { - vk_buffer.setDepthBounds(ctx.GetDepthBoundsMin(), ctx.GetDepthBoundsMax()); + dynamic_state.SetDepthBounds(ctx.GetDepthBoundsMin(), ctx.GetDepthBoundsMax()); } #endif @@ -391,7 +391,7 @@ static void SetGraphicsDynamicParams(const CommandBuffer& buffer, vk::CommandBuf const bool use_front = mode.poly_offset_front_enable && !mode.cull_front; const bool use_back = mode.poly_offset_back_enable && !mode.cull_back; const bool depth_bias_enable = use_front || use_back; - vk_buffer.setDepthBiasEnable(depth_bias_enable ? VK_TRUE : VK_FALSE); + dynamic_state.SetDepthBiasEnabled(depth_bias_enable); if (depth_bias_enable) { // Vulkan has one bias for both faces. Prefer a visible front face when both are enabled. const float guest_constant_factor = @@ -400,19 +400,18 @@ static void SetGraphicsDynamicParams(const CommandBuffer& buffer, vk::CommandBuf guest_constant_factor, poly_offset, depth.desc.view_info.format); const float slope_factor = (use_front ? poly_offset.front_scale : poly_offset.back_scale) / 16.0f; - vk_buffer.setDepthBias(constant_factor, poly_offset.clamp, slope_factor); + dynamic_state.SetDepthBias(constant_factor, poly_offset.clamp, slope_factor); } - vk_buffer.setStencilTestEnable(depth.stencil_test_enable ? VK_TRUE : VK_FALSE); + dynamic_state.SetStencilTestEnabled(depth.stencil_test_enable); if (depth.stencil_test_enable) { - const auto set_stencil = [&](vk::StencilFaceFlagBits face, const vk::StencilOpState& state) { - vk_buffer.setStencilOp(face, state.failOp, state.passOp, state.depthFailOp, state.compareOp); - vk_buffer.setStencilCompareMask(face, state.compareMask); - vk_buffer.setStencilWriteMask(face, state.writeMask); - vk_buffer.setStencilReference(face, state.reference); - }; - set_stencil(vk::StencilFaceFlagBits::eFront, depth.stencil_front); - set_stencil(vk::StencilFaceFlagBits::eBack, depth.stencil_back); + const auto& front = depth.stencil_front; + const auto& back = depth.stencil_back; + dynamic_state.SetStencilOps({front.failOp, front.passOp, front.depthFailOp, front.compareOp}, + {back.failOp, back.passOp, back.depthFailOp, back.compareOp}); + dynamic_state.SetStencilCompareMasks(front.compareMask, back.compareMask); + dynamic_state.SetStencilWriteMasks(front.writeMask, back.writeMask); + dynamic_state.SetStencilReferences(front.reference, back.reference); } #if defined(__APPLE__) @@ -423,9 +422,7 @@ static void SetGraphicsDynamicParams(const CommandBuffer& buffer, vk::CommandBuf for (uint32_t slot = 0; slot < rendering.num_color_attachments; slot++) { enable[slot] = rendering.color_attachments[slot].image_view != nullptr; } - if (rendering.num_color_attachments != 0) { - vk_buffer.setColorWriteEnableEXT(rendering.num_color_attachments, enable); - } + dynamic_state.SetColorWriteEnables(std::span {enable}.first(rendering.num_color_attachments)); #endif } @@ -1139,10 +1136,11 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff CommitIndexBuffer(vk_buffer, index_binding); } - SetGraphicsDynamicParams(buffer, vk_buffer, vertex_stages.back(), state.depth_info, rendering); - if (m_context.GetGraphics().attachment_feedback_loop_enabled) { - vk_buffer.setAttachmentFeedbackLoopEnableEXT(feedback_aspects); - } + auto& dynamic_state = m_context.GetCommandScheduler().GetDynamicState(); + UpdateGraphicsDynamicState(buffer, dynamic_state, vertex_stages.back(), state.depth_info, + rendering); + dynamic_state.SetAttachmentFeedbackLoopAspects(feedback_aspects); + dynamic_state.Commit(m_context.GetGraphics(), vk_buffer); LogDrawPhase(draw.Name(), "BeginRendering"); if (!draw.IsIndexed()) {