video_core: Add per-image anisotropy heuristics (format & mip count)
This commit is contained in:
		| @@ -275,9 +275,9 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c | ||||
| template <typename Spec> | ||||
| void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | ||||
|     std::array<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views; | ||||
|     std::array<GLuint, MAX_TEXTURES> samplers; | ||||
|     std::array<const Sampler*, MAX_TEXTURES> samplers; | ||||
|     size_t views_index{}; | ||||
|     GLsizei sampler_binding{}; | ||||
|     size_t samplers_index{}; | ||||
|  | ||||
|     texture_cache.SynchronizeGraphicsDescriptors(); | ||||
|  | ||||
| @@ -337,7 +337,6 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | ||||
|                 for (u32 index = 0; index < desc.count; ++index) { | ||||
|                     const auto handle{read_handle(desc, index)}; | ||||
|                     views[views_index++] = {handle.first}; | ||||
|                     samplers[sampler_binding++] = 0; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -352,7 +351,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | ||||
|                 views[views_index++] = {handle.first}; | ||||
|  | ||||
|                 Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)}; | ||||
|                 samplers[sampler_binding++] = sampler->Handle(); | ||||
|                 samplers[samplers_index++] = sampler; | ||||
|             } | ||||
|         } | ||||
|         if constexpr (Spec::has_images) { | ||||
| @@ -445,10 +444,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | ||||
|         program_manager.BindSourcePrograms(source_programs); | ||||
|     } | ||||
|     const VideoCommon::ImageViewInOut* views_it{views.data()}; | ||||
|     const Sampler** samplers_it{samplers.data()}; | ||||
|     GLsizei texture_binding = 0; | ||||
|     GLsizei image_binding = 0; | ||||
|     GLsizei sampler_binding{}; | ||||
|     std::array<GLuint, MAX_TEXTURES> textures; | ||||
|     std::array<GLuint, MAX_IMAGES> images; | ||||
|     std::array<GLuint, MAX_TEXTURES> gl_samplers; | ||||
|     const auto prepare_stage{[&](size_t stage) { | ||||
|         buffer_cache.runtime.SetImagePointers(&textures[texture_binding], &images[image_binding]); | ||||
|         buffer_cache.BindHostStageBuffers(stage); | ||||
| @@ -465,6 +467,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | ||||
|         u32 stage_image_binding{}; | ||||
|  | ||||
|         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 (u32 index = 0; index < desc.count; ++index) { | ||||
|                 ImageView& image_view{texture_cache.GetImageView((views_it++)->id)}; | ||||
| @@ -474,6 +483,12 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | ||||
|                 } | ||||
|                 ++texture_binding; | ||||
|                 ++stage_texture_binding; | ||||
|  | ||||
|                 const Sampler& sampler{**(samplers_it++)}; | ||||
|                 const bool use_fallback_sampler{sampler.HasAddedAnisotropy() && | ||||
|                                                 !image_view.SupportsAnisotropy()}; | ||||
|                 gl_samplers[sampler_binding++] = | ||||
|                     use_fallback_sampler ? sampler.HandleWithoutAnisotropy() : sampler.Handle(); | ||||
|             } | ||||
|         } | ||||
|         for (const auto& desc : info.image_descriptors) { | ||||
| @@ -534,7 +549,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | ||||
|     if (texture_binding != 0) { | ||||
|         ASSERT(texture_binding == sampler_binding); | ||||
|         glBindTextures(0, texture_binding, textures.data()); | ||||
|         glBindSamplers(0, sampler_binding, samplers.data()); | ||||
|         glBindSamplers(0, sampler_binding, gl_samplers.data()); | ||||
|     } | ||||
|     if (image_binding != 0) { | ||||
|         glBindImageTextures(0, image_binding, images.data()); | ||||
|   | ||||
| @@ -1268,36 +1268,46 @@ Sampler::Sampler(TextureCacheRuntime& runtime, const TSCEntry& config) { | ||||
|  | ||||
|     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()); | ||||
|     const f32 max_anisotropy = std::clamp(config.MaxAnisotropy(), 1.0f, 16.0f); | ||||
|  | ||||
|     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 auto create_sampler = [&](const f32 max_anisotropy) { | ||||
|         OGLSampler sampler; | ||||
|         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) { | ||||
|             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"); | ||||
|         } | ||||
|         return sampler; | ||||
|     }; | ||||
|  | ||||
|     sampler = create_sampler(max_anisotropy); | ||||
|     if (Settings::values.max_anisotropy.GetValue() > 0 && max_anisotropy > 1.0f) { | ||||
|         sampler_without_anisotropy = create_sampler(1.0f); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -309,12 +309,21 @@ class Sampler { | ||||
| public: | ||||
|     explicit Sampler(TextureCacheRuntime&, const Tegra::Texture::TSCEntry&); | ||||
|  | ||||
|     GLuint Handle() const noexcept { | ||||
|     [[nodiscard]] GLuint Handle() const noexcept { | ||||
|         return sampler.handle; | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] GLuint HandleWithoutAnisotropy() const noexcept { | ||||
|         return sampler_without_anisotropy.handle; | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] bool HasAddedAnisotropy() const noexcept { | ||||
|         return static_cast<bool>(sampler_without_anisotropy.handle); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     OGLSampler sampler; | ||||
|     OGLSampler sampler_without_anisotropy; | ||||
| }; | ||||
|  | ||||
| class Framebuffer { | ||||
|   | ||||
| @@ -178,7 +178,7 @@ public: | ||||
| inline void PushImageDescriptors(TextureCache& texture_cache, | ||||
|                                  GuestDescriptorQueue& guest_descriptor_queue, | ||||
|                                  const Shader::Info& info, RescalingPushConstant& rescaling, | ||||
|                                  const VkSampler*& samplers, | ||||
|                                  const Sampler**& samplers, | ||||
|                                  const VideoCommon::ImageViewInOut*& views) { | ||||
|     const u32 num_texture_buffers = Shader::NumDescriptors(info.texture_buffer_descriptors); | ||||
|     const u32 num_image_buffers = Shader::NumDescriptors(info.image_buffer_descriptors); | ||||
| @@ -187,10 +187,14 @@ inline void PushImageDescriptors(TextureCache& texture_cache, | ||||
|     for (const auto& desc : info.texture_descriptors) { | ||||
|         for (u32 index = 0; index < desc.count; ++index) { | ||||
|             const VideoCommon::ImageViewId image_view_id{(views++)->id}; | ||||
|             const VkSampler sampler{*(samplers++)}; | ||||
|             ImageView& image_view{texture_cache.GetImageView(image_view_id)}; | ||||
|             const VkImageView vk_image_view{image_view.Handle(desc.type)}; | ||||
|             guest_descriptor_queue.AddSampledImage(vk_image_view, sampler); | ||||
|             const Sampler& sampler{**(samplers++)}; | ||||
|             const bool use_fallback_sampler{sampler.HasAddedAnisotropy() && | ||||
|                                             !image_view.SupportsAnisotropy()}; | ||||
|             const VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithoutAnisotropy() | ||||
|                                                             : sampler.Handle()}; | ||||
|             guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler); | ||||
|             rescaling.PushTexture(texture_cache.IsRescaling(image_view)); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -115,7 +115,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute, | ||||
|  | ||||
|     static constexpr size_t max_elements = 64; | ||||
|     boost::container::static_vector<VideoCommon::ImageViewInOut, max_elements> views; | ||||
|     boost::container::static_vector<VkSampler, max_elements> samplers; | ||||
|     boost::container::static_vector<const Sampler*, max_elements> samplers; | ||||
|  | ||||
|     const auto& qmd{kepler_compute.launch_description}; | ||||
|     const auto& cbufs{qmd.const_buffer_config}; | ||||
| @@ -161,7 +161,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute, | ||||
|             views.push_back({handle.first}); | ||||
|  | ||||
|             Sampler* const sampler = texture_cache.GetComputeSampler(handle.second); | ||||
|             samplers.push_back(sampler->Handle()); | ||||
|             samplers.push_back(sampler); | ||||
|         } | ||||
|     } | ||||
|     for (const auto& desc : info.image_descriptors) { | ||||
| @@ -192,7 +192,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute, | ||||
|     buffer_cache.BindHostComputeBuffers(); | ||||
|  | ||||
|     RescalingPushConstant rescaling; | ||||
|     const VkSampler* samplers_it{samplers.data()}; | ||||
|     const Sampler** samplers_it{samplers.data()}; | ||||
|     const VideoCommon::ImageViewInOut* views_it{views.data()}; | ||||
|     PushImageDescriptors(texture_cache, guest_descriptor_queue, info, rescaling, samplers_it, | ||||
|                          views_it); | ||||
|   | ||||
| @@ -298,7 +298,7 @@ void GraphicsPipeline::AddTransition(GraphicsPipeline* transition) { | ||||
| template <typename Spec> | ||||
| void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | ||||
|     std::array<VideoCommon::ImageViewInOut, MAX_IMAGE_ELEMENTS> views; | ||||
|     std::array<VkSampler, MAX_IMAGE_ELEMENTS> samplers; | ||||
|     std::array<const Sampler*, MAX_IMAGE_ELEMENTS> samplers; | ||||
|     size_t sampler_index{}; | ||||
|     size_t view_index{}; | ||||
|  | ||||
| @@ -368,7 +368,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | ||||
|                 views[view_index++] = {handle.first}; | ||||
|  | ||||
|                 Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)}; | ||||
|                 samplers[sampler_index++] = sampler->Handle(); | ||||
|                 samplers[sampler_index++] = sampler; | ||||
|             } | ||||
|         } | ||||
|         if constexpr (Spec::has_images) { | ||||
| @@ -453,7 +453,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) { | ||||
|  | ||||
|     RescalingPushConstant rescaling; | ||||
|     RenderAreaPushConstant render_area; | ||||
|     const VkSampler* samplers_it{samplers.data()}; | ||||
|     const Sampler** samplers_it{samplers.data()}; | ||||
|     const VideoCommon::ImageViewInOut* views_it{views.data()}; | ||||
|     const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE { | ||||
|         buffer_cache.BindHostStageBuffers(stage); | ||||
|   | ||||
| @@ -1802,27 +1802,34 @@ 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); | ||||
|  | ||||
|     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 max_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>(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, | ||||
|         }); | ||||
|     }; | ||||
|  | ||||
|     sampler = create_sampler(max_anisotropy); | ||||
|     if (Settings::values.max_anisotropy.GetValue() > 0 && max_anisotropy > 1.0f) { | ||||
|         sampler_without_anisotropy = create_sampler(1.0f); | ||||
|     } | ||||
| } | ||||
|  | ||||
| Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM_RT> color_buffers, | ||||
|   | ||||
| @@ -279,8 +279,17 @@ public: | ||||
|         return *sampler; | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] VkSampler HandleWithoutAnisotropy() const noexcept { | ||||
|         return *sampler_without_anisotropy; | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] bool HasAddedAnisotropy() const noexcept { | ||||
|         return static_cast<bool>(sampler_without_anisotropy); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     vk::Sampler sampler; | ||||
|     vk::Sampler sampler_without_anisotropy; | ||||
| }; | ||||
|  | ||||
| class Framebuffer { | ||||
|   | ||||
| @@ -45,4 +45,43 @@ ImageViewBase::ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_in | ||||
|  | ||||
| ImageViewBase::ImageViewBase(const NullImageViewParams&) : image_id{NULL_IMAGE_ID} {} | ||||
|  | ||||
| bool ImageViewBase::SupportsAnisotropy() const noexcept { | ||||
|     using namespace VideoCommon; | ||||
|     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: | ||||
|         return false; | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
|     return range.extent.levels > 1; | ||||
| } | ||||
|  | ||||
| } // namespace VideoCommon | ||||
|   | ||||
| @@ -33,6 +33,8 @@ struct ImageViewBase { | ||||
|         return type == ImageViewType::Buffer; | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] bool SupportsAnisotropy() const noexcept; | ||||
|  | ||||
|     ImageId image_id{}; | ||||
|     GPUVAddr gpu_addr = 0; | ||||
|     PixelFormat format{}; | ||||
|   | ||||
| @@ -62,12 +62,14 @@ std::array<float, 4> TSCEntry::BorderColor() const noexcept { | ||||
| } | ||||
|  | ||||
| float TSCEntry::MaxAnisotropy() const noexcept { | ||||
|     const bool is_unsupported_mipmap_filter = Settings::values.use_aggressive_anisotropic_filtering | ||||
|                                                   ? mipmap_filter == TextureMipmapFilter::None | ||||
|                                                   : mipmap_filter != TextureMipmapFilter::Linear; | ||||
|     const bool is_suitable_mipmap_filter = Settings::values.use_aggressive_anisotropic_filtering | ||||
|                                                ? mipmap_filter != TextureMipmapFilter::None | ||||
|                                                : mipmap_filter == TextureMipmapFilter::Linear; | ||||
|     const bool has_regular_lods = min_lod_clamp == 0 && max_lod_clamp >= 256; | ||||
|     if (max_anisotropy == 0 && | ||||
|         (depth_compare_enabled.Value() || !has_regular_lods || is_unsupported_mipmap_filter)) { | ||||
|     const bool is_bilinear_filter = min_filter == TextureFilter::Linear && | ||||
|                                     reduction_filter == SamplerReduction::WeightedAverage; | ||||
|     if (max_anisotropy == 0 && (depth_compare_enabled.Value() || !has_regular_lods || | ||||
|                                 !is_bilinear_filter || !is_suitable_mipmap_filter)) { | ||||
|         return 1.0f; | ||||
|     } | ||||
|     const auto anisotropic_settings = Settings::values.max_anisotropy.GetValue(); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user