Просмотр исходного кода

Merge pull request #10744 from Wollnashorn/af-for-all

video_core: Improved anisotropic filtering heuristics
Fernando S 3 лет назад
Родитель
Сommit
27a36cd51b

+ 18 - 5
src/video_core/renderer_opengl/gl_compute_pipeline.cpp

@@ -87,7 +87,8 @@ void ComputePipeline::Configure() {
     texture_cache.SynchronizeComputeDescriptors();
     texture_cache.SynchronizeComputeDescriptors();
 
 
     boost::container::static_vector<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views;
     boost::container::static_vector<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views;
-    std::array<GLuint, MAX_TEXTURES> samplers;
+    boost::container::static_vector<VideoCommon::SamplerId, MAX_TEXTURES> samplers;
+    std::array<GLuint, MAX_TEXTURES> gl_samplers;
     std::array<GLuint, MAX_TEXTURES> textures;
     std::array<GLuint, MAX_TEXTURES> textures;
     std::array<GLuint, MAX_IMAGES> images;
     std::array<GLuint, MAX_IMAGES> images;
     GLsizei sampler_binding{};
     GLsizei sampler_binding{};
@@ -131,7 +132,6 @@ void ComputePipeline::Configure() {
         for (u32 index = 0; index < desc.count; ++index) {
         for (u32 index = 0; index < desc.count; ++index) {
             const auto handle{read_handle(desc, index)};
             const auto handle{read_handle(desc, index)};
             views.push_back({handle.first});
             views.push_back({handle.first});
-            samplers[sampler_binding++] = 0;
         }
         }
     }
     }
     for (const auto& desc : info.image_buffer_descriptors) {
     for (const auto& desc : info.image_buffer_descriptors) {
@@ -142,8 +142,8 @@ void ComputePipeline::Configure() {
             const auto handle{read_handle(desc, index)};
             const auto handle{read_handle(desc, index)};
             views.push_back({handle.first});
             views.push_back({handle.first});
 
 
-            Sampler* const sampler = texture_cache.GetComputeSampler(handle.second);
-            samplers[sampler_binding++] = sampler->Handle();
+            VideoCommon::SamplerId sampler = texture_cache.GetComputeSamplerId(handle.second);
+            samplers.push_back(sampler);
         }
         }
     }
     }
     for (const auto& desc : info.image_descriptors) {
     for (const auto& desc : info.image_descriptors) {
@@ -186,10 +186,17 @@ void ComputePipeline::Configure() {
 
 
     const VideoCommon::ImageViewInOut* views_it{views.data() + num_texture_buffers +
     const VideoCommon::ImageViewInOut* views_it{views.data() + num_texture_buffers +
                                                 num_image_buffers};
                                                 num_image_buffers};
+    const VideoCommon::SamplerId* samplers_it{samplers.data()};
     texture_binding += num_texture_buffers;
     texture_binding += num_texture_buffers;
     image_binding += num_image_buffers;
     image_binding += num_image_buffers;
 
 
     u32 texture_scaling_mask{};
     u32 texture_scaling_mask{};
+
+    for (const auto& desc : info.texture_buffer_descriptors) {
+        for (u32 index = 0; index < desc.count; ++index) {
+            gl_samplers[sampler_binding++] = 0;
+        }
+    }
     for (const auto& desc : info.texture_descriptors) {
     for (const auto& desc : info.texture_descriptors) {
         for (u32 index = 0; index < desc.count; ++index) {
         for (u32 index = 0; index < desc.count; ++index) {
             ImageView& image_view{texture_cache.GetImageView((views_it++)->id)};
             ImageView& image_view{texture_cache.GetImageView((views_it++)->id)};
@@ -198,6 +205,12 @@ void ComputePipeline::Configure() {
                 texture_scaling_mask |= 1u << texture_binding;
                 texture_scaling_mask |= 1u << texture_binding;
             }
             }
             ++texture_binding;
             ++texture_binding;
+
+            const Sampler& sampler{texture_cache.GetSampler(*(samplers_it++))};
+            const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
+                                            !image_view.SupportsAnisotropy()};
+            gl_samplers[sampler_binding++] =
+                use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() : sampler.Handle();
         }
         }
     }
     }
     u32 image_scaling_mask{};
     u32 image_scaling_mask{};
@@ -228,7 +241,7 @@ void ComputePipeline::Configure() {
     if (texture_binding != 0) {
     if (texture_binding != 0) {
         ASSERT(texture_binding == sampler_binding);
         ASSERT(texture_binding == sampler_binding);
         glBindTextures(0, texture_binding, textures.data());
         glBindTextures(0, texture_binding, textures.data());
-        glBindSamplers(0, sampler_binding, samplers.data());
+        glBindSamplers(0, sampler_binding, gl_samplers.data());
     }
     }
     if (image_binding != 0) {
     if (image_binding != 0) {
         glBindImageTextures(0, image_binding, images.data());
         glBindImageTextures(0, image_binding, images.data());

+ 21 - 6
src/video_core/renderer_opengl/gl_graphics_pipeline.cpp

@@ -275,9 +275,9 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
 template <typename Spec>
 template <typename Spec>
 void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
 void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
     std::array<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views;
     std::array<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views;
-    std::array<GLuint, MAX_TEXTURES> samplers;
+    std::array<VideoCommon::SamplerId, MAX_TEXTURES> samplers;
     size_t views_index{};
     size_t views_index{};
-    GLsizei sampler_binding{};
+    size_t samplers_index{};
 
 
     texture_cache.SynchronizeGraphicsDescriptors();
     texture_cache.SynchronizeGraphicsDescriptors();
 
 
@@ -337,7 +337,6 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
                 for (u32 index = 0; index < desc.count; ++index) {
                 for (u32 index = 0; index < desc.count; ++index) {
                     const auto handle{read_handle(desc, index)};
                     const auto handle{read_handle(desc, index)};
                     views[views_index++] = {handle.first};
                     views[views_index++] = {handle.first};
-                    samplers[sampler_binding++] = 0;
                 }
                 }
             }
             }
         }
         }
@@ -351,8 +350,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
                 const auto handle{read_handle(desc, index)};
                 const auto handle{read_handle(desc, index)};
                 views[views_index++] = {handle.first};
                 views[views_index++] = {handle.first};
 
 
-                Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)};
-                samplers[sampler_binding++] = sampler->Handle();
+                VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)};
+                samplers[samplers_index++] = sampler;
             }
             }
         }
         }
         if constexpr (Spec::has_images) {
         if constexpr (Spec::has_images) {
@@ -445,10 +444,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
         program_manager.BindSourcePrograms(source_programs);
         program_manager.BindSourcePrograms(source_programs);
     }
     }
     const VideoCommon::ImageViewInOut* views_it{views.data()};
     const VideoCommon::ImageViewInOut* views_it{views.data()};
+    const VideoCommon::SamplerId* samplers_it{samplers.data()};
     GLsizei texture_binding = 0;
     GLsizei texture_binding = 0;
     GLsizei image_binding = 0;
     GLsizei image_binding = 0;
+    GLsizei sampler_binding{};
     std::array<GLuint, MAX_TEXTURES> textures;
     std::array<GLuint, MAX_TEXTURES> textures;
     std::array<GLuint, MAX_IMAGES> images;
     std::array<GLuint, MAX_IMAGES> images;
+    std::array<GLuint, MAX_TEXTURES> gl_samplers;
     const auto prepare_stage{[&](size_t stage) {
     const auto prepare_stage{[&](size_t stage) {
         buffer_cache.runtime.SetImagePointers(&textures[texture_binding], &images[image_binding]);
         buffer_cache.runtime.SetImagePointers(&textures[texture_binding], &images[image_binding]);
         buffer_cache.BindHostStageBuffers(stage);
         buffer_cache.BindHostStageBuffers(stage);
@@ -465,6 +467,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
         u32 stage_image_binding{};
         u32 stage_image_binding{};
 
 
         const auto& info{stage_infos[stage]};
         const auto& info{stage_infos[stage]};
+        if constexpr (Spec::has_texture_buffers) {
+            for (const auto& desc : info.texture_buffer_descriptors) {
+                for (u32 index = 0; index < desc.count; ++index) {
+                    gl_samplers[sampler_binding++] = 0;
+                }
+            }
+        }
         for (const auto& desc : info.texture_descriptors) {
         for (const auto& desc : info.texture_descriptors) {
             for (u32 index = 0; index < desc.count; ++index) {
             for (u32 index = 0; index < desc.count; ++index) {
                 ImageView& image_view{texture_cache.GetImageView((views_it++)->id)};
                 ImageView& image_view{texture_cache.GetImageView((views_it++)->id)};
@@ -474,6 +483,12 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
                 }
                 }
                 ++texture_binding;
                 ++texture_binding;
                 ++stage_texture_binding;
                 ++stage_texture_binding;
+
+                const Sampler& sampler{texture_cache.GetSampler(*(samplers_it++))};
+                const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
+                                                !image_view.SupportsAnisotropy()};
+                gl_samplers[sampler_binding++] =
+                    use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() : sampler.Handle();
             }
             }
         }
         }
         for (const auto& desc : info.image_descriptors) {
         for (const auto& desc : info.image_descriptors) {
@@ -534,7 +549,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
     if (texture_binding != 0) {
     if (texture_binding != 0) {
         ASSERT(texture_binding == sampler_binding);
         ASSERT(texture_binding == sampler_binding);
         glBindTextures(0, texture_binding, textures.data());
         glBindTextures(0, texture_binding, textures.data());
-        glBindSamplers(0, sampler_binding, samplers.data());
+        glBindSamplers(0, sampler_binding, gl_samplers.data());
     }
     }
     if (image_binding != 0) {
     if (image_binding != 0) {
         glBindImageTextures(0, image_binding, images.data());
         glBindImageTextures(0, image_binding, images.data());

+ 42 - 30
src/video_core/renderer_opengl/gl_texture_cache.cpp

@@ -1268,36 +1268,48 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const TSCEntry& config) {
 
 
     UNIMPLEMENTED_IF(config.cubemap_anisotropy != 1);
     UNIMPLEMENTED_IF(config.cubemap_anisotropy != 1);
 
 
-    sampler.Create();
-    const GLuint handle = sampler.handle;
-    glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(config.wrap_u));
-    glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(config.wrap_v));
-    glSamplerParameteri(handle, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(config.wrap_p));
-    glSamplerParameteri(handle, GL_TEXTURE_COMPARE_MODE, compare_mode);
-    glSamplerParameteri(handle, GL_TEXTURE_COMPARE_FUNC, compare_func);
-    glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, mag);
-    glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, min);
-    glSamplerParameterf(handle, GL_TEXTURE_LOD_BIAS, config.LodBias());
-    glSamplerParameterf(handle, GL_TEXTURE_MIN_LOD, config.MinLod());
-    glSamplerParameterf(handle, GL_TEXTURE_MAX_LOD, config.MaxLod());
-    glSamplerParameterfv(handle, GL_TEXTURE_BORDER_COLOR, config.BorderColor().data());
-
-    if (GLAD_GL_ARB_texture_filter_anisotropic || GLAD_GL_EXT_texture_filter_anisotropic) {
-        const f32 max_anisotropy = std::clamp(config.MaxAnisotropy(), 1.0f, 16.0f);
-        glSamplerParameterf(handle, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropy);
-    } else {
-        LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_anisotropic is required");
-    }
-    if (GLAD_GL_ARB_texture_filter_minmax || GLAD_GL_EXT_texture_filter_minmax) {
-        glSamplerParameteri(handle, GL_TEXTURE_REDUCTION_MODE_ARB, reduction_filter);
-    } else if (reduction_filter != GL_WEIGHTED_AVERAGE_ARB) {
-        LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_minmax is required");
-    }
-    if (GLAD_GL_ARB_seamless_cubemap_per_texture || GLAD_GL_AMD_seamless_cubemap_per_texture) {
-        glSamplerParameteri(handle, GL_TEXTURE_CUBE_MAP_SEAMLESS, seamless);
-    } else if (seamless == GL_FALSE) {
-        // We default to false because it's more common
-        LOG_WARNING(Render_OpenGL, "GL_ARB_seamless_cubemap_per_texture is required");
+    const f32 max_anisotropy = std::clamp(config.MaxAnisotropy(), 1.0f, 16.0f);
+
+    const auto create_sampler = [&](const f32 anisotropy) {
+        OGLSampler new_sampler;
+        new_sampler.Create();
+        const GLuint handle = new_sampler.handle;
+        glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(config.wrap_u));
+        glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(config.wrap_v));
+        glSamplerParameteri(handle, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(config.wrap_p));
+        glSamplerParameteri(handle, GL_TEXTURE_COMPARE_MODE, compare_mode);
+        glSamplerParameteri(handle, GL_TEXTURE_COMPARE_FUNC, compare_func);
+        glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, mag);
+        glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, min);
+        glSamplerParameterf(handle, GL_TEXTURE_LOD_BIAS, config.LodBias());
+        glSamplerParameterf(handle, GL_TEXTURE_MIN_LOD, config.MinLod());
+        glSamplerParameterf(handle, GL_TEXTURE_MAX_LOD, config.MaxLod());
+        glSamplerParameterfv(handle, GL_TEXTURE_BORDER_COLOR, config.BorderColor().data());
+
+        if (GLAD_GL_ARB_texture_filter_anisotropic || GLAD_GL_EXT_texture_filter_anisotropic) {
+            glSamplerParameterf(handle, GL_TEXTURE_MAX_ANISOTROPY, anisotropy);
+        } else {
+            LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_anisotropic is required");
+        }
+        if (GLAD_GL_ARB_texture_filter_minmax || GLAD_GL_EXT_texture_filter_minmax) {
+            glSamplerParameteri(handle, GL_TEXTURE_REDUCTION_MODE_ARB, reduction_filter);
+        } else if (reduction_filter != GL_WEIGHTED_AVERAGE_ARB) {
+            LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_minmax is required");
+        }
+        if (GLAD_GL_ARB_seamless_cubemap_per_texture || GLAD_GL_AMD_seamless_cubemap_per_texture) {
+            glSamplerParameteri(handle, GL_TEXTURE_CUBE_MAP_SEAMLESS, seamless);
+        } else if (seamless == GL_FALSE) {
+            // We default to false because it's more common
+            LOG_WARNING(Render_OpenGL, "GL_ARB_seamless_cubemap_per_texture is required");
+        }
+        return new_sampler;
+    };
+
+    sampler = create_sampler(max_anisotropy);
+
+    const f32 max_anisotropy_default = static_cast<f32>(1U << config.max_anisotropy);
+    if (max_anisotropy > max_anisotropy_default) {
+        sampler_default_anisotropy = create_sampler(max_anisotropy_default);
     }
     }
 }
 }
 
 

+ 10 - 1
src/video_core/renderer_opengl/gl_texture_cache.h

@@ -309,12 +309,21 @@ class Sampler {
 public:
 public:
     explicit Sampler(TextureCacheRuntime&, const Tegra::Texture::TSCEntry&);
     explicit Sampler(TextureCacheRuntime&, const Tegra::Texture::TSCEntry&);
 
 
-    GLuint Handle() const noexcept {
+    [[nodiscard]] GLuint Handle() const noexcept {
         return sampler.handle;
         return sampler.handle;
     }
     }
 
 
+    [[nodiscard]] GLuint HandleWithDefaultAnisotropy() const noexcept {
+        return sampler_default_anisotropy.handle;
+    }
+
+    [[nodiscard]] bool HasAddedAnisotropy() const noexcept {
+        return static_cast<bool>(sampler_default_anisotropy.handle);
+    }
+
 private:
 private:
     OGLSampler sampler;
     OGLSampler sampler;
+    OGLSampler sampler_default_anisotropy;
 };
 };
 
 
 class Framebuffer {
 class Framebuffer {

+ 8 - 3
src/video_core/renderer_vulkan/pipeline_helper.h

@@ -178,7 +178,7 @@ public:
 inline void PushImageDescriptors(TextureCache& texture_cache,
 inline void PushImageDescriptors(TextureCache& texture_cache,
                                  GuestDescriptorQueue& guest_descriptor_queue,
                                  GuestDescriptorQueue& guest_descriptor_queue,
                                  const Shader::Info& info, RescalingPushConstant& rescaling,
                                  const Shader::Info& info, RescalingPushConstant& rescaling,
-                                 const VkSampler*& samplers,
+                                 const VideoCommon::SamplerId*& samplers,
                                  const VideoCommon::ImageViewInOut*& views) {
                                  const VideoCommon::ImageViewInOut*& views) {
     const u32 num_texture_buffers = Shader::NumDescriptors(info.texture_buffer_descriptors);
     const u32 num_texture_buffers = Shader::NumDescriptors(info.texture_buffer_descriptors);
     const u32 num_image_buffers = Shader::NumDescriptors(info.image_buffer_descriptors);
     const u32 num_image_buffers = Shader::NumDescriptors(info.image_buffer_descriptors);
@@ -187,10 +187,15 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
     for (const auto& desc : info.texture_descriptors) {
     for (const auto& desc : info.texture_descriptors) {
         for (u32 index = 0; index < desc.count; ++index) {
         for (u32 index = 0; index < desc.count; ++index) {
             const VideoCommon::ImageViewId image_view_id{(views++)->id};
             const VideoCommon::ImageViewId image_view_id{(views++)->id};
-            const VkSampler sampler{*(samplers++)};
+            const VideoCommon::SamplerId sampler_id{*(samplers++)};
             ImageView& image_view{texture_cache.GetImageView(image_view_id)};
             ImageView& image_view{texture_cache.GetImageView(image_view_id)};
             const VkImageView vk_image_view{image_view.Handle(desc.type)};
             const VkImageView vk_image_view{image_view.Handle(desc.type)};
-            guest_descriptor_queue.AddSampledImage(vk_image_view, sampler);
+            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()};
+            guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler);
             rescaling.PushTexture(texture_cache.IsRescaling(image_view));
             rescaling.PushTexture(texture_cache.IsRescaling(image_view));
         }
         }
     }
     }

+ 4 - 4
src/video_core/renderer_vulkan/vk_compute_pipeline.cpp

@@ -115,7 +115,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
 
 
     static constexpr size_t max_elements = 64;
     static constexpr size_t max_elements = 64;
     boost::container::static_vector<VideoCommon::ImageViewInOut, max_elements> views;
     boost::container::static_vector<VideoCommon::ImageViewInOut, max_elements> views;
-    boost::container::static_vector<VkSampler, max_elements> samplers;
+    boost::container::static_vector<VideoCommon::SamplerId, max_elements> samplers;
 
 
     const auto& qmd{kepler_compute.launch_description};
     const auto& qmd{kepler_compute.launch_description};
     const auto& cbufs{qmd.const_buffer_config};
     const auto& cbufs{qmd.const_buffer_config};
@@ -160,8 +160,8 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
             const auto handle{read_handle(desc, index)};
             const auto handle{read_handle(desc, index)};
             views.push_back({handle.first});
             views.push_back({handle.first});
 
 
-            Sampler* const sampler = texture_cache.GetComputeSampler(handle.second);
-            samplers.push_back(sampler->Handle());
+            VideoCommon::SamplerId sampler = texture_cache.GetComputeSamplerId(handle.second);
+            samplers.push_back(sampler);
         }
         }
     }
     }
     for (const auto& desc : info.image_descriptors) {
     for (const auto& desc : info.image_descriptors) {
@@ -192,7 +192,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
     buffer_cache.BindHostComputeBuffers();
     buffer_cache.BindHostComputeBuffers();
 
 
     RescalingPushConstant rescaling;
     RescalingPushConstant rescaling;
-    const VkSampler* samplers_it{samplers.data()};
+    const VideoCommon::SamplerId* samplers_it{samplers.data()};
     const VideoCommon::ImageViewInOut* views_it{views.data()};
     const VideoCommon::ImageViewInOut* views_it{views.data()};
     PushImageDescriptors(texture_cache, guest_descriptor_queue, info, rescaling, samplers_it,
     PushImageDescriptors(texture_cache, guest_descriptor_queue, info, rescaling, samplers_it,
                          views_it);
                          views_it);

+ 4 - 4
src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp

@@ -298,7 +298,7 @@ void GraphicsPipeline::AddTransition(GraphicsPipeline* transition) {
 template <typename Spec>
 template <typename Spec>
 void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
 void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
     std::array<VideoCommon::ImageViewInOut, MAX_IMAGE_ELEMENTS> views;
     std::array<VideoCommon::ImageViewInOut, MAX_IMAGE_ELEMENTS> views;
-    std::array<VkSampler, MAX_IMAGE_ELEMENTS> samplers;
+    std::array<VideoCommon::SamplerId, MAX_IMAGE_ELEMENTS> samplers;
     size_t sampler_index{};
     size_t sampler_index{};
     size_t view_index{};
     size_t view_index{};
 
 
@@ -367,8 +367,8 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
                 const auto handle{read_handle(desc, index)};
                 const auto handle{read_handle(desc, index)};
                 views[view_index++] = {handle.first};
                 views[view_index++] = {handle.first};
 
 
-                Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)};
-                samplers[sampler_index++] = sampler->Handle();
+                VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)};
+                samplers[sampler_index++] = sampler;
             }
             }
         }
         }
         if constexpr (Spec::has_images) {
         if constexpr (Spec::has_images) {
@@ -453,7 +453,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
 
 
     RescalingPushConstant rescaling;
     RescalingPushConstant rescaling;
     RenderAreaPushConstant render_area;
     RenderAreaPushConstant render_area;
-    const VkSampler* samplers_it{samplers.data()};
+    const VideoCommon::SamplerId* samplers_it{samplers.data()};
     const VideoCommon::ImageViewInOut* views_it{views.data()};
     const VideoCommon::ImageViewInOut* views_it{views.data()};
     const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE {
     const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE {
         buffer_cache.BindHostStageBuffers(stage);
         buffer_cache.BindHostStageBuffers(stage);

+ 30 - 21
src/video_core/renderer_vulkan/vk_texture_cache.cpp

@@ -1802,27 +1802,36 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t
     // Some games have samplers with garbage. Sanitize them here.
     // Some games have samplers with garbage. Sanitize them here.
     const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f);
     const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f);
 
 
-    sampler = 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),
-        .mipLodBias = tsc.LodBias(),
-        .anisotropyEnable = static_cast<VkBool32>(max_anisotropy > 1.0f ? VK_TRUE : VK_FALSE),
-        .maxAnisotropy = max_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 =
-            arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color),
-        .unnormalizedCoordinates = VK_FALSE,
-    });
+    const auto create_sampler = [&](const f32 anisotropy) {
+        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),
+            .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<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 =
+                arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color),
+            .unnormalizedCoordinates = VK_FALSE,
+        });
+    };
+
+    sampler = create_sampler(max_anisotropy);
+
+    const f32 max_anisotropy_default = static_cast<f32>(1U << tsc.max_anisotropy);
+    if (max_anisotropy > max_anisotropy_default) {
+        sampler_default_anisotropy = create_sampler(max_anisotropy_default);
+    }
 }
 }
 
 
 Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM_RT> color_buffers,
 Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM_RT> color_buffers,

+ 9 - 0
src/video_core/renderer_vulkan/vk_texture_cache.h

@@ -279,8 +279,17 @@ public:
         return *sampler;
         return *sampler;
     }
     }
 
 
+    [[nodiscard]] VkSampler HandleWithDefaultAnisotropy() const noexcept {
+        return *sampler_default_anisotropy;
+    }
+
+    [[nodiscard]] bool HasAddedAnisotropy() const noexcept {
+        return static_cast<bool>(sampler_default_anisotropy);
+    }
+
 private:
 private:
     vk::Sampler sampler;
     vk::Sampler sampler;
+    vk::Sampler sampler_default_anisotropy;
 };
 };
 
 
 class Framebuffer {
 class Framebuffer {

+ 52 - 0
src/video_core/texture_cache/image_view_base.cpp

@@ -45,4 +45,56 @@ ImageViewBase::ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_in
 
 
 ImageViewBase::ImageViewBase(const NullImageViewParams&) : image_id{NULL_IMAGE_ID} {}
 ImageViewBase::ImageViewBase(const NullImageViewParams&) : image_id{NULL_IMAGE_ID} {}
 
 
+bool ImageViewBase::SupportsAnisotropy() const noexcept {
+    const bool has_mips = range.extent.levels > 1;
+    const bool is_2d = type == ImageViewType::e2D || type == ImageViewType::e2DArray;
+    if (!has_mips || !is_2d) {
+        return false;
+    }
+
+    switch (format) {
+    case PixelFormat::R8_UNORM:
+    case PixelFormat::R8_SNORM:
+    case PixelFormat::R8_SINT:
+    case PixelFormat::R8_UINT:
+    case PixelFormat::BC4_UNORM:
+    case PixelFormat::BC4_SNORM:
+    case PixelFormat::BC5_UNORM:
+    case PixelFormat::BC5_SNORM:
+    case PixelFormat::R32G32_FLOAT:
+    case PixelFormat::R32G32_SINT:
+    case PixelFormat::R32_FLOAT:
+    case PixelFormat::R16_FLOAT:
+    case PixelFormat::R16_UNORM:
+    case PixelFormat::R16_SNORM:
+    case PixelFormat::R16_UINT:
+    case PixelFormat::R16_SINT:
+    case PixelFormat::R16G16_UNORM:
+    case PixelFormat::R16G16_FLOAT:
+    case PixelFormat::R16G16_UINT:
+    case PixelFormat::R16G16_SINT:
+    case PixelFormat::R16G16_SNORM:
+    case PixelFormat::R8G8_UNORM:
+    case PixelFormat::R8G8_SNORM:
+    case PixelFormat::R8G8_SINT:
+    case PixelFormat::R8G8_UINT:
+    case PixelFormat::R32G32_UINT:
+    case PixelFormat::R32_UINT:
+    case PixelFormat::R32_SINT:
+    case PixelFormat::G4R4_UNORM:
+    // Depth formats
+    case PixelFormat::D32_FLOAT:
+    case PixelFormat::D16_UNORM:
+    // Stencil formats
+    case PixelFormat::S8_UINT:
+    // DepthStencil formats
+    case PixelFormat::D24_UNORM_S8_UINT:
+    case PixelFormat::S8_UINT_D24_UNORM:
+    case PixelFormat::D32_FLOAT_S8_UINT:
+        return false;
+    default:
+        return true;
+    }
+}
+
 } // namespace VideoCommon
 } // namespace VideoCommon

+ 2 - 0
src/video_core/texture_cache/image_view_base.h

@@ -33,6 +33,8 @@ struct ImageViewBase {
         return type == ImageViewType::Buffer;
         return type == ImageViewType::Buffer;
     }
     }
 
 
+    [[nodiscard]] bool SupportsAnisotropy() const noexcept;
+
     ImageId image_id{};
     ImageId image_id{};
     GPUVAddr gpu_addr = 0;
     GPUVAddr gpu_addr = 0;
     PixelFormat format{};
     PixelFormat format{};

+ 25 - 5
src/video_core/texture_cache/texture_cache.h

@@ -222,30 +222,50 @@ void TextureCache<P>::CheckFeedbackLoop(std::span<const ImageViewInOut> views) {
 
 
 template <class P>
 template <class P>
 typename P::Sampler* TextureCache<P>::GetGraphicsSampler(u32 index) {
 typename P::Sampler* TextureCache<P>::GetGraphicsSampler(u32 index) {
+    return &slot_samplers[GetGraphicsSamplerId(index)];
+}
+
+template <class P>
+typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) {
+    return &slot_samplers[GetComputeSamplerId(index)];
+}
+
+template <class P>
+SamplerId TextureCache<P>::GetGraphicsSamplerId(u32 index) {
     if (index > channel_state->graphics_sampler_table.Limit()) {
     if (index > channel_state->graphics_sampler_table.Limit()) {
         LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index);
         LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index);
-        return &slot_samplers[NULL_SAMPLER_ID];
+        return NULL_SAMPLER_ID;
     }
     }
     const auto [descriptor, is_new] = channel_state->graphics_sampler_table.Read(index);
     const auto [descriptor, is_new] = channel_state->graphics_sampler_table.Read(index);
     SamplerId& id = channel_state->graphics_sampler_ids[index];
     SamplerId& id = channel_state->graphics_sampler_ids[index];
     if (is_new) {
     if (is_new) {
         id = FindSampler(descriptor);
         id = FindSampler(descriptor);
     }
     }
-    return &slot_samplers[id];
+    return id;
 }
 }
 
 
 template <class P>
 template <class P>
-typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) {
+SamplerId TextureCache<P>::GetComputeSamplerId(u32 index) {
     if (index > channel_state->compute_sampler_table.Limit()) {
     if (index > channel_state->compute_sampler_table.Limit()) {
         LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index);
         LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index);
-        return &slot_samplers[NULL_SAMPLER_ID];
+        return NULL_SAMPLER_ID;
     }
     }
     const auto [descriptor, is_new] = channel_state->compute_sampler_table.Read(index);
     const auto [descriptor, is_new] = channel_state->compute_sampler_table.Read(index);
     SamplerId& id = channel_state->compute_sampler_ids[index];
     SamplerId& id = channel_state->compute_sampler_ids[index];
     if (is_new) {
     if (is_new) {
         id = FindSampler(descriptor);
         id = FindSampler(descriptor);
     }
     }
-    return &slot_samplers[id];
+    return id;
+}
+
+template <class P>
+const typename P::Sampler& TextureCache<P>::GetSampler(SamplerId id) const noexcept {
+    return slot_samplers[id];
+}
+
+template <class P>
+typename P::Sampler& TextureCache<P>::GetSampler(SamplerId id) noexcept {
+    return slot_samplers[id];
 }
 }
 
 
 template <class P>
 template <class P>

+ 12 - 0
src/video_core/texture_cache/texture_cache_base.h

@@ -159,6 +159,18 @@ public:
     /// Get the sampler from the compute descriptor table in the specified index
     /// Get the sampler from the compute descriptor table in the specified index
     Sampler* GetComputeSampler(u32 index);
     Sampler* GetComputeSampler(u32 index);
 
 
+    /// Get the sampler id from the graphics descriptor table in the specified index
+    SamplerId GetGraphicsSamplerId(u32 index);
+
+    /// Get the sampler id from the compute descriptor table in the specified index
+    SamplerId GetComputeSamplerId(u32 index);
+
+    /// Return a constant reference to the given sampler id
+    [[nodiscard]] const Sampler& GetSampler(SamplerId id) const noexcept;
+
+    /// Return a reference to the given sampler id
+    [[nodiscard]] Sampler& GetSampler(SamplerId id) noexcept;
+
     /// Refresh the state for graphics image view and sampler descriptors
     /// Refresh the state for graphics image view and sampler descriptors
     void SynchronizeGraphicsDescriptors();
     void SynchronizeGraphicsDescriptors();
 
 

+ 6 - 1
src/video_core/textures/texture.cpp

@@ -62,7 +62,12 @@ std::array<float, 4> TSCEntry::BorderColor() const noexcept {
 }
 }
 
 
 float TSCEntry::MaxAnisotropy() const noexcept {
 float TSCEntry::MaxAnisotropy() const noexcept {
-    if (max_anisotropy == 0 && mipmap_filter != TextureMipmapFilter::Linear) {
+    const bool is_suitable_mipmap_filter = mipmap_filter != TextureMipmapFilter::None;
+    const bool has_regular_lods = min_lod_clamp == 0 && max_lod_clamp >= 256;
+    const bool is_bilinear_filter = min_filter == TextureFilter::Linear &&
+                                    reduction_filter == SamplerReduction::WeightedAverage;
+    if (max_anisotropy == 0 && (!is_suitable_mipmap_filter || !has_regular_lods ||
+                                !is_bilinear_filter || depth_compare_enabled)) {
         return 1.0f;
         return 1.0f;
     }
     }
     const auto anisotropic_settings = Settings::values.max_anisotropy.GetValue();
     const auto anisotropic_settings = Settings::values.max_anisotropy.GetValue();