From c90781d3e21ce2f141a81d6160d3d2b350c1d754 Mon Sep 17 00:00:00 2001 From: TheTurtle Date: Mon, 25 Aug 2025 00:30:59 +0300 Subject: [PATCH] renderer_vulkan: Resore color write dynamic state (#3453) --- src/video_core/amdgpu/pixel_format.h | 8 ++++++++ .../renderer_vulkan/vk_graphics_pipeline.cpp | 9 ++++++++- .../renderer_vulkan/vk_graphics_pipeline.h | 6 +++++- src/video_core/renderer_vulkan/vk_instance.cpp | 14 ++++++++++++++ src/video_core/renderer_vulkan/vk_instance.h | 8 ++++++++ .../renderer_vulkan/vk_pipeline_cache.cpp | 6 ++---- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 1 + src/video_core/renderer_vulkan/vk_scheduler.cpp | 6 ++++++ src/video_core/renderer_vulkan/vk_scheduler.h | 9 +++++++++ 9 files changed, 61 insertions(+), 6 deletions(-) diff --git a/src/video_core/amdgpu/pixel_format.h b/src/video_core/amdgpu/pixel_format.h index af7a69746..27c9313a2 100644 --- a/src/video_core/amdgpu/pixel_format.h +++ b/src/video_core/amdgpu/pixel_format.h @@ -127,6 +127,14 @@ union CompMapping { }; } + [[nodiscard]] u32 ApplyMask(u32 mask) const { + u32 swizzled_mask{}; + for (u32 i = 0; i < 4; ++i) { + swizzled_mask |= ((mask >> i) & 1) << Map(i); + } + return swizzled_mask; + } + [[nodiscard]] CompMapping Inverse() const { CompMapping result{}; InverseSingle(result.r, CompSwizzle::Red); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index e4ebb8104..8094bc260 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -168,6 +168,9 @@ GraphicsPipeline::GraphicsPipeline( dynamic_states.push_back(vk::DynamicState::eDepthBoundsTestEnable); dynamic_states.push_back(vk::DynamicState::eDepthBounds); } + if (instance.IsDynamicColorWriteMaskSupported()) { + dynamic_states.push_back(vk::DynamicState::eColorWriteMaskEXT); + } if (instance.IsVertexInputDynamicState()) { dynamic_states.push_back(vk::DynamicState::eVertexInputEXT); } else if (!vertex_bindings.empty()) { @@ -288,7 +291,11 @@ GraphicsPipeline::GraphicsPipeline( .alphaBlendOp = control.separate_alpha_blend ? LiverpoolToVK::BlendOp(control.alpha_func) : color_blend, - .colorWriteMask = vk::ColorComponentFlags{key.write_masks[i]}, + .colorWriteMask = + instance.IsDynamicColorWriteMaskSupported() + ? vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | + vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA + : key.write_masks[i], }; // On GCN GPU there is an additional mask which allows to control color components exported diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index e7d4c9463..9a3199de9 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -38,7 +38,7 @@ struct GraphicsPipelineKey { u32 num_color_attachments; std::array color_buffers; std::array blend_controls; - std::array write_masks; + std::array write_masks; Liverpool::ColorBufferMask cb_shader_mask; Liverpool::ColorControl::LogicOp logic_op; u32 num_samples; @@ -80,6 +80,10 @@ public: return fetch_shader; } + auto GetWriteMasks() const { + return key.write_masks; + } + u32 GetMrtMask() const { return key.mrt_mask; } diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 2201c6493..d13aeec99 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -255,6 +255,13 @@ bool Instance::CreateDevice() { // Optional maintenance_8 = add_extension(VK_KHR_MAINTENANCE_8_EXTENSION_NAME); depth_range_unrestricted = add_extension(VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME); + dynamic_state_3 = add_extension(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); + if (dynamic_state_3) { + dynamic_state_3_features = + feature_chain.get(); + LOG_INFO(Render_Vulkan, "- extendedDynamicState3ColorWriteMask: {}", + dynamic_state_3_features.extendedDynamicState3ColorWriteMask); + } robustness2 = add_extension(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); if (robustness2) { robustness2_features = feature_chain.get(); @@ -419,6 +426,10 @@ bool Instance::CreateDevice() { .customBorderColors = true, .customBorderColorWithoutFormat = true, }, + vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT{ + .extendedDynamicState3ColorWriteMask = + dynamic_state_3_features.extendedDynamicState3ColorWriteMask, + }, vk::PhysicalDeviceDepthClipControlFeaturesEXT{ .depthClipControl = true, }, @@ -494,6 +505,9 @@ bool Instance::CreateDevice() { if (!custom_border_color) { device_chain.unlink(); } + if (!dynamic_state_3) { + device_chain.unlink(); + } if (!depth_clip_control) { device_chain.unlink(); } diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index 3bf7d335d..e1fa180fb 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -139,6 +139,12 @@ public: return depth_range_unrestricted; } + /// Returns true when the extendedDynamicState3ColorWriteMask feature o + /// VK_EXT_extended_dynamic_state3 is supported. + bool IsDynamicColorWriteMaskSupported() const { + return dynamic_state_3 && dynamic_state_3_features.extendedDynamicState3ColorWriteMask; + } + /// Returns true when VK_EXT_vertex_input_dynamic_state is supported. bool IsVertexInputDynamicState() const { return vertex_input_dynamic_state; @@ -433,6 +439,7 @@ private: vk::PhysicalDeviceFeatures features; vk::PhysicalDeviceVulkan12Features vk12_features; vk::PhysicalDevicePortabilitySubsetFeaturesKHR portability_features; + vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT dynamic_state_3_features; vk::PhysicalDeviceRobustness2FeaturesEXT robustness2_features; vk::PhysicalDeviceShaderAtomicFloat2FeaturesEXT shader_atomic_float2_features; vk::PhysicalDeviceWorkgroupMemoryExplicitLayoutFeaturesKHR @@ -453,6 +460,7 @@ private: bool amd_shader_explicit_vertex_parameter{}; bool depth_clip_control{}; bool depth_clip_enable{}; + bool dynamic_state_3{}; bool depth_range_unrestricted{}; bool vertex_input_dynamic_state{}; bool robustness2{}; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 5e98810f9..e95260554 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -345,10 +345,8 @@ bool PipelineCache::RefreshGraphicsKey() { !col_buf.info.blend_bypass); // Apply swizzle to target mask - const auto& swizzle = key.color_buffers[cb].swizzle; - for (u32 i = 0; i < 4; ++i) { - key.write_masks[cb] |= ((target_mask >> i) & 1) << swizzle.Map(i); - } + key.write_masks[cb] = + vk::ColorComponentFlags{key.color_buffers[cb].swizzle.ApplyMask(target_mask)}; } // Compile and bind shader stages diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 7fe2ce0cc..5be19726b 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1097,6 +1097,7 @@ void Rasterizer::UpdateDynamicState(const GraphicsPipeline& pipeline) const { auto& dynamic_state = scheduler.GetDynamicState(); dynamic_state.SetBlendConstants(liverpool->regs.blend_constants); + dynamic_state.SetColorWriteMasks(pipeline.GetWriteMasks()); // Commit new dynamic state to the command buffer. dynamic_state.Commit(instance, scheduler.CommandBuffer()); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 9e5a4b521..a34bb15ad 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -323,6 +323,12 @@ void DynamicState::Commit(const Instance& instance, const vk::CommandBuffer& cmd dirty_state.blend_constants = false; cmdbuf.setBlendConstants(blend_constants.data()); } + if (dirty_state.color_write_masks) { + dirty_state.color_write_masks = false; + if (instance.IsDynamicColorWriteMaskSupported()) { + cmdbuf.setColorWriteMaskEXT(0, color_write_masks); + } + } if (dirty_state.line_width) { dirty_state.line_width = false; cmdbuf.setLineWidth(line_width); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 949dbd6f5..2c4a0b27e 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -108,6 +108,7 @@ struct DynamicState { bool front_face : 1; bool blend_constants : 1; + bool color_write_masks : 1; bool line_width : 1; } dirty_state{}; @@ -143,6 +144,7 @@ struct DynamicState { vk::FrontFace front_face{}; std::array blend_constants{}; + ColorWriteMasks color_write_masks{}; float line_width{}; /// Commits the dynamic state to the provided command buffer. @@ -306,6 +308,13 @@ struct DynamicState { } } + void SetColorWriteMasks(const ColorWriteMasks& color_write_masks_) { + if (!std::ranges::equal(color_write_masks, color_write_masks_)) { + color_write_masks = color_write_masks_; + dirty_state.color_write_masks = true; + } + } + void SetLineWidth(const float width) { if (line_width != width) { line_width = width;