From 2606fa57090085a0ebe00baca4e4045d7e7646bd Mon Sep 17 00:00:00 2001 From: Triang3l Date: Sun, 26 Jun 2022 18:36:26 +0300 Subject: [PATCH] [GPU] Apply BaseMap MipFilter via samplers as it may be overridden Make it have no effect on the texture resource as a resource may be used with samplers with different overrides. Also make sure magnification vs. minification is not undefined with it on Direct3D 12. --- src/xenia/gpu/d3d12/d3d12_texture_cache.cc | 39 ++++++++++++++++------ src/xenia/gpu/d3d12/d3d12_texture_cache.h | 4 ++- src/xenia/gpu/texture_util.cc | 11 +++--- src/xenia/gpu/texture_util.h | 4 +-- src/xenia/gpu/xenos.h | 4 ++- 5 files changed, 40 insertions(+), 22 deletions(-) diff --git a/src/xenia/gpu/d3d12/d3d12_texture_cache.cc b/src/xenia/gpu/d3d12/d3d12_texture_cache.cc index 68932ef08..1cd7635c9 100644 --- a/src/xenia/gpu/d3d12/d3d12_texture_cache.cc +++ b/src/xenia/gpu/d3d12/d3d12_texture_cache.cc @@ -971,9 +971,9 @@ D3D12TextureCache::SamplerParameters D3D12TextureCache::GetSamplerParameters( parameters.border_color = fetch.border_color; uint32_t mip_min_level; - texture_util::GetSubresourcesFromFetchConstant( - fetch, nullptr, nullptr, nullptr, nullptr, nullptr, &mip_min_level, - nullptr, binding.mip_filter); + texture_util::GetSubresourcesFromFetchConstant(fetch, nullptr, nullptr, + nullptr, nullptr, nullptr, + &mip_min_level, nullptr); parameters.mip_min_level = mip_min_level; xenos::AnisoFilter aniso_filter = @@ -982,6 +982,10 @@ D3D12TextureCache::SamplerParameters D3D12TextureCache::GetSamplerParameters( : binding.aniso_filter; aniso_filter = std::min(aniso_filter, xenos::AnisoFilter::kMax_16_1); parameters.aniso_filter = aniso_filter; + xenos::TextureFilter mip_filter = + binding.mip_filter == xenos::TextureFilter::kUseFetchConst + ? fetch.mip_filter + : binding.mip_filter; if (aniso_filter != xenos::AnisoFilter::kDisabled) { parameters.mag_linear = 1; parameters.min_linear = 1; @@ -997,12 +1001,9 @@ D3D12TextureCache::SamplerParameters D3D12TextureCache::GetSamplerParameters( ? fetch.min_filter : binding.min_filter; parameters.min_linear = min_filter == xenos::TextureFilter::kLinear; - xenos::TextureFilter mip_filter = - binding.mip_filter == xenos::TextureFilter::kUseFetchConst - ? fetch.mip_filter - : binding.mip_filter; parameters.mip_linear = mip_filter == xenos::TextureFilter::kLinear; } + parameters.mip_base_map = mip_filter == xenos::TextureFilter::kBaseMap; return parameters; } @@ -1046,7 +1047,7 @@ void D3D12TextureCache::WriteSampler(SamplerParameters parameters, desc.AddressU = kAddressModeMap[uint32_t(parameters.clamp_x)]; desc.AddressV = kAddressModeMap[uint32_t(parameters.clamp_y)]; desc.AddressW = kAddressModeMap[uint32_t(parameters.clamp_z)]; - // LOD is calculated in shaders. + // LOD biasing is performed in shaders. desc.MipLODBias = 0.0f; desc.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; // TODO(Triang3l): Border colors k_ACBYCR_BLACK and k_ACBCRY_BLACK. @@ -1062,8 +1063,26 @@ void D3D12TextureCache::WriteSampler(SamplerParameters parameters, desc.BorderColor[3] = 0.0f; } desc.MinLOD = float(parameters.mip_min_level); - // Maximum mip level is in the texture resource itself. - desc.MaxLOD = FLT_MAX; + if (parameters.mip_base_map) { + // "It is undefined whether LOD clamping based on MinLOD and MaxLOD Sampler + // states should happen before or after deciding if magnification is + // occuring" - Direct3D 11.3 Functional Specification. + // Using the GL_NEAREST / GL_LINEAR minification filter emulation logic + // described in the Vulkan VkSamplerCreateInfo specification, preserving + // magnification vs. minification - point mip sampling (usable only without + // anisotropic filtering on Direct3D 12) and MaxLOD 0.25. With anisotropic + // filtering, magnification vs. minification doesn't matter as the filter is + // always linear for both on Direct3D 12 - but linear filtering specifically + // is what must not be done for kBaseMap, so setting MaxLOD to MinLOD. + desc.MaxLOD = desc.MinLOD; + if (parameters.aniso_filter == xenos::AnisoFilter::kDisabled) { + assert_false(parameters.mip_linear); + desc.MaxLOD += 0.25f; + } + } else { + // Maximum mip level is in the texture resource itself. + desc.MaxLOD = FLT_MAX; + } ID3D12Device* device = command_processor_.GetD3D12Provider().GetDevice(); device->CreateSampler(&desc, handle); } diff --git a/src/xenia/gpu/d3d12/d3d12_texture_cache.h b/src/xenia/gpu/d3d12/d3d12_texture_cache.h index 21fe237e7..50c51fa14 100644 --- a/src/xenia/gpu/d3d12/d3d12_texture_cache.h +++ b/src/xenia/gpu/d3d12/d3d12_texture_cache.h @@ -58,7 +58,9 @@ class D3D12TextureCache final : public TextureCache { uint32_t mip_linear : 1; // 14 xenos::AnisoFilter aniso_filter : 3; // 17 uint32_t mip_min_level : 4; // 21 - // Maximum mip level is in the texture resource itself. + uint32_t mip_base_map : 1; // 22 + // Maximum mip level is in the texture resource itself, but mip_base_map + // can be used to limit fetching to mip_min_level. }; SamplerParameters() : value(0) { static_assert_size(*this, sizeof(value)); } diff --git a/src/xenia/gpu/texture_util.cc b/src/xenia/gpu/texture_util.cc index ef57fd427..f07a3df50 100644 --- a/src/xenia/gpu/texture_util.cc +++ b/src/xenia/gpu/texture_util.cc @@ -23,8 +23,7 @@ void GetSubresourcesFromFetchConstant( const xenos::xe_gpu_texture_fetch_t& fetch, uint32_t* width_minus_1_out, uint32_t* height_minus_1_out, uint32_t* depth_or_array_size_minus_1_out, uint32_t* base_page_out, uint32_t* mip_page_out, - uint32_t* mip_min_level_out, uint32_t* mip_max_level_out, - xenos::TextureFilter sampler_mip_filter) { + uint32_t* mip_min_level_out, uint32_t* mip_max_level_out) { uint32_t width_minus_1 = 0; uint32_t height_minus_1 = 0; uint32_t depth_or_array_size_minus_1 = 0; @@ -72,16 +71,14 @@ void GetSubresourcesFromFetchConstant( } uint32_t size_mip_max_level = xe::log2_floor(longest_axis_minus_1 + uint32_t(1)); - xenos::TextureFilter mip_filter = - sampler_mip_filter == xenos::TextureFilter::kUseFetchConst - ? fetch.mip_filter - : sampler_mip_filter; uint32_t base_page = fetch.base_address & 0x1FFFF; uint32_t mip_page = fetch.mip_address & 0x1FFFF; uint32_t mip_min_level, mip_max_level; - if (mip_filter == xenos::TextureFilter::kBaseMap || mip_page == 0) { + // Not taking mip_filter == kBaseMap into account for mip_max_level because + // the mip filter may be overridden by shader fetch instructions. + if (mip_page == 0) { mip_min_level = 0; mip_max_level = 0; } else { diff --git a/src/xenia/gpu/texture_util.h b/src/xenia/gpu/texture_util.h index 06e3c5b44..3eab10c31 100644 --- a/src/xenia/gpu/texture_util.h +++ b/src/xenia/gpu/texture_util.h @@ -30,9 +30,7 @@ void GetSubresourcesFromFetchConstant( const xenos::xe_gpu_texture_fetch_t& fetch, uint32_t* width_minus_1_out, uint32_t* height_minus_1_out, uint32_t* depth_or_array_size_minus_1_out, uint32_t* base_page_out, uint32_t* mip_page_out, - uint32_t* mip_min_level_out, uint32_t* mip_max_level_out, - xenos::TextureFilter sampler_mip_filter = - xenos::TextureFilter::kUseFetchConst); + uint32_t* mip_min_level_out, uint32_t* mip_max_level_out); // Gets the number of the mipmap level where the packed mips are stored. inline uint32_t GetPackedMipLevel(uint32_t width, uint32_t height) { diff --git a/src/xenia/gpu/xenos.h b/src/xenia/gpu/xenos.h index 6a6fb55cc..acbff6642 100644 --- a/src/xenia/gpu/xenos.h +++ b/src/xenia/gpu/xenos.h @@ -113,7 +113,9 @@ enum class TextureSign : uint32_t { enum class TextureFilter : uint32_t { kPoint = 0, kLinear = 1, - kBaseMap = 2, // Only applicable for mip-filter - always fetch from level 0. + // Only applicable to the mip filter - like OpenGL minification filters + // GL_NEAREST / GL_LINEAR without MIPMAP_NEAREST / MIPMAP_LINEAR. + kBaseMap = 2, kUseFetchConst = 3, };