Merge pull request #22430 from hrydgard/light-exponent

GPU: Saturate the lighting pow's exponent below 512, as the GE does
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-10-05 14:42:43 -06:00
commit d2ea558e49
7 files changed
+28 -15

No files matched your search

+2 -2
View File
@@ -223,7 +223,7 @@ void LightUpdateUniforms(UB_VS_Lights *ub, uint64_t dirtyUniforms) {
Uint8x3ToFloat4(ub->materialDiffuse, gstate.materialdiffuse);
}
if (dirtyUniforms & DIRTY_MATSPECULAR) {
Uint8x3ToFloat4_Alpha(ub->materialSpecular, gstate.materialspecular, std::max(0.0f, PSPSpecularCoef(getFloat24(gstate.materialspecularcoef))));
Uint8x3ToFloat4_Alpha(ub->materialSpecular, gstate.materialspecular, std::max(0.0f, PSPLightExponent(getFloat24(gstate.materialspecularcoef))));
}
if (dirtyUniforms & DIRTY_MATEMISSIVE) {
// We're not touching the fourth f32 here, because we store an u32 of control bits in it.
@@ -243,7 +243,7 @@ void LightUpdateUniforms(UB_VS_Lights *ub, uint64_t dirtyUniforms) {
// ldir is only used for spotlights. Prenormalize it.
ExpandFloat24x3ToFloat4AndNormalize(ub->ldir[i], &gstate.ldir[i * 3]);
ExpandFloat24x3ToFloat4(ub->latt[i], &gstate.latt[i * 3]);
float lightAngle_spotCoef[2] = { getFloat24(gstate.lcutoff[i]), getFloat24(gstate.lconv[i]) };
float lightAngle_spotCoef[2] = { getFloat24(gstate.lcutoff[i]), PSPLightExponent(getFloat24(gstate.lconv[i])) };
CopyFloat2To4(ub->lightAngle_SpotCoef[i], lightAngle_spotCoef);
Uint8x3ToFloat4(ub->lightAmbient[i], gstate.lcolor[i * 3]);
Uint8x3ToFloat4(ub->lightDiffuse[i], gstate.lcolor[i * 3 + 1]);
+2 -2
View File
@@ -40,14 +40,14 @@ Lighter::Lighter(int vertType) {
materialDiffuse.a = 1.0f;
materialSpecular.GetFromRGB(gstate.materialspecular);
materialSpecular.a = 1.0f;
specCoef_ = PSPSpecularCoef(getFloat24(gstate.materialspecularcoef));
specCoef_ = PSPLightExponent(getFloat24(gstate.materialspecularcoef));
viewDir_ = PSPViewDirection(gstate.viewMatrix);
bool hasColor = (vertType & GE_VTYPE_COL_MASK) != 0;
materialUpdate_ = hasColor ? (gstate.materialupdate & 7) : 0;
for (int l = 0; l < 4; l++) {
lcutoff[l] = getFloat24(gstate.lcutoff[l]);
lconv[l] = getFloat24(gstate.lconv[l]);
lconv[l] = PSPLightExponent(getFloat24(gstate.lconv[l]));
int i = l * 3;
if (gstate.isLightChanEnabled(l)) {
lpos[l] = Vec3fFromGE(&gstate.lpos[i]);
+9 -2
View File
@@ -81,8 +81,15 @@ inline float PSPLightPow(float x, float e) {
return y;
}
// The GE only uses the top 4 bits of the specular coefficient's mantissa.
inline float PSPSpecularCoef(float e) {
// The exponent of the GE's lighting pow (specular and spot): the top 4 bits of the mantissa, truncated, and
// saturated below 512, so 512 and up, infinity and NaN all act as 496 (gpu/probe exp221-223).
inline float PSPLightExponent(float e) {
if (std::isnan(e)) {
return std::signbit(e) ? 0.0f : 496.0f;
}
if (e >= 512.0f) {
return 496.0f;
}
u32 bits;
memcpy(&bits, &e, sizeof(bits));
bits &= 0xFFF80000;
+2 -1
View File
@@ -440,7 +440,8 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
// pow is close enough.
WRITE(p, "float pspPow(float x, float e) {\n");
if (compat.bitwiseOps) {
WRITE(p, " float t = max(e, 0.0) * float(floatBitsToInt(max(x, 1e-30)) - 0x3F800000) + 1065353216.0;\n");
// 1065353216 is 1.0's bits. Not written in hex: HLSL makes 0x3F800000 a uint, and the subtraction wraps.
WRITE(p, " float t = max(e, 0.0) * float(floatBitsToInt(max(x, 1e-30)) - 1065353216) + 1065353216.0;\n");
WRITE(p, " return x > 0.0 || e <= 0.0 ? intBitsToFloat(int(max(t, 0.0))) : 0.0;\n");
} else {
WRITE(p, " return e <= 0.0 ? 1.0 : pow(max(x, 0.0), e);\n");
+2 -2
View File
@@ -550,7 +550,7 @@ void LinkedShader::UpdateUniforms(const ShaderID &vsid, const ShaderLanguageDesc
SetColorUniform3(render_, &u_matemissive, gstate.materialemissive);
}
if (dirty & DIRTY_MATSPECULAR) {
SetColorUniform3ExtraFloat(render_, &u_matspecular, gstate.materialspecular, PSPSpecularCoef(getFloat24(gstate.materialspecularcoef)));
SetColorUniform3ExtraFloat(render_, &u_matspecular, gstate.materialspecular, PSPLightExponent(getFloat24(gstate.materialspecularcoef)));
}
for (int i = 0; i < 4; i++) {
@@ -564,7 +564,7 @@ void LinkedShader::UpdateUniforms(const ShaderID &vsid, const ShaderLanguageDesc
if (u_lightdir[i] != -1) SetFloat24Uniform3Normalized(render_, &u_lightdir[i], &gstate.ldir[i * 3]);
if (u_lightatt[i] != -1) SetFloat24Uniform3(render_, &u_lightatt[i], &gstate.latt[i * 3]);
if (u_lightangle_spotCoef[i] != -1) {
float lightangle_spotCoef[2] = { getFloat24(gstate.lcutoff[i]), getFloat24(gstate.lconv[i]) };
float lightangle_spotCoef[2] = { getFloat24(gstate.lcutoff[i]), PSPLightExponent(getFloat24(gstate.lconv[i])) };
SetFloatUniform2(render_, &u_lightangle_spotCoef[i], lightangle_spotCoef);
}
if (u_lightambient[i] != -1) SetColorUniform3(render_, &u_lightambient[i], gstate.lcolor[i * 3]);
+2 -6
View File
@@ -141,11 +141,9 @@ void ComputeState(State *state, bool hasColor0) {
if (std::isnan(lstate.spotCutoff) && std::signbit(lstate.spotCutoff))
lstate.spotCutoff = 0.0f;
lstate.spotExp = getFloat24(gstate.lconv[light]);
lstate.spotExp = PSPLightExponent(getFloat24(gstate.lconv[light]));
if (lstate.spotExp <= 0.0f)
lstate.spotExp = 0.0f;
else if (std::isnan(lstate.spotExp))
lstate.spotExp = std::signbit(lstate.spotExp) ? 0.0f : INFINITY;
}
}
@@ -181,11 +179,9 @@ void ComputeState(State *state, bool hasColor0) {
}
if (anyDiffuse || anySpecular) {
state->specularExp = PSPSpecularCoef(gstate.getMaterialSpecularCoef());
state->specularExp = PSPLightExponent(gstate.getMaterialSpecularCoef());
if (state->specularExp <= 0.0f)
state->specularExp = 0.0f;
else if (std::isnan(state->specularExp))
state->specularExp = std::signbit(state->specularExp) ? 0.0f : INFINITY;
}
state->baseAmbientColorFactor = LightColorFactor(gstate.getAmbientRGBA(), ones);
+9
View File
@@ -23,6 +23,7 @@
#include <cstdio>
#include <cstring>
#include "GPU/Common/TransformCommon.h"
#include "GPU/Software/GEMath.h"
#include "unittest/UnitTest.h"
@@ -166,6 +167,14 @@ static bool TestGELightPow() {
// multiply gives 0x3DA64400, and flooring 0x3F69FFFC (one step lower in the 8-bit factor, exp102).
EXPECT_EQ_HEX(ToBits(GELightPow(FromBits(0x3DC22200), 1.0625f)), 0x3DA64420U);
EXPECT_EQ_HEX(ToBits(GELightPow(FromBits(0x3F35E500), 0.296875f)), 0x3F6A0000U);
// The exponent keeps 5 significant bits, truncated, and saturates below 512 (exp221-223).
EXPECT_EQ_FLOAT(PSPLightExponent(127.0f), 124.0f);
EXPECT_EQ_FLOAT(PSPLightExponent(150.0f), 144.0f);
EXPECT_EQ_FLOAT(PSPLightExponent(511.0f), 496.0f);
EXPECT_EQ_FLOAT(PSPLightExponent(512.0f), 496.0f);
EXPECT_EQ_FLOAT(PSPLightExponent(INFINITY), 496.0f);
EXPECT_EQ_FLOAT(PSPLightExponent(NAN), 496.0f);
EXPECT_EQ_FLOAT(PSPLightExponent(-2.0f), -2.0f);
return true;
}