Merge pull request #21813 from hrydgard/optimize-shader-depal

Optimize shader-based palette lookups
This commit is contained in:
Henrik Rydgård
2026-06-11 10:36:49 +02:00
committed by GitHub
11 changed files with 169 additions and 110 deletions
-1
View File
@@ -9,7 +9,6 @@
const char * const vulkan_glsl_preamble_fs =
"#extension GL_ARB_separate_shader_objects : enable\n"
"#extension GL_ARB_shading_language_420pack : enable\n"
"#extension GL_ARB_conservative_depth : enable\n"
"#extension GL_ARB_shader_image_load_store : enable\n"
"#define splat3(x) vec3(x)\n"
"#define DISCARD discard\n"
+86 -56
View File
@@ -70,6 +70,8 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
bool texture3D = id.Bit(FS_BIT_3D_TEXTURE);
bool arrayTexture = id.Bit(FS_BIT_SAMPLE_ARRAY_TEXTURE);
bool forceDepthWritesOff = id.Bit(FS_BIT_DEPTH_TEST_NEVER);
bool useDiscardStencilBugWorkaround = id.Bit(FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL) && !forceDepthWritesOff;
ReplaceAlphaType stencilToAlpha = static_cast<ReplaceAlphaType>(id.Bits(FS_BIT_STENCIL_TO_ALPHA, 2));
@@ -87,6 +89,10 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
if (gl_extensions.OES_texture_3D && texture3D) {
extensions.push_back("#extension GL_OES_texture_3D: enable");
}
if (useDiscardStencilBugWorkaround) {
// The only use of layout (depth_...)
extensions.push_back("#extension GL_ARB_conservative_depth : enable\n");
}
}
ShaderWriterFlags flags = ShaderWriterFlags::NONE;
@@ -129,6 +135,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
}
ShaderDepalMode shaderDepalMode = (ShaderDepalMode)id.Bits(FS_BIT_SHADER_DEPAL_MODE, 2);
GEBufferFormat shaderDepalFmt = (GEBufferFormat)(id.Bits(FS_BIT_SHADER_DEPAL_FORMAT, 3));
if (texture3D) {
shaderDepalMode = ShaderDepalMode::OFF;
}
@@ -159,9 +166,6 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
shading = doFlatShading ? "flat" : "";
}
bool forceDepthWritesOff = id.Bit(FS_BIT_DEPTH_TEST_NEVER);
bool useDiscardStencilBugWorkaround = id.Bit(FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL) && !forceDepthWritesOff;
GEBlendSrcFactor replaceBlendFuncA = (GEBlendSrcFactor)id.Bits(FS_BIT_BLENDFUNC_A, 4);
GEBlendDstFactor replaceBlendFuncB = (GEBlendDstFactor)id.Bits(FS_BIT_BLENDFUNC_B, 4);
@@ -689,74 +693,100 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu
WRITE(p, " } else {\n");
WRITE(p, " uv_round = uv;\n");
WRITE(p, " }\n");
p.C(" highp vec4 t = ").SampleTexture2D("tex", "uv_round").C(";\n");
p.C(" highp vec4 t1 = ").SampleTexture2DOffset("tex", "uv_round", 1, 0).C(";\n");
p.C(" highp vec4 t2 = ").SampleTexture2DOffset("tex", "uv_round", 0, 1).C(";\n");
p.C(" highp vec4 t3 = ").SampleTexture2DOffset("tex", "uv_round", 1, 1).C(";\n");
WRITE(p, " uint depalMask = (u_depal_mask_shift_off_fmt & 0xFFu);\n");
WRITE(p, " uint depalShift = (u_depal_mask_shift_off_fmt >> 0x8u) & 0xFFu;\n");
WRITE(p, " uint depalOffset = ((u_depal_mask_shift_off_fmt >> 0x10u) & 0xFFu) << 0x4u;\n");
WRITE(p, " uint depalFmt = (u_depal_mask_shift_off_fmt >> 0x18u) & 0x3u;\n");
WRITE(p, " uvec4 col; uint index0; uint index1; uint index2; uint index3;\n");
WRITE(p, " switch (int(depalFmt)) {\n"); // We might want to include fmt in the shader ID if this is a performance issue.
WRITE(p, " case 0:\n"); // 565
WRITE(p, " col = uvec4(t.rgb * vec3(31.99, 63.99, 31.99), 0);\n");
WRITE(p, " index0 = (col.b << 0xBu) | (col.g << 0x5u) | (col.r);\n");
WRITE(p, " if (bilinear) {\n");
WRITE(p, " col = uvec4(t1.rgb * vec3(31.99, 63.99, 31.99), 0);\n");
WRITE(p, " index1 = (col.b << 0xBu) | (col.g << 0x5u) | (col.r);\n");
WRITE(p, " col = uvec4(t2.rgb * vec3(31.99, 63.99, 31.99), 0);\n");
WRITE(p, " index2 = (col.b << 0xBu) | (col.g << 0x5u) | (col.r);\n");
WRITE(p, " col = uvec4(t3.rgb * vec3(31.99, 63.99, 31.99), 0);\n");
WRITE(p, " index3 = (col.b << 0xBu) | (col.g << 0x5u) | (col.r);\n");
WRITE(p, " }\n");
WRITE(p, " break;\n");
WRITE(p, " case 1:\n"); // 5551
WRITE(p, " col = uvec4(t.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n");
WRITE(p, " index0 = (col.a << 0xFu) | (col.b << 0xAu) | (col.g << 0x5u) | (col.r);\n");
WRITE(p, " if (bilinear) {\n");
WRITE(p, " col = uvec4(t1.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n");
WRITE(p, " index1 = (col.a << 0xFu) | (col.b << 0xAu) | (col.g << 0x5u) | (col.r);\n");
WRITE(p, " col = uvec4(t2.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n");
WRITE(p, " index2 = (col.a << 0xFu) | (col.b << 0xAu) | (col.g << 0x5u) | (col.r);\n");
WRITE(p, " col = uvec4(t3.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n");
WRITE(p, " index3 = (col.a << 0xFu) | (col.b << 0xAu) | (col.g << 0x5u) | (col.r);\n");
WRITE(p, " }\n");
WRITE(p, " break;\n");
WRITE(p, " case 2:\n"); // 4444
WRITE(p, " col = uvec4(t.rgba * 15.99);\n");
WRITE(p, " index0 = (col.a << 0xCu) | (col.b << 0x8u) | (col.g << 0x4u) | (col.r);\n");
WRITE(p, " if (bilinear) {\n");
WRITE(p, " col = uvec4(t1.rgba * 15.99);\n");
WRITE(p, " index1 = (col.a << 0xCu) | (col.b << 0x8u) | (col.g << 0x4u) | (col.r);\n");
WRITE(p, " col = uvec4(t2.rgba * 15.99);\n");
WRITE(p, " index2 = (col.a << 0xCu) | (col.b << 0x8u) | (col.g << 0x4u) | (col.r);\n");
WRITE(p, " col = uvec4(t3.rgba * 15.99);\n");
WRITE(p, " index3 = (col.a << 0xCu) | (col.b << 0x8u) | (col.g << 0x4u) | (col.r);\n");
WRITE(p, " }\n");
WRITE(p, " break;\n");
WRITE(p, " case 3:\n"); // 8888
WRITE(p, " col = uvec4(t.rgba * 255.99);\n");
WRITE(p, " index0 = (col.a << 0x18u) | (col.b << 0x10u) | (col.g << 0x8u) | (col.r);\n");
WRITE(p, " if (bilinear) {\n");
WRITE(p, " col = uvec4(t1.rgba * 255.99);\n");
WRITE(p, " index1 = (col.a << 0x18u) | (col.b << 0x10u) | (col.g << 0x8u) | (col.r);\n");
WRITE(p, " col = uvec4(t2.rgba * 255.99);\n");
WRITE(p, " index2 = (col.a << 0x18u) | (col.b << 0x10u) | (col.g << 0x8u) | (col.r);\n");
WRITE(p, " col = uvec4(t3.rgba * 255.99);\n");
WRITE(p, " index3 = (col.a << 0x18u) | (col.b << 0x10u) | (col.g << 0x8u) | (col.r);\n");
WRITE(p, " }\n");
WRITE(p, " break;\n");
WRITE(p, " };\n");
switch (shaderDepalFmt) {
case GE_FORMAT_565:
WRITE(p, " col = uvec4(t.rgb * vec3(31.99, 63.99, 31.99), 0);\n");
WRITE(p, " index0 = (col.b << 0xBu) | (col.g << 0x5u) | (col.r);\n");
WRITE(p, " if (bilinear) {\n");
WRITE(p, " col = uvec4(t1.rgb * vec3(31.99, 63.99, 31.99), 0);\n");
WRITE(p, " index1 = (col.b << 0xBu) | (col.g << 0x5u) | (col.r);\n");
WRITE(p, " col = uvec4(t2.rgb * vec3(31.99, 63.99, 31.99), 0);\n");
WRITE(p, " index2 = (col.b << 0xBu) | (col.g << 0x5u) | (col.r);\n");
WRITE(p, " col = uvec4(t3.rgb * vec3(31.99, 63.99, 31.99), 0);\n");
WRITE(p, " index3 = (col.b << 0xBu) | (col.g << 0x5u) | (col.r);\n");
WRITE(p, " }\n");
break;
case GE_FORMAT_5551:
WRITE(p, " col = uvec4(t.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n");
WRITE(p, " index0 = (col.a << 0xFu) | (col.b << 0xAu) | (col.g << 0x5u) | (col.r);\n");
WRITE(p, " if (bilinear) {\n");
WRITE(p, " col = uvec4(t1.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n");
WRITE(p, " index1 = (col.a << 0xFu) | (col.b << 0xAu) | (col.g << 0x5u) | (col.r);\n");
WRITE(p, " col = uvec4(t2.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n");
WRITE(p, " index2 = (col.a << 0xFu) | (col.b << 0xAu) | (col.g << 0x5u) | (col.r);\n");
WRITE(p, " col = uvec4(t3.rgba * vec4(31.99, 31.99, 31.99, 1.0));\n");
WRITE(p, " index3 = (col.a << 0xFu) | (col.b << 0xAu) | (col.g << 0x5u) | (col.r);\n");
WRITE(p, " }\n");
break;
case GE_FORMAT_4444:
WRITE(p, " col = uvec4(t.rgba * 15.99);\n");
WRITE(p, " index0 = (col.a << 0xCu) | (col.b << 0x8u) | (col.g << 0x4u) | (col.r);\n");
WRITE(p, " if (bilinear) {\n");
WRITE(p, " col = uvec4(t1.rgba * 15.99);\n");
WRITE(p, " index1 = (col.a << 0xCu) | (col.b << 0x8u) | (col.g << 0x4u) | (col.r);\n");
WRITE(p, " col = uvec4(t2.rgba * 15.99);\n");
WRITE(p, " index2 = (col.a << 0xCu) | (col.b << 0x8u) | (col.g << 0x4u) | (col.r);\n");
WRITE(p, " col = uvec4(t3.rgba * 15.99);\n");
WRITE(p, " index3 = (col.a << 0xCu) | (col.b << 0x8u) | (col.g << 0x4u) | (col.r);\n");
WRITE(p, " }\n");
break;
case GE_FORMAT_8888:
WRITE(p, " col = uvec4(t.rgba * 255.99);\n");
WRITE(p, " index0 = (col.a << 0x18u) | (col.b << 0x10u) | (col.g << 0x8u) | (col.r);\n");
WRITE(p, " if (bilinear) {\n");
WRITE(p, " col = uvec4(t1.rgba * 255.99);\n");
WRITE(p, " index1 = (col.a << 0x18u) | (col.b << 0x10u) | (col.g << 0x8u) | (col.r);\n");
WRITE(p, " col = uvec4(t2.rgba * 255.99);\n");
WRITE(p, " index2 = (col.a << 0x18u) | (col.b << 0x10u) | (col.g << 0x8u) | (col.r);\n");
WRITE(p, " col = uvec4(t3.rgba * 255.99);\n");
WRITE(p, " index3 = (col.a << 0x18u) | (col.b << 0x10u) | (col.g << 0x8u) | (col.r);\n");
WRITE(p, " }\n");
break;
case GE_FORMAT_CLUT8:
WRITE(p, " index0 = uint(t.r * 255.99);\n");
WRITE(p, " if (bilinear) {\n");
WRITE(p, " index1 = uint(t1.r * 255.99);\n");
WRITE(p, " index2 = uint(t2.r * 255.99);\n");
WRITE(p, " index3 = uint(t3.r * 255.99);\n");
WRITE(p, " }\n");
break;
case GE_FORMAT_DEPTH16:
WRITE(p, " index0 = uint(t.r * 65535.99);\n");
WRITE(p, " if (bilinear) {\n");
WRITE(p, " index1 = uint(t1.r * 65535.99);\n");
WRITE(p, " index2 = uint(t2.r * 65535.99);\n");
WRITE(p, " index3 = uint(t3.r * 65535.99);\n");
WRITE(p, " }\n");
break;
default:
// Invalid, but the unit test can produce this inadvertently.
WRITE(p, " index0 = 0u;\n");
WRITE(p, " index1 = 0u;\n");
WRITE(p, " index2 = 0u;\n");
WRITE(p, " index3 = 0u;\n");
break;
}
WRITE(p, " index0 = ((index0 >> depalShift) & depalMask) | depalOffset;\n");
p.C(" t = ").LoadTexture2D("pal", "ivec2(index0, 0)", 0).C(";\n");
WRITE(p, " if (bilinear && !(index0 == index1 && index1 == index2 && index2 == index3)) {\n");
WRITE(p, " index1 = ((index1 >> depalShift) & depalMask) | depalOffset;\n");
WRITE(p, " index2 = ((index2 >> depalShift) & depalMask) | depalOffset;\n");
WRITE(p, " index3 = ((index3 >> depalShift) & depalMask) | depalOffset;\n");
p.C(" t1 = ").LoadTexture2D("pal", "ivec2(index1, 0)", 0).C(";\n");
p.C(" t2 = ").LoadTexture2D("pal", "ivec2(index2, 0)", 0).C(";\n");
p.C(" t3 = ").LoadTexture2D("pal", "ivec2(index3, 0)", 0).C(";\n");
p.C(" t1 = ").LoadTexture2D("pal", "ivec2(index1, 0)", 0).C(";\n");
p.C(" t2 = ").LoadTexture2D("pal", "ivec2(index2, 0)", 0).C(";\n");
p.C(" t3 = ").LoadTexture2D("pal", "ivec2(index3, 0)", 0).C(";\n");
WRITE(p, " t = mix(t, t1, fraction.x);\n");
WRITE(p, " t2 = mix(t2, t3, fraction.x);\n");
WRITE(p, " t = mix(t, t2, fraction.y);\n");
+19 -6
View File
@@ -202,12 +202,6 @@ std::string FragmentShaderDesc(const FShaderID &id) {
if (id.Bit(FS_BIT_FLATSHADE)) desc << "Flat ";
if (id.Bit(FS_BIT_BGRA_TEXTURE)) desc << "BGRA ";
if (id.Bit(FS_BIT_DEPTH_TEST_NEVER)) desc << "DepthNever ";
switch ((ShaderDepalMode)id.Bits(FS_BIT_SHADER_DEPAL_MODE, 2)) {
case ShaderDepalMode::OFF: break;
case ShaderDepalMode::NORMAL: desc << "Depal "; break;
case ShaderDepalMode::SMOOTHED: desc << "SmoothDepal "; break;
case ShaderDepalMode::CLUT8_8888: desc << "CLUT8From8888Depal"; break;
}
if (id.Bit(FS_BIT_COLOR_WRITEMASK)) desc << "WriteMask ";
if (id.Bit(FS_BIT_SHADER_TEX_CLAMP)) {
desc << "TClamp";
@@ -275,6 +269,20 @@ std::string FragmentShaderDesc(const FShaderID &id) {
if (id.Bit(FS_BIT_USE_FRAMEBUFFER_FETCH)) desc << "(fetch)";
if (id.Bit(FS_BIT_MINMAX_DISCARD)) desc << "FragMinMaxDiscard ";
if (id.Bit(FS_BIT_DEPTH_CLAMP)) desc << "FragDepthClamp ";
const ShaderDepalMode depalMode = (ShaderDepalMode)id.Bits(FS_BIT_SHADER_DEPAL_MODE, 2);
switch (depalMode) {
case ShaderDepalMode::OFF: break;
case ShaderDepalMode::NORMAL: desc << "Depal(";
{
const GEBufferFormat shaderDepalFormat = (GEBufferFormat)id.Bits(FS_BIT_SHADER_DEPAL_FORMAT, 3);
desc << GeBufferFormatToString(shaderDepalFormat) << ") ";
break;
}
case ShaderDepalMode::SMOOTHED: desc << "SmoothDepal "; break;
case ShaderDepalMode::CLUT8_8888: desc << "CLUT8From8888Depal"; break;
}
return desc.str();
}
@@ -324,6 +332,10 @@ void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pip
bool enableTexAlpha = gstate.isTextureAlphaUsed();
ShaderDepalMode shaderDepalMode = gstate_c.shaderDepalMode;
GEBufferFormat shaderDepalFormat = {};
if (shaderDepalMode == ShaderDepalMode::NORMAL) {
shaderDepalFormat = gstate_c.depalTextureFormat;
}
bool colorWriteMask = pipelineState.maskState.applyFramebufferRead;
ReplaceBlendType replaceBlend = pipelineState.blendState.replaceBlend;
@@ -343,6 +355,7 @@ void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pip
}
id.SetBit(FS_BIT_BGRA_TEXTURE, gstate_c.bgraTexture);
id.SetBits(FS_BIT_SHADER_DEPAL_MODE, 2, (int)shaderDepalMode);
id.SetBits(FS_BIT_SHADER_DEPAL_FORMAT, 3, (int)shaderDepalFormat);
id.SetBit(FS_BIT_3D_TEXTURE, gstate_c.curTextureIs3D);
}
+1 -1
View File
@@ -88,7 +88,7 @@ enum FShaderBit : uint8_t {
FS_BIT_SHADER_TEX_CLAMP = 5,
FS_BIT_CLAMP_S = 6,
FS_BIT_CLAMP_T = 7,
// Free bits: 8-11
FS_BIT_SHADER_DEPAL_FORMAT = 8, // 3 bits (GEBufferFormat), connected to FS_BIT_SHADER_DEPAL_MODE
FS_BIT_ALPHA_TEST = 12,
FS_BIT_ALPHA_TEST_FUNC = 13, // 3 bits
FS_BIT_ALPHA_AGAINST_ZERO = 16,
+1 -1
View File
@@ -191,7 +191,7 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool useBuffe
int indexMask = gstate.getClutIndexMask();
int indexShift = gstate.getClutIndexShift();
int indexOffset = gstate.getClutIndexStartPos() >> 4;
int format = gstate_c.depalFramebufferFormat;
int format = gstate_c.depalTextureFormat;
uint32_t val = BytesToUint32(indexMask, indexShift, indexOffset, format);
// Poke in a bilinear filter flag in the top bit.
if (gstate.isMagnifyFilteringEnabled())
+37 -28
View File
@@ -1012,20 +1012,41 @@ static bool ExpandRectangles(int vertexCount, int &numDecodedVerts, int vertsSiz
}
bool pixelMapped = g_Config.bSmart2DTexFiltering && !gstate_c.textureIsVideo;
if (pixelMapped) {
// Check for pixel mapping. If we find pixel mapping, abandon spriteBorderFix.
for (int i = 0; i < vertexCount; i += 2) {
const TransformedVertex &transVtxTL = transformed[indsIn[i + 0]];
const TransformedVertex &transVtxBR = transformed[indsIn[i + 1]];
if (pixelMapped) {
float dx = transVtxBR.x - transVtxTL.x;
float dy = transVtxBR.y - transVtxTL.y;
float du = transVtxBR.u - transVtxTL.u;
float dv = transVtxBR.v - transVtxTL.v;
// NOTE: We will accept it as pixel mapped if only one dimension is stretched. This fixes dialog frames in FFI.
// Though, there could be false positives in other games due to this. Let's see if it is a problem...
if (dx <= 0 || dy <= 0 || (dx != du && dy != dv)) {
pixelMapped = false;
break;
}
}
}
}
float spriteBorderFixL = 0.0f;
float spriteBorderFixR = 0.0f;
float spriteBorderFixT = 0.0f;
float spriteBorderFixB = 0.0f;
float spriteBorderFix = PSP_CoreParameter().compat.flags().SpriteBorderFix;
if (spriteBorderFix && !ShouldApplySpriteBorderFix(gstate)) {
spriteBorderFix = 0.0f;
const float spriteBorderFix = PSP_CoreParameter().compat.flags().SpriteBorderFix;
if (pixelMapped || (spriteBorderFix && !ShouldApplySpriteBorderFix(gstate))) {
// Don't apply spriteBorderFix if pixel mapped.
} else {
if (spriteBorderFix < 0.0f) {
spriteBorderFixL = (spriteBorderFix / uScale) / gstate_c.curTextureWidth;
spriteBorderFixT = (spriteBorderFix / vScale) / gstate_c.curTextureHeight;
spriteBorderFixR = (spriteBorderFix / uScale) / gstate_c.curTextureWidth;
spriteBorderFixB = (spriteBorderFix / vScale) / gstate_c.curTextureHeight;
spriteBorderFixL = (-spriteBorderFix / uScale) / gstate_c.curTextureWidth;
spriteBorderFixT = (-spriteBorderFix / vScale) / gstate_c.curTextureHeight;
spriteBorderFixR = (-spriteBorderFix / uScale) / gstate_c.curTextureWidth;
spriteBorderFixB = (-spriteBorderFix / vScale) / gstate_c.curTextureHeight;
} else if (spriteBorderFix > 0.0f) {
spriteBorderFixL = 0.0f;
spriteBorderFixR = (spriteBorderFix / uScale) / gstate_c.curTextureWidth;
@@ -1034,23 +1055,11 @@ static bool ExpandRectangles(int vertexCount, int &numDecodedVerts, int vertsSiz
}
}
for (int i = 0; i < vertexCount; i += 2) {
const TransformedVertex &transVtxTL = transformed[indsIn[i + 0]];
const TransformedVertex &transVtxBR = transformed[indsIn[i + 1]];
if (pixelMapped) {
float dx = transVtxBR.x - transVtxTL.x;
float dy = transVtxBR.y - transVtxTL.y;
float du = transVtxBR.u - transVtxTL.u;
float dv = transVtxBR.v - transVtxTL.v;
// NOTE: We will accept it as pixel mapped if only one dimension is stretched. This fixes dialog frames in FFI.
// Though, there could be false positives in other games due to this. Let's see if it is a problem...
if (dx <= 0 || dy <= 0 || (dx != du && dy != dv)) {
pixelMapped = false;
}
}
float z = transVtxBR.z;
// Apply Z clamping. It appears clipping/culling does not affect rectangles, see #12058.
// TODO: We might want to make this 65536.999. Since those will pass, and if a game mixes through and non-through...
@@ -1065,30 +1074,30 @@ static bool ExpandRectangles(int vertexCount, int &numDecodedVerts, int vertsSiz
// bottom right
trans[0] = transVtxBR;
trans[0].u = (transVtxBR.u + spriteBorderFixR) * uScale;
trans[0].v = (transVtxBR.v + spriteBorderFixB) * vScale;
trans[0].u = (transVtxBR.u - spriteBorderFixR) * uScale;
trans[0].v = (transVtxBR.v - spriteBorderFixB) * vScale;
trans[0].z = z;
// top right
trans[1] = transVtxBR;
trans[1].y = transVtxTL.y;
trans[1].u = (transVtxBR.u + spriteBorderFixR) * uScale;
trans[1].v = (transVtxTL.v - spriteBorderFixT) * vScale;
trans[1].u = (transVtxBR.u - spriteBorderFixR) * uScale;
trans[1].v = (transVtxTL.v + spriteBorderFixT) * vScale;
trans[1].z = z;
// top left
trans[2] = transVtxBR;
trans[2].x = transVtxTL.x;
trans[2].y = transVtxTL.y;
trans[2].u = (transVtxTL.u - spriteBorderFixL) * uScale;
trans[2].v = (transVtxTL.v - spriteBorderFixT) * vScale;
trans[2].u = (transVtxTL.u + spriteBorderFixL) * uScale;
trans[2].v = (transVtxTL.v + spriteBorderFixT) * vScale;
trans[2].z = z;
// bottom left
trans[3] = transVtxBR;
trans[3].x = transVtxTL.x;
trans[3].u = (transVtxTL.u - spriteBorderFixL) * uScale;
trans[3].v = (transVtxBR.v + spriteBorderFixB) * vScale;
trans[3].u = (transVtxTL.u + spriteBorderFixL) * uScale;
trans[3].v = (transVtxBR.v - spriteBorderFixB) * vScale;
trans[3].z = z;
// That's the four corners. Now process UV rotation.
+17 -13
View File
@@ -2188,8 +2188,9 @@ void TextureCacheCommon::ApplyTexture(bool doBind, bool flatZ) {
}
}
// Can we depalettize at all? This refers to both in-fragment-shader depal and "traditional" depal through a separate pass.
static bool CanDepalettize(GETextureFormat texFormat, GEBufferFormat bufferFormat) {
// Can we depalettize the bufferFormat as texFormat at all?
// This refers to both in-fragment-shader depal and "traditional" depal through a separate pass.
static bool CanDepalettizeBufferAs(GETextureFormat texFormat, GEBufferFormat bufferFormat) {
if (IsClutFormat(texFormat)) {
switch (bufferFormat) {
case GE_FORMAT_4444:
@@ -2255,17 +2256,18 @@ static bool CanUseSmoothDepal(const GPUgstate &gstate, GEBufferFormat framebuffe
void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer, GETextureFormat texFormat, RasterChannel channel) {
Draw2DPipeline *textureShader = nullptr;
uint32_t clutMode = gstate.clutformat & 0xFFFFFF;
const uint32_t clutMode = gstate.clutformat & 0xFFFFFF;
bool depth = channel == RASTER_DEPTH;
bool need_depalettize = CanDepalettize(texFormat, depth ? GE_FORMAT_DEPTH16 : framebuffer->fb_format);
const bool depth = channel == RASTER_DEPTH;
const GEBufferFormat fbFormat = depth ? GE_FORMAT_DEPTH16 : framebuffer->fb_format;
const bool need_depalettize = CanDepalettizeBufferAs(texFormat, fbFormat);
const bool selfRender = framebufferManager_->GetCurrentRenderVFB() == framebuffer;
// Shader depal is not supported during 3D texturing or depth texturing, and requires 32-bit integer instructions in the shader.
bool useShaderDepal = framebufferManager_->GetCurrentRenderVFB() != framebuffer &&
!depth && clutRenderAddress_ == 0xFFFFFFFF &&
bool useShaderDepal = !selfRender && !depth && clutRenderAddress_ == 0xFFFFFFFF &&
!gstate_c.curTextureIs3D &&
draw_->GetShaderLanguageDesc().bitwiseOps &&
!(texFormat == GE_TFMT_CLUT8 && framebuffer->fb_format == GE_FORMAT_5551); // socom
!(texFormat == GE_TFMT_CLUT8 && fbFormat == GE_FORMAT_5551); // socom
switch (draw_->GetShaderLanguageDesc().shaderLanguage) {
case ShaderLanguage::GLSL_1xx:
@@ -2284,7 +2286,7 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
if (need_depalettize) {
if (clutRenderAddress_ == 0xFFFFFFFF) {
clutTexture = textureShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBufRaw_);
smoothedDepal = CanUseSmoothDepal(gstate, framebuffer->fb_format, clutTexture);
smoothedDepal = CanUseSmoothDepal(gstate, fbFormat, clutTexture);
} else {
// The CLUT texture is dynamic, it's the framebuffer pointed to by clutRenderAddress.
// Instead of texturing directly from that, we copy to a temporary CLUT texture.
@@ -2298,6 +2300,8 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
}
if (useShaderDepal) {
_dbg_assert_(!depth);
// Very icky conflation here of native and thin3d rendering. This will need careful work per backend in BindAsClutTexture.
BindAsClutTexture(clutTexture.texture, smoothedDepal);
@@ -2312,16 +2316,16 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
ApplySamplingParams(samplerKey);
ShaderDepalMode mode = ShaderDepalMode::NORMAL;
if (texFormat == GE_TFMT_CLUT8 && framebuffer->fb_format == GE_FORMAT_8888) {
if (texFormat == GE_TFMT_CLUT8 && fbFormat == GE_FORMAT_8888) {
mode = ShaderDepalMode::CLUT8_8888;
smoothedDepal = false; // just in case
} else if (smoothedDepal) {
mode = ShaderDepalMode::SMOOTHED;
}
gstate_c.Dirty(DIRTY_DEPAL);
gstate_c.Dirty(DIRTY_DEPAL | DIRTY_FRAGMENTSHADER_STATE);
gstate_c.SetUseShaderDepal(mode);
gstate_c.depalFramebufferFormat = framebuffer->fb_format;
gstate_c.depalTextureFormat = fbFormat;
const u32 bytesPerColor = clutFormat == GE_CMODE_32BIT_ABGR8888 ? sizeof(u32) : sizeof(u16);
const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor;
@@ -2334,7 +2338,7 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer
depthUpperBits = (depth && framebuffer->fb_format == GE_FORMAT_8888) ? ((gstate.getTextureAddress(0) & 0x600000) >> 20) : 0;
textureShader = textureShaderCache_->GetDepalettizeShader(clutMode, texFormat, depth ? GE_FORMAT_DEPTH16 : framebuffer->fb_format, smoothedDepal, depthUpperBits);
textureShader = textureShaderCache_->GetDepalettizeShader(clutMode, texFormat, fbFormat, smoothedDepal, depthUpperBits);
gstate_c.SetUseShaderDepal(ShaderDepalMode::OFF);
}
+1 -1
View File
@@ -377,7 +377,7 @@ void LinkedShader::UpdateUniforms(const ShaderID &vsid, const ShaderLanguageDesc
int indexMask = gstate.getClutIndexMask();
int indexShift = gstate.getClutIndexShift();
int indexOffset = gstate.getClutIndexStartPos() >> 4;
int format = gstate_c.depalFramebufferFormat;
int format = gstate_c.depalTextureFormat;
uint32_t val = BytesToUint32(indexMask, indexShift, indexOffset, format);
// Poke in a bilinear filter flag in the top bit.
val |= gstate.isMagnifyFilteringEnabled() << 31;
+1 -1
View File
@@ -700,7 +700,7 @@ public:
int spline_num_points_u;
ShaderDepalMode shaderDepalMode;
GEBufferFormat depalFramebufferFormat;
GEBufferFormat depalTextureFormat;
u32 getRelativeAddress(u32 data) const {
u32 baseExtended = ((gstate.base & 0x000F0000) << 8) | data;
+4
View File
@@ -2136,3 +2136,7 @@ ULJS00097 = -0.25
ULES00262 = 0.5
ULUS10064 = 0.5
ULKS46087 = 0.5
# Tokiden (issue #19422)
# This works in the provided dump, but needs more testing.
# NPJH50878 = -0.4
+2 -2
View File
@@ -890,7 +890,7 @@ if (!skiprest &&
float DD = float(eq(A, D));
float FF = float(eq(C, F));
float sideFactor = eq_B_D && DD!=FF ? 1.0 : -0.145898;
float sideFactor = eq_D_F && DD!=FF ? 1.0 : -0.145898;
Bl = fract(Bl) - (B1 +B2 +B1*B2) *0.381966;
Dl = fract(Dl) - step(El, Dl) + DD*sideFactor;
@@ -920,7 +920,7 @@ if (!skiprest &&
float DD = float(eq(D, G));
float FF = float(eq(F, I));
float sideFactor = eq_B_D && DD!=FF ? 1.0 : -0.145898;
float sideFactor = eq_D_F && DD!=FF ? 1.0 : -0.145898;
Hl = fract(Hl) - (H1+H2+H1*H2) *0.381966;
Dl = fract(Dl) - step(El, Dl) + DD*sideFactor;