mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-12 17:19:28 +01:00
Depal: Skip the bilinear filter on exactly pixel-mapped draws
Shader depal filters by hand, with four texel and four palette lookups per pixel. DBZ Tenkaichi Tag Team does two full-screen through-mode depal passes per frame, and on a PowerVR GE8320 (Galaxy A21) they took DBZ from 8.3 to 14.4 seconds per 10 emulated seconds once #22204 had made framebuffer depal work again. They're 1:1, where bilinear gives the nearest texel anyway, so rectangles that put every pixel center on a texel center (equal UV and position deltas, at a whole-texel offset) now count as pixel mapped for depal, which turns the filter off. Unlike the smart 2D filtering check, this changes nothing on screen: the frame dumps render identically on Vulkan and OpenGL, 69 of them through this path. DBZ is down to 9.2s. The depal uniform is also repacked when pixelMapped changes between draws, rather than only when the texture is reapplied. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6f90e74176
commit
8be9cbbe54
8 files changed
+39
-1
No files matched your search
@@ -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;
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_)
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user