D3D9: Generate shaders directly from shader IDs, just like we do in GL.

This commit is contained in:
Henrik Rydgard 2015-10-25 00:34:23 +02:00
parent edafa9bc17
commit 85229efef4
11 changed files with 253 additions and 240 deletions

View File

@ -146,6 +146,7 @@ std::string FragmentShaderDesc(const ShaderID &id) {
if (id.Bit(FS_BIT_ENABLE_FOG)) desc << "Fog ";
if (id.Bit(FS_BIT_COLOR_DOUBLE)) desc << "2x ";
if (id.Bit(FS_BIT_FLATSHADE)) desc << "Flat ";
if (id.Bit(FS_BIT_BGRA_TEXTURE)) desc << "BGRA ";
if (id.Bit(FS_BIT_SHADER_TEX_CLAMP)) {
desc << "TClamp";
if (id.Bit(FS_BIT_CLAMP_S)) desc << "S";
@ -232,6 +233,7 @@ void ComputeFragmentShaderID(ShaderID *id_out, uint32_t vertType) {
id.SetBit(FS_BIT_CLAMP_T, gstate.isTexCoordClampedT());
id.SetBit(FS_BIT_TEXTURE_AT_OFFSET, textureAtOffset);
}
id.SetBit(FS_BIT_BGRA_TEXTURE, gstate_c.bgraTexture);
}
id.SetBit(FS_BIT_LMODE, lmode);

View File

@ -72,6 +72,7 @@ enum {
FS_BIT_BLENDFUNC_A = 38, // 4 bits
FS_BIT_BLENDFUNC_B = 42,
FS_BIT_FLATSHADE = 46,
FS_BIT_BGRA_TEXTURE = 47,
};
struct ShaderID {

View File

@ -2186,4 +2186,20 @@ bool DIRECTX9_GPU::GetCurrentSimpleVertices(int count, std::vector<GPUDebugVerte
return transformDraw_.GetCurrentSimpleVertices(count, vertices, indices);
}
std::vector<std::string> DIRECTX9_GPU::DebugGetShaderIDs(DebugShaderType type) {
if (type == SHADER_TYPE_VERTEXLOADER) {
return transformDraw_.DebugGetVertexLoaderIDs();
} else {
return shaderManager_->DebugGetShaderIDs(type);
}
}
std::string DIRECTX9_GPU::DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType) {
if (type == SHADER_TYPE_VERTEXLOADER) {
return transformDraw_.DebugGetVertexLoaderString(id, stringType);
} else {
return shaderManager_->DebugGetShaderString(id, type, stringType);
}
}
} // namespace DX9

View File

@ -143,6 +143,10 @@ public:
void Execute_BoneMtxNum(u32 op, u32 diff);
void Execute_BoneMtxData(u32 op, u32 diff);
// Using string because it's generic - makes no assumptions on the size of the shader IDs of this backend.
std::vector<std::string> DebugGetShaderIDs(DebugShaderType shader) override;
std::string DebugGetShaderString(std::string id, DebugShaderType shader, DebugShaderStringType stringType) override;
protected:
void FastRunLoop(DisplayList &list) override;
void ProcessEvent(GPUEvent ev) override;

View File

@ -22,8 +22,8 @@
#include "GPU/Directx9/helper/global.h"
#include "GPU/Directx9/PixelShaderGeneratorDX9.h"
#include "GPU/ge_constants.h"
#include "GPU/GPUState.h"
#include "GPU/Common/GPUStateUtils.h"
#include "GPU/GPUState.h"
#define WRITE p+=sprintf
@ -31,131 +31,54 @@
namespace DX9 {
// Here we must take all the bits of the gstate that determine what the fragment shader will
// look like, and concatenate them together into an ID.
void ComputeFragmentShaderIDDX9(ShaderID *id) {
int id0 = 0;
int id1 = 0;
if (gstate.isModeClear()) {
// We only need one clear shader, so let's ignore the rest of the bits.
id0 = 1;
} else {
bool lmode = gstate.isUsingSecondaryColor() && gstate.isLightingEnabled() && !gstate.isModeThrough();
bool enableFog = gstate.isFogEnabled() && !gstate.isModeThrough();
bool enableAlphaTest = gstate.isAlphaTestEnabled() && !IsAlphaTestTriviallyTrue() && !g_Config.bDisableAlphaTest;
bool enableColorTest = gstate.isColorTestEnabled() && !IsColorTestTriviallyTrue();
bool enableColorDoubling = gstate.isColorDoublingEnabled();
bool doTextureProjection = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX;
bool doTextureAlpha = gstate.isTextureAlphaUsed();
ReplaceBlendType replaceBlend = ReplaceBlendWithShader(gstate_c.allowShaderBlend);
ReplaceAlphaType stencilToAlpha = ReplaceAlphaWithStencil(replaceBlend);
// All texfuncs except replace are the same for RGB as for RGBA with full alpha.
if (gstate_c.textureFullAlpha && gstate.getTextureFunction() != GE_TEXFUNC_REPLACE)
doTextureAlpha = false;
// id0 |= (gstate.isModeClear() & 1);
if (gstate.isTextureMapEnabled()) {
id0 |= 1 << 1;
id0 |= gstate.getTextureFunction() << 2;
id0 |= (doTextureAlpha & 1) << 5; // rgb or rgba
id0 |= (gstate_c.flipTexture & 1) << 6;
if (gstate_c.needShaderTexClamp) {
bool textureAtOffset = gstate_c.curTextureXOffset != 0 || gstate_c.curTextureYOffset != 0;
// 3 bits total.
id0 |= 1 << 7;
id0 |= gstate.isTexCoordClampedS() << 8;
id0 |= gstate.isTexCoordClampedT() << 9;
id0 |= (textureAtOffset & 1) << 10;
}
}
id0 |= (lmode & 1) << 11;
#if !defined(DX9_USE_HW_ALPHA_TEST)
if (enableAlphaTest) {
// 5 bits total.
id0 |= 1 << 12;
id0 |= gstate.getAlphaTestFunction() << 13;
id0 |= (IsAlphaTestAgainstZero() & 1) << 16;
}
#endif
if (enableColorTest) {
// 4 bits total.
id0 |= 1 << 17;
id0 |= gstate.getColorTestFunction() << 18;
id0 |= (IsColorTestAgainstZero() & 1) << 20;
}
id0 |= (enableFog & 1) << 21;
id0 |= (doTextureProjection & 1) << 22;
id0 |= (enableColorDoubling & 1) << 23;
// 2 bits
id0 |= (stencilToAlpha) << 24;
if (stencilToAlpha != REPLACE_ALPHA_NO) {
// 4 bits
id0 |= ReplaceAlphaWithStencilType() << 26;
}
if (enableAlphaTest)
gpuStats.numAlphaTestedDraws++;
else
gpuStats.numNonAlphaTestedDraws++;
// 2 bits.
id0 |= ReplaceLogicOpType() << 30;
// 3 bits.
id1 |= replaceBlend << 0;
if (replaceBlend > REPLACE_BLEND_STANDARD) {
// 11 bits total.
id1 |= gstate.getBlendEq() << 3;
id1 |= gstate.getBlendFuncA() << 6;
id1 |= gstate.getBlendFuncB() << 10;
}
// TODO: Flat shading?
id1 |= (gstate_c.bgraTexture & 1) << 15;
}
id->d[0] = id0;
id->d[1] = id1;
}
// Missing: Z depth range
// Also, logic ops etc, of course. Urgh.
void GenerateFragmentShaderDX9(char *buffer) {
// Also, logic ops etc, of course, as they are not supported in DX9.
bool GenerateFragmentShaderDX9(const ShaderID &id, char *buffer) {
char *p = buffer;
bool lmode = gstate.isUsingSecondaryColor() && gstate.isLightingEnabled() && !gstate.isModeThrough();
bool doTexture = gstate.isTextureMapEnabled() && !gstate.isModeClear();
bool enableFog = gstate.isFogEnabled() && !gstate.isModeThrough() && !gstate.isModeClear();
bool enableAlphaTest = gstate.isAlphaTestEnabled() && !IsAlphaTestTriviallyTrue() && !gstate.isModeClear() && !g_Config.bDisableAlphaTest;
bool alphaTestAgainstZero = IsAlphaTestAgainstZero();
bool enableColorTest = gstate.isColorTestEnabled() && !IsColorTestTriviallyTrue() && !gstate.isModeClear();
bool colorTestAgainstZero = IsColorTestAgainstZero();
bool enableColorDoubling = gstate.isColorDoublingEnabled() && gstate.isTextureMapEnabled();
bool doTextureProjection = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX;
bool doTextureAlpha = gstate.isTextureAlphaUsed();
bool textureAtOffset = gstate_c.curTextureXOffset != 0 || gstate_c.curTextureYOffset != 0;
ReplaceBlendType replaceBlend = ReplaceBlendWithShader(gstate_c.allowShaderBlend);
ReplaceAlphaType stencilToAlpha = ReplaceAlphaWithStencil(replaceBlend);
bool lmode = id.Bit(FS_BIT_LMODE);
bool doTexture = id.Bit(FS_BIT_DO_TEXTURE);
bool enableFog = id.Bit(FS_BIT_ENABLE_FOG);
bool enableAlphaTest = id.Bit(FS_BIT_ALPHA_TEST);
if (gstate_c.textureFullAlpha && gstate.getTextureFunction() != GE_TEXFUNC_REPLACE)
doTextureAlpha = false;
bool alphaTestAgainstZero = id.Bit(FS_BIT_ALPHA_AGAINST_ZERO);
bool enableColorTest = id.Bit(FS_BIT_COLOR_TEST);
bool colorTestAgainstZero = id.Bit(FS_BIT_COLOR_AGAINST_ZERO);
bool enableColorDoubling = id.Bit(FS_BIT_COLOR_DOUBLE);
bool doTextureProjection = id.Bit(FS_BIT_DO_TEXTURE_PROJ);
bool doTextureAlpha = id.Bit(FS_BIT_TEXALPHA);
bool doFlatShading = id.Bit(FS_BIT_FLATSHADE);
bool isModeClear = id.Bit(FS_BIT_CLEARMODE);
bool bgraTexture = id.Bit(FS_BIT_BGRA_TEXTURE);
GEComparison alphaTestFunc = (GEComparison)id.Bits(FS_BIT_ALPHA_TEST_FUNC, 3);
GEComparison colorTestFunc = (GEComparison)id.Bits(FS_BIT_COLOR_TEST_FUNC, 2);
bool needShaderTexClamp = id.Bit(FS_BIT_SHADER_TEX_CLAMP);
ReplaceBlendType replaceBlend = static_cast<ReplaceBlendType>(id.Bits(FS_BIT_REPLACE_BLEND, 3));
ReplaceAlphaType stencilToAlpha = static_cast<ReplaceAlphaType>(id.Bits(FS_BIT_STENCIL_TO_ALPHA, 2));
GETexFunc texFunc = (GETexFunc)id.Bits(FS_BIT_TEXFUNC, 3);
bool textureAtOffset = id.Bit(FS_BIT_TEXTURE_AT_OFFSET);
GEBlendSrcFactor replaceBlendFuncA = (GEBlendSrcFactor)id.Bits(FS_BIT_BLENDFUNC_A, 4);
GEBlendDstFactor replaceBlendFuncB = (GEBlendDstFactor)id.Bits(FS_BIT_BLENDFUNC_B, 4);
GEBlendMode replaceBlendEq = (GEBlendMode)id.Bits(FS_BIT_BLENDEQ, 3);
StencilValueType replaceAlphaWithStencilType = (StencilValueType)id.Bits(FS_BIT_REPLACE_ALPHA_WITH_STENCIL_TYPE, 4);
if (doTexture)
WRITE(p, "sampler tex : register(s0);\n");
if (!gstate.isModeClear() && replaceBlend > REPLACE_BLEND_STANDARD) {
if (!isModeClear && replaceBlend > REPLACE_BLEND_STANDARD) {
if (replaceBlend == REPLACE_BLEND_COPY_FBO) {
WRITE(p, "float2 u_fbotexSize : register(c%i);\n", CONST_PS_FBOTEXSIZE);
WRITE(p, "sampler fbotex : register(s1);\n");
}
if (gstate.getBlendFuncA() == GE_SRCBLEND_FIXA) {
if (replaceBlendFuncA == GE_SRCBLEND_FIXA) {
WRITE(p, "float3 u_blendFixA : register(c%i);\n", CONST_PS_BLENDFIXA);
}
if (gstate.getBlendFuncB() == GE_DSTBLEND_FIXB) {
if (replaceBlendFuncB == GE_DSTBLEND_FIXB) {
WRITE(p, "float3 u_blendFixB : register(c%i);\n", CONST_PS_BLENDFIXB);
}
}
@ -170,10 +93,10 @@ void GenerateFragmentShaderDX9(char *buffer) {
WRITE(p, "float4 u_alphacolorref : register(c%i);\n", CONST_PS_ALPHACOLORREF);
WRITE(p, "float4 u_alphacolormask : register(c%i);\n", CONST_PS_ALPHACOLORMASK);
}
if (stencilToAlpha && ReplaceAlphaWithStencilType() == STENCIL_VALUE_UNIFORM) {
if (stencilToAlpha && replaceAlphaWithStencilType == STENCIL_VALUE_UNIFORM) {
WRITE(p, "float u_stencilReplaceValue : register(c%i);\n", CONST_PS_STENCILREPLACE);
}
if (gstate.isTextureMapEnabled() && gstate.getTextureFunction() == GE_TEXFUNC_BLEND) {
if (doTexture && texFunc == GE_TEXFUNC_BLEND) {
WRITE(p, "float3 u_texenv : register(c%i);\n", CONST_PS_TEXENV);
}
if (enableFog) {
@ -205,7 +128,7 @@ void GenerateFragmentShaderDX9(char *buffer) {
WRITE(p, "float4 main( PS_IN In ) : COLOR\n");
WRITE(p, "{\n");
if (gstate.isModeClear()) {
if (isModeClear) {
// Clear mode does not allow any fancy shading.
WRITE(p, " float4 v = In.v_color0;\n");
} else {
@ -218,10 +141,10 @@ void GenerateFragmentShaderDX9(char *buffer) {
secondary = "";
}
if (gstate.isTextureMapEnabled()) {
if (doTexture) {
const char *texcoord = "In.v_texcoord";
// TODO: Not sure the right way to do this for projection.
if (gstate_c.needShaderTexClamp) {
if (needShaderTexClamp) {
// We may be clamping inside a larger surface (tex = 64x64, buffer=480x272).
// We may also be wrapping in such a surface, or either one in a too-small surface.
// Obviously, clamping to a smaller surface won't work. But better to clamp to something.
@ -235,12 +158,12 @@ void GenerateFragmentShaderDX9(char *buffer) {
vcoord = "1.0 - " + vcoord;
}
if (gstate.isTexCoordClampedS()) {
if (id.Bit(FS_BIT_CLAMP_S)) {
ucoord = "clamp(" + ucoord + ", u_texclamp.z, u_texclamp.x - u_texclamp.z)";
} else {
ucoord = "fmod(" + ucoord + ", u_texclamp.x)";
}
if (gstate.isTexCoordClampedT()) {
if (id.Bit(FS_BIT_CLAMP_T)) {
vcoord = "clamp(" + vcoord + ", u_texclamp.w, u_texclamp.y - u_texclamp.w)";
} else {
vcoord = "fmod(" + vcoord + ", u_texclamp.y)";
@ -266,14 +189,14 @@ void GenerateFragmentShaderDX9(char *buffer) {
}
if (doTextureProjection) {
WRITE(p, " float4 t = tex2Dproj(tex, float4(In.v_texcoord.x, In.v_texcoord.y, 0, In.v_texcoord.z))%s;\n", gstate_c.bgraTexture ? ".bgra" : "");
WRITE(p, " float4 t = tex2Dproj(tex, float4(In.v_texcoord.x, In.v_texcoord.y, 0, In.v_texcoord.z))%s;\n", bgraTexture ? ".bgra" : "");
} else {
WRITE(p, " float4 t = tex2D(tex, %s.xy)%s;\n", texcoord, gstate_c.bgraTexture ? ".bgra" : "");
WRITE(p, " float4 t = tex2D(tex, %s.xy)%s;\n", texcoord, bgraTexture ? ".bgra" : "");
}
WRITE(p, " float4 p = In.v_color0;\n");
if (doTextureAlpha) { // texfmt == RGBA
switch (gstate.getTextureFunction()) {
switch (texFunc) {
case GE_TEXFUNC_MODULATE:
WRITE(p, " float4 v = p * t%s;\n", secondary); break;
case GE_TEXFUNC_DECAL:
@ -292,7 +215,7 @@ void GenerateFragmentShaderDX9(char *buffer) {
}
} else { // texfmt == RGB
switch (gstate.getTextureFunction()) {
switch (texFunc) {
case GE_TEXFUNC_MODULATE:
WRITE(p, " float4 v = float4(t.rgb * p.rgb, p.a)%s;\n", secondary); break;
case GE_TEXFUNC_DECAL:
@ -318,7 +241,6 @@ void GenerateFragmentShaderDX9(char *buffer) {
#if !defined(DX9_USE_HW_ALPHA_TEST)
if (enableAlphaTest) {
if (alphaTestAgainstZero) {
GEComparison alphaTestFunc = gstate.getAlphaTestFunction();
// When testing against 0 (extremely common), we can avoid some math.
// 0.002 is approximately half of 1.0 / 255.0.
if (alphaTestFunc == GE_COMP_NOTEQUAL || alphaTestFunc == GE_COMP_GREATER) {
@ -332,7 +254,6 @@ void GenerateFragmentShaderDX9(char *buffer) {
WRITE(p, " clip(-1);\n");
}
} else {
GEComparison alphaTestFunc = gstate.getAlphaTestFunction();
const char *alphaTestFuncs[] = { "#", "#", " != ", " == ", " >= ", " > ", " <= ", " < " }; // never/always don't make sense
if (alphaTestFuncs[alphaTestFunc][0] != '#') {
// TODO: Rewrite this to use clip() appropriately (like, clip(v.a - u_alphacolorref.a))
@ -346,7 +267,6 @@ void GenerateFragmentShaderDX9(char *buffer) {
#endif
if (enableColorTest) {
if (colorTestAgainstZero) {
GEComparison colorTestFunc = gstate.getColorTestFunction();
// When testing against 0 (common), we can avoid some math.
// 0.002 is approximately half of 1.0 / 255.0.
if (colorTestFunc == GE_COMP_NOTEQUAL) {
@ -360,9 +280,7 @@ void GenerateFragmentShaderDX9(char *buffer) {
WRITE(p, " clip(-1);\n");
}
} else {
GEComparison colorTestFunc = gstate.getColorTestFunction();
const char *colorTestFuncs[] = { "#", "#", " != ", " == " }; // never/always don't make sense
u32 colorTestMask = gstate.getColorTestMask();
if (colorTestFuncs[colorTestFunc][0] != '#') {
const char * test = colorTestFuncs[colorTestFunc];
WRITE(p, " float3 colortest = roundAndScaleTo255v(v.rgb);\n");
@ -386,9 +304,8 @@ void GenerateFragmentShaderDX9(char *buffer) {
}
if (replaceBlend == REPLACE_BLEND_PRE_SRC || replaceBlend == REPLACE_BLEND_PRE_SRC_2X_ALPHA) {
GEBlendSrcFactor funcA = gstate.getBlendFuncA();
const char *srcFactor = "ERROR";
switch (funcA) {
switch (replaceBlendFuncA) {
case GE_SRCBLEND_DSTCOLOR: srcFactor = "ERROR"; break;
case GE_SRCBLEND_INVDSTCOLOR: srcFactor = "ERROR"; break;
case GE_SRCBLEND_SRCALPHA: srcFactor = "float3(v.a, v.a, v.a)"; break;
@ -417,7 +334,7 @@ void GenerateFragmentShaderDX9(char *buffer) {
std::string replacedAlpha = "0.0";
char replacedAlphaTemp[64] = "";
if (stencilToAlpha != REPLACE_ALPHA_NO) {
switch (ReplaceAlphaWithStencilType()) {
switch (replaceAlphaWithStencilType) {
case STENCIL_VALUE_UNIFORM:
replacedAlpha = "u_stencilReplaceValue";
break;
@ -467,7 +384,8 @@ void GenerateFragmentShaderDX9(char *buffer) {
break;
}
switch (ReplaceLogicOpType()) {
LogicOpReplaceType replaceLogicOpType = (LogicOpReplaceType)id.Bits(FS_BIT_REPLACE_LOGIC_OP_TYPE, 2);
switch (replaceLogicOpType) {
case LOGICOPTYPE_ONE:
WRITE(p, " v.rgb = float3(1.0, 1.0, 1.0);\n");
break;
@ -480,6 +398,7 @@ void GenerateFragmentShaderDX9(char *buffer) {
WRITE(p, " return v;\n");
WRITE(p, "}\n");
return true;
}
};

View File

@ -26,8 +26,7 @@
namespace DX9 {
void ComputeFragmentShaderIDDX9(ShaderID *id);
void GenerateFragmentShaderDX9(char *buffer);
bool GenerateFragmentShaderDX9(const ShaderID &id, char *buffer);
#define CONST_PS_TEXENV 0
#define CONST_PS_ALPHACOLORREF 1

View File

@ -39,7 +39,7 @@
namespace DX9 {
PSShader::PSShader(const char *code, bool useHWTransform) : shader(nullptr), failed_(false), useHWTransform_(useHWTransform) {
PSShader::PSShader(ShaderID id, const char *code, bool useHWTransform) : id_(id), shader(nullptr), failed_(false), useHWTransform_(useHWTransform) {
source_ = code;
#ifdef SHADERLOG
OutputDebugString(ConvertUTF8ToWString(code).c_str());
@ -79,7 +79,18 @@ PSShader::~PSShader() {
shader->Release();
}
VSShader::VSShader(const char *code, int vertType, bool useHWTransform) : shader(nullptr), failed_(false), useHWTransform_(useHWTransform) {
std::string PSShader::GetShaderString(DebugShaderStringType type) const {
switch (type) {
case SHADER_STRING_SOURCE_CODE:
return source_;
case SHADER_STRING_SHORT_DESC:
return FragmentShaderDesc(id_);
default:
return "N/A";
}
}
VSShader::VSShader(ShaderID id, const char *code, int vertType, bool useHWTransform) : id_(id), shader(nullptr), failed_(false), useHWTransform_(useHWTransform) {
source_ = code;
#ifdef SHADERLOG
OutputDebugString(ConvertUTF8ToWString(code).c_str());
@ -118,6 +129,16 @@ VSShader::~VSShader() {
shader->Release();
}
std::string VSShader::GetShaderString(DebugShaderStringType type) const {
switch (type) {
case SHADER_STRING_SOURCE_CODE:
return source_;
case SHADER_STRING_SHORT_DESC:
return VertexShaderDesc(id_);
default:
return "N/A";
}
}
void ShaderManagerDX9::PSSetColorUniform3(int creg, u32 color) {
const float col[4] = {
@ -591,12 +612,12 @@ void ShaderManagerDX9::DirtyLastShader() { // disables vertex arrays
VSShader *ShaderManagerDX9::ApplyShader(int prim, u32 vertType) {
bool useHWTransform = CanUseHardwareTransformDX9(prim);
bool useHWTransform = CanUseHardwareTransform(prim);
ShaderID VSID;
ComputeVertexShaderIDDX9(&VSID, vertType, useHWTransform);
ComputeVertexShaderID(&VSID, vertType, useHWTransform);
ShaderID FSID;
ComputeFragmentShaderIDDX9(&FSID);
ComputeFragmentShaderID(&FSID, vertType);
// Just update uniforms if this is the same shader as last time.
if (lastVShader_ != nullptr && lastPShader_ != nullptr && VSID == lastVSID_ && FSID == lastFSID_) {
@ -612,21 +633,23 @@ VSShader *ShaderManagerDX9::ApplyShader(int prim, u32 vertType) {
VSShader *vs;
if (vsIter == vsCache_.end()) {
// Vertex shader not in cache. Let's compile it.
GenerateVertexShaderDX9(prim, codeBuffer_, useHWTransform);
vs = new VSShader(codeBuffer_, vertType, useHWTransform);
GenerateVertexShaderDX9(VSID, codeBuffer_);
vs = new VSShader(VSID, codeBuffer_, vertType, useHWTransform);
if (vs->Failed()) {
ERROR_LOG(HLE, "Shader compilation failed, falling back to software transform");
osm.Show("hardware transform error - falling back to software", 2.5f, 0xFF3030FF, -1, true);
delete vs;
ComputeVertexShaderID(&VSID, vertType, false);
// TODO: Look for existing shader with the appropriate ID, use that instead of generating a new one - however, need to make sure
// that that shader ID is not used when computing the linked shader ID below, because then IDs won't match
// next time and we'll do this over and over...
// Can still work with software transform.
GenerateVertexShaderDX9(prim, codeBuffer_, false);
vs = new VSShader(codeBuffer_, vertType, false);
GenerateVertexShaderDX9(VSID, codeBuffer_);
vs = new VSShader(VSID, codeBuffer_, vertType, false);
}
vsCache_[VSID] = vs;
@ -639,8 +662,8 @@ VSShader *ShaderManagerDX9::ApplyShader(int prim, u32 vertType) {
PSShader *fs;
if (fsIter == fsCache_.end()) {
// Fragment shader not in cache. Let's compile it.
GenerateFragmentShaderDX9(codeBuffer_);
fs = new PSShader(codeBuffer_, useHWTransform);
GenerateFragmentShaderDX9(FSID, codeBuffer_);
fs = new PSShader(FSID, codeBuffer_, useHWTransform);
fsCache_[FSID] = fs;
} else {
fs = fsIter->second;
@ -662,4 +685,54 @@ VSShader *ShaderManagerDX9::ApplyShader(int prim, u32 vertType) {
return vs;
}
std::vector<std::string> ShaderManagerDX9::DebugGetShaderIDs(DebugShaderType type) {
std::string id;
std::vector<std::string> ids;
switch (type) {
case SHADER_TYPE_VERTEX:
{
for (auto iter : vsCache_) {
iter.first.ToString(&id);
ids.push_back(id);
}
}
break;
case SHADER_TYPE_FRAGMENT:
{
for (auto iter : fsCache_) {
iter.first.ToString(&id);
ids.push_back(id);
}
}
break;
}
return ids;
}
std::string ShaderManagerDX9::DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType) {
ShaderID shaderId;
shaderId.FromString(id);
switch (type) {
case SHADER_TYPE_VERTEX:
{
auto iter = vsCache_.find(shaderId);
if (iter == vsCache_.end()) {
return "";
}
return iter->second->GetShaderString(stringType);
}
case SHADER_TYPE_FRAGMENT:
{
auto iter = fsCache_.find(shaderId);
if (iter == fsCache_.end()) {
return "";
}
return iter->second->GetShaderString(stringType);
}
default:
return "N/A";
}
}
} // namespace

View File

@ -23,6 +23,7 @@
#include "Globals.h"
#include "GPU/Directx9/VertexShaderGeneratorDX9.h"
#include "GPU/Directx9/PixelShaderGeneratorDX9.h"
#include "GPU/Common/ShaderCommon.h"
#include "GPU/Common/ShaderId.h"
#include "thin3d/d3dx9_loader.h"
#include "math/lin/matrix4x4.h"
@ -80,38 +81,44 @@ enum {
class PSShader {
public:
PSShader(const char *code, bool useHWTransform);
PSShader(ShaderID id, const char *code, bool useHWTransform);
~PSShader();
const std::string &source() const { return source_; }
bool Failed() const { return failed_; }
bool UseHWTransform() const { return useHWTransform_; }
std::string GetShaderString(DebugShaderStringType type) const;
LPDIRECT3DPIXELSHADER9 shader;
protected:
std::string source_;
bool failed_;
bool useHWTransform_;
ShaderID id_;
};
class VSShader {
public:
VSShader(const char *code, int vertType, bool useHWTransform);
VSShader(ShaderID id, const char *code, int vertType, bool useHWTransform);
~VSShader();
const std::string &source() const { return source_; }
bool Failed() const { return failed_; }
bool UseHWTransform() const { return useHWTransform_; }
std::string GetShaderString(DebugShaderStringType type) const;
LPDIRECT3DVERTEXSHADER9 shader;
protected:
std::string source_;
bool failed_;
bool useHWTransform_;
ShaderID id_;
};
class ShaderManagerDX9 {
@ -130,6 +137,9 @@ public:
int NumVertexShaders() const { return (int)vsCache_.size(); }
int NumFragmentShaders() const { return (int)fsCache_.size(); }
std::vector<std::string> DebugGetShaderIDs(DebugShaderType type);
std::string DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType);
private:
void PSUpdateUniforms(int dirtyUniforms);
void VSUpdateUniforms(int dirtyUniforms);

View File

@ -129,39 +129,60 @@ enum DoLightComputation {
LIGHT_FULL,
};
void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) {
void GenerateVertexShaderDX9(const ShaderID &id, char *buffer) {
char *p = buffer;
const u32 vertType = gstate.vertType;
bool lmode = gstate.isUsingSecondaryColor() && gstate.isLightingEnabled() && !gstate.isModeThrough();
bool doTexture = gstate.isTextureMapEnabled() && !gstate.isModeClear();
bool doTextureProjection = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX;
bool doShadeMapping = gstate.getUVGenMode() == GE_TEXMAP_ENVIRONMENT_MAP;
bool isModeThrough = id.Bit(VS_BIT_IS_THROUGH);
bool lmode = id.Bit(VS_BIT_LMODE) && !isModeThrough; // TODO: Different expression than in shaderIDgen
bool doTexture = id.Bit(VS_BIT_DO_TEXTURE);
bool doTextureProjection = id.Bit(VS_BIT_DO_TEXTURE_PROJ);
bool hasColor = (vertType & GE_VTYPE_COL_MASK) != 0 || !useHWTransform;
bool hasNormal = (vertType & GE_VTYPE_NRM_MASK) != 0 && useHWTransform;
bool hasTexcoord = (vertType & GE_VTYPE_TC_MASK) != 0 || !useHWTransform;
bool enableFog = gstate.isFogEnabled() && !gstate.isModeThrough() && !gstate.isModeClear();
bool throughmode = (vertType & GE_VTYPE_THROUGH_MASK) != 0;
bool flipV = gstate_c.flipTexture;
bool flipNormal = gstate.areNormalsReversed();
bool prescale = g_Config.bPrescaleUV && !throughmode && (gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_COORDS || gstate.getUVGenMode() == GE_TEXMAP_UNKNOWN);
GETexMapMode uvGenMode = static_cast<GETexMapMode>(id.Bits(VS_BIT_UVGEN_MODE, 2));
DoLightComputation doLight[4] = {LIGHT_OFF, LIGHT_OFF, LIGHT_OFF, LIGHT_OFF};
// this is only valid for some settings of uvGenMode
GETexProjMapMode uvProjMode = static_cast<GETexProjMapMode>(id.Bits(VS_BIT_UVPROJ_MODE, 2));
bool doShadeMapping = uvGenMode == GE_TEXMAP_ENVIRONMENT_MAP;
bool doFlatShading = id.Bit(VS_BIT_FLATSHADE);
bool useHWTransform = id.Bit(VS_BIT_USE_HW_TRANSFORM);
bool hasColor = id.Bit(VS_BIT_HAS_COLOR) || !useHWTransform;
bool hasNormal = id.Bit(VS_BIT_HAS_NORMAL) && useHWTransform;
bool hasTexcoord = id.Bit(VS_BIT_HAS_TEXCOORD) || !useHWTransform;
bool enableFog = id.Bit(VS_BIT_ENABLE_FOG);
bool throughmode = id.Bit(VS_BIT_IS_THROUGH);
bool flipV = id.Bit(VS_BIT_FLIP_TEXTURE); // This also means that we are texturing from a render target
bool flipNormal = id.Bit(VS_BIT_NORM_REVERSE);
int ls0 = id.Bits(VS_BIT_LS0, 2);
int ls1 = id.Bits(VS_BIT_LS1, 2);
bool enableBones = id.Bit(VS_BIT_ENABLE_BONES);
bool enableLighting = id.Bit(VS_BIT_LIGHTING_ENABLE);
int matUpdate = id.Bits(VS_BIT_MATERIAL_UPDATE, 3);
bool prescale = g_Config.bPrescaleUV && !throughmode && (uvGenMode == GE_TEXMAP_TEXTURE_COORDS || uvGenMode == GE_TEXMAP_UNKNOWN);
DoLightComputation doLight[4] = { LIGHT_OFF, LIGHT_OFF, LIGHT_OFF, LIGHT_OFF };
if (useHWTransform) {
int shadeLight0 = doShadeMapping ? gstate.getUVLS0() : -1;
int shadeLight1 = doShadeMapping ? gstate.getUVLS1() : -1;
int shadeLight0 = doShadeMapping ? ls0 : -1;
int shadeLight1 = doShadeMapping ? ls1 : -1;
for (int i = 0; i < 4; i++) {
if (i == shadeLight0 || i == shadeLight1)
doLight[i] = LIGHT_SHADE;
if (gstate.isLightingEnabled() && gstate.isLightChanEnabled(i))
if (id.Bit(VS_BIT_LIGHTING_ENABLE) && id.Bit(VS_BIT_LIGHT0_ENABLE + i))
doLight[i] = LIGHT_FULL;
}
}
int numBoneWeights = 0;
int boneWeightScale = id.Bits(VS_BIT_WEIGHT_FMTSCALE, 2);
if (enableBones) {
numBoneWeights = 1 + id.Bits(VS_BIT_BONES, 3);
}
int texFmtScale = id.Bits(VS_BIT_TEXCOORD_FMTSCALE, 2);
WRITE(p, "#pragma warning( disable : 3571 )\n");
if (gstate.isModeThrough()) {
if (isModeThrough) {
WRITE(p, "float4x4 u_proj_through : register(c%i);\n", CONST_VS_PROJ_THROUGH);
} else {
WRITE(p, "float4x4 u_proj : register(c%i);\n", CONST_VS_PROJ);
@ -180,17 +201,16 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) {
WRITE(p, "float4x3 u_view : register(c%i);\n", CONST_VS_VIEW);
if (doTextureProjection)
WRITE(p, "float4x3 u_texmtx : register(c%i);\n", CONST_VS_TEXMTX);
if (vertTypeIsSkinningEnabled(vertType)) {
int numBones = TranslateNumBones(vertTypeGetNumBoneWeights(vertType));
if (enableBones) {
#ifdef USE_BONE_ARRAY
WRITE(p, "float4x3 u_bone[%i] : register(c%i);\n", numBones, CONST_VS_BONE0);
#else
for (int i = 0; i < numBones; i++) {
for (int i = 0; i < numBoneWeights; i++) {
WRITE(p, "float4x3 u_bone%i : register(c%i);\n", i, CONST_VS_BONE0 + i * 3);
}
#endif
}
if (doTexture && (flipV || !prescale || gstate.getUVGenMode() == GE_TEXMAP_ENVIRONMENT_MAP || gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX)) {
if (doTexture && (flipV || !prescale || uvGenMode == GE_TEXMAP_ENVIRONMENT_MAP || uvGenMode == GE_TEXMAP_TEXTURE_MATRIX)) {
WRITE(p, "float4 u_uvscaleoffset : register(c%i);\n", CONST_VS_UVSCALEOFFSET);
}
for (int i = 0; i < 4; i++) {
@ -199,7 +219,8 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) {
WRITE(p, "float3 u_lightpos%i : register(c%i);\n", i, CONST_VS_LIGHTPOS + i);
}
if (doLight[i] == LIGHT_FULL) {
GELightType type = gstate.getLightType(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)
WRITE(p, "float3 u_lightatt%i : register(c%i);\n", i, CONST_VS_LIGHTATT + i);
@ -212,11 +233,12 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) {
WRITE(p, "float3 u_lightambient%i : register(c%i);\n", i, CONST_VS_LIGHTAMBIENT + i);
WRITE(p, "float3 u_lightdiffuse%i : register(c%i);\n", i, CONST_VS_LIGHTDIFFUSE + i);
if (gstate.isUsingSpecularLight(i))
if (comp != GE_LIGHTCOMP_ONLYDIFFUSE) {
WRITE(p, "float3 u_lightspecular%i : register(c%i);\n", i, CONST_VS_LIGHTSPECULAR + i);
}
}
}
if (gstate.isLightingEnabled()) {
if (enableLighting) {
WRITE(p, "float4 u_ambient : register(c%i);\n", CONST_VS_AMBIENT);
if ((gstate.materialupdate & 2) == 0 || !hasColor)
WRITE(p, "float3 u_matdiffuse : register(c%i);\n", CONST_VS_MATDIFFUSE);
@ -226,15 +248,15 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) {
}
}
if (!gstate.isModeThrough() && gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) {
if (!isModeThrough && gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) {
WRITE(p, "float4 u_depthRange : register(c%i);\n", CONST_VS_DEPTHRANGE);
}
// And the "varyings".
if (useHWTransform) {
WRITE(p, "struct VS_IN { \n");
if (vertTypeIsSkinningEnabled(vertType)) {
WRITE(p, "%s", boneWeightAttrDecl[TranslateNumBones(vertTypeGetNumBoneWeights(vertType))]);
if (enableBones) {
WRITE(p, "%s", boneWeightAttrDecl[numBoneWeights]);
}
if (doTexture && hasTexcoord) {
WRITE(p, " float2 texcoord : TEXCOORD0;\n");
@ -286,7 +308,7 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) {
// Confirmed: Through mode gets through exactly the same in GL and D3D in Phantasy Star: Text is 38023.0 in the test scene.
if (!gstate.isModeThrough() && gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) {
if (!isModeThrough && gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) {
// Apply the projection and viewport to get the Z buffer value, floor to integer, undo the viewport and projection.
// The Z range in D3D is different but we compensate for that using parameters.
WRITE(p, "\nfloat4 depthRoundZVP(float4 v) {\n");
@ -336,7 +358,7 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) {
}
} else {
// Step 1: World Transform / Skinning
if (!vertTypeIsSkinningEnabled(vertType)) {
if (!enableBones) {
// No skinning, just standard T&L.
WRITE(p, " float3 worldpos = mul(float4(In.position.xyz, 1.0), u_world);\n");
if (hasNormal)
@ -344,8 +366,6 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) {
else
WRITE(p, " float3 worldnormal = float3(0.0, 0.0, 1.0);\n");
} else {
int numWeights = TranslateNumBones(vertTypeGetNumBoneWeights(vertType));
static const char * const boneWeightAttr[8] = {
"a_w1.x", "a_w1.y", "a_w1.z", "a_w1.w",
"a_w2.x", "a_w2.y", "a_w2.z", "a_w2.w",
@ -355,7 +375,7 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) {
// To loop through the weights, we unfortunately need to put them in a float array.
// GLSL ES sucks - no way to directly initialize an array!
switch (numWeights) {
switch (numBoneWeights) {
case 1: WRITE(p, " float w[1]; w[0] = a_w1;\n"); break;
case 2: WRITE(p, " float w[2]; w[0] = a_w1.x; w[1] = a_w1.y;\n"); break;
case 3: WRITE(p, " float w[3]; w[0] = a_w1.x; w[1] = a_w1.y; w[2] = a_w1.z;\n"); break;
@ -367,8 +387,8 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) {
}
WRITE(p, " mat4 skinMatrix = w[0] * u_bone[0];\n");
if (numWeights > 1) {
WRITE(p, " for (int i = 1; i < %i; i++) {\n", numWeights);
if (numBoneWeights > 1) {
WRITE(p, " for (int i = 1; i < %i; i++) {\n", numBoneWeights);
WRITE(p, " skinMatrix += w[i] * u_bone[i];\n");
WRITE(p, " }\n");
}
@ -376,29 +396,29 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) {
#else
#ifdef USE_BONE_ARRAY
if (numWeights == 1)
if (numBoneWeights == 1)
WRITE(p, " float4x3 skinMatrix = a_w1 * u_bone[0]");
else
WRITE(p, " float4x3 skinMatrix = a_w1.x * u_bone[0]");
for (int i = 1; i < numWeights; i++) {
for (int i = 1; i < numBoneWeights; i++) {
const char *weightAttr = boneWeightAttr[i];
// workaround for "cant do .x of scalar" issue
if (numWeights == 1 && i == 0) weightAttr = "a_w1";
if (numWeights == 5 && i == 4) weightAttr = "a_w2";
if (numBoneWeights == 1 && i == 0) weightAttr = "a_w1";
if (numBoneWeights == 5 && i == 4) weightAttr = "a_w2";
WRITE(p, " + %s * u_bone[%i]", weightAttr, i);
}
#else
// Uncomment this to screw up bone shaders to check the vertex shader software fallback
// WRITE(p, "THIS SHOULD ERROR! #error");
if (numWeights == 1)
if (numBoneWeights == 1)
WRITE(p, " float4x3 skinMatrix = mul(In.a_w1, u_bone0)");
else
WRITE(p, " float4x3 skinMatrix = mul(In.a_w1.x, u_bone0)");
for (int i = 1; i < numWeights; i++) {
for (int i = 1; i < numBoneWeights; i++) {
const char *weightAttr = boneWeightAttr[i];
// workaround for "cant do .x of scalar" issue
if (numWeights == 1 && i == 0) weightAttr = "a_w1";
if (numWeights == 5 && i == 4) weightAttr = "a_w2";
if (numBoneWeights == 1 && i == 0) weightAttr = "a_w1";
if (numBoneWeights == 5 && i == 4) weightAttr = "a_w2";
WRITE(p, " + mul(In.%s, u_bone%i)", weightAttr, i);
}
#endif
@ -437,16 +457,17 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) {
bool specularIsZero = true;
bool distanceNeeded = false;
if (gstate.isLightingEnabled()) {
if (enableLighting) {
WRITE(p, " float4 lightSum0 = u_ambient * %s + float4(u_matemissive, 0.0);\n", ambientStr);
for (int i = 0; i < 4; 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 (doLight[i] != LIGHT_FULL)
continue;
diffuseIsZero = false;
if (gstate.isUsingSpecularLight(i))
if (comp != GE_LIGHTCOMP_ONLYDIFFUSE)
specularIsZero = false;
GELightType type = gstate.getLightType(i);
if (type != GE_LIGHTTYPE_DIRECTIONAL)
distanceNeeded = true;
}
@ -470,7 +491,8 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) {
if (doLight[i] != LIGHT_FULL)
continue;
GELightType type = gstate.getLightType(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.
@ -481,8 +503,8 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) {
WRITE(p, " toLight /= distance;\n");
}
bool doSpecular = gstate.isUsingSpecularLight(i);
bool poweredDiffuse = gstate.isUsingPoweredDiffuseLight(i);
bool doSpecular = comp != GE_LIGHTCOMP_ONLYDIFFUSE;
bool poweredDiffuse = comp == GE_LIGHTCOMP_BOTHWITHPOWDIFFUSE;
if (poweredDiffuse) {
WRITE(p, " float dot%i = pow(dot(toLight, worldnormal), u_matspecular.a);\n", i);
@ -525,7 +547,7 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) {
WRITE(p, " lightSum0.rgb += (u_lightambient%i * %s.rgb + diffuse)%s;\n", i, ambientStr, timesLightScale);
}
if (gstate.isLightingEnabled()) {
if (enableLighting) {
// Sum up ambient, emissive here.
if (lmode) {
WRITE(p, " Out.v_color0 = clamp(lightSum0, 0.0, 1.0);\n");
@ -555,7 +577,7 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) {
// Step 3: UV generation
if (doTexture) {
switch (gstate.getUVGenMode()) {
switch (uvGenMode) {
case GE_TEXMAP_TEXTURE_COORDS: // Scale-offset. Easy.
case GE_TEXMAP_UNKNOWN: // Not sure what this is, but Riviera uses it. Treating as coords works.
if (prescale && !flipV) {
@ -576,7 +598,7 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) {
case GE_TEXMAP_TEXTURE_MATRIX: // Projection mapping.
{
std::string temp_tc;
switch (gstate.getUVProjMode()) {
switch (uvProjMode) {
case GE_PROJMAP_POSITION: // Use model space XYZ as source
temp_tc = "float4(In.position.xyz, 1.0)";
break;
@ -617,7 +639,7 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) {
}
// Will flip in the fragment for GE_TEXMAP_TEXTURE_MATRIX.
if (flipV && gstate.getUVGenMode() != GE_TEXMAP_TEXTURE_MATRIX)
if (flipV && uvGenMode != GE_TEXMAP_TEXTURE_MATRIX)
WRITE(p, " Out.v_texcoord.y = 1.0 - Out.v_texcoord.y;\n");
}

View File

@ -25,40 +25,7 @@ namespace DX9 {
// #define USE_BONE_ARRAY
struct VertexShaderIDDX9
{
VertexShaderIDDX9() {d[0] = 0xFFFFFFFF;}
void clear() {d[0] = 0xFFFFFFFF;}
u32 d[2];
bool operator < (const VertexShaderIDDX9 &other) const
{
for (size_t i = 0; i < sizeof(d) / sizeof(u32); i++)
{
if (d[i] < other.d[i])
return true;
if (d[i] > other.d[i])
return false;
}
return false;
}
bool operator == (const VertexShaderIDDX9 &other) const
{
for (size_t i = 0; i < sizeof(d) / sizeof(u32); i++)
{
if (d[i] != other.d[i])
return false;
}
return true;
}
};
bool CanUseHardwareTransformDX9(int prim);
void ComputeVertexShaderIDDX9(ShaderID *id, u32 vertType, bool useHWTransform);
void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform);
// Collapse to less skinning shaders to reduce shader switching, which is expensive.
int TranslateNumBonesDX9(int bones);
void GenerateVertexShaderDX9(const ShaderID &id, char *buffer);
#define CONST_VS_PROJ 0
#define CONST_VS_PROJ_THROUGH 4

View File

@ -292,7 +292,7 @@ void GenerateVertexShader(const ShaderID &id, char *buffer) {
WRITE(p, "uniform highp vec2 u_fogcoef;\n");
}
if (!gstate.isModeThrough() && gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) {
if (!isModeThrough && gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) {
WRITE(p, "uniform highp vec4 u_depthRange;\n");
}
@ -561,7 +561,7 @@ void GenerateVertexShader(const ShaderID &id, char *buffer) {
WRITE(p, " lightSum0.rgb += (u_lightambient%i * %s.rgb + diffuse)%s;\n", i, ambientStr, timesLightScale);
}
if (id.Bit(VS_BIT_LIGHTING_ENABLE)) {
if (enableLighting) {
// Sum up ambient, emissive here.
if (lmode) {
WRITE(p, " v_color0 = clamp(lightSum0, 0.0, 1.0);\n");