gl,vk,dx: use nearest filtering for mipmap with PT polys

Interpolating between LoDs produces unexpected alpha values.
dx,vk: add negative LoD bias
Fixes "Press Start" text in Trizeal title screen

dx11: get rid of bogus nearest wrap hack
Can't reproduce original issue with daytona
Fixes Virtua Tennis title screen
Issue #1230
This commit is contained in:
Flyinghead 2023-10-09 21:09:03 +02:00
parent 23d1db9c75
commit 38d4689b63
15 changed files with 47 additions and 51 deletions

View File

@ -624,7 +624,6 @@ void DX11Renderer::setRenderState(const PolyParam *gp)
gp->pcw.Gouraud,
Type == ListType_Punch_Through,
clipmode == TileClipping::Inside,
gp->pcw.Texture && gp->tsp.FilterMode == 0 && !gp->tsp.ClampU && !gp->tsp.ClampV && !gp->tsp.FlipU && !gp->tsp.FlipV,
dithering);
deviceContext->PSSetShader(pixelShader, nullptr, 0);
@ -670,7 +669,7 @@ void DX11Renderer::setRenderState(const PolyParam *gp)
linearFiltering = false;
else
linearFiltering = true;
auto sampler = samplers->getSampler(linearFiltering, gp->tsp.ClampU, gp->tsp.ClampV, gp->tsp.FlipU, gp->tsp.FlipV);
auto sampler = samplers->getSampler(linearFiltering, gp->tsp.ClampU, gp->tsp.ClampV, gp->tsp.FlipU, gp->tsp.FlipV, Type == ListType_Punch_Through);
deviceContext->PSSetSamplers(0, 1, &sampler.get());
}
@ -768,7 +767,6 @@ void DX11Renderer::drawSorted(int first, int count, bool multipass)
true,
false,
false,
false,
false);
deviceContext->PSSetShader(pixelShader, nullptr, 0);

View File

@ -242,9 +242,6 @@ PSO main(in Pixel inpix)
#if DIV_POS_Z != 1
uv /= inpix.uv.w;
#endif
#if NearestWrapFix == 1
uv = min(fmod(uv, 1.f), 0.9997f);
#endif
#if pp_Palette == 0
float4 texcol = texture0.Sample(sampler0, uv);
#else
@ -429,7 +426,6 @@ enum PixelMacroEnum {
MacroPalette,
MacroAlphaTest,
MacroClipInside,
MacroNearestWrapFix,
MacroDithering
};
@ -449,14 +445,13 @@ static D3D_SHADER_MACRO PixelMacros[]
{ "pp_Palette", "0" },
{ "cp_AlphaTest", "0" },
{ "pp_ClipInside", "0" },
{ "NearestWrapFix", "0" },
{ "DITHERING", "0" },
{ nullptr, nullptr }
};
const ComPtr<ID3D11PixelShader>& DX11Shaders::getShader(bool pp_Texture, bool pp_UseAlpha, bool pp_IgnoreTexA, u32 pp_ShadInstr,
bool pp_Offset, u32 pp_FogCtrl, bool pp_BumpMap, bool fog_clamping,
bool trilinear, bool palette, bool gouraud, bool alphaTest, bool clipInside, bool nearestWrapFix, bool dithering)
bool trilinear, bool palette, bool gouraud, bool alphaTest, bool clipInside, bool dithering)
{
bool divPosZ = !settings.platform.isNaomi2() && config::NativeDepthInterpolation;
const u32 hash = (int)pp_Texture
@ -472,9 +467,8 @@ const ComPtr<ID3D11PixelShader>& DX11Shaders::getShader(bool pp_Texture, bool pp
| (gouraud << 12)
| (alphaTest << 13)
| (clipInside << 14)
| (nearestWrapFix << 15)
| (divPosZ << 16)
| (dithering << 17);
| (divPosZ << 15)
| (dithering << 16);
auto& shader = shaders[hash];
if (shader == nullptr)
{
@ -493,7 +487,6 @@ const ComPtr<ID3D11PixelShader>& DX11Shaders::getShader(bool pp_Texture, bool pp
PixelMacros[MacroPalette].Definition = MacroValues[palette];
PixelMacros[MacroAlphaTest].Definition = MacroValues[alphaTest];
PixelMacros[MacroClipInside].Definition = MacroValues[clipInside];
PixelMacros[MacroNearestWrapFix].Definition = MacroValues[nearestWrapFix];
PixelMacros[MacroDivPosZ].Definition = MacroValues[divPosZ];
PixelMacros[MacroDithering].Definition = MacroValues[dithering];

View File

@ -52,7 +52,7 @@ public:
const ComPtr<ID3D11PixelShader>& getShader(bool pp_Texture, bool pp_UseAlpha, bool pp_IgnoreTexA, u32 pp_ShadInstr,
bool pp_Offset, u32 pp_FogCtrl, bool pp_BumpMap, bool fog_clamping, bool trilinear, bool palette, bool gouraud,
bool alphaTest, bool clipInside, bool nearestWrapFix, bool dithering);
bool alphaTest, bool clipInside, bool dithering);
const ComPtr<ID3D11VertexShader>& getVertexShader(bool gouraud, bool naomi2);
const ComPtr<ID3D11PixelShader>& getModVolShader();
const ComPtr<ID3D11VertexShader>& getMVVertexShader(bool naomi2);

View File

@ -67,9 +67,9 @@ private:
class Samplers
{
public:
ComPtr<ID3D11SamplerState> getSampler(bool linear, bool clampU = true, bool clampV = true, bool flipU = false, bool flipV = false)
ComPtr<ID3D11SamplerState> getSampler(bool linear, bool clampU = true, bool clampV = true, bool flipU = false, bool flipV = false, bool punchThrough = false)
{
int hash = (int)clampU | ((int)clampV << 1) | ((int)flipU << 2) | ((int)flipV << 3) | ((int)linear << 4);
int hash = (int)clampU | ((int)clampV << 1) | ((int)flipU << 2) | ((int)flipV << 3) | ((int)linear << 4) | ((int)punchThrough << 5);
auto& sampler = samplers[hash];
if (!sampler)
{
@ -77,19 +77,23 @@ public:
D3D11_SAMPLER_DESC desc{};
if (linear)
{
if (config::AnisotropicFiltering > 1)
if (punchThrough)
desc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
else if (config::AnisotropicFiltering > 1)
desc.Filter = D3D11_FILTER_ANISOTROPIC;
else
desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
}
else
else {
desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
}
desc.AddressU = clampU ? D3D11_TEXTURE_ADDRESS_CLAMP : flipU ? D3D11_TEXTURE_ADDRESS_MIRROR : D3D11_TEXTURE_ADDRESS_WRAP;
desc.AddressV = clampV ? D3D11_TEXTURE_ADDRESS_CLAMP : flipV ? D3D11_TEXTURE_ADDRESS_MIRROR : D3D11_TEXTURE_ADDRESS_WRAP;
desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
desc.ComparisonFunc = D3D11_COMPARISON_NEVER;
desc.MaxAnisotropy = config::AnisotropicFiltering;
desc.MaxLOD = D3D11_FLOAT32_MAX;
desc.MipLODBias = -1.5f;
createSampler(&desc, &sampler.get());
}
return sampler;

View File

@ -188,7 +188,6 @@ struct DX11OITRenderer : public DX11Renderer
gp->pcw.Gouraud,
useTexture,
clipmode == TileClipping::Inside,
false,
two_volumes_mode,
pass);
}
@ -212,7 +211,6 @@ struct DX11OITRenderer : public DX11Renderer
gp->pcw.Gouraud,
Type == ListType_Punch_Through,
clipmode == TileClipping::Inside,
gp->pcw.Texture && gp->tsp.FilterMode == 0 && !gp->tsp.ClampU && !gp->tsp.ClampV && !gp->tsp.FlipU && !gp->tsp.FlipV,
two_volumes_mode,
pass);
@ -296,7 +294,7 @@ struct DX11OITRenderer : public DX11Renderer
linearFiltering = false;
else
linearFiltering = true;
auto sampler = samplers->getSampler(linearFiltering, tsp.ClampU, tsp.ClampV, tsp.FlipU, tsp.FlipV);
auto sampler = samplers->getSampler(linearFiltering, tsp.ClampU, tsp.ClampV, tsp.FlipU, tsp.FlipV, Type == ListType_Punch_Through);
deviceContext->PSSetSamplers(slot, 1, &sampler.get());
}
}

View File

@ -449,9 +449,6 @@ PSO main(in VertexIn inpix)
uv /= inpix.uv.w;
#endif
#if NearestWrapFix == 1
uv = min(fmod(uv, 1.f), 0.9997f);
#endif
float4 texcol;
#if pp_TwoVolumes == 1
if (area1)
@ -846,7 +843,6 @@ enum PixelMacroEnum {
MacroPalette,
MacroAlphaTest,
MacroClipInside,
MacroNearestWrapFix,
MacroPass
};
@ -866,14 +862,13 @@ static D3D_SHADER_MACRO PixelMacros[]
{ "pp_Palette", "0" },
{ "cp_AlphaTest", "0" },
{ "pp_ClipInside", "0" },
{ "NearestWrapFix", "0" },
{ "PASS", "0" },
{ nullptr, nullptr }
};
const ComPtr<ID3D11PixelShader>& DX11OITShaders::getShader(bool pp_Texture, bool pp_UseAlpha, bool pp_IgnoreTexA, u32 pp_ShadInstr,
bool pp_Offset, u32 pp_FogCtrl, bool pp_BumpMap, bool fog_clamping,
bool palette, bool gouraud, bool alphaTest, bool clipInside, bool nearestWrapFix, bool twoVolumes, Pass pass)
bool palette, bool gouraud, bool alphaTest, bool clipInside, bool twoVolumes, Pass pass)
{
bool divPosZ = !settings.platform.isNaomi2() && config::NativeDepthInterpolation;
const u32 hash = (int)pp_Texture
@ -888,10 +883,9 @@ const ComPtr<ID3D11PixelShader>& DX11OITShaders::getShader(bool pp_Texture, bool
| ((int)gouraud << 11)
| ((int)alphaTest << 12)
| ((int)clipInside << 13)
| ((int)nearestWrapFix << 14)
| ((int)twoVolumes << 15)
| ((int)pass << 16)
| ((int)divPosZ << 18);
| ((int)twoVolumes << 14)
| ((int)pass << 15)
| ((int)divPosZ << 17);
auto& shader = shaders[hash];
if (shader == nullptr)
{
@ -910,7 +904,6 @@ const ComPtr<ID3D11PixelShader>& DX11OITShaders::getShader(bool pp_Texture, bool
PixelMacros[MacroPalette].Definition = MacroValues[palette];
PixelMacros[MacroAlphaTest].Definition = MacroValues[alphaTest];
PixelMacros[MacroClipInside].Definition = MacroValues[clipInside];
PixelMacros[MacroNearestWrapFix].Definition = MacroValues[nearestWrapFix];
PixelMacros[MacroTwoVolumes].Definition = MacroValues[twoVolumes];
PixelMacros[MacroDivPosZ].Definition = MacroValues[divPosZ];
PixelMacros[MacroPass].Definition = MacroValues[pass];

View File

@ -34,7 +34,7 @@ public:
const ComPtr<ID3D11PixelShader>& getShader(bool pp_Texture, bool pp_UseAlpha, bool pp_IgnoreTexA, u32 pp_ShadInstr,
bool pp_Offset, u32 pp_FogCtrl, bool pp_BumpMap, bool fog_clamping,
bool palette, bool gouraud, bool alphaTest, bool clipInside, bool nearestWrapFix, bool twoVolumes, Pass pass);
bool palette, bool gouraud, bool alphaTest, bool clipInside, bool twoVolumes, Pass pass);
const ComPtr<ID3D11VertexShader>& getVertexShader(bool gouraud, bool naomi2, bool positionOnly, bool lightOn, bool twoVolumes = true);
const ComPtr<ID3D11PixelShader>& getModVolShader();
const ComPtr<ID3D11VertexShader>& getMVVertexShader(bool naomi2);

View File

@ -440,9 +440,17 @@ void D3DRenderer::setGPState(const PolyParam *gp)
//bilinear filtering
devCache.SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
devCache.SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
devCache.SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); // LINEAR for Trilinear filtering
devCache.SetSamplerState(0, D3DSAMP_MAXANISOTROPY, std::min(maxAnisotropy, (int)config::AnisotropicFiltering));
if (Type == ListType_Punch_Through) {
devCache.SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT);
devCache.SetSamplerState(0, D3DSAMP_MAXANISOTROPY, 1);
}
else {
devCache.SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); // LINEAR for Trilinear filtering
devCache.SetSamplerState(0, D3DSAMP_MAXANISOTROPY, std::min(maxAnisotropy, (int)config::AnisotropicFiltering));
}
}
float bias = -1.f;
devCache.SetSamplerState(0, D3DSAMP_MIPMAPLODBIAS, *(DWORD *)&bias);
}
// Apparently punch-through polys support blending, or at least some combinations

View File

@ -244,7 +244,7 @@ static void SetGPState(const PolyParam* gp)
{
//bilinear filtering
//PowerVR supports also trilinear via two passes, but we ignore that for now
glSamplerParameteri(texSamplers[i], GL_TEXTURE_MIN_FILTER, mipmapped ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
glSamplerParameteri(texSamplers[i], GL_TEXTURE_MIN_FILTER, mipmapped && Type != ListType_Punch_Through ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
glSamplerParameteri(texSamplers[i], GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}

View File

@ -199,7 +199,7 @@ void SetGPState(const PolyParam* gp,u32 cflip=0)
{
//bilinear filtering
//PowerVR supports also trilinear via two passes, but we ignore that for now
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mipmapped ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mipmapped && Type != ListType_Punch_Through ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
@ -215,7 +215,7 @@ void SetGPState(const PolyParam* gp,u32 cflip=0)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY,
std::min<float>(config::AnisotropicFiltering, gl.max_anisotropy));
// Set the recommended minification filter for best results
if (mipmapped)
if (mipmapped && Type != ListType_Punch_Through)
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
}
else

View File

@ -225,7 +225,8 @@ void Drawer::DrawPoly(const vk::CommandBuffer& cmdBuffer, u32 listType, bool sor
break;
}
}
descriptorSets.bindPerPolyDescriptorSets(cmdBuffer, poly, index, *GetMainBuffer(0)->buffer, offset, offsets.lightsOffset);
descriptorSets.bindPerPolyDescriptorSets(cmdBuffer, poly, index, *GetMainBuffer(0)->buffer, offset, offsets.lightsOffset,
listType == ListType_Punch_Through);
}
cmdBuffer.drawIndexed(count, 1, first, 0, 0);
}

View File

@ -111,7 +111,8 @@ void OITDrawer::DrawPoly(const vk::CommandBuffer& cmdBuffer, u32 listType, bool
break;
}
}
descriptorSets.bindPerPolyDescriptorSets(cmdBuffer, poly, polyNumber, *GetMainBuffer(0)->buffer, offset, offsets.lightsOffset);
descriptorSets.bindPerPolyDescriptorSets(cmdBuffer, poly, polyNumber, *GetMainBuffer(0)->buffer, offset, offsets.lightsOffset,
listType == ListType_Punch_Through);
}
vk::Pipeline pipeline = pipelineManager->GetPipeline(listType, autosort, poly, pass, gpuPalette);

View File

@ -154,7 +154,7 @@ public:
}
void bindPerPolyDescriptorSets(vk::CommandBuffer cmdBuffer, const PolyParam& poly, int polyNumber, vk::Buffer buffer,
vk::DeviceSize uniformOffset, vk::DeviceSize lightOffset)
vk::DeviceSize uniformOffset, vk::DeviceSize lightOffset, bool punchThrough)
{
vk::DescriptorSet perPolyDescSet;
auto it = perPolyDescSets.find(&poly);
@ -166,14 +166,14 @@ public:
vk::DescriptorImageInfo imageInfo0;
if (poly.texture != nullptr)
{
imageInfo0 = vk::DescriptorImageInfo{ samplerManager->GetSampler(poly.tsp), ((Texture *)poly.texture)->GetReadOnlyImageView(),
imageInfo0 = vk::DescriptorImageInfo{ samplerManager->GetSampler(poly.tsp, punchThrough), ((Texture *)poly.texture)->GetReadOnlyImageView(),
vk::ImageLayout::eShaderReadOnlyOptimal };
writeDescriptorSets.emplace_back(perPolyDescSet, 0, 0, vk::DescriptorType::eCombinedImageSampler, imageInfo0);
}
vk::DescriptorImageInfo imageInfo1;
if (poly.texture1 != nullptr)
{
imageInfo1 = vk::DescriptorImageInfo{ samplerManager->GetSampler(poly.tsp1), ((Texture *)poly.texture1)->GetReadOnlyImageView(),
imageInfo1 = vk::DescriptorImageInfo{ samplerManager->GetSampler(poly.tsp1, punchThrough), ((Texture *)poly.texture1)->GetReadOnlyImageView(),
vk::ImageLayout::eShaderReadOnlyOptimal };
writeDescriptorSets.emplace_back(perPolyDescSet, 1, 0, vk::DescriptorType::eCombinedImageSampler, imageInfo1);
}

View File

@ -77,7 +77,7 @@ public:
}
void bindPerPolyDescriptorSets(vk::CommandBuffer cmdBuffer, const PolyParam& poly, int polyNumber, vk::Buffer buffer,
vk::DeviceSize uniformOffset, vk::DeviceSize lightOffset)
vk::DeviceSize uniformOffset, vk::DeviceSize lightOffset, bool punchThrough)
{
vk::DescriptorSet perPolyDescSet;
auto it = perPolyDescSets.find(&poly);
@ -89,7 +89,7 @@ public:
vk::DescriptorImageInfo imageInfo;
if (poly.texture != nullptr)
{
imageInfo = vk::DescriptorImageInfo(samplerManager->GetSampler(poly.tsp),
imageInfo = vk::DescriptorImageInfo(samplerManager->GetSampler(poly.tsp, punchThrough),
((Texture *)poly.texture)->GetReadOnlyImageView(), vk::ImageLayout::eShaderReadOnlyOptimal);
writeDescriptorSets.emplace_back(perPolyDescSet, 0, 0, vk::DescriptorType::eCombinedImageSampler, imageInfo);
}

View File

@ -99,9 +99,9 @@ public:
samplers.clear();
}
vk::Sampler GetSampler(TSP tsp)
vk::Sampler GetSampler(TSP tsp, bool punchThrough = false)
{
const u32 samplerHash = tsp.full & TSP_Mask; // MipMapD, FilterMode, ClampU, ClampV, FlipU, FlipV
const u32 samplerHash = (tsp.full & TSP_Mask) | punchThrough; // MipMapD, FilterMode, ClampU, ClampV, FlipU, FlipV
const auto& it = samplers.find(samplerHash);
if (it != samplers.end())
return it->second.get();
@ -119,21 +119,21 @@ public:
: tsp.FlipV ? vk::SamplerAddressMode::eMirroredRepeat : vk::SamplerAddressMode::eRepeat;
const bool anisotropicFiltering = config::AnisotropicFiltering > 1 && VulkanContext::Instance()->SupportsSamplerAnisotropy()
&& filter == vk::Filter::eLinear;
&& filter == vk::Filter::eLinear && !punchThrough;
#ifndef __APPLE__
const float mipLodBias = D_Adjust_LoD_Bias[tsp.MipMapD];
const float mipLodBias = D_Adjust_LoD_Bias[tsp.MipMapD] - 1.f;
#else
// not supported by metal
const float mipLodBias = 0;
#endif
const vk::SamplerMipmapMode mipmapMode = filter == vk::Filter::eLinear ? vk::SamplerMipmapMode::eLinear : vk::SamplerMipmapMode::eNearest;
const vk::SamplerMipmapMode mipmapMode = !punchThrough && filter == vk::Filter::eLinear ? vk::SamplerMipmapMode::eLinear : vk::SamplerMipmapMode::eNearest;
return samplers.emplace(
std::make_pair(samplerHash, VulkanContext::Instance()->GetDevice().createSamplerUnique(
vk::SamplerCreateInfo(vk::SamplerCreateFlags(), filter, filter,
mipmapMode, uRepeat, vRepeat, vk::SamplerAddressMode::eClampToEdge, mipLodBias,
anisotropicFiltering, std::min((float)config::AnisotropicFiltering, VulkanContext::Instance()->GetMaxSamplerAnisotropy()),
false, vk::CompareOp::eNever,
0.0f, 256.0f, vk::BorderColor::eFloatOpaqueBlack)))).first->second.get();
0.0f, vk::LodClampNone, vk::BorderColor::eFloatOpaqueBlack)))).first->second.get();
}
static const u32 TSP_Mask = 0x7ef00;