diff --git a/GPU/Common/ShaderUniforms.h b/GPU/Common/ShaderUniforms.h index a3db480c4aad..fe04ad0ced82 100644 --- a/GPU/Common/ShaderUniforms.h +++ b/GPU/Common/ShaderUniforms.h @@ -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 diff --git a/GPU/Common/VertexShaderGenerator.cpp b/GPU/Common/VertexShaderGenerator.cpp index 55d380fb6367..f051ec03e0b4 100644 --- a/GPU/Common/VertexShaderGenerator.cpp +++ b/GPU/Common/VertexShaderGenerator.cpp @@ -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 @@ -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"); @@ -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"); @@ -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(id.Bits(VS_BIT_LIGHT0_TYPE + 4 * i, 2)); GELightComputation comp = static_cast(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"); } @@ -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"); @@ -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"); @@ -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); } } @@ -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;