Skip to content

Commit

Permalink
Merge pull request #16787 from hrydgard/lighting-loop
Browse files Browse the repository at this point in the history
Vertex shaders: On platforms with uniform buffers, use indexing and loop over the lights.
  • Loading branch information
hrydgard authored Jan 11, 2023
2 parents 350cb55 + 7b62b46 commit 8d3a328
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 64 deletions.
37 changes: 8 additions & 29 deletions GPU/Common/ShaderUniforms.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,35 +91,14 @@ R"( vec4 u_ambient;
vec3 u_matdiffuse;
vec4 u_matspecular;
vec3 u_matemissive;
uint u_lightControl; // light ubershader
vec3 u_lightpos0;
vec3 u_lightpos1;
vec3 u_lightpos2;
vec3 u_lightpos3;
vec3 u_lightdir0;
vec3 u_lightdir1;
vec3 u_lightdir2;
vec3 u_lightdir3;
vec3 u_lightatt0;
vec3 u_lightatt1;
vec3 u_lightatt2;
vec3 u_lightatt3;
vec4 u_lightangle_spotCoef0;
vec4 u_lightangle_spotCoef1;
vec4 u_lightangle_spotCoef2;
vec4 u_lightangle_spotCoef3;
vec3 u_lightambient0;
vec3 u_lightambient1;
vec3 u_lightambient2;
vec3 u_lightambient3;
vec3 u_lightdiffuse0;
vec3 u_lightdiffuse1;
vec3 u_lightdiffuse2;
vec3 u_lightdiffuse3;
vec3 u_lightspecular0;
vec3 u_lightspecular1;
vec3 u_lightspecular2;
vec3 u_lightspecular3;
uint u_lightControl; // light ubershader control bits
vec3 u_lightpos[4];
vec3 u_lightdir[4];
vec3 u_lightatt[4];
vec4 u_lightangle_spotCoef[4];
vec3 u_lightambient[4];
vec3 u_lightdiffuse[4];
vec3 u_lightspecular[4];
)";

// With some cleverness, we could get away with uploading just half this when only the four or five first
Expand Down
98 changes: 63 additions & 35 deletions GPU/Common/VertexShaderGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1012,22 +1012,37 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
}
}

bool useIndexing = compat.shaderLanguage == HLSL_D3D11 || compat.shaderLanguage == GLSL_VULKAN;

char iStr[4];

if (lightUberShader) {
// TODO: Actually loop in the shader. For now, we write it all out.
// Will need to change how the data is stored to loop efficiently.
// u_lightControl is computed in PackLightControlBits().
p.F(" uint comp;");
p.F(" uint type;");
for (int i = 0; i < 4; i++) {
p.F(" if ((u_lightControl & %du) != 0x0u) { \n", 1 << i);
p.F(" comp = (u_lightControl >> 0x%02xu) & 0x3u;\n", 4 + 4 * i);
p.F(" type = (u_lightControl >> 0x%02xu) & 0x3u;\n", 4 + 4 * i + 2);
p.C(" if (type == 0x0u) {\n"); // GE_LIGHTTYPE_DIRECTIONAL
p.F(" toLight = u_lightpos%d;\n", i);
p.C(" } else {\n");
p.F(" toLight = u_lightpos%d - worldpos;\n", i);
p.F(" distance = length(toLight);\n", i);
p.F(" toLight /= distance;\n", i);
// We generate generic code that can calculate any combination of lights specified
// in u_lightControl. u_lightControl is computed in PackLightControlBits().
p.C(" uint comp; uint type;\n");
if (useIndexing) {
p.C(" for (uint i = 0; i < 4; i++) {\n");
}
// If we can use indexing, we actually loop in the shader now, using the loop emitted
// above. In that case, we only need to emit the code once, so the for loop here will
// only run for a single pass.
int count = useIndexing ? 1 : 4;
for (int i = 0; i < count; i++) {
snprintf(iStr, sizeof(iStr), useIndexing ? "[i]" : "%d", i);
if (useIndexing) {
p.C(" if ((u_lightControl & (1u << i)) != 0x0u) { \n");
p.C(" comp = (u_lightControl >> uint(4u + 4u * i)) & 0x3u;\n");
p.C(" type = (u_lightControl >> uint(4u + 4u * i + 2u)) & 0x3u;\n");
} else {
p.F(" if ((u_lightControl & %du) != 0x0u) { \n", 1 << i);
p.F(" comp = (u_lightControl >> 0x%02xu) & 0x3u;\n", 4 + 4 * i);
p.F(" type = (u_lightControl >> 0x%02xu) & 0x3u;\n", 4 + 4 * i + 2);
}
p.F(" toLight = u_lightpos%s;\n", iStr);
p.C(" if (type != 0x0u) {\n"); // GE_LIGHTTYPE_DIRECTIONAL
p.F(" toLight -= worldpos;\n", iStr);
p.F(" distance = length(toLight);\n");
p.F(" toLight /= distance;\n");
p.C(" }\n");
p.C(" ldot = dot(toLight, worldnormal);\n");
p.C(" if (comp == 0x2u) {\n"); // GE_LIGHTCOMP_ONLYPOWDIFFUSE
Expand All @@ -1039,12 +1054,12 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
p.C(" }\n");
p.C(" switch (int(type)) {\n"); // Attenuation
p.C(" case 1:\n"); // GE_LIGHTTYPE_POINT
p.F(" lightScale = clamp(1.0 / dot(u_lightatt%i, vec3(1.0, distance, distance*distance)), 0.0, 1.0);\n", i);
p.F(" lightScale = clamp(1.0 / dot(u_lightatt%s, vec3(1.0, distance, distance*distance)), 0.0, 1.0);\n", iStr);
p.C(" break;\n");
p.C(" case 2:\n"); // GE_LIGHTTYPE_SPOT
p.F(" angle = length(u_lightdir%i) == 0.0 ? 0.0 : dot(normalize(u_lightdir%i), toLight);\n", i, i);
p.F(" if (angle >= u_lightangle_spotCoef%i.x) {\n", i);
p.F(" lightScale = clamp(1.0 / dot(u_lightatt%i, vec3(1.0, distance, distance*distance)), 0.0, 1.0) * (u_lightangle_spotCoef%i.y <= 0.0 ? 1.0 : pow(angle, u_lightangle_spotCoef%i.y));\n", i, i, i);
p.F(" angle = length(u_lightdir%s) == 0.0 ? 0.0 : dot(normalize(u_lightdir%s), toLight);\n", iStr, iStr);
p.F(" if (angle >= u_lightangle_spotCoef%s.x) {\n", iStr);
p.F(" lightScale = clamp(1.0 / dot(u_lightatt%s, vec3(1.0, distance, distance*distance)), 0.0, 1.0) * (u_lightangle_spotCoef%s.y <= 0.0 ? 1.0 : pow(angle, u_lightangle_spotCoef%s.y));\n", iStr, iStr, iStr);
p.C(" } else {\n");
p.C(" lightScale = 0.0;\n");
p.C(" }\n");
Expand All @@ -1053,7 +1068,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
p.C(" lightScale = 1.0;\n");
p.C(" break;\n");
p.C(" }\n");
p.F(" diffuse = (u_lightdiffuse%i * diffuseColor) * max(ldot, 0.0);\n", i);
p.F(" diffuse = (u_lightdiffuse%s * diffuseColor) * max(ldot, 0.0);\n", iStr);
p.C(" if (comp == 0x1u) {\n"); // do specular
p.C(" if (ldot >= 0.0) {\n");
p.C(" ldot = dot(normalize(toLight + vec3(0.0, 0.0, 1.0)), worldnormal);\n");
Expand All @@ -1063,27 +1078,31 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
p.C(" ldot = pow(max(ldot, 0.0), u_matspecular.a);\n");
p.C(" }\n");
p.C(" if (ldot > 0.0)\n");
p.F(" lightSum1 += u_lightspecular%i * specularColor * ldot * lightScale;\n", i);
p.F(" lightSum1 += u_lightspecular%s * specularColor * ldot * lightScale;\n", iStr);
p.C(" }\n");
p.C(" }\n");
p.F(" lightSum0.rgb += (u_lightambient%i * ambientColor.rgb + diffuse) * lightScale;\n", i);
p.F(" lightSum0.rgb += (u_lightambient%s * ambientColor.rgb + diffuse) * lightScale;\n", iStr);
p.C(" }\n");
}
if (useIndexing) {
p.F(" }");
}
} else {
// Calculate lights if needed. If shade mapping is enabled, lights may need to be
// at least partially calculated.
// Generate specific code for calculating the enabled lights only.
for (int i = 0; i < 4; i++) {
if (doLight[i] != LIGHT_FULL)
continue;

snprintf(iStr, sizeof(iStr), useIndexing ? "[%d]" : "%d", i);

GELightType type = static_cast<GELightType>(id.Bits(VS_BIT_LIGHT0_TYPE + 4 * i, 2));
GELightComputation comp = static_cast<GELightComputation>(id.Bits(VS_BIT_LIGHT0_COMP + 4 * i, 2));

if (type == GE_LIGHTTYPE_DIRECTIONAL) {
// We prenormalize light positions for directional lights.
p.F(" toLight = u_lightpos%i;\n", i);
p.F(" toLight = u_lightpos%s;\n", iStr);
} else {
p.F(" toLight = u_lightpos%i - worldpos;\n", i);
p.F(" toLight = u_lightpos%s - worldpos;\n", iStr);
p.C(" distance = length(toLight);\n");
p.C(" toLight /= distance;\n");
}
Expand All @@ -1110,13 +1129,13 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
timesLightScale = "";
break;
case GE_LIGHTTYPE_POINT:
p.F(" lightScale = clamp(1.0 / dot(u_lightatt%i, vec3(1.0, distance, distance*distance)), 0.0, 1.0);\n", i);
p.F(" lightScale = clamp(1.0 / dot(u_lightatt%s, vec3(1.0, distance, distance*distance)), 0.0, 1.0);\n", iStr);
break;
case GE_LIGHTTYPE_SPOT:
case GE_LIGHTTYPE_UNKNOWN:
p.F(" angle = length(u_lightdir%i) == 0.0 ? 0.0 : dot(normalize(u_lightdir%i), toLight);\n", i, i);
p.F(" if (angle >= u_lightangle_spotCoef%i.x) {\n", i);
p.F(" lightScale = clamp(1.0 / dot(u_lightatt%i, vec3(1.0, distance, distance*distance)), 0.0, 1.0) * (u_lightangle_spotCoef%i.y <= 0.0 ? 1.0 : pow(angle, u_lightangle_spotCoef%i.y));\n", i, i, i);
p.F(" angle = length(u_lightdir%s) == 0.0 ? 0.0 : dot(normalize(u_lightdir%s), toLight);\n", iStr, iStr);
p.F(" if (angle >= u_lightangle_spotCoef%s.x) {\n", iStr);
p.F(" lightScale = clamp(1.0 / dot(u_lightatt%s, vec3(1.0, distance, distance*distance)), 0.0, 1.0) * (u_lightangle_spotCoef%s.y <= 0.0 ? 1.0 : pow(angle, u_lightangle_spotCoef%s.y));\n", iStr, iStr, iStr);
p.C(" } else {\n");
p.C(" lightScale = 0.0;\n");
p.C(" }\n");
Expand All @@ -1126,7 +1145,7 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
break;
}

p.F(" diffuse = (u_lightdiffuse%i * diffuseColor) * max(ldot, 0.0);\n", i);
p.F(" diffuse = (u_lightdiffuse%s * diffuseColor) * max(ldot, 0.0);\n", iStr);
if (doSpecular) {
p.C(" if (ldot >= 0.0) {\n");
p.C(" ldot = dot(normalize(toLight + vec3(0.0, 0.0, 1.0)), worldnormal);\n");
Expand All @@ -1136,10 +1155,10 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag
p.C(" ldot = pow(max(ldot, 0.0), u_matspecular.a);\n");
p.C(" }\n");
p.C(" if (ldot > 0.0)\n");
p.F(" lightSum1 += u_lightspecular%i * specularColor * ldot %s;\n", i, timesLightScale);
p.F(" lightSum1 += u_lightspecular%s * specularColor * ldot %s;\n", iStr, timesLightScale);
p.C(" }\n");
}
p.F(" lightSum0.rgb += (u_lightambient%i * ambientColor.rgb + diffuse)%s;\n", i, timesLightScale);
p.F(" lightSum0.rgb += (u_lightambient%s * ambientColor.rgb + diffuse)%s;\n", iStr, timesLightScale);
}
}

Expand Down Expand Up @@ -1260,8 +1279,17 @@ bool GenerateVertexShader(const VShaderID &id, char *buffer, const ShaderLanguag

case GE_TEXMAP_ENVIRONMENT_MAP: // Shade mapping - use dots from light sources.
{
std::string lightFactor0 = StringFromFormat("(length(u_lightpos%i) == 0.0 ? worldnormal.z : dot(normalize(u_lightpos%i), worldnormal))", ls0, ls0);
std::string lightFactor1 = StringFromFormat("(length(u_lightpos%i) == 0.0 ? worldnormal.z : dot(normalize(u_lightpos%i), worldnormal))", ls1, ls1);
char ls0Str[4];
char ls1Str[4];
if (useIndexing) {
snprintf(ls0Str, sizeof(ls0Str), "[%d]", ls0);
snprintf(ls1Str, sizeof(ls1Str), "[%d]", ls1);
} else {
snprintf(ls0Str, sizeof(ls0Str), "%d", ls0);
snprintf(ls1Str, sizeof(ls1Str), "%d", ls1);
}
std::string lightFactor0 = StringFromFormat("(length(u_lightpos%s) == 0.0 ? worldnormal.z : dot(normalize(u_lightpos%s), worldnormal))", ls0Str, ls0Str);
std::string lightFactor1 = StringFromFormat("(length(u_lightpos%s) == 0.0 ? worldnormal.z : dot(normalize(u_lightpos%s), worldnormal))", ls1Str, ls1Str);
WRITE(p, " %sv_texcoord = vec3(u_uvscaleoffset.xy * vec2(1.0 + %s, 1.0 + %s) * 0.5, 1.0);\n", compat.vsOutPrefix, lightFactor0.c_str(), lightFactor1.c_str());
}
break;
Expand Down

0 comments on commit 8d3a328

Please sign in to comment.