diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 3c041a48cb..1378c30efd 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -80,6 +80,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe extended_dynamic_state_2_logic_op.Assign(features.has_extended_dynamic_state_2_logic_op ? 1 : 0); extended_dynamic_state_3_blend.Assign(features.has_extended_dynamic_state_3_blend ? 1 : 0); extended_dynamic_state_3_enables.Assign(features.has_extended_dynamic_state_3_enables ? 1 : 0); + color_write_enable_dynamic.Assign(features.has_color_write_enable ? 1 : 0); dynamic_vertex_input.Assign(features.has_dynamic_vertex_input ? 1 : 0); xfb_enabled.Assign(regs.transform_feedback_enabled != 0); ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); @@ -209,6 +210,15 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe maxwell3d.dirty.flags[Dirty::Blending] = false; for (size_t index = 0; index < attachments.size(); ++index) { attachments[index].Refresh(regs, index); + auto& attachment = attachments[index]; + if (color_write_enable_dynamic && attachment.mask_r == 0 && + attachment.mask_g == 0 && attachment.mask_b == 0 && + attachment.mask_a == 0) { + attachment.mask_r.Assign(1); + attachment.mask_g.Assign(1); + attachment.mask_b.Assign(1); + attachment.mask_a.Assign(1); + } } } } diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 44157d686d..0b631a9e89 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -31,6 +31,7 @@ struct DynamicFeatures { bool has_dynamic_state3_logic_op_enable; bool has_dynamic_state3_line_stipple_enable; bool has_dynamic_vertex_input; + bool has_color_write_enable; bool has_provoking_vertex; bool has_provoking_vertex_first_mode; bool has_provoking_vertex_last_mode; @@ -208,6 +209,7 @@ struct FixedPipelineState { BitField<12, 2, u32> tessellation_spacing; BitField<14, 1, u32> tessellation_clockwise; BitField<15, 5, u32> patch_control_points_minus_one; + BitField<20, 1, u32> color_write_enable_dynamic; BitField<24, 4, Maxwell::PrimitiveTopology> topology; BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 6f1c07f84b..b6c2d855d2 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -869,15 +869,13 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { }; dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end()); - // VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT is part of EDS1 - // Only use it if VIDS is not active (VIDS replaces it with full vertex input control) + // VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT if (!key.state.dynamic_vertex_input) { dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT); } } - // VK_DYNAMIC_STATE_VERTEX_INPUT_EXT (VIDS) - Independent from EDS - // Provides full dynamic vertex input control, replaces VERTEX_INPUT_BINDING_STRIDE + // VK_DYNAMIC_STATE_VERTEX_INPUT_EXT if (key.state.dynamic_vertex_input) { dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT); } @@ -907,6 +905,11 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end()); } + // VK_EXT_color_write_enable fallback for fully on/off render targets when EDS3 blending is not available. + if (!key.state.extended_dynamic_state_3_blend && key.state.color_write_enable_dynamic) { + dynamic_states.push_back(VK_DYNAMIC_STATE_COLOR_WRITE_ENABLE_EXT); + } + // EDS3 - Enables (composite: per-feature) if (key.state.extended_dynamic_state_3_enables) { if (device.SupportsDynamicState3DepthClampEnable()) { diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 15f7e9bf43..24ec481018 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -500,6 +500,8 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, device.IsExtExtendedDynamicState3BlendingSupported(); dynamic_features.has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported(); + dynamic_features.has_color_write_enable = + device.IsExtColorWriteEnableSupported(); dynamic_features.has_dynamic_state3_depth_clamp_enable = dynamic_features.has_extended_dynamic_state_3_enables && device.SupportsDynamicState3DepthClampEnable(); @@ -632,7 +634,10 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading dynamic_features.has_extended_dynamic_state_3_blend || (key.state.extended_dynamic_state_3_enables != 0) != dynamic_features.has_extended_dynamic_state_3_enables || - (key.state.dynamic_vertex_input != 0) != dynamic_features.has_dynamic_vertex_input) { + (key.state.color_write_enable_dynamic != 0) != + dynamic_features.has_color_write_enable || + (key.state.dynamic_vertex_input != 0) != + dynamic_features.has_dynamic_vertex_input) { return; } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index c4ac861927..618a8a8a6b 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1080,6 +1080,8 @@ void RasterizerVulkan::UpdateDynamicStates() { if (device.IsExtExtendedDynamicState3BlendingSupported()) { UpdateBlending(regs); + } else if (device.IsExtColorWriteEnableSupported()) { + UpdateColorWriteEnable(regs); } if (device.IsExtVertexInputDynamicStateSupported()) { @@ -1094,7 +1096,6 @@ void RasterizerVulkan::HandleTransformFeedback() { const auto& regs = maxwell3d->regs; if (!device.IsExtTransformFeedbackSupported()) { - // If the guest enabled transform feedback, warn once that the device lacks support. if (regs.transform_feedback_enabled != 0) { std::call_once(warn_unsupported, [&] { LOG_WARNING(Render_Vulkan, "Transform feedback requested by guest but VK_EXT_transform_feedback is unavailable; queries disabled"); @@ -1774,6 +1775,20 @@ void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) { } } +void RasterizerVulkan::UpdateColorWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs) { + if (!state_tracker.TouchColorMask()) { + return; + } + std::array setup_enables{}; + for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { + const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : index]; + setup_enables[index] = (mask.R || mask.G || mask.B || mask.A) ? VK_TRUE : VK_FALSE; + } + scheduler.Record([setup_enables](vk::CommandBuffer cmdbuf) { + cmdbuf.SetColorWriteEnableEXT(0, setup_enables); + }); +} + void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) { if (!state_tracker.TouchStencilTestEnable()) { return; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 841933d31d..206bf1043a 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -191,6 +191,7 @@ private: void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs); + void UpdateColorWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 7804e33d46..93ea5cf8db 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -2297,12 +2297,15 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t const void* pnext = nullptr; if (has_custom_border_colors) { pnext = &border_ci; - // Log extension usage for custom border color if (GPU::Logging::IsActive()) { GPU::Logging::GPULogger::GetInstance().LogExtensionUsage( "VK_EXT_custom_border_color", "Sampler::Sampler"); } } + if (device.IsExtBorderColorSwizzleSupported() && GPU::Logging::IsActive()) { + GPU::Logging::GPULogger::GetInstance().LogExtensionUsage( + "VK_EXT_border_color_swizzle", "Sampler::Sampler"); + } const VkSamplerReductionModeCreateInfoEXT reduction_ci{ .sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT, .pNext = pnext, diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index ce46234fff..e99d721c16 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1170,6 +1170,21 @@ bool Device::GetSuitability(bool requires_swapchain) { } void Device::RemoveUnsuitableExtensions() { + // VK_EXT_color_write_enable + extensions.color_write_enable = features.color_write_enable.colorWriteEnable; + RemoveExtensionFeatureIfUnsuitable(extensions.color_write_enable, features.color_write_enable, + VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME); + + // VK_EXT_border_color_swizzle + if (extensions.border_color_swizzle) { + extensions.border_color_swizzle = + features.border_color_swizzle.borderColorSwizzle && + features.border_color_swizzle.borderColorSwizzleFromImage; + } + RemoveExtensionFeatureIfUnsuitable(extensions.border_color_swizzle, + features.border_color_swizzle, + VK_EXT_BORDER_COLOR_SWIZZLE_EXTENSION_NAME); + // VK_EXT_custom_border_color if (extensions.custom_border_color) { extensions.custom_border_color = diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index d224ad62b0..e0924543e6 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -50,6 +50,8 @@ VK_DEFINE_HANDLE(VmaAllocator) // Define all features which may be used by the implementation and require an extension here. #define FOR_EACH_VK_FEATURE_EXT(FEATURE) \ + FEATURE(EXT, BorderColorSwizzle, BORDER_COLOR_SWIZZLE, border_color_swizzle) \ + FEATURE(EXT, ColorWriteEnable, COLOR_WRITE_ENABLE, color_write_enable) \ FEATURE(EXT, CustomBorderColor, CUSTOM_BORDER_COLOR, custom_border_color) \ FEATURE(EXT, DepthBiasControl, DEPTH_BIAS_CONTROL, depth_bias_control) \ FEATURE(EXT, DepthClipControl, DEPTH_CLIP_CONTROL, depth_clip_control) \ @@ -584,6 +586,21 @@ FN_MAX_LIMIT_LIST return features.custom_border_color.customBorderColorWithoutFormat; } + /// Returns true if the device supports VK_EXT_color_write_enable. + bool IsExtColorWriteEnableSupported() const { + return extensions.color_write_enable; + } + + /// Returns true if the device supports VK_EXT_border_color_swizzle. + bool IsExtBorderColorSwizzleSupported() const { + return extensions.border_color_swizzle; + } + + /// Returns true if borderColorSwizzleFromImage is available. + bool IsBorderColorSwizzleFromImageSupported() const { + return features.border_color_swizzle.borderColorSwizzleFromImage; + } + /// Returns true if the device supports VK_EXT_extended_dynamic_state. bool IsExtExtendedDynamicStateSupported() const { return extensions.extended_dynamic_state; diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 95793d252f..6f35e0b0c3 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -162,6 +162,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCmdSetStencilTestEnableEXT); X(vkCmdSetVertexInputEXT); X(vkCmdSetColorWriteMaskEXT); + X(vkCmdSetColorWriteEnableEXT); X(vkCmdSetColorBlendEnableEXT); X(vkCmdSetColorBlendEquationEXT); X(vkCmdResolveImage); diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 6b458080d3..80e1a34154 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -277,6 +277,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdSetVertexInputEXT vkCmdSetVertexInputEXT{}; PFN_vkCmdSetViewport vkCmdSetViewport{}; PFN_vkCmdSetColorWriteMaskEXT vkCmdSetColorWriteMaskEXT{}; + PFN_vkCmdSetColorWriteEnableEXT vkCmdSetColorWriteEnableEXT{}; PFN_vkCmdSetColorBlendEnableEXT vkCmdSetColorBlendEnableEXT{}; PFN_vkCmdSetColorBlendEquationEXT vkCmdSetColorBlendEquationEXT{}; PFN_vkCmdWaitEvents vkCmdWaitEvents{}; @@ -1589,6 +1590,10 @@ public: dld->vkCmdSetColorWriteMaskEXT(handle, first, masks.size(), masks.data()); } + void SetColorWriteEnableEXT(u32 first, Span enables) const noexcept { + dld->vkCmdSetColorWriteEnableEXT(handle, first, enables.size(), enables.data()); + } + void SetColorBlendEnableEXT(u32 first, Span enables) const noexcept { dld->vkCmdSetColorBlendEnableEXT(handle, first, enables.size(), enables.data()); }