diff --git a/GPU/Common/ShaderUniforms.cpp b/GPU/Common/ShaderUniforms.cpp index d2525a5f5e..7bb4f6b92a 100644 --- a/GPU/Common/ShaderUniforms.cpp +++ b/GPU/Common/ShaderUniforms.cpp @@ -180,7 +180,14 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool useBuffe } } +void DirtyDepalIfPixelMappedChanged(bool pixelMapped) { + if (gstate_c.shaderDepalMode != ShaderDepalMode::OFF && pixelMapped != gstate_c.depalPixelMapped) { + gstate_c.Dirty(DIRTY_DEPAL); + } +} + uint32_t PackDepalBits(bool pixelMapped) { + gstate_c.depalPixelMapped = pixelMapped; const int indexMask = gstate.getClutIndexMask(); const int indexShift = gstate.getClutIndexShift(); const int indexOffset = gstate.getClutIndexStartPos() >> 4; diff --git a/GPU/Common/ShaderUniforms.h b/GPU/Common/ShaderUniforms.h index f387978d0d..e85fc16b83 100644 --- a/GPU/Common/ShaderUniforms.h +++ b/GPU/Common/ShaderUniforms.h @@ -110,6 +110,8 @@ void LightUpdateUniforms(UB_VS_Lights *ub, uint64_t dirtyUniforms); uint32_t PackLightControlBits(); uint32_t PackDepalBits(bool pixelMapped); +// Call before reading the dirty uniforms: the depal uniform depends on pixelMapped, which changes per draw. +void DirtyDepalIfPixelMappedChanged(bool pixelMapped); void UpdateFogCoef(const GEState &state, float fogCoef[2]); diff --git a/GPU/Common/SoftwareTransformCommon.cpp b/GPU/Common/SoftwareTransformCommon.cpp index 745c03c250..d8c43ded07 100644 --- a/GPU/Common/SoftwareTransformCommon.cpp +++ b/GPU/Common/SoftwareTransformCommon.cpp @@ -1095,6 +1095,29 @@ static bool ExpandRectangles(int vertexCount, int &numDecodedVerts, int vertsSiz } } + // Shader depal filters by hand, with four texel and four palette lookups per pixel, which is a lot for a slow + // GPU on a full-screen pass (DBZ Tenkaichi Tag Team has two per frame). When every pixel center lands on a texel + // center (1:1 in both directions, at a whole-texel offset), that gives the nearest texel anyway, so report the + // draw as pixel mapped, which turns the filter off. Unlike the check above, this changes nothing on screen. + bool depalPixelMapped = false; + if (!pixelMapped && throughmode && gstate_c.shaderDepalMode != ShaderDepalMode::OFF && + spriteBorderFixL == 0.0f && spriteBorderFixR == 0.0f && spriteBorderFixT == 0.0f && spriteBorderFixB == 0.0f) { + depalPixelMapped = true; + for (int i = 0; i < vertexCount; i += 2) { + const TransformedVertex &transVtxTL = transformed[indsIn[i + 0]]; + const TransformedVertex &transVtxBR = transformed[indsIn[i + 1]]; + const float dx = transVtxBR.x - transVtxTL.x; + const float dy = transVtxBR.y - transVtxTL.y; + const float offsetU = transVtxTL.u - transVtxTL.x; + const float offsetV = transVtxTL.v - transVtxTL.y; + if (dx <= 0 || dy <= 0 || transVtxBR.u - transVtxTL.u != dx || transVtxBR.v - transVtxTL.v != dy || + offsetU != floorf(offsetU) || offsetV != floorf(offsetV)) { + depalPixelMapped = false; + break; + } + } + } + // Like the software renderer's ProcessRect: rectangles aren't clipped, one with a vertex behind the camera // or outside the screen range is dropped whole. With depth clamp, a vertex past the near plane is exempt @@ -1213,7 +1236,7 @@ static bool ExpandRectangles(int vertexCount, int &numDecodedVerts, int vertsSiz } numDecodedVerts = outVerts; inds = newInds; - *pixelMappedExactly = pixelMapped; + *pixelMappedExactly = pixelMapped || depalPixelMapped; *drawIndexCount = indsOut - newInds; return true; } diff --git a/GPU/D3D11/ShaderManagerD3D11.cpp b/GPU/D3D11/ShaderManagerD3D11.cpp index ef0bfc08f3..b7db0c056c 100644 --- a/GPU/D3D11/ShaderManagerD3D11.cpp +++ b/GPU/D3D11/ShaderManagerD3D11.cpp @@ -149,6 +149,7 @@ void ShaderManagerD3D11::ClearShaders() { } uint64_t ShaderManagerD3D11::UpdateUniforms(bool useBufferedRendering, bool pixelMapped) { + DirtyDepalIfPixelMappedChanged(pixelMapped); uint64_t dirty = gstate_c.GetDirtyUniforms(); if (dirty != 0) { D3D11_MAPPED_SUBRESOURCE map; diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index e64ab6d1dd..5d1191af4f 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -717,6 +717,7 @@ Shader *ShaderManagerGLES::ApplyVertexShader(bool useHWTransform, u32 vertexType } LinkedShader *ShaderManagerGLES::ApplyFragmentShader(VShaderID VSID, Shader *vs, const ComputedPipelineState &pipelineState, ClipInfoFlags clipInfoFlags, bool pixelMapped) { + DirtyDepalIfPixelMappedChanged(pixelMapped); uint64_t dirty = gstate_c.GetDirtyUniforms(); if (dirty) { if (lastShader_) diff --git a/GPU/GPUState.h b/GPU/GPUState.h index 3cdf60b567..b22c2fec11 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -682,6 +682,8 @@ public: ShaderDepalMode shaderDepalMode; GEBufferFormat depalTextureFormat; + // The pixelMapped that the depal uniform was last packed with (it turns bilinear depal off). + bool depalPixelMapped; void SetShaderDepal(ShaderDepalMode mode, GEBufferFormat texFormat = {}) { if (mode != shaderDepalMode || (mode != ShaderDepalMode::OFF && texFormat != depalTextureFormat)) { diff --git a/GPU/Metal/ShaderManagerMetal.mm b/GPU/Metal/ShaderManagerMetal.mm index 9cecf3bf9c..068b495d69 100644 --- a/GPU/Metal/ShaderManagerMetal.mm +++ b/GPU/Metal/ShaderManagerMetal.mm @@ -86,6 +86,7 @@ void ShaderManagerMetal::ClearShaders() { } uint64_t ShaderManagerMetal::UpdateUniforms(bool useBufferedRendering, bool pixelMapped) { + DirtyDepalIfPixelMappedChanged(pixelMapped); uint64_t dirty = gstate_c.GetDirtyUniforms(); if (dirty != 0) { if (dirty & DIRTY_BASE_UNIFORMS) diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index 6bcc8b4c00..7aec3872c7 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -218,6 +218,7 @@ void ShaderManagerVulkan::ClearShaders() { } uint64_t ShaderManagerVulkan::UpdateUniforms(bool useBufferedRendering, bool pixelMapped) { + DirtyDepalIfPixelMappedChanged(pixelMapped); uint64_t dirty = gstate_c.GetDirtyUniforms(); if (dirty != 0) { if (dirty & DIRTY_BASE_UNIFORMS)