From 6e73a5e395bdbb7cf0ab54d6844f4bfc902dc276 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 31 Jan 2026 23:43:51 -0400 Subject: [PATCH] [vulkan] Adding VK_EXT_color_write_enable --- .../renderer_vulkan/vk_rasterizer.cpp | 50 ++++++++++++------- .../vulkan_common/vulkan_device.cpp | 27 ++++++---- src/video_core/vulkan_common/vulkan_device.h | 6 +++ .../vulkan_common/vulkan_wrapper.cpp | 3 +- src/video_core/vulkan_common/vulkan_wrapper.h | 7 ++- 5 files changed, 65 insertions(+), 28 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 60b899a811..1cb4632106 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1070,6 +1070,7 @@ void RasterizerVulkan::UpdateDynamicStates() { } // EDS3 Blending: ColorBlendEnable, ColorBlendEquation, ColorWriteMask + // or VK_EXT_color_write_enable if EDS3 is not available if (device.IsExtExtendedDynamicState3BlendingSupported()) { UpdateBlending(regs); } @@ -1673,26 +1674,41 @@ void RasterizerVulkan::UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs) { } if (state_tracker.TouchColorMask()) { - std::array setup_masks{}; - for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { - const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : index]; - auto& current = setup_masks[index]; - if (mask.R) { - current |= VK_COLOR_COMPONENT_R_BIT; - } - if (mask.G) { - current |= VK_COLOR_COMPONENT_G_BIT; - } - if (mask.B) { - current |= VK_COLOR_COMPONENT_B_BIT; + // Use VK_EXT_color_write_enable if available and EDS3 is not active + if (device.IsExtColorWriteEnableSupported()) { + // With color_write_enable, we set enable/disable per attachment + 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]; + // Enable writing only if at least one component is enabled + setup_enables[index] = (mask.R || mask.G || mask.B || mask.A) ? VK_TRUE : VK_FALSE; } - if (mask.A) { - current |= VK_COLOR_COMPONENT_A_BIT; + scheduler.Record([setup_enables](vk::CommandBuffer cmdbuf) { + cmdbuf.SetColorWriteEnableEXT(0, setup_enables); + }); + } else { + // Fallback: Use ColorWriteMask from EDS3 + std::array setup_masks{}; + for (size_t index = 0; index < Maxwell::NumRenderTargets; index++) { + const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : index]; + auto& current = setup_masks[index]; + if (mask.R) { + current |= VK_COLOR_COMPONENT_R_BIT; + } + if (mask.G) { + current |= VK_COLOR_COMPONENT_G_BIT; + } + if (mask.B) { + current |= VK_COLOR_COMPONENT_B_BIT; + } + if (mask.A) { + current |= VK_COLOR_COMPONENT_A_BIT; + } } + scheduler.Record([setup_masks](vk::CommandBuffer cmdbuf) { + cmdbuf.SetColorWriteMaskEXT(0, setup_masks); + }); } - scheduler.Record([setup_masks](vk::CommandBuffer cmdbuf) { - cmdbuf.SetColorWriteMaskEXT(0, setup_masks); - }); } if (state_tracker.TouchBlendEnable()) { diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index d985671e51..f52c28e7ef 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1394,8 +1394,19 @@ void Device::RemoveUnsuitableExtensions() { features.extended_dynamic_state3, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); + // VK_EXT_color_write_enable + if (extensions.extended_dynamic_state3 && dynamic_state3_blending) { + LOG_INFO(Render_Vulkan, "VK_EXT_color_write_enable disabled: VK_EXT_extended_dynamic_state3 provides ColorWriteMask functionality"); + RemoveExtensionFeature(extensions.color_write_enable, features.color_write_enable, + VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME); + } else { + 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_robustness2 - // Enable if at least one robustness2 feature is available extensions.robustness_2 = features.robustness2.robustBufferAccess2 || features.robustness2.robustImageAccess2 || features.robustness2.nullDescriptor; @@ -1404,7 +1415,6 @@ void Device::RemoveUnsuitableExtensions() { VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); // VK_EXT_image_robustness - // Enable if robustImageAccess is available extensions.image_robustness = features.image_robustness.robustImageAccess; RemoveExtensionFeatureIfUnsuitable(extensions.image_robustness, features.image_robustness, VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME); @@ -1505,7 +1515,6 @@ void Device::RemoveUnsuitableExtensions() { VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME); // VK_EXT_swapchain_maintenance1 (extension only, has features) - // Requires VK_EXT_surface_maintenance1 instance extension extensions.swapchain_maintenance1 = features.swapchain_maintenance1.swapchainMaintenance1; if (extensions.swapchain_maintenance1) { // Check if VK_EXT_surface_maintenance1 instance extension is available @@ -1524,15 +1533,15 @@ void Device::RemoveUnsuitableExtensions() { RemoveExtensionFeatureIfUnsuitable(extensions.swapchain_maintenance1, features.swapchain_maintenance1, VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME); - // VK_KHR_maintenance1 (core in Vulkan 1.1, no features) + // VK_KHR_maintenance1 extensions.maintenance1 = loaded_extensions.contains(VK_KHR_MAINTENANCE_1_EXTENSION_NAME); RemoveExtensionIfUnsuitable(extensions.maintenance1, VK_KHR_MAINTENANCE_1_EXTENSION_NAME); - // VK_KHR_maintenance2 (core in Vulkan 1.1, no features) + // VK_KHR_maintenance2 extensions.maintenance2 = loaded_extensions.contains(VK_KHR_MAINTENANCE_2_EXTENSION_NAME); RemoveExtensionIfUnsuitable(extensions.maintenance2, VK_KHR_MAINTENANCE_2_EXTENSION_NAME); - // VK_KHR_maintenance3 (core in Vulkan 1.1, no features) + // VK_KHR_maintenance3 extensions.maintenance3 = loaded_extensions.contains(VK_KHR_MAINTENANCE_3_EXTENSION_NAME); RemoveExtensionIfUnsuitable(extensions.maintenance3, VK_KHR_MAINTENANCE_3_EXTENSION_NAME); @@ -1562,15 +1571,15 @@ void Device::RemoveUnsuitableExtensions() { RemoveExtensionFeatureIfUnsuitable(extensions.maintenance6, features.maintenance6, VK_KHR_MAINTENANCE_6_EXTENSION_NAME); - // VK_KHR_maintenance7 (proposed for Vulkan 1.4, no features) + // VK_KHR_maintenance7 extensions.maintenance7 = loaded_extensions.contains(VK_KHR_MAINTENANCE_7_EXTENSION_NAME); RemoveExtensionIfUnsuitable(extensions.maintenance7, VK_KHR_MAINTENANCE_7_EXTENSION_NAME); - // VK_KHR_maintenance8 (proposed for Vulkan 1.4, no features) + // VK_KHR_maintenance8 extensions.maintenance8 = loaded_extensions.contains(VK_KHR_MAINTENANCE_8_EXTENSION_NAME); RemoveExtensionIfUnsuitable(extensions.maintenance8, VK_KHR_MAINTENANCE_8_EXTENSION_NAME); - // VK_KHR_maintenance9 (proposed for Vulkan 1.4, no features) + // VK_KHR_maintenance9 extensions.maintenance9 = loaded_extensions.contains(VK_KHR_MAINTENANCE_9_EXTENSION_NAME); RemoveExtensionIfUnsuitable(extensions.maintenance9, VK_KHR_MAINTENANCE_9_EXTENSION_NAME); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index f18b8278ad..0fdbb7aadc 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -48,6 +48,7 @@ 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, 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) \ @@ -571,6 +572,11 @@ public: return extensions.custom_border_color; } + /// 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_image_robustness. bool IsExtImageRobustnessSupported() const { return extensions.image_robustness; diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 534a11edd4..fdf40fde87 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -165,6 +165,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCmdSetColorWriteMaskEXT); X(vkCmdSetColorBlendEnableEXT); X(vkCmdSetColorBlendEquationEXT); + X(vkCmdSetColorWriteEnableEXT); X(vkCmdResolveImage); X(vkCreateBuffer); X(vkCreateBufferView); diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 04b9420d98..57bb8351fb 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -265,6 +265,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdSetColorWriteMaskEXT vkCmdSetColorWriteMaskEXT{}; PFN_vkCmdSetColorBlendEnableEXT vkCmdSetColorBlendEnableEXT{}; PFN_vkCmdSetColorBlendEquationEXT vkCmdSetColorBlendEquationEXT{}; + PFN_vkCmdSetColorWriteEnableEXT vkCmdSetColorWriteEnableEXT{}; PFN_vkCmdWaitEvents vkCmdWaitEvents{}; PFN_vkCreateBuffer vkCreateBuffer{}; PFN_vkCreateBufferView vkCreateBufferView{}; @@ -1521,6 +1522,10 @@ public: dld->vkCmdSetColorBlendEquationEXT(handle, first, equations.size(), equations.data()); } + void SetColorWriteEnableEXT(u32 first, Span enables) const noexcept { + dld->vkCmdSetColorWriteEnableEXT(handle, first, enables.size(), enables.data()); + } + void SetLineWidth(float line_width) const noexcept { dld->vkCmdSetLineWidth(handle, line_width); }