diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h index 910e07a606..b105fcd1ab 100644 --- a/src/video_core/renderer_vulkan/pipeline_helper.h +++ b/src/video_core/renderer_vulkan/pipeline_helper.h @@ -13,6 +13,7 @@ #include "video_core/renderer_vulkan/vk_texture_cache.h" #include "video_core/renderer_vulkan/vk_update_descriptor.h" #include "video_core/texture_cache/types.h" +#include "video_core/surface.h" #include "video_core/vulkan_common/vulkan_device.h" namespace Vulkan { @@ -193,8 +194,33 @@ inline void PushImageDescriptors(TextureCache& texture_cache, const Sampler& sampler{texture_cache.GetSampler(sampler_id)}; const bool use_fallback_sampler{sampler.HasAddedAnisotropy() && !image_view.SupportsAnisotropy()}; - const VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() - : sampler.Handle()}; + const auto format_type = VideoCore::Surface::GetFormatType(image_view.format); + const bool view_supports_depth_compare = + format_type == VideoCore::Surface::SurfaceType::Depth || + format_type == VideoCore::Surface::SurfaceType::DepthStencil; + const bool force_disable_compare = + sampler.DepthCompareEnabled() && !view_supports_depth_compare; + const bool is_integer_format = VideoCore::Surface::IsPixelFormatInteger(image_view.format); + const bool needs_nearest = is_integer_format && sampler.HasLinearFiltering(); + VkSampler vk_sampler{}; + if (use_fallback_sampler) { + if (needs_nearest) { + vk_sampler = force_disable_compare + ? sampler.HandleNearestWithDefaultAnisotropyNoCompare() + : sampler.HandleNearestWithDefaultAnisotropy(); + } else { + vk_sampler = force_disable_compare + ? sampler.HandleWithDefaultAnisotropyNoCompare() + : sampler.HandleWithDefaultAnisotropy(); + } + } else { + if (needs_nearest) { + vk_sampler = force_disable_compare ? sampler.HandleNearestNoCompare() + : sampler.HandleNearest(); + } else { + vk_sampler = force_disable_compare ? sampler.HandleNoCompare() : sampler.Handle(); + } + } guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler); rescaling.PushTexture(texture_cache.IsRescaling(image_view)); } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 575651905e..ab8d320990 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -2209,6 +2209,7 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t const auto& device = runtime.device; const bool arbitrary_borders = runtime.device.IsExtCustomBorderColorSupported(); const auto color = tsc.BorderColor(); + compare_enabled = tsc.depth_compare_enabled != 0; const VkSamplerCustomBorderColorCreateInfoEXT border_ci{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT, @@ -2234,21 +2235,52 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t // 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) { + const VkFilter orig_mag = MaxwellToVK::Sampler::Filter(tsc.mag_filter); + const VkFilter orig_min = MaxwellToVK::Sampler::Filter(tsc.min_filter); + const VkSamplerMipmapMode orig_mipmap = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter); + + has_linear_filtering = (orig_mag == VK_FILTER_LINEAR) || (orig_min == VK_FILTER_LINEAR) || + (orig_mipmap == VK_SAMPLER_MIPMAP_MODE_LINEAR); + + const auto create_sampler = [&](const f32 anisotropy, VkBool32 compare_enable) { + return device.GetLogical().CreateSampler(VkSamplerCreateInfo{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .pNext = pnext, + .flags = 0, + .magFilter = orig_mag, + .minFilter = orig_min, + .mipmapMode = orig_mipmap, + .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), + .mipLodBias = tsc.LodBias(), + .anisotropyEnable = static_cast(anisotropy > 1.0f ? VK_TRUE : VK_FALSE), + .maxAnisotropy = anisotropy, + .compareEnable = compare_enable, + .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 = + arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color), + .unnormalizedCoordinates = VK_FALSE, + }); + }; + + const auto create_sampler_force_nearest = [&](const f32 anisotropy, VkBool32 compare_enable) { return 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), + .magFilter = VK_FILTER_NEAREST, + .minFilter = VK_FILTER_NEAREST, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, .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), .mipLodBias = tsc.LodBias(), .anisotropyEnable = static_cast(anisotropy > 1.0f ? VK_TRUE : VK_FALSE), .maxAnisotropy = anisotropy, - .compareEnable = tsc.depth_compare_enabled, + .compareEnable = compare_enable, .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(), @@ -2258,11 +2290,35 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t }); }; - sampler = create_sampler(max_anisotropy); + const VkBool32 compare_flag = compare_enabled ? VK_TRUE : VK_FALSE; + sampler = create_sampler(max_anisotropy, compare_flag); + if (compare_enabled) { + sampler_no_compare = create_sampler(max_anisotropy, VK_FALSE); + } const f32 max_anisotropy_default = static_cast(1U << tsc.max_anisotropy); if (max_anisotropy > max_anisotropy_default) { - sampler_default_anisotropy = create_sampler(max_anisotropy_default); + sampler_default_anisotropy = create_sampler(max_anisotropy_default, compare_flag); + if (compare_enabled) { + sampler_default_anisotropy_no_compare = + create_sampler(max_anisotropy_default, VK_FALSE); + } + } + + // If any linear filtering was requested, create forced-nearest variants for integer formats. + if (has_linear_filtering) { + sampler_nearest = create_sampler_force_nearest(max_anisotropy, compare_flag); + if (compare_enabled) { + sampler_nearest_no_compare = create_sampler_force_nearest(max_anisotropy, VK_FALSE); + } + if (max_anisotropy > max_anisotropy_default) { + sampler_nearest_default_anisotropy = + create_sampler_force_nearest(max_anisotropy_default, compare_flag); + if (compare_enabled) { + sampler_nearest_default_anisotropy_no_compare = + create_sampler_force_nearest(max_anisotropy_default, VK_FALSE); + } + } } } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h index cd11cc8fc7..e495c741d3 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.h +++ b/src/video_core/renderer_vulkan/vk_texture_cache.h @@ -299,13 +299,76 @@ public: return *sampler_default_anisotropy; } + [[nodiscard]] VkSampler HandleNoCompare() const noexcept { + if (compare_enabled && sampler_no_compare) { + return *sampler_no_compare; + } + return *sampler; + } + + [[nodiscard]] VkSampler HandleWithDefaultAnisotropyNoCompare() const noexcept { + if (compare_enabled && sampler_default_anisotropy_no_compare) { + return *sampler_default_anisotropy_no_compare; + } + if (sampler_default_anisotropy) { + return *sampler_default_anisotropy; + } + return HandleNoCompare(); + } + + // Forced-nearest variants used when sampling integer formats that cannot be linearly filtered. + [[nodiscard]] VkSampler HandleNearest() const noexcept { + if (sampler_nearest) { + return *sampler_nearest; + } + return Handle(); + } + + [[nodiscard]] VkSampler HandleNearestNoCompare() const noexcept { + if (compare_enabled && sampler_nearest_no_compare) { + return *sampler_nearest_no_compare; + } + return HandleNearest(); + } + + [[nodiscard]] VkSampler HandleNearestWithDefaultAnisotropy() const noexcept { + if (sampler_nearest_default_anisotropy) { + return *sampler_nearest_default_anisotropy; + } + return HandleNearest(); + } + + [[nodiscard]] VkSampler HandleNearestWithDefaultAnisotropyNoCompare() const noexcept { + if (compare_enabled && sampler_nearest_default_anisotropy_no_compare) { + return *sampler_nearest_default_anisotropy_no_compare; + } + return HandleNearestWithDefaultAnisotropy(); + } + [[nodiscard]] bool HasAddedAnisotropy() const noexcept { return static_cast(sampler_default_anisotropy); } + [[nodiscard]] bool DepthCompareEnabled() const noexcept { + return compare_enabled; + } + + // Whether the original sampler requested any linear filtering (min/mag/mipmap) + [[nodiscard]] bool HasLinearFiltering() const noexcept { + return has_linear_filtering; + } + private: vk::Sampler sampler; vk::Sampler sampler_default_anisotropy; + vk::Sampler sampler_no_compare; + vk::Sampler sampler_default_anisotropy_no_compare; + vk::Sampler sampler_nearest; + vk::Sampler sampler_nearest_default_anisotropy; + vk::Sampler sampler_nearest_no_compare; + vk::Sampler sampler_nearest_default_anisotropy_no_compare; + bool compare_enabled{}; + bool has_linear_filtering{}; }; class Framebuffer {