diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h index 051305ec42..8a4cb6273f 100644 --- a/src/video_core/renderer_vulkan/pipeline_helper.h +++ b/src/video_core/renderer_vulkan/pipeline_helper.h @@ -383,6 +383,12 @@ inline void PushImageDescriptors(TextureCache& texture_cache, !image_view.SupportsDepthComparison()) { vk_sampler = sampler.HandleWithoutDepthComparison(); } + if (sampler.HasMinmaxReduction() && !image_view.SupportsMinmaxFilter()) { + vk_sampler = sampler.HandleWithDefaultReduction(); + } + if (sampler.HasCustomBorderColor() && image_view.RequiresBorderColorFormat()) { + vk_sampler = sampler.HandleWithDefaultBorderColor(); + } guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler); const bool element_rescaled{texture_cache.IsRescaling(image_view)}; is_rescaled |= element_rescaled; diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp index 7b67665829..efb37bc787 100644 --- a/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.cpp @@ -90,16 +90,27 @@ using VideoCore::Surface::SurfaceType; constexpr VkResolveModeFlagBits mode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT; const ResolveAspects aspects = GetResolveAspects(format); + const bool depth_mode_supported = (device.GetDepthResolveModes() & mode) != 0; + const bool stencil_mode_supported = (device.GetStencilResolveModes() & mode) != 0; + ResolveModes modes{ .depth = VK_RESOLVE_MODE_NONE, .stencil = VK_RESOLVE_MODE_NONE, }; - if (aspects.depth && (device.GetDepthResolveModes() & mode) != 0) { + if (aspects.depth && depth_mode_supported) { modes.depth = mode; } - if (aspects.stencil && (device.GetStencilResolveModes() & mode) != 0) { + if (aspects.stencil && stencil_mode_supported) { modes.stencil = mode; } + if (modes.depth == modes.stencil || device.SupportsIndependentResolveNone()) { + return modes; + } + if (modes.depth != VK_RESOLVE_MODE_NONE && stencil_mode_supported) { + modes.stencil = mode; + } else if (modes.stencil != VK_RESOLVE_MODE_NONE && depth_mode_supported) { + modes.depth = mode; + } return modes; } } // Anonymous namespace diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 2f15a90977..7aa9f19935 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -55,6 +55,17 @@ namespace { constexpr bool ENABLE_MSAA_RESOLVE_CONSUME = true; constexpr bool ENABLE_MSAA_COLOR_DISCARD = true; +[[nodiscard]] constexpr bool RequiresBorderColorFormat(VkFormat format) { + switch (format) { + case VK_FORMAT_B4G4R4A4_UNORM_PACK16: + case VK_FORMAT_B5G6R5_UNORM_PACK16: + case VK_FORMAT_B5G5R5A1_UNORM_PACK16: + return true; + default: + return false; + } +} + constexpr VkBorderColor ConvertBorderColor(const std::array& color) { if (color == std::array{0, 0, 0, 0}) { return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; @@ -2385,9 +2396,15 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI supports_depth_comparison = (properties3.optimalTilingFeatures & VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT) != 0; + supports_minmax_filter = (properties3.optimalTilingFeatures & + VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_MINMAX_BIT) != 0; } else { supports_depth_comparison = true; + supports_minmax_filter = + (device->GetPhysical().GetFormatProperties(format_info.format).optimalTilingFeatures & + VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT) != 0; } + requires_border_color_format = RequiresBorderColorFormat(format_info.format); const VkImageUsageFlags requested_view_usage = ImageUsageFlags(format_info, format); const VkImageUsageFlags image_usage = image.UsageFlags(); const VkImageUsageFlags clamped_view_usage = requested_view_usage & image_usage; @@ -2588,11 +2605,7 @@ 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_colors = runtime.device.IsCustomBorderColorUsable(); const auto color = tsc.BorderColor(); const VkSamplerCustomBorderColorCreateInfoEXT border_ci{ @@ -2618,8 +2631,18 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t .pNext = pnext, .reductionMode = MaxwellToVK::SamplerReduction(tsc.reduction_filter), }; + const VkSamplerReductionModeCreateInfoEXT reduction_ci_without_border{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT, + .pNext = nullptr, + .reductionMode = MaxwellToVK::SamplerReduction(tsc.reduction_filter), + }; + const void* pnext_without_border = nullptr; + const bool has_minmax_reduction = + runtime.device.IsExtSamplerFilterMinmaxSupported() && + reduction_ci.reductionMode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT; if (runtime.device.IsExtSamplerFilterMinmaxSupported()) { pnext = &reduction_ci; + pnext_without_border = &reduction_ci_without_border; } else if (reduction_ci.reductionMode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT) { LOG_WARNING(Render_Vulkan, "VK_EXT_sampler_filter_minmax is required"); } @@ -2634,10 +2657,22 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t mipmap_mode == VK_SAMPLER_MIPMAP_MODE_LINEAR}; const auto create_sampler = [&](const f32 anisotropy, bool force_nearest, - bool disable_compare = false) { + bool disable_compare = false, + bool disable_custom_border = false, + bool disable_minmax = false) { + const bool custom_border = has_custom_border_colors && !disable_custom_border; + const bool minmax = has_minmax_reduction && !disable_minmax; + const void* chain = nullptr; + if (custom_border && minmax) { + chain = pnext; + } else if (minmax) { + chain = pnext_without_border; + } else if (custom_border) { + chain = &border_ci; + } return device.GetLogical().CreateSampler(VkSamplerCreateInfo{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .pNext = pnext, + .pNext = chain, .flags = 0, .magFilter = force_nearest ? VK_FILTER_NEAREST : mag_filter, .minFilter = force_nearest ? VK_FILTER_NEAREST : min_filter, @@ -2654,8 +2689,8 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t .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 = custom_border ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT + : ConvertBorderColor(color), .unnormalizedCoordinates = VK_FALSE, }); }; @@ -2672,6 +2707,12 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t if (tsc.depth_compare_enabled) { sampler_noncompare = create_sampler(max_anisotropy, false, true); } + if (has_custom_border_colors) { + sampler_default_border = create_sampler(max_anisotropy, false, false, true, true); + } + if (has_minmax_reduction) { + sampler_default_reduction = create_sampler(max_anisotropy, false, false, false, true); + } } Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span color_buffers, diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index 5dc8f81e4b..bea9964313 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -414,6 +414,14 @@ public: return supports_depth_comparison; } + [[nodiscard]] bool RequiresBorderColorFormat() const noexcept { + return requires_border_color_format; + } + + [[nodiscard]] bool SupportsMinmaxFilter() const noexcept { + return supports_minmax_filter; + } + [[nodiscard]] GPUVAddr GpuAddr() const noexcept { return gpu_addr; } @@ -447,6 +455,8 @@ private: u32 buffer_size = 0; bool supports_depth_comparison = false; + bool requires_border_color_format = false; + bool supports_minmax_filter = false; }; class ImageAlloc : public VideoCommon::ImageAllocBase {}; @@ -483,11 +493,29 @@ public: return static_cast(sampler_noncompare); } + [[nodiscard]] VkSampler HandleWithDefaultBorderColor() const noexcept { + return *sampler_default_border; + } + + [[nodiscard]] bool HasCustomBorderColor() const noexcept { + return static_cast(sampler_default_border); + } + + [[nodiscard]] VkSampler HandleWithDefaultReduction() const noexcept { + return *sampler_default_reduction; + } + + [[nodiscard]] bool HasMinmaxReduction() const noexcept { + return static_cast(sampler_default_reduction); + } + private: vk::Sampler sampler; vk::Sampler sampler_default_anisotropy; vk::Sampler sampler_nearest; vk::Sampler sampler_noncompare; + vk::Sampler sampler_default_border; + vk::Sampler sampler_default_reduction; }; struct TextureCacheParams { diff --git a/src/video_core/textures/texture.cpp b/src/video_core/textures/texture.cpp index 78eb577e10..1d899eb490 100644 --- a/src/video_core/textures/texture.cpp +++ b/src/video_core/textures/texture.cpp @@ -1,10 +1,11 @@ -// 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 // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include "common/cityhash.h" #include "common/settings.h" @@ -17,51 +18,22 @@ namespace Tegra::Texture { namespace { -[[maybe_unused]] constexpr std::array SRGB_CONVERSION_LUT = { - 0.000000f, 0.000000f, 0.000000f, 0.000012f, 0.000021f, 0.000033f, 0.000046f, 0.000062f, - 0.000081f, 0.000102f, 0.000125f, 0.000151f, 0.000181f, 0.000214f, 0.000251f, 0.000293f, - 0.000338f, 0.000388f, 0.000443f, 0.000503f, 0.000568f, 0.000639f, 0.000715f, 0.000798f, - 0.000887f, 0.000983f, 0.001085f, 0.001195f, 0.001312f, 0.001437f, 0.001569f, 0.001710f, - 0.001860f, 0.002019f, 0.002186f, 0.002364f, 0.002551f, 0.002748f, 0.002955f, 0.003174f, - 0.003403f, 0.003643f, 0.003896f, 0.004160f, 0.004436f, 0.004725f, 0.005028f, 0.005343f, - 0.005672f, 0.006015f, 0.006372f, 0.006744f, 0.007130f, 0.007533f, 0.007950f, 0.008384f, - 0.008834f, 0.009301f, 0.009785f, 0.010286f, 0.010805f, 0.011342f, 0.011898f, 0.012472f, - 0.013066f, 0.013680f, 0.014313f, 0.014967f, 0.015641f, 0.016337f, 0.017054f, 0.017793f, - 0.018554f, 0.019337f, 0.020144f, 0.020974f, 0.021828f, 0.022706f, 0.023609f, 0.024536f, - 0.025489f, 0.026468f, 0.027473f, 0.028504f, 0.029563f, 0.030649f, 0.031762f, 0.032904f, - 0.034074f, 0.035274f, 0.036503f, 0.037762f, 0.039050f, 0.040370f, 0.041721f, 0.043103f, - 0.044518f, 0.045964f, 0.047444f, 0.048956f, 0.050503f, 0.052083f, 0.053699f, 0.055349f, - 0.057034f, 0.058755f, 0.060513f, 0.062307f, 0.064139f, 0.066008f, 0.067915f, 0.069861f, - 0.071845f, 0.073869f, 0.075933f, 0.078037f, 0.080182f, 0.082369f, 0.084597f, 0.086867f, - 0.089180f, 0.091535f, 0.093935f, 0.096378f, 0.098866f, 0.101398f, 0.103977f, 0.106601f, - 0.109271f, 0.111988f, 0.114753f, 0.117565f, 0.120426f, 0.123335f, 0.126293f, 0.129301f, - 0.132360f, 0.135469f, 0.138629f, 0.141841f, 0.145105f, 0.148421f, 0.151791f, 0.155214f, - 0.158691f, 0.162224f, 0.165810f, 0.169453f, 0.173152f, 0.176907f, 0.180720f, 0.184589f, - 0.188517f, 0.192504f, 0.196549f, 0.200655f, 0.204820f, 0.209046f, 0.213334f, 0.217682f, - 0.222093f, 0.226567f, 0.231104f, 0.235704f, 0.240369f, 0.245099f, 0.249894f, 0.254754f, - 0.259681f, 0.264674f, 0.269736f, 0.274864f, 0.280062f, 0.285328f, 0.290664f, 0.296070f, - 0.301546f, 0.307094f, 0.312713f, 0.318404f, 0.324168f, 0.330006f, 0.335916f, 0.341902f, - 0.347962f, 0.354097f, 0.360309f, 0.366597f, 0.372961f, 0.379403f, 0.385924f, 0.392524f, - 0.399202f, 0.405960f, 0.412798f, 0.419718f, 0.426719f, 0.433802f, 0.440967f, 0.448216f, - 0.455548f, 0.462965f, 0.470465f, 0.478052f, 0.485725f, 0.493484f, 0.501329f, 0.509263f, - 0.517285f, 0.525396f, 0.533595f, 0.541885f, 0.550265f, 0.558736f, 0.567299f, 0.575954f, - 0.584702f, 0.593542f, 0.602477f, 0.611507f, 0.620632f, 0.629852f, 0.639168f, 0.648581f, - 0.658092f, 0.667700f, 0.677408f, 0.687214f, 0.697120f, 0.707127f, 0.717234f, 0.727443f, - 0.737753f, 0.748167f, 0.758685f, 0.769305f, 0.780031f, 0.790861f, 0.801798f, 0.812839f, - 0.823989f, 0.835246f, 0.846611f, 0.858085f, 0.869668f, 0.881360f, 0.893164f, 0.905078f, - 0.917104f, 0.929242f, 0.941493f, 0.953859f, 0.966338f, 1.000000f, 1.000000f, 1.000000f, -}; +float SrgbToLinear(u32 value) { + const float encoded = static_cast(value) / 255.0f; + if (encoded <= 0.04045f) { + return encoded / 12.92f; + } + return std::pow((encoded + 0.055f) / 1.055f, 2.4f); +} } // 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; + } + return {SrgbToLinear(srgb_border_color_r), SrgbToLinear(srgb_border_color_g), + SrgbToLinear(srgb_border_color_b), 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 ff8f834785..09e7f88f71 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -523,12 +523,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR LOG_WARNING(Render_Vulkan, "Qualcomm drivers require scaled vertex format emulation."); has_broken_descriptor_aliasing = true; LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken descriptor aliasing."); - LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken custom border color."); - RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color, - VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); - LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken border color swizzle."); - RemoveExtensionFeature(extensions.border_color_swizzle, features.border_color_swizzle, - VK_EXT_BORDER_COLOR_SWIZZLE_EXTENSION_NAME); LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken color write enable."); RemoveExtensionFeature(extensions.color_write_enable, features.color_write_enable, VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 97a241a474..1b420696ab 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -645,7 +645,6 @@ FN_MAX_LIMIT_LIST } /// Returns true if depth/stencil operations can be performed efficiently. - /// Either through shader export or hardware blits. bool CanPerformDepthStencilOperations() const { return extensions.shader_stencil_export || is_blit_depth24_stencil8_supported || is_blit_depth32_stencil8_supported; @@ -702,19 +701,12 @@ FN_MAX_LIMIT_LIST return features.transform_feedback.geometryStreams; } - /// Returns true if the device supports VK_EXT_custom_border_color. - bool IsExtCustomBorderColorSupported() const { - return extensions.custom_border_color; - } - - /// Returns true if customBorderColors feature is available. - bool IsCustomBorderColorsSupported() const { - return features.custom_border_color.customBorderColors; - } - - /// Returns true if customBorderColorWithoutFormat feature is available. - bool IsCustomBorderColorWithoutFormatSupported() const { - return features.custom_border_color.customBorderColorWithoutFormat; + /// Returns true if custom border colors stay defined under non-identity view swizzles. + bool IsCustomBorderColorUsable() const { + return extensions.custom_border_color && + features.custom_border_color.customBorderColors && + features.custom_border_color.customBorderColorWithoutFormat && + extensions.border_color_swizzle; } /// Returns true if the device supports VK_EXT_color_write_enable.