From 7dff85124513a9b9241b06430dc20800fcdedf76 Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 1 Jul 2026 05:22:23 +0000 Subject: [PATCH] [vk] implement VK_EXT_border_color_swizzle Signed-off-by: lizzie --- .../renderer_vulkan/vk_texture_cache.cpp | 56 ++++++++++++------- src/video_core/textures/texture.cpp | 17 +++--- .../vulkan_common/vulkan_device.cpp | 8 +-- src/video_core/vulkan_common/vulkan_device.h | 6 ++ 4 files changed, 54 insertions(+), 33 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 567fd560af..5aabf6eb76 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -2314,29 +2314,46 @@ vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_ } Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& tsc) { - const auto& device = runtime.device; - const bool has_custom_border_extension = runtime.device.IsExtCustomBorderColorSupported(); - const bool has_format_undefined = - has_custom_border_extension && runtime.device.IsCustomBorderColorWithoutFormatSupported(); - const bool has_custom_border_colors = - has_format_undefined && runtime.device.IsCustomBorderColorsSupported(); + const bool has_custom_border_ext = runtime.device.IsExtCustomBorderColorSupported(); + const bool has_format_undefined = has_custom_border_ext && runtime.device.IsCustomBorderColorWithoutFormatSupported(); + const bool has_custom_border_colors = has_format_undefined && runtime.device.IsCustomBorderColorsSupported(); + const bool has_border_color_swizzle = has_custom_border_ext && runtime.device.IsExtBorderColorSwizzleSupported(); const auto color = tsc.BorderColor(); + const void* pnext = nullptr; const VkSamplerCustomBorderColorCreateInfoEXT border_ci{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT, - .pNext = nullptr, + .pNext = pnext, .customBorderColor = std::bit_cast(color), .format = VK_FORMAT_UNDEFINED, }; - 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"); + GPU::Logging::GPULogger::GetInstance().LogExtensionUsage("VK_EXT_custom_border_color", "Sampler::Sampler"); + } + } + + const VkSamplerBorderColorComponentMappingCreateInfoEXT border_swizzle_ci{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_BORDER_COLOR_COMPONENT_MAPPING_CREATE_INFO_EXT, + .pNext = pnext, + .components = VkComponentMapping{ + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, + }, + .srgb = tsc.srgb_conversion + }; + if (has_border_color_swizzle) { + pnext = &border_ci; + // Log extension usage for custom border color + if (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, @@ -2347,36 +2364,33 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t } else if (reduction_ci.reductionMode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT) { LOG_WARNING(Render_Vulkan, "VK_EXT_sampler_filter_minmax is required"); } + // Some games have samplers with garbage. Sanitize them here. const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f); - const auto create_sampler = [&](const f32 anisotropy) { - return device.GetLogical().CreateSampler(VkSamplerCreateInfo{ + return runtime.device.GetLogical().CreateSampler(VkSamplerCreateInfo{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .pNext = pnext, .flags = 0, .magFilter = MaxwellToVK::Sampler::Filter(tsc.mag_filter), .minFilter = MaxwellToVK::Sampler::Filter(tsc.min_filter), .mipmapMode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter), - .addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter), - .addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter), - .addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter), + .addressModeU = MaxwellToVK::Sampler::WrapMode(runtime.device, tsc.wrap_u, tsc.mag_filter), + .addressModeV = MaxwellToVK::Sampler::WrapMode(runtime.device, tsc.wrap_v, tsc.mag_filter), + .addressModeW = MaxwellToVK::Sampler::WrapMode(runtime.device, tsc.wrap_p, tsc.mag_filter), .mipLodBias = tsc.LodBias(), - .anisotropyEnable = static_cast(anisotropy > 1.0f ? VK_TRUE : VK_FALSE), + .anisotropyEnable = VkBool32(anisotropy > 1.0f ? VK_TRUE : VK_FALSE), .maxAnisotropy = anisotropy, .compareEnable = tsc.depth_compare_enabled, .compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func), .minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(), .maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(), - .borderColor = has_custom_border_colors ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT - : ConvertBorderColor(color), + .borderColor = has_custom_border_colors ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color), .unnormalizedCoordinates = VK_FALSE, }); }; - sampler = create_sampler(max_anisotropy); - - const f32 max_anisotropy_default = static_cast(1U << tsc.max_anisotropy); + const f32 max_anisotropy_default = f32(1U << tsc.max_anisotropy); if (max_anisotropy > max_anisotropy_default) { sampler_default_anisotropy = create_sampler(max_anisotropy_default); } diff --git a/src/video_core/textures/texture.cpp b/src/video_core/textures/texture.cpp index d32872e35d..325e872030 100644 --- a/src/video_core/textures/texture.cpp +++ b/src/video_core/textures/texture.cpp @@ -55,13 +55,16 @@ namespace { } // Anonymous namespace std::array TSCEntry::BorderColor() const noexcept { - // TODO: Handle SRGB correctly. Using this breaks shadows in some games (Xenoblade). - // if (!srgb_conversion) { - // return border_color; - //} - // return {SRGB_CONVERSION_LUT[srgb_border_color_r], SRGB_CONVERSION_LUT[srgb_border_color_g], - // SRGB_CONVERSION_LUT[srgb_border_color_b], border_color[3]}; - return border_color; + if (!srgb_conversion) + return border_color; + // SRGB will be handled by custom border color swizzle + // if not, we are in big, big trouble and may break shadows on Xenoblade + return { + f32(srgb_border_color_r) / 255.f, + f32(srgb_border_color_g) / 255.f, + f32(srgb_border_color_b) / 255.f, + border_color[3] + }; } float TSCEntry::MaxAnisotropy() const noexcept { diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 7bf73dcead..53d462f421 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1172,12 +1172,10 @@ bool Device::GetSuitability(bool requires_swapchain) { void Device::RemoveUnsuitableExtensions() { // VK_EXT_custom_border_color if (extensions.custom_border_color) { - extensions.custom_border_color = - features.custom_border_color.customBorderColors && - features.custom_border_color.customBorderColorWithoutFormat; + extensions.custom_border_color = features.custom_border_color.customBorderColors + && features.custom_border_color.customBorderColorWithoutFormat; } - RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color, - VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); + RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); // VK_EXT_depth_bias_control extensions.depth_bias_control = diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 110d0c1199..7970f3a692 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -50,6 +50,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, CustomBorderColor, CUSTOM_BORDER_COLOR, custom_border_color) \ + FEATURE(EXT, BorderColorSwizzle, BORDER_COLOR_SWIZZLE, border_color_swizzle) \ FEATURE(EXT, DepthBiasControl, DEPTH_BIAS_CONTROL, depth_bias_control) \ FEATURE(EXT, DepthClipControl, DEPTH_CLIP_CONTROL, depth_clip_control) \ FEATURE(EXT, ExtendedDynamicState, EXTENDED_DYNAMIC_STATE, extended_dynamic_state) \ @@ -582,6 +583,11 @@ FN_MAX_LIMIT_LIST return features.custom_border_color.customBorderColorWithoutFormat; } + /// Returns true if the device supports VK_EXT_border_color_swizzle . + bool IsExtBorderColorSwizzleSupported() const { + return extensions.border_color_swizzle; + } + /// Returns true if the device supports VK_EXT_extended_dynamic_state. bool IsExtExtendedDynamicStateSupported() const { return extensions.extended_dynamic_state;