From 24ddea04ce3c843dd1de3d34d775e9a49d195225 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 9 Sep 2017 18:30:15 +1000 Subject: [PATCH] VideoBackends: Move SamplerState to common --- Source/Core/VideoBackends/D3D/D3DState.cpp | 101 ++++------- Source/Core/VideoBackends/D3D/D3DState.h | 16 +- Source/Core/VideoBackends/D3D/Render.cpp | 41 +---- Source/Core/VideoBackends/D3D/Render.h | 2 +- .../VideoBackends/OGL/ProgramShaderCache.cpp | 6 +- Source/Core/VideoBackends/OGL/Render.cpp | 12 +- Source/Core/VideoBackends/OGL/Render.h | 2 +- .../Core/VideoBackends/OGL/SamplerCache.cpp | 163 +++++++----------- Source/Core/VideoBackends/OGL/SamplerCache.h | 45 +---- Source/Core/VideoBackends/Vulkan/Constants.h | 16 -- .../Core/VideoBackends/Vulkan/ObjectCache.cpp | 45 +++-- Source/Core/VideoBackends/Vulkan/Renderer.cpp | 59 +------ Source/Core/VideoBackends/Vulkan/Renderer.h | 2 +- .../Core/VideoBackends/Vulkan/ShaderCache.cpp | 35 +--- .../Core/VideoBackends/Vulkan/ShaderCache.h | 4 - Source/Core/VideoCommon/GeometryShaderGen.cpp | 17 +- Source/Core/VideoCommon/GeometryShaderGen.h | 2 +- Source/Core/VideoCommon/RenderBase.h | 2 +- Source/Core/VideoCommon/RenderState.cpp | 82 +++++++++ Source/Core/VideoCommon/RenderState.h | 52 ++++++ Source/Core/VideoCommon/VertexManagerBase.cpp | 49 +++++- 21 files changed, 352 insertions(+), 401 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/D3DState.cpp b/Source/Core/VideoBackends/D3D/D3DState.cpp index 7664eb9938..56bcb58fa1 100644 --- a/Source/Core/VideoBackends/D3D/D3DState.cpp +++ b/Source/Core/VideoBackends/D3D/D3DState.cpp @@ -12,7 +12,7 @@ #include "VideoBackends/D3D/D3DBase.h" #include "VideoBackends/D3D/D3DState.h" -#include "VideoCommon/SamplerCommon.h" +#include "VideoCommon/VideoConfig.h" namespace DX11 { @@ -265,90 +265,55 @@ void StateManager::SetTextureByMask(u32 textureSlotMask, ID3D11ShaderResourceVie ID3D11SamplerState* StateCache::Get(SamplerState state) { - auto it = m_sampler.find(state.packed); - + auto it = m_sampler.find(state.hex); if (it != m_sampler.end()) - { return it->second; - } - - const unsigned int d3dMipFilters[4] = { - TexMode0::TEXF_NONE, TexMode0::TEXF_POINT, TexMode0::TEXF_LINEAR, - TexMode0::TEXF_NONE, // reserved - }; - const D3D11_TEXTURE_ADDRESS_MODE d3dClamps[4] = { - D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_WRAP, D3D11_TEXTURE_ADDRESS_MIRROR, - D3D11_TEXTURE_ADDRESS_WRAP // reserved - }; D3D11_SAMPLER_DESC sampdc = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT()); - - unsigned int mip = d3dMipFilters[state.min_filter & 3]; - - if (state.max_anisotropy > 1 && !SamplerCommon::IsBpTexMode0PointFiltering(state)) + if (state.mipmap_filter == SamplerState::Filter::Linear) { - sampdc.Filter = D3D11_FILTER_ANISOTROPIC; - sampdc.MaxAnisotropy = (u32)state.max_anisotropy; + if (state.min_filter == SamplerState::Filter::Linear) + sampdc.Filter = (state.mag_filter == SamplerState::Filter::Linear) ? + D3D11_FILTER_MIN_MAG_MIP_LINEAR : + D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; + else + sampdc.Filter = (state.mag_filter == SamplerState::Filter::Linear) ? + D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR : + D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR; } - else if (state.min_filter & 4) // linear min filter + else { - if (state.mag_filter) // linear mag filter - { - if (mip == TexMode0::TEXF_NONE) - sampdc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; - else if (mip == TexMode0::TEXF_POINT) - sampdc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; - else if (mip == TexMode0::TEXF_LINEAR) - sampdc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; - } - else // point mag filter - { - if (mip == TexMode0::TEXF_NONE) - sampdc.Filter = D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; - else if (mip == TexMode0::TEXF_POINT) - sampdc.Filter = D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; - else if (mip == TexMode0::TEXF_LINEAR) - sampdc.Filter = D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; - } - } - else // point min filter - { - if (state.mag_filter) // linear mag filter - { - if (mip == TexMode0::TEXF_NONE) - sampdc.Filter = D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT; - else if (mip == TexMode0::TEXF_POINT) - sampdc.Filter = D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT; - else if (mip == TexMode0::TEXF_LINEAR) - sampdc.Filter = D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR; - } - else // point mag filter - { - if (mip == TexMode0::TEXF_NONE) - sampdc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; - else if (mip == TexMode0::TEXF_POINT) - sampdc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; - else if (mip == TexMode0::TEXF_LINEAR) - sampdc.Filter = D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR; - } + if (state.min_filter == SamplerState::Filter::Linear) + sampdc.Filter = (state.mag_filter == SamplerState::Filter::Linear) ? + D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT : + D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; + else + sampdc.Filter = (state.mag_filter == SamplerState::Filter::Linear) ? + D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT : + D3D11_FILTER_MIN_MAG_MIP_POINT; } - sampdc.AddressU = d3dClamps[state.wrap_s]; - sampdc.AddressV = d3dClamps[state.wrap_t]; - - sampdc.MaxLOD = SamplerCommon::AreBpTexMode0MipmapsEnabled(state) ? state.max_lod / 16.f : 0.f; - sampdc.MinLOD = std::min(state.min_lod / 16.f, sampdc.MaxLOD); + 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 / 32.0f; - ID3D11SamplerState* res = nullptr; + if (state.anisotropic_filtering) + { + sampdc.Filter = D3D11_FILTER_ANISOTROPIC; + sampdc.MaxAnisotropy = 1u << g_ActiveConfig.iMaxAnisotropy; + } + ID3D11SamplerState* res = nullptr; HRESULT hr = D3D::device->CreateSamplerState(&sampdc, &res); if (FAILED(hr)) PanicAlert("Fail %s %d\n", __FILE__, __LINE__); D3D::SetDebugObjectName(res, "sampler state used to emulate the GX pipeline"); - m_sampler.emplace(state.packed, res); - + m_sampler.emplace(state.hex, res); return res; } diff --git a/Source/Core/VideoBackends/D3D/D3DState.h b/Source/Core/VideoBackends/D3D/D3DState.h index 1c9e0fa0fb..fc7cda88b3 100644 --- a/Source/Core/VideoBackends/D3D/D3DState.h +++ b/Source/Core/VideoBackends/D3D/D3DState.h @@ -20,20 +20,6 @@ struct ID3D11RasterizerState; namespace DX11 { -union SamplerState -{ - BitField<0, 3, u64> min_filter; - BitField<3, 1, u64> mag_filter; - BitField<4, 8, u64> min_lod; - BitField<12, 8, u64> max_lod; - BitField<20, 8, s64> lod_bias; - BitField<28, 2, u64> wrap_s; - BitField<30, 2, u64> wrap_t; - BitField<32, 5, u64> max_anisotropy; - - u64 packed; -}; - class StateCache { public: @@ -54,7 +40,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; }; namespace D3D diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp index dcf55d03c8..5fce3ab961 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -249,7 +249,7 @@ Renderer::Renderer() : ::Renderer(D3D::GetBackBufferWidth(), D3D::GetBackBufferH // Setup GX pipeline state for (auto& sampler : s_gx_state.samplers) - sampler.packed = 0; + sampler.hex = RenderState::GetPointSamplerState().hex; s_gx_state.zmode.testenable = false; s_gx_state.zmode.updateenable = false; @@ -870,12 +870,8 @@ void Renderer::ApplyState() StateCache::GetPrimitiveTopology(s_gx_state.raster.primitive)); FramebufferManager::SetIntegerEFBRenderTarget(s_gx_state.blend.logicopenable); - for (size_t stage = 0; stage < s_gx_state.samplers.size(); stage++) - { - // TODO: cache SamplerState directly, not d3d object - s_gx_state.samplers[stage].max_anisotropy = UINT64_C(1) << g_ActiveConfig.iMaxAnisotropy; + for (u32 stage = 0; stage < static_cast(s_gx_state.samplers.size()); stage++) D3D::stateman->SetSampler(stage, s_gx_state_cache.Get(s_gx_state.samplers[stage])); - } ID3D11Buffer* vertexConstants = VertexShaderCache::GetConstantBuffer(); @@ -902,38 +898,9 @@ void Renderer::SetDepthState(const DepthState& state) s_gx_state.zmode.hex = state.hex; } -void Renderer::SetSamplerState(int stage, int texindex, bool custom_tex) +void Renderer::SetSamplerState(u32 index, const SamplerState& state) { - const FourTexUnits& tex = bpmem.tex[texindex]; - const TexMode0& tm0 = tex.texMode0[stage]; - const TexMode1& tm1 = tex.texMode1[stage]; - - if (texindex) - stage += 4; - - if (g_ActiveConfig.bForceFiltering) - { - // Only use mipmaps if the game says they are available. - s_gx_state.samplers[stage].min_filter = SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? 6 : 4; - s_gx_state.samplers[stage].mag_filter = 1; // linear mag - } - else - { - s_gx_state.samplers[stage].min_filter = (u32)tm0.min_filter; - s_gx_state.samplers[stage].mag_filter = (u32)tm0.mag_filter; - } - - s_gx_state.samplers[stage].wrap_s = (u32)tm0.wrap_s; - s_gx_state.samplers[stage].wrap_t = (u32)tm0.wrap_t; - s_gx_state.samplers[stage].max_lod = (u32)tm1.max_lod; - s_gx_state.samplers[stage].min_lod = (u32)tm1.min_lod; - s_gx_state.samplers[stage].lod_bias = (s32)tm0.lod_bias; - - // custom textures may have higher resolution, so disable the max_lod - if (custom_tex) - { - s_gx_state.samplers[stage].max_lod = 255; - } + s_gx_state.samplers[index].hex = state.hex; } void Renderer::SetInterlacingMode() diff --git a/Source/Core/VideoBackends/D3D/Render.h b/Source/Core/VideoBackends/D3D/Render.h index 2db2740fb0..a33eba7cd2 100644 --- a/Source/Core/VideoBackends/D3D/Render.h +++ b/Source/Core/VideoBackends/D3D/Render.h @@ -23,7 +23,7 @@ public: void SetScissorRect(const EFBRectangle& rc) override; void SetRasterizationState(const RasterizationState& state) override; void SetDepthState(const DepthState& state) override; - void SetSamplerState(int stage, int texindex, bool custom_tex) override; + void SetSamplerState(u32 index, const SamplerState& state) override; void SetInterlacingMode() override; void SetViewport() override; void SetFullscreen(bool enable_fullscreen) override; diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp index c82a0cba01..a5133023af 100644 --- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp +++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp @@ -1119,7 +1119,8 @@ bool ProgramShaderCache::ShaderCompileWorkItem::Compile() gcode = GenerateGeometryShaderCode(APIType::OpenGL, host_config, m_uid.guid.GetUidData()); CompileShader(m_program, vcode.GetBuffer(), pcode.GetBuffer(), gcode.GetBuffer()); - DrawPrerenderArray(m_program, m_uid.guid.GetUidData()->primitive_type); + DrawPrerenderArray(m_program, + static_cast(m_uid.guid.GetUidData()->primitive_type)); return true; } @@ -1157,7 +1158,8 @@ bool ProgramShaderCache::UberShaderCompileWorkItem::Compile() gcode = GenerateGeometryShaderCode(APIType::OpenGL, host_config, m_uid.guid.GetUidData()); CompileShader(m_program, vcode.GetBuffer(), pcode.GetBuffer(), gcode.GetBuffer()); - DrawPrerenderArray(m_program, m_uid.guid.GetUidData()->primitive_type); + DrawPrerenderArray(m_program, + static_cast(m_uid.guid.GetUidData()->primitive_type)); return true; } diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index b3535709f2..ccf89b45f2 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -1503,9 +1503,13 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, g_Config.iSaveTargetId = 0; + int old_anisotropy = g_ActiveConfig.iMaxAnisotropy; UpdateActiveConfig(); g_texture_cache->OnConfigChanged(g_ActiveConfig); + if (old_anisotropy != g_ActiveConfig.iMaxAnisotropy) + g_sampler_cache->Clear(); + // Invalidate shader cache when the host config changes. if (CheckForHostConfigChanges()) ProgramShaderCache::Reload(); @@ -1834,13 +1838,9 @@ void Renderer::SetDepthState(const DepthState& state) } } -void Renderer::SetSamplerState(int stage, int texindex, bool custom_tex) +void Renderer::SetSamplerState(u32 index, const SamplerState& state) { - auto const& tex = bpmem.tex[texindex]; - auto const& tm0 = tex.texMode0[stage]; - auto const& tm1 = tex.texMode1[stage]; - - g_sampler_cache->SetSamplerState((texindex * 4) + stage, tm0, tm1, custom_tex); + g_sampler_cache->SetSamplerState(index, state); } void Renderer::SetInterlacingMode() diff --git a/Source/Core/VideoBackends/OGL/Render.h b/Source/Core/VideoBackends/OGL/Render.h index e52a1193e1..8efea70bec 100644 --- a/Source/Core/VideoBackends/OGL/Render.h +++ b/Source/Core/VideoBackends/OGL/Render.h @@ -81,7 +81,7 @@ public: void SetScissorRect(const EFBRectangle& rc) override; void SetRasterizationState(const RasterizationState& state) override; void SetDepthState(const DepthState& state) override; - void SetSamplerState(int stage, int texindex, bool custom_tex) override; + void SetSamplerState(u32 index, const SamplerState& state) override; void SetInterlacingMode() override; void SetViewport() override; diff --git a/Source/Core/VideoBackends/OGL/SamplerCache.cpp b/Source/Core/VideoBackends/OGL/SamplerCache.cpp index cbde39be6c..59d3d3a842 100644 --- a/Source/Core/VideoBackends/OGL/SamplerCache.cpp +++ b/Source/Core/VideoBackends/OGL/SamplerCache.cpp @@ -15,141 +15,106 @@ namespace OGL { std::unique_ptr g_sampler_cache; -SamplerCache::SamplerCache() : m_last_max_anisotropy() +SamplerCache::SamplerCache() { - glGenSamplers(2, m_sampler_id); - glSamplerParameteri(m_sampler_id[0], GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glSamplerParameteri(m_sampler_id[0], GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glSamplerParameteri(m_sampler_id[0], GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glSamplerParameteri(m_sampler_id[0], GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glSamplerParameteri(m_sampler_id[1], GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glSamplerParameteri(m_sampler_id[1], GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glSamplerParameteri(m_sampler_id[1], GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glSamplerParameteri(m_sampler_id[1], GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glGenSamplers(1, &m_point_sampler); + glGenSamplers(1, &m_linear_sampler); + glSamplerParameteri(m_point_sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glSamplerParameteri(m_point_sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glSamplerParameteri(m_point_sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glSamplerParameteri(m_point_sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glSamplerParameteri(m_linear_sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glSamplerParameteri(m_linear_sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glSamplerParameteri(m_linear_sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glSamplerParameteri(m_linear_sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } SamplerCache::~SamplerCache() { Clear(); - glDeleteSamplers(2, m_sampler_id); + glDeleteSamplers(1, &m_point_sampler); + glDeleteSamplers(1, &m_linear_sampler); } void SamplerCache::BindNearestSampler(int stage) { - glBindSampler(stage, m_sampler_id[0]); + glBindSampler(stage, m_point_sampler); } void SamplerCache::BindLinearSampler(int stage) { - glBindSampler(stage, m_sampler_id[1]); + glBindSampler(stage, m_linear_sampler); } -void SamplerCache::SetSamplerState(int stage, const TexMode0& tm0, const TexMode1& tm1, - bool custom_tex) +void SamplerCache::SetSamplerState(u32 stage, const SamplerState& state) { - // TODO: can this go somewhere else? - if (m_last_max_anisotropy != g_ActiveConfig.iMaxAnisotropy) + if (m_active_samplers[stage].first == state && m_active_samplers[stage].second != 0) + return; + + auto it = m_cache.find(state); + if (it == m_cache.end()) { - m_last_max_anisotropy = g_ActiveConfig.iMaxAnisotropy; - Clear(); + GLuint sampler; + glGenSamplers(1, &sampler); + SetParameters(sampler, state); + it = m_cache.emplace(state, sampler).first; } - Params params(tm0, tm1); - - // take equivalent forced linear when bForceFiltering - if (g_ActiveConfig.bForceFiltering) - { - params.tm0.min_filter = SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? 6 : 4; - params.tm0.mag_filter = 1; - } - - // custom textures may have higher resolution, so disable the max_lod - if (custom_tex) - { - params.tm1.max_lod = 255; - } - - // TODO: Should keep a circular buffer for each stage of recently used samplers. - - auto& active_sampler = m_active_samplers[stage]; - if (active_sampler.first != params || !active_sampler.second.sampler_id) - { - // Active sampler does not match parameters (or is invalid), bind the proper one. - active_sampler.first = params; - active_sampler.second = GetEntry(params); - glBindSampler(stage, active_sampler.second.sampler_id); - } + m_active_samplers[stage].first = state; + m_active_samplers[stage].second = it->second; + glBindSampler(stage, it->second); } -SamplerCache::Value& SamplerCache::GetEntry(const Params& params) +void SamplerCache::InvalidateBinding(u32 stage) { - auto& val = m_cache[params]; - if (!val.sampler_id) - { - // Sampler not found in cache, create it. - glGenSamplers(1, &val.sampler_id); - SetParameters(val.sampler_id, params); - - // TODO: Maybe kill old samplers if the cache gets huge. It doesn't seem to get huge though. - // ERROR_LOG(VIDEO, "Sampler cache size is now %ld.", m_cache.size()); - } - - return val; + m_active_samplers[stage].second = 0; } -void SamplerCache::SetParameters(GLuint sampler_id, const Params& params) +void SamplerCache::SetParameters(GLuint sampler_id, const SamplerState& params) { - static const GLint min_filters[8] = { - GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST, - GL_LINEAR, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, - }; - - static const GLint wrap_settings[4] = { - GL_CLAMP_TO_EDGE, GL_REPEAT, GL_MIRRORED_REPEAT, GL_REPEAT, - }; - - auto& tm0 = params.tm0; - auto& tm1 = params.tm1; - - glSamplerParameteri(sampler_id, GL_TEXTURE_WRAP_S, wrap_settings[tm0.wrap_s]); - glSamplerParameteri(sampler_id, GL_TEXTURE_WRAP_T, wrap_settings[tm0.wrap_t]); - - glSamplerParameterf(sampler_id, GL_TEXTURE_MIN_LOD, tm1.min_lod / 16.f); - glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_LOD, tm1.max_lod / 16.f); - - if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGL) - glSamplerParameterf(sampler_id, GL_TEXTURE_LOD_BIAS, (s32)tm0.lod_bias / 32.f); - - GLint min_filter = min_filters[tm0.min_filter]; - GLint mag_filter = tm0.mag_filter ? GL_LINEAR : GL_NEAREST; - - if (g_ActiveConfig.iMaxAnisotropy > 0 && g_ogl_config.bSupportsAniso && - !SamplerCommon::IsBpTexMode0PointFiltering(tm0)) + GLenum min_filter; + GLenum mag_filter = (params.mag_filter == SamplerState::Filter::Point) ? GL_NEAREST : GL_LINEAR; + if (params.mipmap_filter == SamplerState::Filter::Linear) { - // https://www.opengl.org/registry/specs/EXT/texture_filter_anisotropic.txt - // For predictable results on all hardware/drivers, only use one of: - // GL_LINEAR + GL_LINEAR (No Mipmaps [Bilinear]) - // GL_LINEAR + GL_LINEAR_MIPMAP_LINEAR (w/ Mipmaps [Trilinear]) - // 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. - min_filter = - SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR; - mag_filter = GL_LINEAR; - glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, - (float)(1 << g_ActiveConfig.iMaxAnisotropy)); + min_filter = (params.min_filter == SamplerState::Filter::Point) ? 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; } glSamplerParameteri(sampler_id, GL_TEXTURE_MIN_FILTER, min_filter); glSamplerParameteri(sampler_id, GL_TEXTURE_MAG_FILTER, mag_filter); + + static constexpr std::array address_modes = { + {GL_CLAMP_TO_EDGE, GL_REPEAT, GL_MIRRORED_REPEAT}}; + + glSamplerParameteri(sampler_id, GL_TEXTURE_WRAP_S, + address_modes[static_cast(params.wrap_u.Value())]); + glSamplerParameteri(sampler_id, GL_TEXTURE_WRAP_T, + address_modes[static_cast(params.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); + + if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGL) + glSamplerParameterf(sampler_id, GL_TEXTURE_LOD_BIAS, params.lod_bias / 32.f); + + if (params.anisotropic_filtering && g_ogl_config.bSupportsAniso) + { + glSamplerParameterf(sampler_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, + static_cast(1 << g_ActiveConfig.iMaxAnisotropy)); + } } void SamplerCache::Clear() { for (auto& p : m_cache) - { - glDeleteSamplers(1, &p.second.sampler_id); - } + glDeleteSamplers(1, &p.second); + for (auto& p : m_active_samplers) + p.second = 0; m_cache.clear(); } } diff --git a/Source/Core/VideoBackends/OGL/SamplerCache.h b/Source/Core/VideoBackends/OGL/SamplerCache.h index c6bd76dae1..7b437e326b 100644 --- a/Source/Core/VideoBackends/OGL/SamplerCache.h +++ b/Source/Core/VideoBackends/OGL/SamplerCache.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include @@ -24,49 +25,21 @@ public: SamplerCache(SamplerCache&&) = delete; SamplerCache& operator=(SamplerCache&&) = delete; - void SetSamplerState(int stage, const TexMode0& tm0, const TexMode1& tm1, bool custom_tex); + void SetSamplerState(u32 stage, const SamplerState& state); + void InvalidateBinding(u32 stage); + void Clear(); void BindNearestSampler(int stage); void BindLinearSampler(int stage); private: - struct Params - { - union - { - struct - { - TexMode0 tm0; - TexMode1 tm1; - }; + static void SetParameters(GLuint sampler_id, const SamplerState& params); - u64 hex; - }; + std::map m_cache; + std::array, 8> m_active_samplers{}; - Params() : hex() {} - Params(const TexMode0& _tm0, const TexMode1& _tm1) : tm0(_tm0), tm1(_tm1) - { - static_assert(sizeof(Params) == 8, "Assuming I can treat this as a 64bit int."); - } - - bool operator<(const Params& other) const { return hex < other.hex; } - bool operator!=(const Params& other) const { return hex != other.hex; } - }; - - struct Value - { - Value() : sampler_id() {} - GLuint sampler_id; - }; - - void SetParameters(GLuint sampler_id, const Params& params); - Value& GetEntry(const Params& params); - - std::map m_cache; - std::pair m_active_samplers[8]; - - int m_last_max_anisotropy; - u32 m_sampler_id[2]; + GLuint m_point_sampler; + GLuint m_linear_sampler; }; extern std::unique_ptr g_sampler_cache; diff --git a/Source/Core/VideoBackends/Vulkan/Constants.h b/Source/Core/VideoBackends/Vulkan/Constants.h index 9086c1a690..3e4012fe17 100644 --- a/Source/Core/VideoBackends/Vulkan/Constants.h +++ b/Source/Core/VideoBackends/Vulkan/Constants.h @@ -132,20 +132,4 @@ union MultisamplingState u32 hex; }; -// Sampler info -union SamplerState -{ - BitField<0, 1, VkFilter> min_filter; - BitField<1, 1, VkFilter> mag_filter; - BitField<2, 1, VkSamplerMipmapMode> mipmap_mode; - BitField<3, 2, VkSamplerAddressMode> wrap_u; - BitField<5, 2, VkSamplerAddressMode> wrap_v; - BitField<7, 8, u32> min_lod; - BitField<15, 8, u32> max_lod; - BitField<23, 8, s32> lod_bias; - BitField<31, 1, u32> enable_anisotropic_filtering; - - u32 bits; -}; - } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp index 6c932c350d..c0196cb7d6 100644 --- a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp @@ -312,29 +312,36 @@ VkSampler ObjectCache::GetSampler(const SamplerState& info) if (iter != m_sampler_cache.end()) return iter->second; + static constexpr std::array filters = {{VK_FILTER_NEAREST, VK_FILTER_LINEAR}}; + static constexpr std::array mipmap_modes = { + {VK_SAMPLER_MIPMAP_MODE_NEAREST, VK_SAMPLER_MIPMAP_MODE_LINEAR}}; + static constexpr std::array address_modes = { + {VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_SAMPLER_ADDRESS_MODE_REPEAT, + VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT}}; + VkSamplerCreateInfo create_info = { - VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, // VkStructureType sType - nullptr, // const void* pNext - 0, // VkSamplerCreateFlags flags - info.mag_filter, // VkFilter magFilter - info.min_filter, // VkFilter minFilter - info.mipmap_mode, // VkSamplerMipmapMode mipmapMode - info.wrap_u, // VkSamplerAddressMode addressModeU - info.wrap_v, // VkSamplerAddressMode addressModeV - VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // VkSamplerAddressMode addressModeW - static_cast(info.lod_bias / 32.0f), // float mipLodBias - VK_FALSE, // VkBool32 anisotropyEnable - 0.0f, // float maxAnisotropy - VK_FALSE, // VkBool32 compareEnable - VK_COMPARE_OP_ALWAYS, // VkCompareOp compareOp - static_cast(info.min_lod / 16.0f), // float minLod - static_cast(info.max_lod / 16.0f), // float maxLod - VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, // VkBorderColor borderColor - VK_FALSE // VkBool32 unnormalizedCoordinates + 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 / 32.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 + VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, // VkBorderColor borderColor + VK_FALSE // VkBool32 unnormalizedCoordinates }; // Can we use anisotropic filtering with this sampler? - if (info.enable_anisotropic_filtering && g_vulkan_context->SupportsAnisotropicFiltering()) + if (info.anisotropic_filtering && g_vulkan_context->SupportsAnisotropicFiltering()) { // Cap anisotropy to device limits. create_info.anisotropyEnable = VK_TRUE; diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index fc3eb59c39..2a90da6f9a 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -52,10 +52,8 @@ Renderer::Renderer(std::unique_ptr swap_chain) m_swap_chain(std::move(swap_chain)) { UpdateActiveConfig(); - - // Set to something invalid, forcing all states to be re-initialized. for (size_t i = 0; i < m_sampler_states.size(); i++) - m_sampler_states[i].bits = std::numeric_limits::max(); + m_sampler_states[i].hex = RenderState::GetPointSamplerState().hex; } Renderer::~Renderer() @@ -1282,65 +1280,22 @@ void Renderer::SetBlendingState(const BlendingState& state) StateTracker::GetInstance()->SetBlendState(state); } -void Renderer::SetSamplerState(int stage, int texindex, bool custom_tex) +void Renderer::SetSamplerState(u32 index, const SamplerState& state) { - const FourTexUnits& tex = bpmem.tex[texindex]; - const TexMode0& tm0 = tex.texMode0[stage]; - const TexMode1& tm1 = tex.texMode1[stage]; - SamplerState new_state = {}; - - if (g_ActiveConfig.bForceFiltering) - { - new_state.min_filter = VK_FILTER_LINEAR; - new_state.mag_filter = VK_FILTER_LINEAR; - new_state.mipmap_mode = SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? - VK_SAMPLER_MIPMAP_MODE_LINEAR : - VK_SAMPLER_MIPMAP_MODE_NEAREST; - } - else - { - // Constants for these? - new_state.min_filter = (tm0.min_filter & 4) != 0 ? VK_FILTER_LINEAR : VK_FILTER_NEAREST; - new_state.mipmap_mode = SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? - VK_SAMPLER_MIPMAP_MODE_LINEAR : - VK_SAMPLER_MIPMAP_MODE_NEAREST; - new_state.mag_filter = tm0.mag_filter != 0 ? VK_FILTER_LINEAR : VK_FILTER_NEAREST; - } - - // If mipmaps are disabled, clamp min/max lod - new_state.max_lod = SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? tm1.max_lod : 0; - new_state.min_lod = std::min(new_state.max_lod.Value(), tm1.min_lod); - new_state.lod_bias = SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? tm0.lod_bias : 0; - - // Custom textures may have a greater number of mips - if (custom_tex) - new_state.max_lod = 255; - - // Address modes - static const VkSamplerAddressMode address_modes[] = { - VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_SAMPLER_ADDRESS_MODE_REPEAT, - VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT, VK_SAMPLER_ADDRESS_MODE_REPEAT}; - new_state.wrap_u = address_modes[tm0.wrap_s]; - new_state.wrap_v = address_modes[tm0.wrap_t]; - - // Only use anisotropic filtering for textures that would be linearly filtered. - new_state.enable_anisotropic_filtering = SamplerCommon::IsBpTexMode0PointFiltering(tm0) ? 0 : 1; - // Skip lookup if the state hasn't changed. - size_t bind_index = (texindex * 4) + stage; - if (m_sampler_states[bind_index].bits == new_state.bits) + if (m_sampler_states[index].hex == state.hex) return; // Look up new state and replace in state tracker. - VkSampler sampler = g_object_cache->GetSampler(new_state); + VkSampler sampler = g_object_cache->GetSampler(state); if (sampler == VK_NULL_HANDLE) { ERROR_LOG(VIDEO, "Failed to create sampler"); sampler = g_object_cache->GetPointSampler(); } - StateTracker::GetInstance()->SetSampler(bind_index, sampler); - m_sampler_states[bind_index].bits = new_state.bits; + StateTracker::GetInstance()->SetSampler(index, sampler); + m_sampler_states[index].hex = state.hex; } void Renderer::ResetSamplerStates() @@ -1352,7 +1307,7 @@ void Renderer::ResetSamplerStates() // Invalidate all sampler states, next draw will re-initialize them. for (size_t i = 0; i < m_sampler_states.size(); i++) { - m_sampler_states[i].bits = std::numeric_limits::max(); + m_sampler_states[i].hex = RenderState::GetPointSamplerState().hex; StateTracker::GetInstance()->SetSampler(i, g_object_cache->GetPointSampler()); } diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.h b/Source/Core/VideoBackends/Vulkan/Renderer.h index 103c28a64e..593c2cd887 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.h +++ b/Source/Core/VideoBackends/Vulkan/Renderer.h @@ -60,7 +60,7 @@ public: void SetScissorRect(const EFBRectangle& rc) override; void SetRasterizationState(const RasterizationState& state) override; void SetDepthState(const DepthState& state) override; - void SetSamplerState(int stage, int texindex, bool custom_tex) override; + void SetSamplerState(u32 index, const SamplerState& state) override; void SetInterlacingMode() override; void SetViewport() override; diff --git a/Source/Core/VideoBackends/Vulkan/ShaderCache.cpp b/Source/Core/VideoBackends/Vulkan/ShaderCache.cpp index 9bd41b882f..ad5cdf523a 100644 --- a/Source/Core/VideoBackends/Vulkan/ShaderCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/ShaderCache.cpp @@ -977,18 +977,6 @@ std::string ShaderCache::GetUtilityShaderHeader() const return ss.str(); } -// Comparison operators for PipelineInfos -// Since these all boil down to POD types, we can just memcmp the entire thing for speed -// The is_trivially_copyable check fails on MSVC due to BitField. -// TODO: Can we work around this any way? -#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5 && !defined(_MSC_VER) -static_assert(std::has_trivial_copy_constructor::value, - "PipelineInfo is trivially copyable"); -#elif !defined(_MSC_VER) -static_assert(std::is_trivially_copyable::value, - "PipelineInfo is trivially copyable"); -#endif - std::size_t PipelineInfoHash::operator()(const PipelineInfo& key) const { return static_cast(XXH64(&key, sizeof(key), 0)); @@ -1014,26 +1002,6 @@ bool operator>(const PipelineInfo& lhs, const PipelineInfo& rhs) return std::memcmp(&lhs, &rhs, sizeof(lhs)) > 0; } -bool operator==(const SamplerState& lhs, const SamplerState& rhs) -{ - return lhs.bits == rhs.bits; -} - -bool operator!=(const SamplerState& lhs, const SamplerState& rhs) -{ - return !operator==(lhs, rhs); -} - -bool operator>(const SamplerState& lhs, const SamplerState& rhs) -{ - return lhs.bits > rhs.bits; -} - -bool operator<(const SamplerState& lhs, const SamplerState& rhs) -{ - return lhs.bits < rhs.bits; -} - std::size_t ComputePipelineInfoHash::operator()(const ComputePipelineInfo& key) const { return static_cast(XXH64(&key, sizeof(key), 0)); @@ -1214,7 +1182,8 @@ void ShaderCache::CreateDummyPipeline(const UberShader::VertexShaderUid& vuid, pinfo.depth_state.hex = RenderState::GetNoDepthTestingDepthStencilState().hex; pinfo.blend_state.hex = RenderState::GetNoBlendingBlendState().hex; pinfo.multisampling_state.hex = FramebufferManager::GetInstance()->GetEFBMultisamplingState().hex; - pinfo.rasterization_state.primitive = guid.GetUidData()->primitive_type; + pinfo.rasterization_state.primitive = + static_cast(guid.GetUidData()->primitive_type); GetPipelineWithCacheResultAsync(pinfo); } diff --git a/Source/Core/VideoBackends/Vulkan/ShaderCache.h b/Source/Core/VideoBackends/Vulkan/ShaderCache.h index 4c0de4a94d..0ad080a3fc 100644 --- a/Source/Core/VideoBackends/Vulkan/ShaderCache.h +++ b/Source/Core/VideoBackends/Vulkan/ShaderCache.h @@ -62,10 +62,6 @@ bool operator==(const PipelineInfo& lhs, const PipelineInfo& rhs); bool operator!=(const PipelineInfo& lhs, const PipelineInfo& rhs); bool operator<(const PipelineInfo& lhs, const PipelineInfo& rhs); bool operator>(const PipelineInfo& lhs, const PipelineInfo& rhs); -bool operator==(const SamplerState& lhs, const SamplerState& rhs); -bool operator!=(const SamplerState& lhs, const SamplerState& rhs); -bool operator>(const SamplerState& lhs, const SamplerState& rhs); -bool operator<(const SamplerState& lhs, const SamplerState& rhs); struct ComputePipelineInfo { diff --git a/Source/Core/VideoCommon/GeometryShaderGen.cpp b/Source/Core/VideoCommon/GeometryShaderGen.cpp index 4c6fb1c559..98335386fa 100644 --- a/Source/Core/VideoCommon/GeometryShaderGen.cpp +++ b/Source/Core/VideoCommon/GeometryShaderGen.cpp @@ -22,7 +22,7 @@ bool geometry_shader_uid_data::IsPassthrough() const { const bool stereo = g_ActiveConfig.iStereoMode > 0; const bool wireframe = g_ActiveConfig.bWireFrame; - return primitive_type >= PrimitiveType::Triangles && !stereo && !wireframe; + return primitive_type >= static_cast(PrimitiveType::Triangles) && !stereo && !wireframe; } GeometryShaderUid GetGeometryShaderUid(PrimitiveType primitive_type) @@ -31,7 +31,7 @@ GeometryShaderUid GetGeometryShaderUid(PrimitiveType primitive_type) geometry_shader_uid_data* uid_data = out.GetUidData(); memset(uid_data, 0, sizeof(geometry_shader_uid_data)); - uid_data->primitive_type = primitive_type; + uid_data->primitive_type = static_cast(primitive_type); uid_data->numTexGens = xfmem.numTexGen.numTexGens; return out; @@ -56,9 +56,10 @@ ShaderCode GenerateGeometryShaderCode(APIType ApiType, const ShaderHostConfig& h const bool msaa = host_config.msaa; const bool ssaa = host_config.ssaa; const bool stereo = host_config.stereo; + const PrimitiveType primitive_type = static_cast(uid_data->primitive_type); const unsigned primitive_type_index = static_cast(uid_data->primitive_type); const unsigned vertex_in = std::min(static_cast(primitive_type_index) + 1, 3u); - unsigned vertex_out = uid_data->primitive_type == PrimitiveType::TriangleStrip ? 3 : 4; + unsigned vertex_out = primitive_type == PrimitiveType::TriangleStrip ? 3 : 4; if (wireframe) vertex_out++; @@ -146,7 +147,7 @@ ShaderCode GenerateGeometryShaderCode(APIType ApiType, const ShaderHostConfig& h out.Write("\tVertexData ps;\n"); } - if (uid_data->primitive_type == PrimitiveType::Lines) + if (primitive_type == PrimitiveType::Lines) { if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) { @@ -177,7 +178,7 @@ ShaderCode GenerateGeometryShaderCode(APIType ApiType, const ShaderHostConfig& h "\t\toffset = float2(0, -" I_LINEPTPARAMS ".z / " I_LINEPTPARAMS ".y);\n" "\t}\n"); } - else if (uid_data->primitive_type == PrimitiveType::Points) + else if (primitive_type == PrimitiveType::Points) { if (ApiType == APIType::OpenGL || ApiType == APIType::Vulkan) { @@ -247,7 +248,7 @@ ShaderCode GenerateGeometryShaderCode(APIType ApiType, const ShaderHostConfig& h out.Write("\tf.pos.x += hoffset * (f.pos.w - " I_STEREOPARAMS ".z);\n"); } - if (uid_data->primitive_type == PrimitiveType::Lines) + if (primitive_type == PrimitiveType::Lines) { out.Write("\tVS_OUTPUT l = f;\n" "\tVS_OUTPUT r = f;\n"); @@ -268,7 +269,7 @@ ShaderCode GenerateGeometryShaderCode(APIType ApiType, const ShaderHostConfig& h EmitVertex(out, host_config, uid_data, "l", ApiType, wireframe, pixel_lighting, true); EmitVertex(out, host_config, uid_data, "r", ApiType, wireframe, pixel_lighting); } - else if (uid_data->primitive_type == PrimitiveType::Points) + else if (primitive_type == PrimitiveType::Points) { out.Write("\tVS_OUTPUT ll = f;\n" "\tVS_OUTPUT lr = f;\n" @@ -376,7 +377,7 @@ void EnumerateGeometryShaderUids(const std::function(); - guid->primitive_type = primitive; + guid->primitive_type = static_cast(primitive); for (u32 texgens = 0; texgens <= 8; texgens++) { diff --git a/Source/Core/VideoCommon/GeometryShaderGen.h b/Source/Core/VideoCommon/GeometryShaderGen.h index b23441f3d3..ace05baca7 100644 --- a/Source/Core/VideoCommon/GeometryShaderGen.h +++ b/Source/Core/VideoCommon/GeometryShaderGen.h @@ -20,7 +20,7 @@ struct geometry_shader_uid_data bool IsPassthrough() const; u32 numTexGens : 4; - PrimitiveType primitive_type : 2; + u32 primitive_type : 2; }; #pragma pack() diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 6c264cd04b..1c6bc44171 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -68,7 +68,7 @@ public: virtual void SetScissorRect(const EFBRectangle& rc) {} virtual void SetRasterizationState(const RasterizationState& state) {} virtual void SetDepthState(const DepthState& state) {} - virtual void SetSamplerState(int stage, int texindex, bool custom_tex) {} + virtual void SetSamplerState(u32 index, const SamplerState& state) {} virtual void SetInterlacingMode() {} virtual void SetViewport() {} virtual void SetFullscreen(bool enable_fullscreen) {} diff --git a/Source/Core/VideoCommon/RenderState.cpp b/Source/Core/VideoCommon/RenderState.cpp index d8cd5e7f11..8f8d421730 100644 --- a/Source/Core/VideoCommon/RenderState.cpp +++ b/Source/Core/VideoCommon/RenderState.cpp @@ -3,6 +3,9 @@ // Refer to the license.txt file included. #include "VideoCommon/RenderState.h" +#include +#include +#include "VideoCommon/SamplerCommon.h" void RasterizationState::Generate(const BPMemory& bp, PrimitiveType primitive_type) { @@ -14,6 +17,12 @@ void RasterizationState::Generate(const BPMemory& bp, PrimitiveType primitive_ty cullmode = GenMode::CULL_NONE; } +RasterizationState& RasterizationState::operator=(const RasterizationState& rhs) +{ + hex = rhs.hex; + return *this; +} + void DepthState::Generate(const BPMemory& bp) { testenable = bp.zmode.testenable.Value(); @@ -21,6 +30,12 @@ void DepthState::Generate(const BPMemory& bp) func = bp.zmode.func.Value(); } +DepthState& DepthState::operator=(const DepthState& rhs) +{ + hex = rhs.hex; + return *this; +} + // If the framebuffer format has no alpha channel, it is assumed to // ONE on blending. As the backends may emulate this framebuffer // configuration with an alpha channel, we just drop all references @@ -145,6 +160,43 @@ void BlendingState::Generate(const BPMemory& bp) } } +BlendingState& BlendingState::operator=(const BlendingState& rhs) +{ + hex = rhs.hex; + return *this; +} + +void SamplerState::Generate(const BPMemory& bp, u32 index) +{ + const FourTexUnits& tex = bpmem.tex[index / 4]; + const TexMode0& tm0 = tex.texMode0[index % 4]; + const TexMode1& tm1 = tex.texMode1[index % 4]; + + // 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 & 4) != 0 ? Filter::Linear : Filter::Point; + mipmap_filter = (tm0.min_filter & 3) == TexMode0::TEXF_LINEAR ? Filter::Linear : Filter::Point; + mag_filter = tm0.mag_filter != 0 ? Filter::Linear : Filter::Point; + + // If mipmaps are disabled, clamp min/max lod + max_lod = SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? tm1.max_lod : 0; + min_lod = std::min(max_lod.Value(), tm1.min_lod); + lod_bias = SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? tm0.lod_bias : 0; + + // Address modes + static constexpr std::array address_modes = { + {AddressMode::Clamp, AddressMode::Repeat, AddressMode::MirroredRepeat, AddressMode::Repeat}}; + wrap_u = address_modes[tm0.wrap_s]; + wrap_v = address_modes[tm0.wrap_t]; + anisotropic_filtering = 0; +} + +SamplerState& SamplerState::operator=(const SamplerState& rhs) +{ + hex = rhs.hex; + return *this; +} + namespace RenderState { RasterizationState GetNoCullRasterizationState() @@ -177,4 +229,34 @@ BlendingState GetNoBlendingBlendState() state.alphaupdate = true; 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; + 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; + return state; +} } diff --git a/Source/Core/VideoCommon/RenderState.h b/Source/Core/VideoCommon/RenderState.h index d3a006a887..76482b6bb9 100644 --- a/Source/Core/VideoCommon/RenderState.h +++ b/Source/Core/VideoCommon/RenderState.h @@ -21,6 +21,11 @@ union RasterizationState { void Generate(const BPMemory& bp, PrimitiveType primitive_type); + RasterizationState& operator=(const RasterizationState& rhs); + + bool operator==(const RasterizationState& rhs) const { return hex == rhs.hex; } + bool operator!=(const RasterizationState& rhs) const { return hex != rhs.hex; } + bool operator<(const RasterizationState& rhs) const { return hex < rhs.hex; } BitField<0, 2, GenMode::CullMode> cullmode; BitField<3, 2, PrimitiveType> primitive; @@ -31,6 +36,11 @@ union DepthState { void Generate(const BPMemory& bp); + DepthState& operator=(const DepthState& rhs); + + bool operator==(const DepthState& rhs) const { return hex == rhs.hex; } + bool operator!=(const DepthState& rhs) const { return hex != rhs.hex; } + bool operator<(const DepthState& rhs) const { return hex < rhs.hex; } BitField<0, 1, u32> testenable; BitField<1, 1, u32> updateenable; BitField<2, 3, ZMode::CompareMode> func; @@ -42,6 +52,11 @@ union BlendingState { void Generate(const BPMemory& bp); + BlendingState& operator=(const BlendingState& rhs); + + bool operator==(const BlendingState& rhs) const { return hex == rhs.hex; } + bool operator!=(const BlendingState& rhs) const { return hex != rhs.hex; } + bool operator<(const BlendingState& rhs) const { return hex < rhs.hex; } BitField<0, 1, u32> blendenable; BitField<1, 1, u32> logicopenable; BitField<2, 1, u32> dstalpha; @@ -59,9 +74,46 @@ union BlendingState u32 hex; }; +union SamplerState +{ + enum class Filter : u32 + { + Point, + Linear + }; + + enum class AddressMode : u32 + { + Clamp, + Repeat, + MirroredRepeat + }; + + void Generate(const BPMemory& bp, u32 index); + + SamplerState& operator=(const SamplerState& rhs); + + 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 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, 8, u32> min_lod; // multiplied by 16 + BitField<15, 8, u32> max_lod; // multiplied by 16 + BitField<23, 8, s32> lod_bias; // multiplied by 32 + BitField<31, 1, u32> anisotropic_filtering; + + u32 hex; +}; + namespace RenderState { RasterizationState GetNoCullRasterizationState(); DepthState GetNoDepthTestingDepthStencilState(); BlendingState GetNoBlendingBlendState(); +SamplerState GetPointSamplerState(); +SamplerState GetLinearSamplerState(); } diff --git a/Source/Core/VideoCommon/VertexManagerBase.cpp b/Source/Core/VideoCommon/VertexManagerBase.cpp index 95f31f86a4..9138b0db09 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.cpp +++ b/Source/Core/VideoCommon/VertexManagerBase.cpp @@ -24,6 +24,7 @@ #include "VideoCommon/PerfQueryBase.h" #include "VideoCommon/PixelShaderManager.h" #include "VideoCommon/RenderBase.h" +#include "VideoCommon/SamplerCommon.h" #include "VideoCommon/TextureCacheBase.h" #include "VideoCommon/VertexLoaderManager.h" #include "VideoCommon/VertexShaderManager.h" @@ -208,6 +209,52 @@ std::pair VertexManagerBase::ResetFlushAspectRatioCount() return val; } +static void SetSamplerState(u32 index, bool custom_tex) +{ + const FourTexUnits& tex = bpmem.tex[index / 4]; + const TexMode0& tm0 = tex.texMode0[index % 4]; + + SamplerState state = {}; + state.Generate(bpmem, index); + + // Force texture filtering config option. + if (g_ActiveConfig.bForceFiltering) + { + state.min_filter = SamplerState::Filter::Linear; + state.mag_filter = SamplerState::Filter::Linear; + state.mipmap_filter = SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? + SamplerState::Filter::Linear : + SamplerState::Filter::Point; + } + + // Custom textures may have a greater number of mips + if (custom_tex) + state.max_lod = 255; + + // Anisotropic filtering option. + if (g_ActiveConfig.iMaxAnisotropy != 0 && !SamplerCommon::IsBpTexMode0PointFiltering(tm0)) + { + // https://www.opengl.org/registry/specs/EXT/texture_filter_anisotropic.txt + // For predictable results on all hardware/drivers, only use one of: + // GL_LINEAR + GL_LINEAR (No Mipmaps [Bilinear]) + // GL_LINEAR + GL_LINEAR_MIPMAP_LINEAR (w/ Mipmaps [Trilinear]) + // 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; + if (SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0)) + state.mipmap_filter = SamplerState::Filter::Linear; + state.anisotropic_filtering = 1; + } + else + { + state.anisotropic_filtering = 0; + } + + g_renderer->SetSamplerState(index, state); +} + void VertexManagerBase::Flush() { if (m_is_flushed) @@ -276,7 +323,7 @@ void VertexManagerBase::Flush() if (tentry) { - g_renderer->SetSamplerState(i & 3, i >> 2, tentry->is_custom_tex); + SetSamplerState(i, tentry->is_custom_tex); PixelShaderManager::SetTexDims(i, tentry->native_width, tentry->native_height); } else