Merge pull request #10744 from Wollnashorn/af-for-all
video_core: Improved anisotropic filtering heuristics
This commit is contained in:
commit
27a36cd51b
|
@ -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);
|
VideoCommon::SamplerId sampler = texture_cache.GetComputeSamplerId(handle.second);
|
||||||
samplers[sampler_binding++] = sampler->Handle();
|
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());
|
||||||
|
|
|
@ -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)};
|
VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)};
|
||||||
samplers[sampler_binding++] = sampler->Handle();
|
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());
|
||||||
|
|
|
@ -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 f32 max_anisotropy = std::clamp(config.MaxAnisotropy(), 1.0f, 16.0f);
|
||||||
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 auto create_sampler = [&](const f32 anisotropy) {
|
||||||
const f32 max_anisotropy = std::clamp(config.MaxAnisotropy(), 1.0f, 16.0f);
|
OGLSampler new_sampler;
|
||||||
glSamplerParameterf(handle, GL_TEXTURE_MAX_ANISOTROPY, max_anisotropy);
|
new_sampler.Create();
|
||||||
} else {
|
const GLuint handle = new_sampler.handle;
|
||||||
LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_anisotropic is required");
|
glSamplerParameteri(handle, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(config.wrap_u));
|
||||||
}
|
glSamplerParameteri(handle, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(config.wrap_v));
|
||||||
if (GLAD_GL_ARB_texture_filter_minmax || GLAD_GL_EXT_texture_filter_minmax) {
|
glSamplerParameteri(handle, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(config.wrap_p));
|
||||||
glSamplerParameteri(handle, GL_TEXTURE_REDUCTION_MODE_ARB, reduction_filter);
|
glSamplerParameteri(handle, GL_TEXTURE_COMPARE_MODE, compare_mode);
|
||||||
} else if (reduction_filter != GL_WEIGHTED_AVERAGE_ARB) {
|
glSamplerParameteri(handle, GL_TEXTURE_COMPARE_FUNC, compare_func);
|
||||||
LOG_WARNING(Render_OpenGL, "GL_ARB_texture_filter_minmax is required");
|
glSamplerParameteri(handle, GL_TEXTURE_MAG_FILTER, mag);
|
||||||
}
|
glSamplerParameteri(handle, GL_TEXTURE_MIN_FILTER, min);
|
||||||
if (GLAD_GL_ARB_seamless_cubemap_per_texture || GLAD_GL_AMD_seamless_cubemap_per_texture) {
|
glSamplerParameterf(handle, GL_TEXTURE_LOD_BIAS, config.LodBias());
|
||||||
glSamplerParameteri(handle, GL_TEXTURE_CUBE_MAP_SEAMLESS, seamless);
|
glSamplerParameterf(handle, GL_TEXTURE_MIN_LOD, config.MinLod());
|
||||||
} else if (seamless == GL_FALSE) {
|
glSamplerParameterf(handle, GL_TEXTURE_MAX_LOD, config.MaxLod());
|
||||||
// We default to false because it's more common
|
glSamplerParameterfv(handle, GL_TEXTURE_BORDER_COLOR, config.BorderColor().data());
|
||||||
LOG_WARNING(Render_OpenGL, "GL_ARB_seamless_cubemap_per_texture is required");
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
VideoCommon::SamplerId sampler = texture_cache.GetComputeSamplerId(handle.second);
|
||||||
samplers.push_back(sampler->Handle());
|
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);
|
||||||
|
|
|
@ -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)};
|
VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)};
|
||||||
samplers[sampler_index++] = sampler->Handle();
|
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);
|
||||||
|
|
|
@ -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{
|
const auto create_sampler = [&](const f32 anisotropy) {
|
||||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
return device.GetLogical().CreateSampler(VkSamplerCreateInfo{
|
||||||
.pNext = pnext,
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||||
.flags = 0,
|
.pNext = pnext,
|
||||||
.magFilter = MaxwellToVK::Sampler::Filter(tsc.mag_filter),
|
.flags = 0,
|
||||||
.minFilter = MaxwellToVK::Sampler::Filter(tsc.min_filter),
|
.magFilter = MaxwellToVK::Sampler::Filter(tsc.mag_filter),
|
||||||
.mipmapMode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter),
|
.minFilter = MaxwellToVK::Sampler::Filter(tsc.min_filter),
|
||||||
.addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter),
|
.mipmapMode = MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter),
|
||||||
.addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter),
|
.addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter),
|
||||||
.addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter),
|
.addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter),
|
||||||
.mipLodBias = tsc.LodBias(),
|
.addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter),
|
||||||
.anisotropyEnable = static_cast<VkBool32>(max_anisotropy > 1.0f ? VK_TRUE : VK_FALSE),
|
.mipLodBias = tsc.LodBias(),
|
||||||
.maxAnisotropy = max_anisotropy,
|
.anisotropyEnable = static_cast<VkBool32>(anisotropy > 1.0f ? VK_TRUE : VK_FALSE),
|
||||||
.compareEnable = tsc.depth_compare_enabled,
|
.maxAnisotropy = anisotropy,
|
||||||
.compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func),
|
.compareEnable = tsc.depth_compare_enabled,
|
||||||
.minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(),
|
.compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func),
|
||||||
.maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(),
|
.minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(),
|
||||||
.borderColor =
|
.maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(),
|
||||||
arbitrary_borders ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT : ConvertBorderColor(color),
|
.borderColor =
|
||||||
.unnormalizedCoordinates = VK_FALSE,
|
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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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{};
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Reference in New Issue