summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Wollnashorn2023-06-15 18:19:32 +0200
committerGravatar Wollnashorn2023-06-15 18:19:32 +0200
commit42c944b250d8d5c8147b24b3a453cba29968d46c (patch)
tree6d156e8247219bf373edd848bd3a89bbd32eb546 /src
parentvideo_core: Apply AF only to samplers with normal LOD range [0, 1+x] (diff)
downloadyuzu-42c944b250d8d5c8147b24b3a453cba29968d46c.tar.gz
yuzu-42c944b250d8d5c8147b24b3a453cba29968d46c.tar.xz
yuzu-42c944b250d8d5c8147b24b3a453cba29968d46c.zip
video_core: Add per-image anisotropy heuristics (format & mip count)
Diffstat (limited to 'src')
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp25
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp70
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h11
-rw-r--r--src/video_core/renderer_vulkan/pipeline_helper.h10
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp49
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h9
-rw-r--r--src/video_core/texture_cache/image_view_base.cpp39
-rw-r--r--src/video_core/texture_cache/image_view_base.h2
-rw-r--r--src/video_core/textures/texture.cpp12
11 files changed, 168 insertions, 71 deletions
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index 89000d6e0..0fa0fc594 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -275,9 +275,9 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
275template <typename Spec> 275template <typename Spec>
276void GraphicsPipeline::ConfigureImpl(bool is_indexed) { 276void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
277 std::array<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views; 277 std::array<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views;
278 std::array<GLuint, MAX_TEXTURES> samplers; 278 std::array<const Sampler*, MAX_TEXTURES> samplers;
279 size_t views_index{}; 279 size_t views_index{};
280 GLsizei sampler_binding{}; 280 size_t samplers_index{};
281 281
282 texture_cache.SynchronizeGraphicsDescriptors(); 282 texture_cache.SynchronizeGraphicsDescriptors();
283 283
@@ -337,7 +337,6 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
337 for (u32 index = 0; index < desc.count; ++index) { 337 for (u32 index = 0; index < desc.count; ++index) {
338 const auto handle{read_handle(desc, index)}; 338 const auto handle{read_handle(desc, index)};
339 views[views_index++] = {handle.first}; 339 views[views_index++] = {handle.first};
340 samplers[sampler_binding++] = 0;
341 } 340 }
342 } 341 }
343 } 342 }
@@ -352,7 +351,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
352 views[views_index++] = {handle.first}; 351 views[views_index++] = {handle.first};
353 352
354 Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)}; 353 Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)};
355 samplers[sampler_binding++] = sampler->Handle(); 354 samplers[samplers_index++] = sampler;
356 } 355 }
357 } 356 }
358 if constexpr (Spec::has_images) { 357 if constexpr (Spec::has_images) {
@@ -445,10 +444,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
445 program_manager.BindSourcePrograms(source_programs); 444 program_manager.BindSourcePrograms(source_programs);
446 } 445 }
447 const VideoCommon::ImageViewInOut* views_it{views.data()}; 446 const VideoCommon::ImageViewInOut* views_it{views.data()};
447 const Sampler** samplers_it{samplers.data()};
448 GLsizei texture_binding = 0; 448 GLsizei texture_binding = 0;
449 GLsizei image_binding = 0; 449 GLsizei image_binding = 0;
450 GLsizei sampler_binding{};
450 std::array<GLuint, MAX_TEXTURES> textures; 451 std::array<GLuint, MAX_TEXTURES> textures;
451 std::array<GLuint, MAX_IMAGES> images; 452 std::array<GLuint, MAX_IMAGES> images;
453 std::array<GLuint, MAX_TEXTURES> gl_samplers;
452 const auto prepare_stage{[&](size_t stage) { 454 const auto prepare_stage{[&](size_t stage) {
453 buffer_cache.runtime.SetImagePointers(&textures[texture_binding], &images[image_binding]); 455 buffer_cache.runtime.SetImagePointers(&textures[texture_binding], &images[image_binding]);
454 buffer_cache.BindHostStageBuffers(stage); 456 buffer_cache.BindHostStageBuffers(stage);
@@ -465,6 +467,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
465 u32 stage_image_binding{}; 467 u32 stage_image_binding{};
466 468
467 const auto& info{stage_infos[stage]}; 469 const auto& info{stage_infos[stage]};
470 if constexpr (Spec::has_texture_buffers) {
471 for (const auto& desc : info.texture_buffer_descriptors) {
472 for (u32 index = 0; index < desc.count; ++index) {
473 gl_samplers[sampler_binding++] = 0;
474 }
475 }
476 }
468 for (const auto& desc : info.texture_descriptors) { 477 for (const auto& desc : info.texture_descriptors) {
469 for (u32 index = 0; index < desc.count; ++index) { 478 for (u32 index = 0; index < desc.count; ++index) {
470 ImageView& image_view{texture_cache.GetImageView((views_it++)->id)}; 479 ImageView& image_view{texture_cache.GetImageView((views_it++)->id)};
@@ -474,6 +483,12 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
474 } 483 }
475 ++texture_binding; 484 ++texture_binding;
476 ++stage_texture_binding; 485 ++stage_texture_binding;
486
487 const Sampler& sampler{**(samplers_it++)};
488 const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
489 !image_view.SupportsAnisotropy()};
490 gl_samplers[sampler_binding++] =
491 use_fallback_sampler ? sampler.HandleWithoutAnisotropy() : sampler.Handle();
477 } 492 }
478 } 493 }
479 for (const auto& desc : info.image_descriptors) { 494 for (const auto& desc : info.image_descriptors) {
@@ -534,7 +549,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
534 if (texture_binding != 0) { 549 if (texture_binding != 0) {
535 ASSERT(texture_binding == sampler_binding); 550 ASSERT(texture_binding == sampler_binding);
536 glBindTextures(0, texture_binding, textures.data()); 551 glBindTextures(0, texture_binding, textures.data());
537 glBindSamplers(0, sampler_binding, samplers.data()); 552 glBindSamplers(0, sampler_binding, gl_samplers.data());
538 } 553 }
539 if (image_binding != 0) { 554 if (image_binding != 0) {
540 glBindImageTextures(0, image_binding, images.data()); 555 glBindImageTextures(0, image_binding, images.data());
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 1c5dbcdd8..ee5a0c723 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -1268,36 +1268,46 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const TSCEntry& config) {
1268 1268
1269 UNIMPLEMENTED_IF(config.cubemap_anisotropy != 1); 1269 UNIMPLEMENTED_IF(config.cubemap_anisotropy != 1);
1270 1270
1271 sampler.Create(); 1271 const f32 max_anisotropy = std::clamp(config.MaxAnisotropy(), 1.0f, 16.0f);
1272 const GLuint handle = sampler.handle; 1272
1273 glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(config.wrap_u)); 1273 const auto create_sampler = [&](const f32 max_anisotropy) {
1274 glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(config.wrap_v)); 1274 OGLSampler sampler;
1275 glSamplerParameteri(handle, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(config.wrap_p)); 1275 sampler.Create();
1276 glSamplerParameteri(handle, GL_TEXTURE_COMPARE_MODE, compare_mode); 1276 const GLuint handle = sampler.handle;
1277 glSamplerParameteri(handle, GL_TEXTURE_COMPARE_FUNC, compare_func); 1277 glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(config.wrap_u));
1278 glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, mag); 1278 glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(config.wrap_v));
1279 glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, min); 1279 glSamplerParameteri(handle, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(config.wrap_p));
1280 glSamplerParameterf(handle, GL_TEXTURE_LOD_BIAS, config.LodBias()); 1280 glSamplerParameteri(handle, GL_TEXTURE_COMPARE_MODE, compare_mode);
1281 glSamplerParameterf(handle, GL_TEXTURE_MIN_LOD, config.MinLod()); 1281 glSamplerParameteri(handle, GL_TEXTURE_COMPARE_FUNC, compare_func);
1282 glSamplerParameterf(handle, GL_TEXTURE_MAX_LOD, config.MaxLod()); 1282 glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, mag);
1283 glSamplerParameterfv(handle, GL_TEXTURE_BORDER_COLOR, config.BorderColor().data()); 1283 glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, min);
1284 1284 glSamplerParameterf(handle, GL_TEXTURE_LOD_BIAS, config.LodBias());
1285 if (GLAD_GL_ARB_texture_filter_anisotropic || GLAD_GL_EXT_texture_filter_anisotropic) { 1285 glSamplerParameterf(handle, GL_TEXTURE_MIN_LOD, config.MinLod());
1286 const f32 max_anisotropy = std::clamp(config.MaxAnisotropy(), 1.0f, 16.0f); 1286 glSamplerParameterf(handle, GL_TEXTURE_MAX_LOD, config.MaxLod());
1287 glSamplerParameterf(handle, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropy); 1287 glSamplerParameterfv(handle, GL_TEXTURE_BORDER_COLOR, config.BorderColor().data());
1288 } else { 1288
1289 LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_anisotropic is required"); 1289 if (GLAD_GL_ARB_texture_filter_anisotropic || GLAD_GL_EXT_texture_filter_anisotropic) {
1290 } 1290 glSamplerParameterf(handle, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropy);
1291 if (GLAD_GL_ARB_texture_filter_minmax || GLAD_GL_EXT_texture_filter_minmax) { 1291 } else {
1292 glSamplerParameteri(handle, GL_TEXTURE_REDUCTION_MODE_ARB, reduction_filter); 1292 LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_anisotropic is required");
1293 } else if (reduction_filter != GL_WEIGHTED_AVERAGE_ARB) { 1293 }
1294 LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_minmax is required"); 1294 if (GLAD_GL_ARB_texture_filter_minmax || GLAD_GL_EXT_texture_filter_minmax) {
1295 } 1295 glSamplerParameteri(handle, GL_TEXTURE_REDUCTION_MODE_ARB, reduction_filter);
1296 if (GLAD_GL_ARB_seamless_cubemap_per_texture || GLAD_GL_AMD_seamless_cubemap_per_texture) { 1296 } else if (reduction_filter != GL_WEIGHTED_AVERAGE_ARB) {
1297 glSamplerParameteri(handle, GL_TEXTURE_CUBE_MAP_SEAMLESS, seamless); 1297 LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_minmax is required");
1298 } else if (seamless == GL_FALSE) { 1298 }
1299 // We default to false because it's more common 1299 if (GLAD_GL_ARB_seamless_cubemap_per_texture || GLAD_GL_AMD_seamless_cubemap_per_texture) {
1300 LOG_WARNING(Render_OpenGL, "GL_ARB_seamless_cubemap_per_texture is required"); 1300 glSamplerParameteri(handle, GL_TEXTURE_CUBE_MAP_SEAMLESS, seamless);
1301 } else if (seamless == GL_FALSE) {
1302 // We default to false because it's more common
1303 LOG_WARNING(Render_OpenGL, "GL_ARB_seamless_cubemap_per_texture is required");
1304 }
1305 return sampler;
1306 };
1307
1308 sampler = create_sampler(max_anisotropy);
1309 if (Settings::values.max_anisotropy.GetValue() > 0 && max_anisotropy > 1.0f) {
1310 sampler_without_anisotropy = create_sampler(1.0f);
1301 } 1311 }
1302} 1312}
1303 1313
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 1148b73d7..82756fca7 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -309,12 +309,21 @@ class Sampler {
309public: 309public:
310 explicit Sampler(TextureCacheRuntime&, const Tegra::Texture::TSCEntry&); 310 explicit Sampler(TextureCacheRuntime&, const Tegra::Texture::TSCEntry&);
311 311
312 GLuint Handle() const noexcept { 312 [[nodiscard]] GLuint Handle() const noexcept {
313 return sampler.handle; 313 return sampler.handle;
314 } 314 }
315 315
316 [[nodiscard]] GLuint HandleWithoutAnisotropy() const noexcept {
317 return sampler_without_anisotropy.handle;
318 }
319
320 [[nodiscard]] bool HasAddedAnisotropy() const noexcept {
321 return static_cast<bool>(sampler_without_anisotropy.handle);
322 }
323
316private: 324private:
317 OGLSampler sampler; 325 OGLSampler sampler;
326 OGLSampler sampler_without_anisotropy;
318}; 327};
319 328
320class Framebuffer { 329class Framebuffer {
diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h
index 983e1c2e1..1632a6f26 100644
--- a/src/video_core/renderer_vulkan/pipeline_helper.h
+++ b/src/video_core/renderer_vulkan/pipeline_helper.h
@@ -178,7 +178,7 @@ public:
178inline void PushImageDescriptors(TextureCache& texture_cache, 178inline void PushImageDescriptors(TextureCache& texture_cache,
179 GuestDescriptorQueue& guest_descriptor_queue, 179 GuestDescriptorQueue& guest_descriptor_queue,
180 const Shader::Info& info, RescalingPushConstant& rescaling, 180 const Shader::Info& info, RescalingPushConstant& rescaling,
181 const VkSampler*& samplers, 181 const Sampler**& samplers,
182 const VideoCommon::ImageViewInOut*& views) { 182 const VideoCommon::ImageViewInOut*& views) {
183 const u32 num_texture_buffers = Shader::NumDescriptors(info.texture_buffer_descriptors); 183 const u32 num_texture_buffers = Shader::NumDescriptors(info.texture_buffer_descriptors);
184 const u32 num_image_buffers = Shader::NumDescriptors(info.image_buffer_descriptors); 184 const u32 num_image_buffers = Shader::NumDescriptors(info.image_buffer_descriptors);
@@ -187,10 +187,14 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
187 for (const auto& desc : info.texture_descriptors) { 187 for (const auto& desc : info.texture_descriptors) {
188 for (u32 index = 0; index < desc.count; ++index) { 188 for (u32 index = 0; index < desc.count; ++index) {
189 const VideoCommon::ImageViewId image_view_id{(views++)->id}; 189 const VideoCommon::ImageViewId image_view_id{(views++)->id};
190 const VkSampler sampler{*(samplers++)};
191 ImageView& image_view{texture_cache.GetImageView(image_view_id)}; 190 ImageView& image_view{texture_cache.GetImageView(image_view_id)};
192 const VkImageView vk_image_view{image_view.Handle(desc.type)}; 191 const VkImageView vk_image_view{image_view.Handle(desc.type)};
193 guest_descriptor_queue.AddSampledImage(vk_image_view, sampler); 192 const Sampler& sampler{**(samplers++)};
193 const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
194 !image_view.SupportsAnisotropy()};
195 const VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithoutAnisotropy()
196 : sampler.Handle()};
197 guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler);
194 rescaling.PushTexture(texture_cache.IsRescaling(image_view)); 198 rescaling.PushTexture(texture_cache.IsRescaling(image_view));
195 } 199 }
196 } 200 }
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 733e70d9d..eb292dc34 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -115,7 +115,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
115 115
116 static constexpr size_t max_elements = 64; 116 static constexpr size_t max_elements = 64;
117 boost::container::static_vector<VideoCommon::ImageViewInOut, max_elements> views; 117 boost::container::static_vector<VideoCommon::ImageViewInOut, max_elements> views;
118 boost::container::static_vector<VkSampler, max_elements> samplers; 118 boost::container::static_vector<const Sampler*, max_elements> samplers;
119 119
120 const auto& qmd{kepler_compute.launch_description}; 120 const auto& qmd{kepler_compute.launch_description};
121 const auto& cbufs{qmd.const_buffer_config}; 121 const auto& cbufs{qmd.const_buffer_config};
@@ -161,7 +161,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
161 views.push_back({handle.first}); 161 views.push_back({handle.first});
162 162
163 Sampler* const sampler = texture_cache.GetComputeSampler(handle.second); 163 Sampler* const sampler = texture_cache.GetComputeSampler(handle.second);
164 samplers.push_back(sampler->Handle()); 164 samplers.push_back(sampler);
165 } 165 }
166 } 166 }
167 for (const auto& desc : info.image_descriptors) { 167 for (const auto& desc : info.image_descriptors) {
@@ -192,7 +192,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
192 buffer_cache.BindHostComputeBuffers(); 192 buffer_cache.BindHostComputeBuffers();
193 193
194 RescalingPushConstant rescaling; 194 RescalingPushConstant rescaling;
195 const VkSampler* samplers_it{samplers.data()}; 195 const Sampler** samplers_it{samplers.data()};
196 const VideoCommon::ImageViewInOut* views_it{views.data()}; 196 const VideoCommon::ImageViewInOut* views_it{views.data()};
197 PushImageDescriptors(texture_cache, guest_descriptor_queue, info, rescaling, samplers_it, 197 PushImageDescriptors(texture_cache, guest_descriptor_queue, info, rescaling, samplers_it,
198 views_it); 198 views_it);
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 506b78f08..e6e004cb6 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -298,7 +298,7 @@ void GraphicsPipeline::AddTransition(GraphicsPipeline* transition) {
298template <typename Spec> 298template <typename Spec>
299void GraphicsPipeline::ConfigureImpl(bool is_indexed) { 299void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
300 std::array<VideoCommon::ImageViewInOut, MAX_IMAGE_ELEMENTS> views; 300 std::array<VideoCommon::ImageViewInOut, MAX_IMAGE_ELEMENTS> views;
301 std::array<VkSampler, MAX_IMAGE_ELEMENTS> samplers; 301 std::array<const Sampler*, MAX_IMAGE_ELEMENTS> samplers;
302 size_t sampler_index{}; 302 size_t sampler_index{};
303 size_t view_index{}; 303 size_t view_index{};
304 304
@@ -368,7 +368,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
368 views[view_index++] = {handle.first}; 368 views[view_index++] = {handle.first};
369 369
370 Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)}; 370 Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)};
371 samplers[sampler_index++] = sampler->Handle(); 371 samplers[sampler_index++] = sampler;
372 } 372 }
373 } 373 }
374 if constexpr (Spec::has_images) { 374 if constexpr (Spec::has_images) {
@@ -453,7 +453,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
453 453
454 RescalingPushConstant rescaling; 454 RescalingPushConstant rescaling;
455 RenderAreaPushConstant render_area; 455 RenderAreaPushConstant render_area;
456 const VkSampler* samplers_it{samplers.data()}; 456 const Sampler** samplers_it{samplers.data()};
457 const VideoCommon::ImageViewInOut* views_it{views.data()}; 457 const VideoCommon::ImageViewInOut* views_it{views.data()};
458 const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE { 458 const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE {
459 buffer_cache.BindHostStageBuffers(stage); 459 buffer_cache.BindHostStageBuffers(stage);
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 8711e2a87..0e8f8a064 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -1802,27 +1802,34 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& t
1802 // Some games have samplers with garbage. Sanitize them here. 1802 // Some games have samplers with garbage. Sanitize them here.
1803 const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f); 1803 const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f);
1804 1804
1805 sampler = device.GetLogical().CreateSampler(VkSamplerCreateInfo{ 1805 const auto create_sampler = [&](const f32 max_anisotropy) {
1806 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, 1806 return device.GetLogical().CreateSampler(VkSamplerCreateInfo{
1807 .pNext = pnext, 1807 .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
1808 .flags = 0, 1808 .pNext = pnext,
1809 .magFilter = MaxwellToVK::Sampler::Filter(tsc.mag_filter), 1809 .flags = 0,
1810 .minFilter = MaxwellToVK::Sampler::Filter(tsc.min_filter), 1810 .magFilter = MaxwellToVK::Sampler::Filter(tsc.mag_filter),
1811 .mipmapMode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter), 1811 .minFilter = MaxwellToVK::Sampler::Filter(tsc.min_filter),
1812 .addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter), 1812 .mipmapMode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter),
1813 .addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter), 1813 .addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter),
1814 .addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter), 1814 .addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter),
1815 .mipLodBias = tsc.LodBias(), 1815 .addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter),
1816 .anisotropyEnable = static_cast<VkBool32>(max_anisotropy > 1.0f ? VK_TRUE : VK_FALSE), 1816 .mipLodBias = tsc.LodBias(),
1817 .maxAnisotropy = max_anisotropy, 1817 .anisotropyEnable = static_cast<VkBool32>(max_anisotropy > 1.0f ? VK_TRUE : VK_FALSE),
1818 .compareEnable = tsc.depth_compare_enabled, 1818 .maxAnisotropy = max_anisotropy,
1819 .compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func), 1819 .compareEnable = tsc.depth_compare_enabled,
1820 .minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(), 1820 .compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func),
1821 .maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(), 1821 .minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(),
1822 .borderColor = 1822 .maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(),
1823 arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color), 1823 .borderColor =
1824 .unnormalizedCoordinates = VK_FALSE, 1824 arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color),
1825 }); 1825 .unnormalizedCoordinates = VK_FALSE,
1826 });
1827 };
1828
1829 sampler = create_sampler(max_anisotropy);
1830 if (Settings::values.max_anisotropy.GetValue() > 0 && max_anisotropy > 1.0f) {
1831 sampler_without_anisotropy = create_sampler(1.0f);
1832 }
1826} 1833}
1827 1834
1828Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM_RT> color_buffers, 1835Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM_RT> 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 0f7a5ffd4..ee0d0480d 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -279,8 +279,17 @@ public:
279 return *sampler; 279 return *sampler;
280 } 280 }
281 281
282 [[nodiscard]] VkSampler HandleWithoutAnisotropy() const noexcept {
283 return *sampler_without_anisotropy;
284 }
285
286 [[nodiscard]] bool HasAddedAnisotropy() const noexcept {
287 return static_cast<bool>(sampler_without_anisotropy);
288 }
289
282private: 290private:
283 vk::Sampler sampler; 291 vk::Sampler sampler;
292 vk::Sampler sampler_without_anisotropy;
284}; 293};
285 294
286class Framebuffer { 295class Framebuffer {
diff --git a/src/video_core/texture_cache/image_view_base.cpp b/src/video_core/texture_cache/image_view_base.cpp
index d134b6738..26fa6d63e 100644
--- a/src/video_core/texture_cache/image_view_base.cpp
+++ b/src/video_core/texture_cache/image_view_base.cpp
@@ -45,4 +45,43 @@ ImageViewBase::ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_in
45 45
46ImageViewBase::ImageViewBase(const NullImageViewParams&) : image_id{NULL_IMAGE_ID} {} 46ImageViewBase::ImageViewBase(const NullImageViewParams&) : image_id{NULL_IMAGE_ID} {}
47 47
48bool ImageViewBase::SupportsAnisotropy() const noexcept {
49 using namespace VideoCommon;
50 switch (format) {
51 case PixelFormat::R8_UNORM:
52 case PixelFormat::R8_SNORM:
53 case PixelFormat::R8_SINT:
54 case PixelFormat::R8_UINT:
55 case PixelFormat::BC4_UNORM:
56 case PixelFormat::BC4_SNORM:
57 case PixelFormat::BC5_UNORM:
58 case PixelFormat::BC5_SNORM:
59 case PixelFormat::R32G32_FLOAT:
60 case PixelFormat::R32G32_SINT:
61 case PixelFormat::R32_FLOAT:
62 case PixelFormat::R16_FLOAT:
63 case PixelFormat::R16_UNORM:
64 case PixelFormat::R16_SNORM:
65 case PixelFormat::R16_UINT:
66 case PixelFormat::R16_SINT:
67 case PixelFormat::R16G16_UNORM:
68 case PixelFormat::R16G16_FLOAT:
69 case PixelFormat::R16G16_UINT:
70 case PixelFormat::R16G16_SINT:
71 case PixelFormat::R16G16_SNORM:
72 case PixelFormat::R8G8_UNORM:
73 case PixelFormat::R8G8_SNORM:
74 case PixelFormat::R8G8_SINT:
75 case PixelFormat::R8G8_UINT:
76 case PixelFormat::R32G32_UINT:
77 case PixelFormat::R32_UINT:
78 case PixelFormat::R32_SINT:
79 case PixelFormat::G4R4_UNORM:
80 return false;
81 default:
82 break;
83 }
84 return range.extent.levels > 1;
85}
86
48} // namespace VideoCommon 87} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/image_view_base.h b/src/video_core/texture_cache/image_view_base.h
index a25ae1d4a..87549ffff 100644
--- a/src/video_core/texture_cache/image_view_base.h
+++ b/src/video_core/texture_cache/image_view_base.h
@@ -33,6 +33,8 @@ struct ImageViewBase {
33 return type == ImageViewType::Buffer; 33 return type == ImageViewType::Buffer;
34 } 34 }
35 35
36 [[nodiscard]] bool SupportsAnisotropy() const noexcept;
37
36 ImageId image_id{}; 38 ImageId image_id{};
37 GPUVAddr gpu_addr = 0; 39 GPUVAddr gpu_addr = 0;
38 PixelFormat format{}; 40 PixelFormat format{};
diff --git a/src/video_core/textures/texture.cpp b/src/video_core/textures/texture.cpp
index 560b3af8a..cb24d0399 100644
--- a/src/video_core/textures/texture.cpp
+++ b/src/video_core/textures/texture.cpp
@@ -62,12 +62,14 @@ std::array<float, 4> TSCEntry::BorderColor() const noexcept {
62} 62}
63 63
64float TSCEntry::MaxAnisotropy() const noexcept { 64float TSCEntry::MaxAnisotropy() const noexcept {
65 const bool is_unsupported_mipmap_filter = Settings::values.use_aggressive_anisotropic_filtering 65 const bool is_suitable_mipmap_filter = Settings::values.use_aggressive_anisotropic_filtering
66 ? mipmap_filter == TextureMipmapFilter::None 66 ? mipmap_filter != TextureMipmapFilter::None
67 : mipmap_filter != TextureMipmapFilter::Linear; 67 : mipmap_filter == TextureMipmapFilter::Linear;
68 const bool has_regular_lods = min_lod_clamp == 0 && max_lod_clamp >= 256; 68 const bool has_regular_lods = min_lod_clamp == 0 && max_lod_clamp >= 256;
69 if (max_anisotropy == 0 && 69 const bool is_bilinear_filter = min_filter == TextureFilter::Linear &&
70 (depth_compare_enabled.Value() || !has_regular_lods || is_unsupported_mipmap_filter)) { 70 reduction_filter == SamplerReduction::WeightedAverage;
71 if (max_anisotropy == 0 && (depth_compare_enabled.Value() || !has_regular_lods ||
72 !is_bilinear_filter || !is_suitable_mipmap_filter)) {
71 return 1.0f; 73 return 1.0f;
72 } 74 }
73 const auto anisotropic_settings = Settings::values.max_anisotropy.GetValue(); 75 const auto anisotropic_settings = Settings::values.max_anisotropy.GetValue();