VideoCommon: Expose SamplerState to shaders

The benefit to exposing this over the raw BP state is that adjustments Dolphin makes, such as LOD biases from arbitrary mipmap detection, will work properly.
This commit is contained in:
Pokechu22 2021-08-08 21:11:50 -07:00
parent 9ef228503a
commit 4a9b26de86
14 changed files with 188 additions and 139 deletions

View File

@ -303,43 +303,43 @@ StateCache::~StateCache() = default;
ID3D11SamplerState* StateCache::Get(SamplerState state)
{
std::lock_guard<std::mutex> 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<D3D11_TEXTURE_ADDRESS_MODE, 3> address_modes = {
{D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_WRAP, D3D11_TEXTURE_ADDRESS_MIRROR}};
sampdc.AddressU = address_modes[static_cast<u32>(state.wrap_u.Value())];
sampdc.AddressV = address_modes[static_cast<u32>(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<u32>(state.tm0.wrap_u.Value())];
sampdc.AddressV = address_modes[static_cast<u32>(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<ID3D11SamplerState> 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)

View File

@ -37,7 +37,7 @@ private:
std::unordered_map<u32, ComPtr<ID3D11DepthStencilState>> m_depth;
std::unordered_map<u32, ComPtr<ID3D11RasterizerState>> m_raster;
std::unordered_map<u32, ComPtr<ID3D11BlendState>> m_blend;
std::unordered_map<SamplerState::StorageType, ComPtr<ID3D11SamplerState>> m_sampler;
std::unordered_map<SamplerState, ComPtr<ID3D11SamplerState>> m_sampler;
std::mutex m_lock;
};

View File

@ -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<D3D12_TEXTURE_ADDRESS_MODE, 3> address_modes = {
{D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_TEXTURE_ADDRESS_MODE_WRAP,
D3D12_TEXTURE_ADDRESS_MODE_MIRROR}};
desc->AddressU = address_modes[static_cast<u32>(state.wrap_u.Value())];
desc->AddressV = address_modes[static_cast<u32>(state.wrap_v.Value())];
desc->AddressU = address_modes[static_cast<u32>(state.tm0.wrap_u.Value())];
desc->AddressV = address_modes[static_cast<u32>(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<s32>(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<s32>(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;

View File

@ -68,6 +68,6 @@ private:
D3D12_CPU_DESCRIPTOR_HANDLE m_heap_base_cpu{};
std::unordered_map<SamplerState::StorageType, D3D12_CPU_DESCRIPTOR_HANDLE> m_sampler_map;
std::unordered_map<SamplerState, D3D12_CPU_DESCRIPTOR_HANDLE> m_sampler_map;
};
} // namespace DX12

View File

@ -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<u32>(params.wrap_u.Value())]);
address_modes[static_cast<u32>(params.tm0.wrap_u.Value())]);
glSamplerParameteri(sampler_id, GL_TEXTURE_WRAP_T,
address_modes[static_cast<u32>(params.wrap_v.Value())]);
address_modes[static_cast<u32>(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<Renderer*>(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<float>(1 << g_ActiveConfig.iMaxAnisotropy));

View File

@ -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<u32>(info.mag_filter.Value())], // VkFilter magFilter
filters[static_cast<u32>(info.min_filter.Value())], // VkFilter minFilter
mipmap_modes[static_cast<u32>(info.mipmap_filter.Value())], // VkSamplerMipmapMode mipmapMode
address_modes[static_cast<u32>(info.wrap_u.Value())], // VkSamplerAddressMode addressModeU
address_modes[static_cast<u32>(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;

View File

@ -49,7 +49,7 @@ Renderer::Renderer(std::unique_ptr<SwapChain> 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());
}

View File

@ -32,7 +32,7 @@ struct PixelShaderConstants
float4 zslope;
std::array<float, 2> 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<uint4, 16> pack1; // .xy - combiners, .z - tevind, .w - iref
std::array<uint4, 8> pack2; // .x - tevorder, .y - tevksel
std::array<uint4, 8> pack2; // .x - tevorder, .y - tevksel, .z/.w - SamplerState tm0/tm1
std::array<int4, 32> konst; // .rgba
// The following are used in ubershaders when using shader_framebuffer_fetch blending
u32 blend_enable;

View File

@ -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"

View File

@ -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;

View File

@ -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);

View File

@ -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<u64>(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<AddressMode, 4> 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;
}

View File

@ -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<u64>(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<SamplerState>
{
std::size_t operator()(SamplerState const& state) const noexcept
{
return std::hash<u64>{}(state.Hex());
}
};
} // namespace std
namespace RenderState
{
RasterizationState GetInvalidRasterizationState();

View File

@ -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<s64>(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<s32>(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)