diff --git a/Source/Core/VideoBackends/D3D/D3DState.cpp b/Source/Core/VideoBackends/D3D/D3DState.cpp index 7cb123de15..63d5a61cca 100644 --- a/Source/Core/VideoBackends/D3D/D3DState.cpp +++ b/Source/Core/VideoBackends/D3D/D3DState.cpp @@ -303,43 +303,43 @@ StateCache::~StateCache() = default; ID3D11SamplerState* StateCache::Get(SamplerState state) { std::lock_guard guard(m_lock); - auto it = m_sampler.find(state.hex); + auto it = m_sampler.find(state); if (it != m_sampler.end()) return it->second.Get(); D3D11_SAMPLER_DESC sampdc = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT()); - if (state.mipmap_filter == SamplerState::Filter::Linear) + if (state.tm0.mipmap_filter == FilterMode::Linear) { - if (state.min_filter == SamplerState::Filter::Linear) - sampdc.Filter = (state.mag_filter == SamplerState::Filter::Linear) ? + if (state.tm0.min_filter == FilterMode::Linear) + sampdc.Filter = (state.tm0.mag_filter == FilterMode::Linear) ? D3D11_FILTER_MIN_MAG_MIP_LINEAR : D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; else - sampdc.Filter = (state.mag_filter == SamplerState::Filter::Linear) ? + sampdc.Filter = (state.tm0.mag_filter == FilterMode::Linear) ? D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR : D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR; } else { - if (state.min_filter == SamplerState::Filter::Linear) - sampdc.Filter = (state.mag_filter == SamplerState::Filter::Linear) ? + if (state.tm0.min_filter == FilterMode::Linear) + sampdc.Filter = (state.tm0.mag_filter == FilterMode::Linear) ? D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT : D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; else - sampdc.Filter = (state.mag_filter == SamplerState::Filter::Linear) ? + sampdc.Filter = (state.tm0.mag_filter == FilterMode::Linear) ? D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT : D3D11_FILTER_MIN_MAG_MIP_POINT; } static constexpr std::array address_modes = { {D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_WRAP, D3D11_TEXTURE_ADDRESS_MIRROR}}; - sampdc.AddressU = address_modes[static_cast(state.wrap_u.Value())]; - sampdc.AddressV = address_modes[static_cast(state.wrap_v.Value())]; - sampdc.MaxLOD = state.max_lod / 16.f; - sampdc.MinLOD = state.min_lod / 16.f; - sampdc.MipLODBias = (s32)state.lod_bias / 256.f; + sampdc.AddressU = address_modes[static_cast(state.tm0.wrap_u.Value())]; + sampdc.AddressV = address_modes[static_cast(state.tm0.wrap_v.Value())]; + sampdc.MaxLOD = state.tm1.max_lod / 16.f; + sampdc.MinLOD = state.tm1.min_lod / 16.f; + sampdc.MipLODBias = state.tm0.lod_bias / 256.f; - if (state.anisotropic_filtering) + if (state.tm0.anisotropic_filtering) { sampdc.Filter = D3D11_FILTER_ANISOTROPIC; sampdc.MaxAnisotropy = 1u << g_ActiveConfig.iMaxAnisotropy; @@ -348,7 +348,7 @@ ID3D11SamplerState* StateCache::Get(SamplerState state) ComPtr res; HRESULT hr = D3D::device->CreateSamplerState(&sampdc, res.GetAddressOf()); CHECK(SUCCEEDED(hr), "Creating D3D sampler state failed"); - return m_sampler.emplace(state.hex, std::move(res)).first->second.Get(); + return m_sampler.emplace(state, std::move(res)).first->second.Get(); } ID3D11BlendState* StateCache::Get(BlendingState state) diff --git a/Source/Core/VideoBackends/D3D/D3DState.h b/Source/Core/VideoBackends/D3D/D3DState.h index b76b85fb44..795e7a0432 100644 --- a/Source/Core/VideoBackends/D3D/D3DState.h +++ b/Source/Core/VideoBackends/D3D/D3DState.h @@ -37,7 +37,7 @@ private: std::unordered_map> m_depth; std::unordered_map> m_raster; std::unordered_map> m_blend; - std::unordered_map> m_sampler; + std::unordered_map> m_sampler; std::mutex m_lock; }; diff --git a/Source/Core/VideoBackends/D3D12/DescriptorHeapManager.cpp b/Source/Core/VideoBackends/D3D12/DescriptorHeapManager.cpp index 412e3c9640..537561559c 100644 --- a/Source/Core/VideoBackends/D3D12/DescriptorHeapManager.cpp +++ b/Source/Core/VideoBackends/D3D12/DescriptorHeapManager.cpp @@ -85,32 +85,32 @@ SamplerHeapManager::~SamplerHeapManager() = default; static void GetD3DSamplerDesc(D3D12_SAMPLER_DESC* desc, const SamplerState& state) { - if (state.mipmap_filter == SamplerState::Filter::Linear) + if (state.tm0.mipmap_filter == FilterMode::Linear) { - if (state.min_filter == SamplerState::Filter::Linear) + if (state.tm0.min_filter == FilterMode::Linear) { - desc->Filter = (state.mag_filter == SamplerState::Filter::Linear) ? + desc->Filter = (state.tm0.mag_filter == FilterMode::Linear) ? D3D12_FILTER_MIN_MAG_MIP_LINEAR : D3D12_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; } else { - desc->Filter = (state.mag_filter == SamplerState::Filter::Linear) ? + desc->Filter = (state.tm0.mag_filter == FilterMode::Linear) ? D3D12_FILTER_MIN_POINT_MAG_MIP_LINEAR : D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR; } } else { - if (state.min_filter == SamplerState::Filter::Linear) + if (state.tm0.min_filter == FilterMode::Linear) { - desc->Filter = (state.mag_filter == SamplerState::Filter::Linear) ? + desc->Filter = (state.tm0.mag_filter == FilterMode::Linear) ? D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT : D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT; } else { - desc->Filter = (state.mag_filter == SamplerState::Filter::Linear) ? + desc->Filter = (state.tm0.mag_filter == FilterMode::Linear) ? D3D12_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT : D3D12_FILTER_MIN_MAG_MIP_POINT; } @@ -119,15 +119,15 @@ static void GetD3DSamplerDesc(D3D12_SAMPLER_DESC* desc, const SamplerState& stat static constexpr std::array address_modes = { {D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_TEXTURE_ADDRESS_MODE_WRAP, D3D12_TEXTURE_ADDRESS_MODE_MIRROR}}; - desc->AddressU = address_modes[static_cast(state.wrap_u.Value())]; - desc->AddressV = address_modes[static_cast(state.wrap_v.Value())]; + desc->AddressU = address_modes[static_cast(state.tm0.wrap_u.Value())]; + desc->AddressV = address_modes[static_cast(state.tm0.wrap_v.Value())]; desc->AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - desc->MaxLOD = state.max_lod / 16.f; - desc->MinLOD = state.min_lod / 16.f; - desc->MipLODBias = static_cast(state.lod_bias) / 256.f; + desc->MaxLOD = state.tm1.max_lod / 16.f; + desc->MinLOD = state.tm1.min_lod / 16.f; + desc->MipLODBias = static_cast(state.tm0.lod_bias) / 256.f; desc->ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; - if (state.anisotropic_filtering) + if (state.tm0.anisotropic_filtering) { desc->Filter = D3D12_FILTER_ANISOTROPIC; desc->MaxAnisotropy = 1u << g_ActiveConfig.iMaxAnisotropy; @@ -136,7 +136,7 @@ static void GetD3DSamplerDesc(D3D12_SAMPLER_DESC* desc, const SamplerState& stat bool SamplerHeapManager::Lookup(const SamplerState& ss, D3D12_CPU_DESCRIPTOR_HANDLE* handle) { - const auto it = m_sampler_map.find(ss.hex); + const auto it = m_sampler_map.find(ss); if (it != m_sampler_map.end()) { *handle = it->second; @@ -158,7 +158,7 @@ bool SamplerHeapManager::Lookup(const SamplerState& ss, D3D12_CPU_DESCRIPTOR_HAN m_current_offset * m_descriptor_increment_size}; g_dx_context->GetDevice()->CreateSampler(&desc, new_handle); - m_sampler_map.emplace(ss.hex, new_handle); + m_sampler_map.emplace(ss, new_handle); m_current_offset++; *handle = new_handle; return true; diff --git a/Source/Core/VideoBackends/D3D12/DescriptorHeapManager.h b/Source/Core/VideoBackends/D3D12/DescriptorHeapManager.h index ce4d23b533..7ff42c8e5b 100644 --- a/Source/Core/VideoBackends/D3D12/DescriptorHeapManager.h +++ b/Source/Core/VideoBackends/D3D12/DescriptorHeapManager.h @@ -68,6 +68,6 @@ private: D3D12_CPU_DESCRIPTOR_HANDLE m_heap_base_cpu{}; - std::unordered_map m_sampler_map; + std::unordered_map m_sampler_map; }; } // namespace DX12 diff --git a/Source/Core/VideoBackends/OGL/SamplerCache.cpp b/Source/Core/VideoBackends/OGL/SamplerCache.cpp index 6e594a1e7e..286f654de2 100644 --- a/Source/Core/VideoBackends/OGL/SamplerCache.cpp +++ b/Source/Core/VideoBackends/OGL/SamplerCache.cpp @@ -71,16 +71,16 @@ void SamplerCache::InvalidateBinding(u32 stage) void SamplerCache::SetParameters(GLuint sampler_id, const SamplerState& params) { GLenum min_filter; - GLenum mag_filter = (params.mag_filter == SamplerState::Filter::Point) ? GL_NEAREST : GL_LINEAR; - if (params.mipmap_filter == SamplerState::Filter::Linear) + GLenum mag_filter = (params.tm0.mag_filter == FilterMode::Near) ? GL_NEAREST : GL_LINEAR; + if (params.tm0.mipmap_filter == FilterMode::Linear) { - min_filter = (params.min_filter == SamplerState::Filter::Point) ? GL_NEAREST_MIPMAP_LINEAR : - GL_LINEAR_MIPMAP_LINEAR; + min_filter = (params.tm0.min_filter == FilterMode::Near) ? GL_NEAREST_MIPMAP_LINEAR : + GL_LINEAR_MIPMAP_LINEAR; } else { - min_filter = (params.min_filter == SamplerState::Filter::Point) ? GL_NEAREST_MIPMAP_NEAREST : - GL_LINEAR_MIPMAP_NEAREST; + min_filter = (params.tm0.min_filter == FilterMode::Near) ? GL_NEAREST_MIPMAP_NEAREST : + GL_LINEAR_MIPMAP_NEAREST; } glSamplerParameteri(sampler_id, GL_TEXTURE_MIN_FILTER, min_filter); @@ -90,17 +90,17 @@ void SamplerCache::SetParameters(GLuint sampler_id, const SamplerState& params) {GL_CLAMP_TO_EDGE, GL_REPEAT, GL_MIRRORED_REPEAT}}; glSamplerParameteri(sampler_id, GL_TEXTURE_WRAP_S, - address_modes[static_cast(params.wrap_u.Value())]); + address_modes[static_cast(params.tm0.wrap_u.Value())]); glSamplerParameteri(sampler_id, GL_TEXTURE_WRAP_T, - address_modes[static_cast(params.wrap_v.Value())]); + address_modes[static_cast(params.tm0.wrap_v.Value())]); - glSamplerParameterf(sampler_id, GL_TEXTURE_MIN_LOD, params.min_lod / 16.f); - glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_LOD, params.max_lod / 16.f); + glSamplerParameterf(sampler_id, GL_TEXTURE_MIN_LOD, params.tm1.min_lod / 16.f); + glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_LOD, params.tm1.max_lod / 16.f); if (!static_cast(g_renderer.get())->IsGLES()) - glSamplerParameterf(sampler_id, GL_TEXTURE_LOD_BIAS, params.lod_bias / 256.f); + glSamplerParameterf(sampler_id, GL_TEXTURE_LOD_BIAS, params.tm0.lod_bias / 256.f); - if (params.anisotropic_filtering && g_ogl_config.bSupportsAniso) + if (params.tm0.anisotropic_filtering && g_ogl_config.bSupportsAniso) { glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, static_cast(1 << g_ActiveConfig.iMaxAnisotropy)); diff --git a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp index a1ccdd6f30..fd296457e4 100644 --- a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp @@ -315,28 +315,28 @@ VkSampler ObjectCache::GetSampler(const SamplerState& info) VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT}}; VkSamplerCreateInfo create_info = { - VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, // VkStructureType sType - nullptr, // const void* pNext - 0, // VkSamplerCreateFlags flags - filters[static_cast(info.mag_filter.Value())], // VkFilter magFilter - filters[static_cast(info.min_filter.Value())], // VkFilter minFilter - mipmap_modes[static_cast(info.mipmap_filter.Value())], // VkSamplerMipmapMode mipmapMode - address_modes[static_cast(info.wrap_u.Value())], // VkSamplerAddressMode addressModeU - address_modes[static_cast(info.wrap_v.Value())], // VkSamplerAddressMode addressModeV - VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // VkSamplerAddressMode addressModeW - info.lod_bias / 256.0f, // float mipLodBias + VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, // VkStructureType sType + nullptr, // const void* pNext + 0, // VkSamplerCreateFlags flags + filters[u32(info.tm0.mag_filter.Value())], // VkFilter magFilter + filters[u32(info.tm0.min_filter.Value())], // VkFilter minFilter + mipmap_modes[u32(info.tm0.mipmap_filter.Value())], // VkSamplerMipmapMode mipmapMode + address_modes[u32(info.tm0.wrap_u.Value())], // VkSamplerAddressMode addressModeU + address_modes[u32(info.tm0.wrap_v.Value())], // VkSamplerAddressMode addressModeV + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // VkSamplerAddressMode addressModeW + info.tm0.lod_bias / 256.0f, // float mipLodBias VK_FALSE, // VkBool32 anisotropyEnable 0.0f, // float maxAnisotropy VK_FALSE, // VkBool32 compareEnable VK_COMPARE_OP_ALWAYS, // VkCompareOp compareOp - info.min_lod / 16.0f, // float minLod - info.max_lod / 16.0f, // float maxLod + info.tm1.min_lod / 16.0f, // float minLod + info.tm1.max_lod / 16.0f, // float maxLod VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, // VkBorderColor borderColor VK_FALSE // VkBool32 unnormalizedCoordinates }; // Can we use anisotropic filtering with this sampler? - if (info.anisotropic_filtering && g_vulkan_context->SupportsAnisotropicFiltering()) + if (info.tm0.anisotropic_filtering && g_vulkan_context->SupportsAnisotropicFiltering()) { // Cap anisotropy to device limits. create_info.anisotropyEnable = VK_TRUE; diff --git a/Source/Core/VideoBackends/Vulkan/VKRenderer.cpp b/Source/Core/VideoBackends/Vulkan/VKRenderer.cpp index 06a3f09518..35c092c147 100644 --- a/Source/Core/VideoBackends/Vulkan/VKRenderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKRenderer.cpp @@ -49,7 +49,7 @@ Renderer::Renderer(std::unique_ptr swap_chain, float backbuffer_scale { UpdateActiveConfig(); for (SamplerState& m_sampler_state : m_sampler_states) - m_sampler_state.hex = RenderState::GetPointSamplerState().hex; + m_sampler_state = RenderState::GetPointSamplerState(); } Renderer::~Renderer() = default; @@ -545,7 +545,7 @@ void Renderer::SetTexture(u32 index, const AbstractTexture* texture) void Renderer::SetSamplerState(u32 index, const SamplerState& state) { // Skip lookup if the state hasn't changed. - if (m_sampler_states[index].hex == state.hex) + if (m_sampler_states[index] == state) return; // Look up new state and replace in state tracker. @@ -557,7 +557,7 @@ void Renderer::SetSamplerState(u32 index, const SamplerState& state) } StateTracker::GetInstance()->SetSampler(index, sampler); - m_sampler_states[index].hex = state.hex; + m_sampler_states[index] = state; } void Renderer::SetComputeImageTexture(AbstractTexture* texture, bool read, bool write) @@ -588,7 +588,7 @@ void Renderer::ResetSamplerStates() // Invalidate all sampler states, next draw will re-initialize them. for (u32 i = 0; i < m_sampler_states.size(); i++) { - m_sampler_states[i].hex = RenderState::GetPointSamplerState().hex; + m_sampler_states[i] = RenderState::GetPointSamplerState(); StateTracker::GetInstance()->SetSampler(i, g_object_cache->GetPointSampler()); } diff --git a/Source/Core/VideoCommon/ConstantManager.h b/Source/Core/VideoCommon/ConstantManager.h index 6046450dec..7144342503 100644 --- a/Source/Core/VideoCommon/ConstantManager.h +++ b/Source/Core/VideoCommon/ConstantManager.h @@ -32,7 +32,7 @@ struct PixelShaderConstants float4 zslope; std::array efbscale; // .xy - // Constants from here onwards are only used in ubershaders. + // Constants from here onwards are only used in ubershaders, other than pack2. u32 genmode; // .z u32 alphaTest; // .w u32 fogParam3; // .x @@ -44,7 +44,7 @@ struct PixelShaderConstants u32 dither; // .z (bool) u32 bounding_box; // .w (bool) std::array pack1; // .xy - combiners, .z - tevind, .w - iref - std::array pack2; // .x - tevorder, .y - tevksel + std::array pack2; // .x - tevorder, .y - tevksel, .z/.w - SamplerState tm0/tm1 std::array konst; // .rgba // The following are used in ubershaders when using shader_framebuffer_fetch blending u32 blend_enable; diff --git a/Source/Core/VideoCommon/PixelShaderGen.cpp b/Source/Core/VideoCommon/PixelShaderGen.cpp index 6f11c605fd..ffc9044916 100644 --- a/Source/Core/VideoCommon/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/PixelShaderGen.cpp @@ -414,7 +414,7 @@ void WritePixelShaderCommonHeader(ShaderCode& out, APIType api_type, "\tbool bpmem_dither;\n" "\tbool bpmem_bounding_box;\n" "\tuint4 bpmem_pack1[16];\n" // .xy - combiners, .z - tevind - "\tuint4 bpmem_pack2[8];\n" // .x - tevorder, .y - tevksel + "\tuint4 bpmem_pack2[8];\n" // .x - tevorder, .y - tevksel, .zw - SamplerState tm0/tm1 "\tint4 konstLookup[32];\n" "\tbool blend_enable;\n" "\tuint blend_src_factor;\n" diff --git a/Source/Core/VideoCommon/PixelShaderManager.cpp b/Source/Core/VideoCommon/PixelShaderManager.cpp index 3a232903af..f63722c9c1 100644 --- a/Source/Core/VideoCommon/PixelShaderManager.cpp +++ b/Source/Core/VideoCommon/PixelShaderManager.cpp @@ -282,6 +282,15 @@ void PixelShaderManager::SetTexDims(int texmapid, u32 width, u32 height) constants.texdims[texmapid][1] = height; } +void PixelShaderManager::SetSamplerState(int texmapid, u32 tm0, u32 tm1) +{ + if (constants.pack2[texmapid][2] != tm0 || constants.pack2[texmapid][3] != tm1) + dirty = true; + + constants.pack2[texmapid][2] = tm0; + constants.pack2[texmapid][3] = tm1; +} + void PixelShaderManager::SetZTextureBias() { constants.zbias[1][3] = bpmem.ztex1.bias; diff --git a/Source/Core/VideoCommon/PixelShaderManager.h b/Source/Core/VideoCommon/PixelShaderManager.h index 4d130178ac..614504998e 100644 --- a/Source/Core/VideoCommon/PixelShaderManager.h +++ b/Source/Core/VideoCommon/PixelShaderManager.h @@ -30,6 +30,7 @@ public: static void SetAlphaTestChanged(); static void SetDestAlphaChanged(); static void SetTexDims(int texmapid, u32 width, u32 height); + static void SetSamplerState(int texmapid, u32 tm0, u32 tm1); static void SetZTextureBias(); static void SetViewportChanged(); static void SetEfbScaleChanged(float scalex, float scaley); diff --git a/Source/Core/VideoCommon/RenderState.cpp b/Source/Core/VideoCommon/RenderState.cpp index d84a17257e..15559ae67f 100644 --- a/Source/Core/VideoCommon/RenderState.cpp +++ b/Source/Core/VideoCommon/RenderState.cpp @@ -202,27 +202,42 @@ void BlendingState::ApproximateLogicOpWithBlending() void SamplerState::Generate(const BPMemory& bp, u32 index) { auto tex = bp.tex.GetUnit(index); - const TexMode0& tm0 = tex.texMode0; - const TexMode1& tm1 = tex.texMode1; + const TexMode0& bp_tm0 = tex.texMode0; + const TexMode1& bp_tm1 = tex.texMode1; // GX can configure the mip filter to none. However, D3D and Vulkan can't express this in their // sampler states. Therefore, we set the min/max LOD to zero if this option is used. - min_filter = tm0.min_filter == FilterMode::Linear ? Filter::Linear : Filter::Point; - mipmap_filter = tm0.mipmap_filter == MipMode::Linear ? Filter::Linear : Filter::Point; - mag_filter = tm0.mag_filter == FilterMode::Linear ? Filter::Linear : Filter::Point; + tm0.min_filter = bp_tm0.min_filter; + tm0.mipmap_filter = + bp_tm0.mipmap_filter == MipMode::Linear ? FilterMode::Linear : FilterMode::Near; + tm0.mag_filter = bp_tm0.mag_filter; // If mipmaps are disabled, clamp min/max lod - max_lod = tm0.mipmap_filter != MipMode::None ? tm1.max_lod.Value() : 0; - min_lod = std::min(max_lod.Value(), static_cast(tm1.min_lod)); - lod_bias = tm0.mipmap_filter != MipMode::None ? tm0.lod_bias * (256 / 32) : 0; + if (bp_tm0.mipmap_filter == MipMode::None) + { + tm1.max_lod = 0; + tm1.min_lod = 0; + tm0.lod_bias = 0; + } + else + { + // NOTE: When comparing, max is checked first, then min; if max is less than min, max wins + tm1.max_lod = bp_tm1.max_lod.Value(); + tm1.min_lod = std::min(tm1.max_lod.Value(), bp_tm1.min_lod.Value()); + tm0.lod_bias = bp_tm0.lod_bias * (256 / 32); + } - // Address modes + // Wrap modes // Hardware testing indicates that wrap_mode set to 3 behaves the same as clamp. - static constexpr std::array address_modes = { - {AddressMode::Clamp, AddressMode::Repeat, AddressMode::MirroredRepeat, AddressMode::Clamp}}; - wrap_u = address_modes[u32(tm0.wrap_s.Value())]; - wrap_v = address_modes[u32(tm0.wrap_t.Value())]; - anisotropic_filtering = 0; + auto filter_invalid_wrap = [](WrapMode mode) { + return (mode <= WrapMode::Mirror) ? mode : WrapMode::Clamp; + }; + tm0.wrap_u = filter_invalid_wrap(bp_tm0.wrap_s); + tm0.wrap_v = filter_invalid_wrap(bp_tm0.wrap_t); + + tm0.diag_lod = bp_tm0.diag_lod; + tm0.anisotropic_filtering = false; // TODO: Respect BP anisotropic filtering mode + tm0.lod_clamp = bp_tm0.lod_clamp; // TODO: What does this do? } namespace RenderState @@ -315,37 +330,42 @@ BlendingState GetNoColorWriteBlendState() SamplerState GetInvalidSamplerState() { SamplerState state; - state.hex = UINT64_C(0xFFFFFFFFFFFFFFFF); + state.tm0.hex = 0xFFFFFFFF; + state.tm1.hex = 0xFFFFFFFF; return state; } SamplerState GetPointSamplerState() { SamplerState state = {}; - state.min_filter = SamplerState::Filter::Point; - state.mag_filter = SamplerState::Filter::Point; - state.mipmap_filter = SamplerState::Filter::Point; - state.wrap_u = SamplerState::AddressMode::Clamp; - state.wrap_v = SamplerState::AddressMode::Clamp; - state.min_lod = 0; - state.max_lod = 255; - state.lod_bias = 0; - state.anisotropic_filtering = false; + state.tm0.min_filter = FilterMode::Near; + state.tm0.mag_filter = FilterMode::Near; + state.tm0.mipmap_filter = FilterMode::Near; + state.tm0.wrap_u = WrapMode::Clamp; + state.tm0.wrap_v = WrapMode::Clamp; + state.tm1.min_lod = 0; + state.tm1.max_lod = 255; + state.tm0.lod_bias = 0; + state.tm0.anisotropic_filtering = false; + state.tm0.diag_lod = LODType::Edge; + state.tm0.lod_clamp = false; return state; } SamplerState GetLinearSamplerState() { SamplerState state = {}; - state.min_filter = SamplerState::Filter::Linear; - state.mag_filter = SamplerState::Filter::Linear; - state.mipmap_filter = SamplerState::Filter::Linear; - state.wrap_u = SamplerState::AddressMode::Clamp; - state.wrap_v = SamplerState::AddressMode::Clamp; - state.min_lod = 0; - state.max_lod = 255; - state.lod_bias = 0; - state.anisotropic_filtering = false; + state.tm0.min_filter = FilterMode::Linear; + state.tm0.mag_filter = FilterMode::Linear; + state.tm0.mipmap_filter = FilterMode::Linear; + state.tm0.wrap_u = WrapMode::Clamp; + state.tm0.wrap_v = WrapMode::Clamp; + state.tm1.min_lod = 0; + state.tm1.max_lod = 255; + state.tm0.lod_bias = 0; + state.tm0.anisotropic_filtering = false; + state.tm0.diag_lod = LODType::Edge; + state.tm0.lod_clamp = false; return state; } diff --git a/Source/Core/VideoCommon/RenderState.h b/Source/Core/VideoCommon/RenderState.h index 76738e2744..6fca2eff15 100644 --- a/Source/Core/VideoCommon/RenderState.h +++ b/Source/Core/VideoCommon/RenderState.h @@ -145,30 +145,16 @@ union BlendingState u32 hex; }; -union SamplerState +struct SamplerState { - using StorageType = u64; - - enum class Filter : StorageType - { - Point, - Linear - }; - - enum class AddressMode : StorageType - { - Clamp, - Repeat, - MirroredRepeat - }; - void Generate(const BPMemory& bp, u32 index); SamplerState() = default; SamplerState(const SamplerState&) = default; SamplerState& operator=(const SamplerState& rhs) { - hex = rhs.hex; + tm0.hex = rhs.tm0.hex; + tm1.hex = rhs.tm1.hex; return *this; } SamplerState(SamplerState&&) = default; @@ -179,22 +165,54 @@ union SamplerState return *this; } - bool operator==(const SamplerState& rhs) const { return hex == rhs.hex; } + bool operator==(const SamplerState& rhs) const { return Hex() == rhs.Hex(); } bool operator!=(const SamplerState& rhs) const { return !operator==(rhs); } - bool operator<(const SamplerState& rhs) const { return hex < rhs.hex; } - BitField<0, 1, Filter> min_filter; - BitField<1, 1, Filter> mag_filter; - BitField<2, 1, Filter> mipmap_filter; - BitField<3, 2, AddressMode> wrap_u; - BitField<5, 2, AddressMode> wrap_v; - BitField<7, 16, s64> lod_bias; // multiplied by 256 - BitField<23, 8, u64> min_lod; // multiplied by 16 - BitField<31, 8, u64> max_lod; // multiplied by 16 - BitField<39, 1, u64> anisotropic_filtering; + bool operator<(const SamplerState& rhs) const { return Hex() < rhs.Hex(); } - StorageType hex; + constexpr u64 Hex() const { return tm0.hex | (static_cast(tm1.hex) << 32); } + + // Based on BPMemory TexMode0/TexMode1, but with slightly higher precision and some + // simplifications + union TM0 + { + // BP's mipmap_filter can be None, but that is represented here by setting min_lod and max_lod + // to 0 + BitField<0, 1, FilterMode> min_filter; + BitField<1, 1, FilterMode> mag_filter; + BitField<2, 1, FilterMode> mipmap_filter; + // Guaranteed to be valid values (i.e. not 3) + BitField<3, 2, WrapMode> wrap_u; + BitField<5, 2, WrapMode> wrap_v; + BitField<7, 1, LODType> diag_lod; + BitField<8, 16, s32> lod_bias; // multiplied by 256, higher precision than normal + BitField<24, 1, bool, u32> lod_clamp; // TODO: This isn't currently implemented + BitField<25, 1, bool, u32> anisotropic_filtering; // TODO: This doesn't use the BP one yet + u32 hex; + }; + union TM1 + { + // Min is guaranteed to be less than or equal to max + BitField<0, 8, u32> min_lod; // multiplied by 16 + BitField<8, 8, u32> max_lod; // multiplied by 16 + u32 hex; + }; + + TM0 tm0; + TM1 tm1; }; +namespace std +{ +template <> +struct hash +{ + std::size_t operator()(SamplerState const& state) const noexcept + { + return std::hash{}(state.Hex()); + } +}; +} // namespace std + namespace RenderState { RasterizationState GetInvalidRasterizationState(); diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 0127137c8d..10b088283d 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -988,15 +988,15 @@ static void SetSamplerState(u32 index, float custom_tex_scale, bool custom_tex, // Force texture filtering config option. if (g_ActiveConfig.bForceFiltering) { - state.min_filter = SamplerState::Filter::Linear; - state.mag_filter = SamplerState::Filter::Linear; - state.mipmap_filter = tm0.mipmap_filter != MipMode::None ? SamplerState::Filter::Linear : - SamplerState::Filter::Point; + state.tm0.min_filter = FilterMode::Linear; + state.tm0.mag_filter = FilterMode::Linear; + state.tm0.mipmap_filter = + tm0.mipmap_filter != MipMode::None ? FilterMode::Linear : FilterMode::Near; } // Custom textures may have a greater number of mips if (custom_tex) - state.max_lod = 255; + state.tm1.max_lod = 255; // Anisotropic filtering option. if (g_ActiveConfig.iMaxAnisotropy != 0 && IsAnisostropicEnhancementSafe(tm0)) @@ -1008,15 +1008,15 @@ static void SetSamplerState(u32 index, float custom_tex_scale, bool custom_tex, // Letting the game set other combinations will have varying arbitrary results; // possibly being interpreted as equal to bilinear/trilinear, implicitly // disabling anisotropy, or changing the anisotropic algorithm employed. - state.min_filter = SamplerState::Filter::Linear; - state.mag_filter = SamplerState::Filter::Linear; + state.tm0.min_filter = FilterMode::Linear; + state.tm0.mag_filter = FilterMode::Linear; if (tm0.mipmap_filter != MipMode::None) - state.mipmap_filter = SamplerState::Filter::Linear; - state.anisotropic_filtering = 1; + state.tm0.mipmap_filter = FilterMode::Linear; + state.tm0.anisotropic_filtering = true; } else { - state.anisotropic_filtering = 0; + state.tm0.anisotropic_filtering = false; } if (has_arbitrary_mips && tm0.mipmap_filter != MipMode::None) @@ -1025,14 +1025,15 @@ static void SetSamplerState(u32 index, float custom_tex_scale, bool custom_tex, // that have arbitrary contents, eg. are used for fog effects where the // distance they kick in at is important to preserve at any resolution. // Correct this with the upscaling factor of custom textures. - s64 lod_offset = std::log2(g_renderer->GetEFBScale() / custom_tex_scale) * 256.f; - state.lod_bias = std::clamp(state.lod_bias + lod_offset, -32768, 32767); + s32 lod_offset = std::log2(g_renderer->GetEFBScale() / custom_tex_scale) * 256.f; + state.tm0.lod_bias = std::clamp(state.tm0.lod_bias + lod_offset, -32768, 32767); // Anisotropic also pushes mips farther away so it cannot be used either - state.anisotropic_filtering = 0; + state.tm0.anisotropic_filtering = false; } g_renderer->SetSamplerState(index, state); + PixelShaderManager::SetSamplerState(index, state.tm0.hex, state.tm1.hex); } void TextureCacheBase::BindTextures(BitSet32 used_textures)