From a5fb4cffd5faa8ffe62fa086cdf610737e0961bc Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Tue, 25 Aug 2015 01:26:29 +0200 Subject: [PATCH 01/16] Failed quick attempt at Z rounding in the vshader --- GPU/GLES/VertexShaderGenerator.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/GPU/GLES/VertexShaderGenerator.cpp b/GPU/GLES/VertexShaderGenerator.cpp index acf8ae11d0..252634a126 100644 --- a/GPU/GLES/VertexShaderGenerator.cpp +++ b/GPU/GLES/VertexShaderGenerator.cpp @@ -357,6 +357,12 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf } } + // Even with this we don't match, because the viewport is different I think. + // I think we need to apply viewportZ, round/floor, and then undo viewportZ. + WRITE(p, "\nvec4 depthRoundZ(vec4 v) {\n"); + WRITE(p, " return vec4(v.x, v.y, (1.0/65536.0) * floor(v.z * 65536.0), v.w);\n"); + WRITE(p, "}\n\n"); + WRITE(p, "void main() {\n"); if (!useHWTransform) { @@ -381,9 +387,9 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf WRITE(p, " v_fogdepth = position.w;\n"); } if (gstate.isModeThrough()) { - WRITE(p, " gl_Position = u_proj_through * vec4(position.xyz, 1.0);\n"); + WRITE(p, " gl_Position = depthRoundZ(u_proj_through * vec4(position.xyz, 1.0));\n"); } else { - WRITE(p, " gl_Position = u_proj * vec4(position.xyz, 1.0);\n"); + WRITE(p, " gl_Position = depthRoundZ(u_proj * vec4(position.xyz, 1.0));\n"); } } else { // Step 1: World Transform / Skinning @@ -476,7 +482,7 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf WRITE(p, " vec4 viewPos = u_view * vec4(worldpos, 1.0);\n"); // Final view and projection transforms. - WRITE(p, " gl_Position = u_proj * viewPos;\n"); + WRITE(p, " gl_Position = depthRoundZ(u_proj * viewPos);\n"); // TODO: Declare variables for dots for shade mapping if needed. From 9e18fb7e908a61f3a9b3637ff0fd92ee10ac298b Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 26 Aug 2015 13:26:08 +0200 Subject: [PATCH 02/16] Round Z in the vertex shader to match a 16-bit Z buffer. However this inexplicably does still not solve the Phantasy Star issue! --- GPU/Common/SoftwareTransformCommon.cpp | 4 ++ GPU/GLES/GLES_GPU.cpp | 8 ++-- GPU/GLES/ShaderManager.cpp | 17 ++++++++- GPU/GLES/ShaderManager.h | 13 +++++-- GPU/GLES/StateMapping.cpp | 6 +-- GPU/GLES/VertexShaderGenerator.cpp | 53 ++++++++++++++++++++++++-- 6 files changed, 85 insertions(+), 16 deletions(-) diff --git a/GPU/Common/SoftwareTransformCommon.cpp b/GPU/Common/SoftwareTransformCommon.cpp index 7fade324e6..cdbd78cf34 100644 --- a/GPU/Common/SoftwareTransformCommon.cpp +++ b/GPU/Common/SoftwareTransformCommon.cpp @@ -211,6 +211,7 @@ void SoftwareTransform( // The w of uv is also never used (hardcoded to 1.0.) } } else { + // Okay, need to actually perform the full transform. for (int index = 0; index < maxIndex; index++) { reader.Goto(index); @@ -401,6 +402,9 @@ void SoftwareTransform( } transformed[index].color0_32 = c0.ToRGBA(); transformed[index].color1_32 = c1.ToRGBA(); + + // The multiplication by the projection matrix is still performed in the vertex shader. + // So is vertex depth rounding, to simulate the 16-bit depth buffer. } } diff --git a/GPU/GLES/GLES_GPU.cpp b/GPU/GLES/GLES_GPU.cpp index 8cc3be391e..d601d13f35 100644 --- a/GPU/GLES/GLES_GPU.cpp +++ b/GPU/GLES/GLES_GPU.cpp @@ -77,8 +77,8 @@ static const CommandTableEntry commandTable[] = { {GE_CMD_FOG2, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, DIRTY_FOGCOEF, &GLES_GPU::Execute_FogCoef}, // Should these maybe flush? - {GE_CMD_MINZ, FLAG_FLUSHBEFOREONCHANGE}, - {GE_CMD_MAXZ, FLAG_FLUSHBEFOREONCHANGE}, + {GE_CMD_MINZ, FLAG_FLUSHBEFOREONCHANGE, DIRTY_DEPTHRANGE}, + {GE_CMD_MAXZ, FLAG_FLUSHBEFOREONCHANGE, DIRTY_DEPTHRANGE}, // Changes that dirty texture scaling. {GE_CMD_TEXMAPMODE, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, DIRTY_UVSCALEOFFSET, &GLES_GPU::Execute_TexMapMode}, @@ -206,8 +206,8 @@ static const CommandTableEntry commandTable[] = { {GE_CMD_VIEWPORTY1, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GLES_GPU::Execute_ViewportType}, {GE_CMD_VIEWPORTX2, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GLES_GPU::Execute_ViewportType}, {GE_CMD_VIEWPORTY2, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GLES_GPU::Execute_ViewportType}, - {GE_CMD_VIEWPORTZ1, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GLES_GPU::Execute_ViewportType}, - {GE_CMD_VIEWPORTZ2, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GLES_GPU::Execute_ViewportType}, + {GE_CMD_VIEWPORTZ1, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, DIRTY_DEPTHRANGE, &GLES_GPU::Execute_ViewportType}, + {GE_CMD_VIEWPORTZ2, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, DIRTY_DEPTHRANGE, &GLES_GPU::Execute_ViewportType}, // Region {GE_CMD_REGION1, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GLES_GPU::Execute_Region}, diff --git a/GPU/GLES/ShaderManager.cpp b/GPU/GLES/ShaderManager.cpp index f8825545cc..5fec17784c 100644 --- a/GPU/GLES/ShaderManager.cpp +++ b/GPU/GLES/ShaderManager.cpp @@ -167,6 +167,7 @@ LinkedShader::LinkedShader(Shader *vs, Shader *fs, u32 vertType, bool useHWTrans numBones = TranslateNumBones(vertTypeGetNumBoneWeights(vertType)); else numBones = 0; + u_depthRange = glGetUniformLocation(program, "u_depthRange"); #ifdef USE_BONE_ARRAY u_bone = glGetUniformLocation(program, "u_bone"); @@ -233,6 +234,8 @@ LinkedShader::LinkedShader(Shader *vs, Shader *fs, u32 vertType, bool useHWTrans if (u_texmtx != -1) availableUniforms |= DIRTY_TEXMATRIX; if (u_stencilReplaceValue != -1) availableUniforms |= DIRTY_STENCILREPLACEVALUE; if (u_blendFixA != -1 || u_blendFixB != -1 || u_fbotexSize != -1) availableUniforms |= DIRTY_SHADERBLEND; + if (u_depthRange != -1) + availableUniforms |= DIRTY_DEPTHRANGE; // Looping up to numBones lets us avoid checking u_bone[i] #ifdef USE_BONE_ARRAY @@ -343,6 +346,10 @@ static void SetFloat24Uniform3(int uniform, const u32 data[3]) { glUniform3fv(uniform, 1, (const GLfloat *)&col[0]); } +static void SetFloatUniform4(int uniform, float data[4]) { + glUniform4fv(uniform, 1, data); +} + static void SetMatrix4x3(int uniform, const float *m4x3) { float m4x4[16]; ConvertMatrix4x3To4x4(m4x4, m4x3); @@ -547,7 +554,7 @@ void LinkedShader::UpdateUniforms(u32 vertType) { glUniform4fv(u_uvscaleoffset, 1, uvscaleoff); } - if (dirty & DIRTY_TEXCLAMP) { + if ((dirty & DIRTY_TEXCLAMP) && u_texclamp != -1) { const float invW = 1.0f / (float)gstate_c.curTextureWidth; const float invH = 1.0f / (float)gstate_c.curTextureHeight; const int w = gstate.getTextureWidth(0); @@ -582,6 +589,14 @@ void LinkedShader::UpdateUniforms(u32 vertType) { if (dirty & DIRTY_TEXMATRIX) { SetMatrix4x3(u_texmtx, gstate.tgenMatrix); } + if ((dirty & DIRTY_DEPTHRANGE) && u_depthRange != -1) { + float viewZScale = gstate.getViewportZScale(); + float viewZCenter = gstate.getViewportZCenter(); + float minZ = gstate.getDepthRangeMin(); + float maxZ = gstate.getDepthRangeMax(); + float data[4] = { viewZScale, viewZCenter, minZ, maxZ }; + SetFloatUniform4(u_depthRange, data); + } if (dirty & DIRTY_STENCILREPLACEVALUE) { glUniform1f(u_stencilReplaceValue, (float)gstate.getStencilTestRef() * (1.0f / 255.0f)); diff --git a/GPU/GLES/ShaderManager.h b/GPU/GLES/ShaderManager.h index 80469c87f0..ff2d29f50c 100644 --- a/GPU/GLES/ShaderManager.h +++ b/GPU/GLES/ShaderManager.h @@ -88,6 +88,8 @@ public: int u_view; int u_texmtx; int u_world; + int u_depthRange; // x,y = viewport xscale/xcenter. z,w=clipping minz/maxz (?) + #ifdef USE_BONE_ARRAY int u_bone; // array, size is numBones #else @@ -132,10 +134,10 @@ public: enum { DIRTY_PROJMATRIX = (1 << 0), DIRTY_PROJTHROUGHMATRIX = (1 << 1), - DIRTY_FOGCOLOR = (1 << 2), - DIRTY_FOGCOEF = (1 << 3), - DIRTY_TEXENV = (1 << 4), - DIRTY_ALPHACOLORREF = (1 << 5), + DIRTY_FOGCOLOR = (1 << 2), + DIRTY_FOGCOEF = (1 << 3), + DIRTY_TEXENV = (1 << 4), + DIRTY_ALPHACOLORREF = (1 << 5), // 1 << 6 is free! Wait, not anymore... DIRTY_STENCILREPLACEVALUE = (1 << 6), @@ -155,7 +157,10 @@ enum { DIRTY_SHADERBLEND = (1 << 17), // Used only for in-shader blending. DIRTY_UVSCALEOFFSET = (1 << 18), // this will be dirtied ALL THE TIME... maybe we'll need to do "last value with this shader compares" + + // Texclamp is fairly rare so let's share it's bit with DIRTY_DEPTHRANGE. DIRTY_TEXCLAMP = (1 << 19), + DIRTY_DEPTHRANGE = (1 << 19), DIRTY_WORLDMATRIX = (1 << 21), DIRTY_VIEWMATRIX = (1 << 22), // Maybe we'll fold this into projmatrix eventually diff --git a/GPU/GLES/StateMapping.cpp b/GPU/GLES/StateMapping.cpp index 4a44f63dda..30aa49ebe4 100644 --- a/GPU/GLES/StateMapping.cpp +++ b/GPU/GLES/StateMapping.cpp @@ -871,11 +871,11 @@ void TransformDrawEngine::ApplyDrawState(int prim) { glstate.viewport.set(left, bottom, right - left, top - bottom); - float zScale = gstate.getViewportZScale() * (1.0f / 65535.0f); - float zCenter = gstate.getViewportZCenter() * (1.0f / 65535.0f); + float zScale = gstate.getViewportZScale(); + float zCenter = gstate.getViewportZCenter(); float depthRangeMin = zCenter - zScale; float depthRangeMax = zCenter + zScale; - glstate.depthRange.set(depthRangeMin, depthRangeMax); + glstate.depthRange.set(depthRangeMin * (1.0f / 65535.0f), depthRangeMax * (1.0f / 65535.0f)); #ifndef MOBILE_DEVICE float minz = gstate.getDepthRangeMin() * (1.0f / 65535.0f); diff --git a/GPU/GLES/VertexShaderGenerator.cpp b/GPU/GLES/VertexShaderGenerator.cpp index 252634a126..98974e5aa2 100644 --- a/GPU/GLES/VertexShaderGenerator.cpp +++ b/GPU/GLES/VertexShaderGenerator.cpp @@ -152,6 +152,33 @@ enum DoLightComputation { LIGHT_FULL, }; + +// Depth range and viewport +// +// After the multiplication with the projection matrix, we have a 4D vector in clip space. +// In OpenGL, Z is from -1 to 1, while in D3D, Z is from 0 to 1. +// PSP appears to use the OpenGL convention. As Z is from -1 to 1, and the viewport is represented +// by a center and a scale, to find the final Z value, all we need to do is to multiply by ZScale and +// add ZCenter - these are properly scaled to directly give a Z value in [0, 65535]. +// +// z = vec.z * ViewportZScale + ViewportZCenter; +// +// That will give us the final value between 0 and 65535, which we can simply floor to simulate +// the limited precision of the PSP's depth buffer. Then we convert it back: +// z = floor(z); +// +// vec.z = (z - ViewportZCenter) / ViewportZScale; +// +// Now, the regular machinery will take over and do the calculation again. +// +// All this above is for full transform mode. +// In through mode, the Z coordinate just goes straight through and there is no perspective division. +// We simulate this of course with pretty much an identity matrix. Rounding Z becomes very easy. +// +// TODO: Skip all this if we can actually get a 16-bit depth buffer along with stencil, which +// is a bit of a rare configuration, although quite common on mobile. + + void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransform) { char *p = buffer; @@ -214,6 +241,7 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf boneWeightDecl = boneWeightInDecl; } + bool lmode = gstate.isUsingSecondaryColor() && gstate.isLightingEnabled() && !gstate.isModeThrough(); bool doTexture = gstate.isTextureMapEnabled() && !gstate.isModeClear(); bool doTextureProjection = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX; @@ -336,6 +364,10 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf WRITE(p, "uniform highp vec2 u_fogcoef;\n"); } + if (!gstate.isModeThrough()) { + WRITE(p, "uniform highp vec4 u_depthRange;\n"); + } + WRITE(p, "%s %s lowp vec4 v_color0;\n", shading, varying); if (lmode) { WRITE(p, "%s %s lowp vec3 v_color1;\n", shading, varying); @@ -357,12 +389,24 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf } } - // Even with this we don't match, because the viewport is different I think. - // I think we need to apply viewportZ, round/floor, and then undo viewportZ. + // See comment above this function (GenerateVertexShader). WRITE(p, "\nvec4 depthRoundZ(vec4 v) {\n"); WRITE(p, " return vec4(v.x, v.y, (1.0/65536.0) * floor(v.z * 65536.0), v.w);\n"); WRITE(p, "}\n\n"); + if (!gstate.isModeThrough()) { + // Apply the transform to get the Z buffer value, floor to integer, undo the transform. + WRITE(p, "\nvec4 depthRoundZVP(vec4 v) {\n"); + WRITE(p, " float z = v.z / v.w;\n"); + WRITE(p, " z *= u_depthRange.x;\n"); + WRITE(p, " z += u_depthRange.y;\n"); + WRITE(p, " z = ceil(z);\n"); + WRITE(p, " z -= u_depthRange.y;\n"); + WRITE(p, " z /= u_depthRange.x;\n"); + WRITE(p, " return vec4(v.x, v.y, z * v.w, v.w);\n"); + WRITE(p, "}\n\n"); + } + WRITE(p, "void main() {\n"); if (!useHWTransform) { @@ -389,7 +433,8 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf if (gstate.isModeThrough()) { WRITE(p, " gl_Position = depthRoundZ(u_proj_through * vec4(position.xyz, 1.0));\n"); } else { - WRITE(p, " gl_Position = depthRoundZ(u_proj * vec4(position.xyz, 1.0));\n"); + // The viewport is used in this case, so need to compensate for that. + WRITE(p, " gl_Position = depthRoundZVP(u_proj * vec4(position.xyz, 1.0));\n"); } } else { // Step 1: World Transform / Skinning @@ -482,7 +527,7 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf WRITE(p, " vec4 viewPos = u_view * vec4(worldpos, 1.0);\n"); // Final view and projection transforms. - WRITE(p, " gl_Position = depthRoundZ(u_proj * viewPos);\n"); + WRITE(p, " gl_Position = depthRoundZVP(u_proj * viewPos);\n"); // TODO: Declare variables for dots for shade mapping if needed. From fee776b6e00a30b684e9d10d15c829a97416c445 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 26 Aug 2015 15:55:58 +0200 Subject: [PATCH 03/16] Correct scale factors for through mode depth rounding. --- GPU/GLES/ShaderManager.cpp | 2 +- GPU/GLES/VertexShaderGenerator.cpp | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/GPU/GLES/ShaderManager.cpp b/GPU/GLES/ShaderManager.cpp index 5fec17784c..79af01faf8 100644 --- a/GPU/GLES/ShaderManager.cpp +++ b/GPU/GLES/ShaderManager.cpp @@ -449,7 +449,7 @@ void LinkedShader::UpdateUniforms(u32 vertType) { if (dirty & DIRTY_PROJTHROUGHMATRIX) { Matrix4x4 proj_through; - proj_through.setOrtho(0.0f, gstate_c.curRTWidth, gstate_c.curRTHeight, 0, 0, 1); + proj_through.setOrtho(0.0f, gstate_c.curRTWidth, gstate_c.curRTHeight, 0, 0.0f, 1.0f); glUniformMatrix4fv(u_proj_through, 1, GL_FALSE, proj_through.getReadPtr()); } if (dirty & DIRTY_TEXENV) { diff --git a/GPU/GLES/VertexShaderGenerator.cpp b/GPU/GLES/VertexShaderGenerator.cpp index 98974e5aa2..cc31a3e6e0 100644 --- a/GPU/GLES/VertexShaderGenerator.cpp +++ b/GPU/GLES/VertexShaderGenerator.cpp @@ -391,17 +391,16 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf // See comment above this function (GenerateVertexShader). WRITE(p, "\nvec4 depthRoundZ(vec4 v) {\n"); - WRITE(p, " return vec4(v.x, v.y, (1.0/65536.0) * floor(v.z * 65536.0), v.w);\n"); + WRITE(p, " return vec4(v.x, v.y, (1.0/65535.0) * floor(v.z * 65535.0), v.w);\n"); WRITE(p, "}\n\n"); if (!gstate.isModeThrough()) { - // Apply the transform to get the Z buffer value, floor to integer, undo the transform. + // Apply the projection and viewport to get the Z buffer value, floor to integer, undo the viewport and projection. WRITE(p, "\nvec4 depthRoundZVP(vec4 v) {\n"); WRITE(p, " float z = v.z / v.w;\n"); - WRITE(p, " z *= u_depthRange.x;\n"); - WRITE(p, " z += u_depthRange.y;\n"); - WRITE(p, " z = ceil(z);\n"); - WRITE(p, " z -= u_depthRange.y;\n"); + WRITE(p, " z = z * u_depthRange.x + u_depthRange.y;\n"); + WRITE(p, " z = floor(z);\n"); + WRITE(p, " z = (z - u_depthRange.y) / u_depthRange.z;\n"); WRITE(p, " z /= u_depthRange.x;\n"); WRITE(p, " return vec4(v.x, v.y, z * v.w, v.w);\n"); WRITE(p, "}\n\n"); From c91b58ac8e57cc8a7efeab6463c9cc98b251e119 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 26 Aug 2015 16:07:11 +0200 Subject: [PATCH 04/16] Fix typo. This breaks it again though, strangely. --- GPU/GLES/VertexShaderGenerator.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/GPU/GLES/VertexShaderGenerator.cpp b/GPU/GLES/VertexShaderGenerator.cpp index cc31a3e6e0..c140dedac6 100644 --- a/GPU/GLES/VertexShaderGenerator.cpp +++ b/GPU/GLES/VertexShaderGenerator.cpp @@ -400,8 +400,7 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf WRITE(p, " float z = v.z / v.w;\n"); WRITE(p, " z = z * u_depthRange.x + u_depthRange.y;\n"); WRITE(p, " z = floor(z);\n"); - WRITE(p, " z = (z - u_depthRange.y) / u_depthRange.z;\n"); - WRITE(p, " z /= u_depthRange.x;\n"); + WRITE(p, " z = (z - u_depthRange.y) / u_depthRange.x;\n"); WRITE(p, " return vec4(v.x, v.y, z * v.w, v.w);\n"); WRITE(p, "}\n\n"); } From 44ce0c8dee4cad910802289acf6ba02d7ea2af7f Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 26 Aug 2015 16:07:40 +0200 Subject: [PATCH 05/16] Implement vertex shader depth rounding in DX9 --- GPU/Directx9/GPU_DX9.cpp | 2 +- GPU/Directx9/ShaderManagerDX9.h | 2 ++ GPU/Directx9/VertexShaderGeneratorDX9.cpp | 28 ++++++++++++++++++++--- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index 42502240fe..12ff6c44d6 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -889,7 +889,7 @@ void DIRECTX9_GPU::Execute_ViewportType(u32 op, u32 diff) { switch (op >> 24) { case GE_CMD_VIEWPORTZ1: case GE_CMD_VIEWPORTZ2: - shaderManager_->DirtyUniform(DIRTY_PROJMATRIX); + shaderManager_->DirtyUniform(DIRTY_PROJMATRIX | DIRTY_DEPTHRANGE); break; } } diff --git a/GPU/Directx9/ShaderManagerDX9.h b/GPU/Directx9/ShaderManagerDX9.h index 7ad723aac4..66f7061b2c 100644 --- a/GPU/Directx9/ShaderManagerDX9.h +++ b/GPU/Directx9/ShaderManagerDX9.h @@ -57,6 +57,8 @@ enum { DIRTY_UVSCALEOFFSET = (1 << 18), // this will be dirtied ALL THE TIME... maybe we'll need to do "last value with this shader compares" DIRTY_TEXCLAMP = (1 << 19), + DIRTY_DEPTHRANGE = (1 << 20), + DIRTY_WORLDMATRIX = (1 << 21), DIRTY_VIEWMATRIX = (1 << 22), // Maybe we'll fold this into projmatrix eventually DIRTY_TEXMATRIX = (1 << 23), diff --git a/GPU/Directx9/VertexShaderGeneratorDX9.cpp b/GPU/Directx9/VertexShaderGeneratorDX9.cpp index 9106d1a2f6..02cf29a292 100644 --- a/GPU/Directx9/VertexShaderGeneratorDX9.cpp +++ b/GPU/Directx9/VertexShaderGeneratorDX9.cpp @@ -226,6 +226,11 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) { } } + if (!gstate.isModeThrough()) { + WRITE(p, "float4 u_depthRange;\n"); + } + + // And the "varyings". if (useHWTransform) { WRITE(p, "struct VS_IN { \n"); if (vertTypeIsSkinningEnabled(vertType)) { @@ -279,6 +284,23 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) { } WRITE(p, "};\n"); + // See comment above this function in the OpenGL vertex shader generator (GenerateVertexShader). + WRITE(p, "\nfloat4 depthRoundZ(float4 v) {\n"); + WRITE(p, " return float4(v.x, v.y, (1.0/65535.0) * floor(v.z * 65535.0), v.w);\n"); + WRITE(p, "}\n\n"); + + if (!gstate.isModeThrough()) { + // Apply the projection and viewport to get the Z buffer value, floor to integer, undo the viewport and projection. + // Not completely sure this is 100% right under DX9 as the Z range is different... + WRITE(p, "\nfloat4 depthRoundZVP(float4 v) {\n"); + WRITE(p, " float z = v.z / v.w;\n"); + WRITE(p, " z = z * u_depthRange.x + u_depthRange.y;\n"); + WRITE(p, " z = floor(z);\n"); + WRITE(p, " z = (z - u_depthRange.y) / u_depthRange.x;\n"); + WRITE(p, " return float4(v.x, v.y, z * v.w, v.w);\n"); + WRITE(p, "}\n\n"); + } + WRITE(p, "VS_OUT main(VS_IN In) {\n"); WRITE(p, " VS_OUT Out = (VS_OUT)0; \n"); if (!useHWTransform) { @@ -307,9 +329,9 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) { WRITE(p, " Out.v_fogdepth.x = In.position.w;\n"); } if (gstate.isModeThrough()) { - WRITE(p, " Out.gl_Position = mul(float4(In.position.xyz, 1.0), u_proj_through);\n"); + WRITE(p, " Out.gl_Position = depthRoundZ(mul(float4(In.position.xyz, 1.0), u_proj_through));\n"); } else { - WRITE(p, " Out.gl_Position = mul(float4(In.position.xyz, 1.0), u_proj);\n"); + WRITE(p, " Out.gl_Position = depthRoundZVP(mul(float4(In.position.xyz, 1.0), u_proj));\n"); } } else { // Step 1: World Transform / Skinning @@ -398,7 +420,7 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) { WRITE(p, " float4 viewPos = float4(mul(float4(worldpos, 1.0), u_view), 1.0);\n"); // Final view and projection transforms. - WRITE(p, " Out.gl_Position = mul(viewPos, u_proj);\n"); + WRITE(p, " Out.gl_Position = depthRoundZVP(mul(viewPos, u_proj));\n"); // TODO: Declare variables for dots for shade mapping if needed. From 160980ee2eba850716b258488258830cd975cab9 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 26 Aug 2015 16:28:10 +0200 Subject: [PATCH 06/16] Properly dirty the uniform in Execute_ViewportType --- GPU/GLES/GLES_GPU.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/GPU/GLES/GLES_GPU.cpp b/GPU/GLES/GLES_GPU.cpp index d601d13f35..f9d53deab8 100644 --- a/GPU/GLES/GLES_GPU.cpp +++ b/GPU/GLES/GLES_GPU.cpp @@ -1012,6 +1012,12 @@ void GLES_GPU::Execute_FramebufType(u32 op, u32 diff) { void GLES_GPU::Execute_ViewportType(u32 op, u32 diff) { gstate_c.framebufChanged = true; gstate_c.textureChanged |= TEXCHANGE_PARAMSONLY; + switch (op >> 24) { + case GE_CMD_VIEWPORTZ1: + case GE_CMD_VIEWPORTZ2: + shaderManager_->DirtyUniform(DIRTY_DEPTHRANGE); + break; + } } void GLES_GPU::Execute_TexScaleU(u32 op, u32 diff) { From dedd2b60e4a66a42fe7a2609df8d536d1ba6291c Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 26 Aug 2015 17:02:07 +0200 Subject: [PATCH 07/16] Subtracting an offset of 0.5 works on my nVidia and I have no idea why, this makes no sense. --- GPU/GLES/VertexShaderGenerator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/GLES/VertexShaderGenerator.cpp b/GPU/GLES/VertexShaderGenerator.cpp index c140dedac6..7557f177ad 100644 --- a/GPU/GLES/VertexShaderGenerator.cpp +++ b/GPU/GLES/VertexShaderGenerator.cpp @@ -399,7 +399,7 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf WRITE(p, "\nvec4 depthRoundZVP(vec4 v) {\n"); WRITE(p, " float z = v.z / v.w;\n"); WRITE(p, " z = z * u_depthRange.x + u_depthRange.y;\n"); - WRITE(p, " z = floor(z);\n"); + WRITE(p, " z = floor(z - 0.5);\n"); WRITE(p, " z = (z - u_depthRange.y) / u_depthRange.x;\n"); WRITE(p, " return vec4(v.x, v.y, z * v.w, v.w);\n"); WRITE(p, "}\n\n"); From 5dbc8abd94b3727858776985cc7aa9b95b7ab3e0 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 26 Aug 2015 17:28:34 +0200 Subject: [PATCH 08/16] experiment: Add a different offset that unknown found during testing, plus remove rounding of through mode depth (which should already be rounded) --- GPU/GLES/VertexShaderGenerator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GPU/GLES/VertexShaderGenerator.cpp b/GPU/GLES/VertexShaderGenerator.cpp index 7557f177ad..b77935a3c5 100644 --- a/GPU/GLES/VertexShaderGenerator.cpp +++ b/GPU/GLES/VertexShaderGenerator.cpp @@ -391,7 +391,7 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf // See comment above this function (GenerateVertexShader). WRITE(p, "\nvec4 depthRoundZ(vec4 v) {\n"); - WRITE(p, " return vec4(v.x, v.y, (1.0/65535.0) * floor(v.z * 65535.0), v.w);\n"); + WRITE(p, " return v; //vec4(v.x, v.y, (1.0/65535.0) * floor(v.z * 65535.0), v.w);\n"); WRITE(p, "}\n\n"); if (!gstate.isModeThrough()) { @@ -399,7 +399,7 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf WRITE(p, "\nvec4 depthRoundZVP(vec4 v) {\n"); WRITE(p, " float z = v.z / v.w;\n"); WRITE(p, " z = z * u_depthRange.x + u_depthRange.y;\n"); - WRITE(p, " z = floor(z - 0.5);\n"); + WRITE(p, " z = floor(z + 0.375);\n"); WRITE(p, " z = (z - u_depthRange.y) / u_depthRange.x;\n"); WRITE(p, " return vec4(v.x, v.y, z * v.w, v.w);\n"); WRITE(p, "}\n\n"); From d89da71057ee5db7401788be31f9c8cd19c4f472 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 26 Aug 2015 17:40:06 +0200 Subject: [PATCH 09/16] Get rid of a division in the vertex shader by precomputing the inverse. --- GPU/Directx9/VertexShaderGeneratorDX9.cpp | 9 ++------- GPU/GLES/ShaderManager.cpp | 8 ++++---- GPU/GLES/VertexShaderGenerator.cpp | 10 +++------- 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/GPU/Directx9/VertexShaderGeneratorDX9.cpp b/GPU/Directx9/VertexShaderGeneratorDX9.cpp index 02cf29a292..f9e29467fd 100644 --- a/GPU/Directx9/VertexShaderGeneratorDX9.cpp +++ b/GPU/Directx9/VertexShaderGeneratorDX9.cpp @@ -284,11 +284,6 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) { } WRITE(p, "};\n"); - // See comment above this function in the OpenGL vertex shader generator (GenerateVertexShader). - WRITE(p, "\nfloat4 depthRoundZ(float4 v) {\n"); - WRITE(p, " return float4(v.x, v.y, (1.0/65535.0) * floor(v.z * 65535.0), v.w);\n"); - WRITE(p, "}\n\n"); - if (!gstate.isModeThrough()) { // Apply the projection and viewport to get the Z buffer value, floor to integer, undo the viewport and projection. // Not completely sure this is 100% right under DX9 as the Z range is different... @@ -296,7 +291,7 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) { WRITE(p, " float z = v.z / v.w;\n"); WRITE(p, " z = z * u_depthRange.x + u_depthRange.y;\n"); WRITE(p, " z = floor(z);\n"); - WRITE(p, " z = (z - u_depthRange.y) / u_depthRange.x;\n"); + WRITE(p, " z = (z - u_depthRange.z) * u_depthRange.w;\n"); WRITE(p, " return float4(v.x, v.y, z * v.w, v.w);\n"); WRITE(p, "}\n\n"); } @@ -329,7 +324,7 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) { WRITE(p, " Out.v_fogdepth.x = In.position.w;\n"); } if (gstate.isModeThrough()) { - WRITE(p, " Out.gl_Position = depthRoundZ(mul(float4(In.position.xyz, 1.0), u_proj_through));\n"); + WRITE(p, " Out.gl_Position = mul(float4(In.position.xyz, 1.0), u_proj_through);\n"); } else { WRITE(p, " Out.gl_Position = depthRoundZVP(mul(float4(In.position.xyz, 1.0), u_proj));\n"); } diff --git a/GPU/GLES/ShaderManager.cpp b/GPU/GLES/ShaderManager.cpp index 79af01faf8..79c6be1e08 100644 --- a/GPU/GLES/ShaderManager.cpp +++ b/GPU/GLES/ShaderManager.cpp @@ -591,10 +591,10 @@ void LinkedShader::UpdateUniforms(u32 vertType) { } if ((dirty & DIRTY_DEPTHRANGE) && u_depthRange != -1) { float viewZScale = gstate.getViewportZScale(); - float viewZCenter = gstate.getViewportZCenter(); - float minZ = gstate.getDepthRangeMin(); - float maxZ = gstate.getDepthRangeMax(); - float data[4] = { viewZScale, viewZCenter, minZ, maxZ }; + float viewZCenter = gstate.getViewportZCenter() + 0.375; + float viewZCenterClean = gstate.getViewportZCenter(); + float viewZInvScale = 1.0f / gstate.getViewportZScale(); + float data[4] = { viewZScale, viewZCenter, viewZCenterClean, viewZInvScale }; SetFloatUniform4(u_depthRange, data); } diff --git a/GPU/GLES/VertexShaderGenerator.cpp b/GPU/GLES/VertexShaderGenerator.cpp index b77935a3c5..379043ca0b 100644 --- a/GPU/GLES/VertexShaderGenerator.cpp +++ b/GPU/GLES/VertexShaderGenerator.cpp @@ -390,17 +390,13 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf } // See comment above this function (GenerateVertexShader). - WRITE(p, "\nvec4 depthRoundZ(vec4 v) {\n"); - WRITE(p, " return v; //vec4(v.x, v.y, (1.0/65535.0) * floor(v.z * 65535.0), v.w);\n"); - WRITE(p, "}\n\n"); - if (!gstate.isModeThrough()) { // Apply the projection and viewport to get the Z buffer value, floor to integer, undo the viewport and projection. WRITE(p, "\nvec4 depthRoundZVP(vec4 v) {\n"); WRITE(p, " float z = v.z / v.w;\n"); WRITE(p, " z = z * u_depthRange.x + u_depthRange.y;\n"); - WRITE(p, " z = floor(z + 0.375);\n"); - WRITE(p, " z = (z - u_depthRange.y) / u_depthRange.x;\n"); + WRITE(p, " z = floor(z);\n"); + WRITE(p, " z = (z - u_depthRange.z) * u_depthRange.w;\n"); WRITE(p, " return vec4(v.x, v.y, z * v.w, v.w);\n"); WRITE(p, "}\n\n"); } @@ -429,7 +425,7 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf WRITE(p, " v_fogdepth = position.w;\n"); } if (gstate.isModeThrough()) { - WRITE(p, " gl_Position = depthRoundZ(u_proj_through * vec4(position.xyz, 1.0));\n"); + WRITE(p, " gl_Position = u_proj_through * vec4(position.xyz, 1.0);\n"); } else { // The viewport is used in this case, so need to compensate for that. WRITE(p, " gl_Position = depthRoundZVP(u_proj * vec4(position.xyz, 1.0));\n"); From bf6aef44cd3c6034b5421249e579fed6469de0e8 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 26 Aug 2015 17:45:21 +0200 Subject: [PATCH 10/16] Oops, didn't finish the D3D code --- GPU/Directx9/ShaderManagerDX9.cpp | 12 ++++++++++++ GPU/Directx9/ShaderManagerDX9.h | 1 + GPU/Directx9/VertexShaderGeneratorDX9.cpp | 2 +- GPU/Directx9/VertexShaderGeneratorDX9.h | 1 + 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index 989befb2ec..489aa616bf 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -176,6 +176,10 @@ void ShaderManagerDX9::VSSetColorUniform3(int creg, u32 color) { pD3Ddevice->SetVertexShaderConstantF(creg, col, 1); } +void ShaderManagerDX9::VSSetFloatUniform4(int creg, float data[4]) { + pD3Ddevice->SetVertexShaderConstantF(creg, data, 1); +} + void ShaderManagerDX9::VSSetFloat24Uniform3(int creg, const u32 data[3]) { const u32 col[4] = { data[0] >> 8, data[1] >> 8, data[2] >> 8, 0 @@ -475,6 +479,14 @@ void ShaderManagerDX9::VSUpdateUniforms(int dirtyUniforms) { VSSetFloatArray(CONST_VS_UVSCALEOFFSET, uvscaleoff, 4); } + if (dirtyUniforms & DIRTY_DEPTHRANGE) { + float viewZScale = gstate.getViewportZScale(); + float viewZCenter = gstate.getViewportZCenter() + 0.375; + float viewZCenterClean = gstate.getViewportZCenter(); + float viewZInvScale = 1.0f / gstate.getViewportZScale(); + float data[4] = { viewZScale, viewZCenter, viewZCenterClean, viewZInvScale }; + VSSetFloatUniform4(CONST_VS_DEPTHRANGE, data); + } // Lighting if (dirtyUniforms & DIRTY_AMBIENT) { VSSetColorUniform3Alpha(CONST_VS_AMBIENT, gstate.ambientcolor, gstate.getAmbientA()); diff --git a/GPU/Directx9/ShaderManagerDX9.h b/GPU/Directx9/ShaderManagerDX9.h index 66f7061b2c..fde7fee71d 100644 --- a/GPU/Directx9/ShaderManagerDX9.h +++ b/GPU/Directx9/ShaderManagerDX9.h @@ -145,6 +145,7 @@ private: void VSSetFloat(int creg, float value); void VSSetFloatArray(int creg, const float *value, int count); void VSSetFloat24Uniform3(int creg, const u32 data[3]); + void VSSetFloatUniform4(int creg, float data[4]); void Clear(); diff --git a/GPU/Directx9/VertexShaderGeneratorDX9.cpp b/GPU/Directx9/VertexShaderGeneratorDX9.cpp index f9e29467fd..12acdfe0d0 100644 --- a/GPU/Directx9/VertexShaderGeneratorDX9.cpp +++ b/GPU/Directx9/VertexShaderGeneratorDX9.cpp @@ -227,7 +227,7 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) { } if (!gstate.isModeThrough()) { - WRITE(p, "float4 u_depthRange;\n"); + WRITE(p, "float4 u_depthRange : register(c%i);\n", CONST_VS_DEPTHRANGE); } // And the "varyings". diff --git a/GPU/Directx9/VertexShaderGeneratorDX9.h b/GPU/Directx9/VertexShaderGeneratorDX9.h index b708003daf..2b29ad03af 100644 --- a/GPU/Directx9/VertexShaderGeneratorDX9.h +++ b/GPU/Directx9/VertexShaderGeneratorDX9.h @@ -87,5 +87,6 @@ int TranslateNumBonesDX9(int bones); #define CONST_VS_LIGHTDIFFUSE 71 #define CONST_VS_LIGHTSPECULAR 75 #define CONST_VS_LIGHTAMBIENT 79 +#define CONST_VS_DEPTHRANGE 83 }; From 7e3292d275b2a38c7ab520d2eb6da7453847bbcc Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 26 Aug 2015 18:46:55 +0200 Subject: [PATCH 11/16] Minor optimization --- GPU/GLES/GLES_GPU.cpp | 16 ++++++++-------- GPU/GLES/GLES_GPU.h | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/GPU/GLES/GLES_GPU.cpp b/GPU/GLES/GLES_GPU.cpp index f9d53deab8..41a2616281 100644 --- a/GPU/GLES/GLES_GPU.cpp +++ b/GPU/GLES/GLES_GPU.cpp @@ -206,8 +206,8 @@ static const CommandTableEntry commandTable[] = { {GE_CMD_VIEWPORTY1, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GLES_GPU::Execute_ViewportType}, {GE_CMD_VIEWPORTX2, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GLES_GPU::Execute_ViewportType}, {GE_CMD_VIEWPORTY2, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GLES_GPU::Execute_ViewportType}, - {GE_CMD_VIEWPORTZ1, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, DIRTY_DEPTHRANGE, &GLES_GPU::Execute_ViewportType}, - {GE_CMD_VIEWPORTZ2, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, DIRTY_DEPTHRANGE, &GLES_GPU::Execute_ViewportType}, + {GE_CMD_VIEWPORTZ1, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, DIRTY_DEPTHRANGE, &GLES_GPU::Execute_ViewportZType}, + {GE_CMD_VIEWPORTZ2, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, DIRTY_DEPTHRANGE, &GLES_GPU::Execute_ViewportZType}, // Region {GE_CMD_REGION1, FLAG_FLUSHBEFOREONCHANGE | FLAG_EXECUTEONCHANGE, 0, &GLES_GPU::Execute_Region}, @@ -1012,12 +1012,12 @@ void GLES_GPU::Execute_FramebufType(u32 op, u32 diff) { void GLES_GPU::Execute_ViewportType(u32 op, u32 diff) { gstate_c.framebufChanged = true; gstate_c.textureChanged |= TEXCHANGE_PARAMSONLY; - switch (op >> 24) { - case GE_CMD_VIEWPORTZ1: - case GE_CMD_VIEWPORTZ2: - shaderManager_->DirtyUniform(DIRTY_DEPTHRANGE); - break; - } +} + +void GLES_GPU::Execute_ViewportZType(u32 op, u32 diff) { + gstate_c.framebufChanged = true; + gstate_c.textureChanged |= TEXCHANGE_PARAMSONLY; + shaderManager_->DirtyUniform(DIRTY_DEPTHRANGE); } void GLES_GPU::Execute_TexScaleU(u32 op, u32 diff) { diff --git a/GPU/GLES/GLES_GPU.h b/GPU/GLES/GLES_GPU.h index 711f34c726..052b409e96 100644 --- a/GPU/GLES/GLES_GPU.h +++ b/GPU/GLES/GLES_GPU.h @@ -101,6 +101,7 @@ public: void Execute_Scissor(u32 op, u32 diff); void Execute_FramebufType(u32 op, u32 diff); void Execute_ViewportType(u32 op, u32 diff); + void Execute_ViewportZType(u32 op, u32 diff); void Execute_TexScaleU(u32 op, u32 diff); void Execute_TexScaleV(u32 op, u32 diff); void Execute_TexOffsetU(u32 op, u32 diff); From fd18f145082bb1bf2281cda3607c44289bcaa40f Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Thu, 27 Aug 2015 17:58:41 +0200 Subject: [PATCH 12/16] Fix GT and hopefully other games that got a black screen --- GPU/GLES/ShaderManager.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/GPU/GLES/ShaderManager.cpp b/GPU/GLES/ShaderManager.cpp index 79c6be1e08..af6cd93039 100644 --- a/GPU/GLES/ShaderManager.cpp +++ b/GPU/GLES/ShaderManager.cpp @@ -593,7 +593,12 @@ void LinkedShader::UpdateUniforms(u32 vertType) { float viewZScale = gstate.getViewportZScale(); float viewZCenter = gstate.getViewportZCenter() + 0.375; float viewZCenterClean = gstate.getViewportZCenter(); - float viewZInvScale = 1.0f / gstate.getViewportZScale(); + float viewZInvScale; + if (viewZScale != 0.0) { + viewZInvScale = 1.0f / gstate.getViewportZScale(); + } else { + viewZInvScale = 0.0; + } float data[4] = { viewZScale, viewZCenter, viewZCenterClean, viewZInvScale }; SetFloatUniform4(u_depthRange, data); } From 2b7d08e3afa204e4fb45776c96c342d53e38b142 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Thu, 27 Aug 2015 18:01:00 +0200 Subject: [PATCH 13/16] Also apply the GT fix in D3D, doh --- GPU/Directx9/ShaderManagerDX9.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index 489aa616bf..991dee1801 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -483,7 +483,12 @@ void ShaderManagerDX9::VSUpdateUniforms(int dirtyUniforms) { float viewZScale = gstate.getViewportZScale(); float viewZCenter = gstate.getViewportZCenter() + 0.375; float viewZCenterClean = gstate.getViewportZCenter(); - float viewZInvScale = 1.0f / gstate.getViewportZScale(); + float viewZInvScale; + if (viewZScale != 0.0) { + viewZInvScale = 1.0f / gstate.getViewportZScale(); + } else { + viewZInvScale = 0.0; + } float data[4] = { viewZScale, viewZCenter, viewZCenterClean, viewZInvScale }; VSSetFloatUniform4(CONST_VS_DEPTHRANGE, data); } From 76eacf32f4ba296ff80da9559b6e3fca8f010095 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Thu, 27 Aug 2015 18:02:45 +0200 Subject: [PATCH 14/16] Change the offset to 0.0 --- GPU/Directx9/ShaderManagerDX9.cpp | 2 +- GPU/GLES/ShaderManager.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index 991dee1801..98fb0df3b8 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -481,7 +481,7 @@ void ShaderManagerDX9::VSUpdateUniforms(int dirtyUniforms) { if (dirtyUniforms & DIRTY_DEPTHRANGE) { float viewZScale = gstate.getViewportZScale(); - float viewZCenter = gstate.getViewportZCenter() + 0.375; + float viewZCenter = gstate.getViewportZCenter(); float viewZCenterClean = gstate.getViewportZCenter(); float viewZInvScale; if (viewZScale != 0.0) { diff --git a/GPU/GLES/ShaderManager.cpp b/GPU/GLES/ShaderManager.cpp index af6cd93039..d8e592669b 100644 --- a/GPU/GLES/ShaderManager.cpp +++ b/GPU/GLES/ShaderManager.cpp @@ -591,7 +591,7 @@ void LinkedShader::UpdateUniforms(u32 vertType) { } if ((dirty & DIRTY_DEPTHRANGE) && u_depthRange != -1) { float viewZScale = gstate.getViewportZScale(); - float viewZCenter = gstate.getViewportZCenter() + 0.375; + float viewZCenter = gstate.getViewportZCenter(); float viewZCenterClean = gstate.getViewportZCenter(); float viewZInvScale; if (viewZScale != 0.0) { From 7b0a31ade539f9e1e90801cf79a2b12deb5d2e00 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sat, 29 Aug 2015 17:43:09 +0200 Subject: [PATCH 15/16] Adjust D3D depth calculations to get them to match OpenGL as close as I can get it. --- GPU/Directx9/ShaderManagerDX9.cpp | 13 ++++++++++--- GPU/Directx9/StateMappingDX9.cpp | 8 +++++--- GPU/Directx9/VertexShaderGeneratorDX9.cpp | 2 ++ GPU/GLES/ShaderManager.cpp | 5 ++--- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index 98fb0df3b8..8874b325c5 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -482,14 +482,21 @@ void ShaderManagerDX9::VSUpdateUniforms(int dirtyUniforms) { if (dirtyUniforms & DIRTY_DEPTHRANGE) { float viewZScale = gstate.getViewportZScale(); float viewZCenter = gstate.getViewportZCenter(); - float viewZCenterClean = gstate.getViewportZCenter(); + + // Adjust for D3D projection matrix. We got squashed up to only 0-1, so we multiply + // the scale factor by 2, and add an offset. + // Given the way we do the rounding, the offset is probably mostly irrelevant as we cancel + // it afterwards anyway. + viewZScale *= 2.0f; + viewZCenter -= 32768.0f; float viewZInvScale; if (viewZScale != 0.0) { - viewZInvScale = 1.0f / gstate.getViewportZScale(); + viewZInvScale = 1.0f / viewZScale; } else { viewZInvScale = 0.0; } - float data[4] = { viewZScale, viewZCenter, viewZCenterClean, viewZInvScale }; + + float data[4] = { viewZScale, viewZCenter, viewZCenter, viewZInvScale }; VSSetFloatUniform4(CONST_VS_DEPTHRANGE, data); } // Lighting diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index 65aefcc292..f67f05d694 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -745,10 +745,12 @@ void TransformDrawEngineDX9::ApplyDrawState(int prim) { float zScale = gstate.getViewportZScale() / 65535.0f; float zCenter = gstate.getViewportZCenter() / 65535.0f; - // Note - we lose the sign of the zscale here. Although I suppose we still keep it in gstate_c.vpDepth... - float depthRangeMin = zCenter - fabsf(zScale); - float depthRangeMax = zCenter + fabsf(zScale); + // Note - We lose the sign of the zscale here. But we keep it in gstate_c.vpDepth. + // That variable is only check for sign later so the multiplication by 2 isn't really necessary. + // It's unclear why we need this Z offset of 1 to match OpenGL, but this checks out in multiple games. + float depthRangeMin = zCenter - fabsf(zScale) - 1.0f/65535.0f; + float depthRangeMax = zCenter + fabsf(zScale) - 1.0f/65535.0f; gstate_c.vpDepth = zScale * 2; // D3D doesn't like viewports partially outside the target, so we diff --git a/GPU/Directx9/VertexShaderGeneratorDX9.cpp b/GPU/Directx9/VertexShaderGeneratorDX9.cpp index 12acdfe0d0..dba65ed8cc 100644 --- a/GPU/Directx9/VertexShaderGeneratorDX9.cpp +++ b/GPU/Directx9/VertexShaderGeneratorDX9.cpp @@ -284,6 +284,8 @@ void GenerateVertexShaderDX9(int prim, char *buffer, bool useHWTransform) { } WRITE(p, "};\n"); + // 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()) { // Apply the projection and viewport to get the Z buffer value, floor to integer, undo the viewport and projection. // Not completely sure this is 100% right under DX9 as the Z range is different... diff --git a/GPU/GLES/ShaderManager.cpp b/GPU/GLES/ShaderManager.cpp index d8e592669b..3216572d45 100644 --- a/GPU/GLES/ShaderManager.cpp +++ b/GPU/GLES/ShaderManager.cpp @@ -592,14 +592,13 @@ void LinkedShader::UpdateUniforms(u32 vertType) { if ((dirty & DIRTY_DEPTHRANGE) && u_depthRange != -1) { float viewZScale = gstate.getViewportZScale(); float viewZCenter = gstate.getViewportZCenter(); - float viewZCenterClean = gstate.getViewportZCenter(); float viewZInvScale; if (viewZScale != 0.0) { - viewZInvScale = 1.0f / gstate.getViewportZScale(); + viewZInvScale = 1.0f / viewZScale; } else { viewZInvScale = 0.0; } - float data[4] = { viewZScale, viewZCenter, viewZCenterClean, viewZInvScale }; + float data[4] = { viewZScale, viewZCenter, viewZCenter, viewZInvScale }; SetFloatUniform4(u_depthRange, data); } From 488c62e5349d3d4bb8bc82aecb173deb71fe2843 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sat, 29 Aug 2015 20:38:19 +0200 Subject: [PATCH 16/16] This makes slightly more sense than the last commit. --- GPU/Directx9/ShaderManagerDX9.cpp | 4 ++-- GPU/Directx9/StateMappingDX9.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index 8874b325c5..0ee849b940 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -485,10 +485,10 @@ void ShaderManagerDX9::VSUpdateUniforms(int dirtyUniforms) { // Adjust for D3D projection matrix. We got squashed up to only 0-1, so we multiply // the scale factor by 2, and add an offset. - // Given the way we do the rounding, the offset is probably mostly irrelevant as we cancel + // Given the way we do the rounding, the integer part of the offset is probably mostly irrelevant as we cancel // it afterwards anyway. viewZScale *= 2.0f; - viewZCenter -= 32768.0f; + viewZCenter -= 32767.5f; float viewZInvScale; if (viewZScale != 0.0) { viewZInvScale = 1.0f / viewZScale; diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index f67f05d694..e671a2df8b 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -749,8 +749,8 @@ void TransformDrawEngineDX9::ApplyDrawState(int prim) { // That variable is only check for sign later so the multiplication by 2 isn't really necessary. // It's unclear why we need this Z offset of 1 to match OpenGL, but this checks out in multiple games. - float depthRangeMin = zCenter - fabsf(zScale) - 1.0f/65535.0f; - float depthRangeMax = zCenter + fabsf(zScale) - 1.0f/65535.0f; + float depthRangeMin = zCenter - fabsf(zScale) - 0.5f/65535.0f; + float depthRangeMax = zCenter + fabsf(zScale) - 0.5f/65535.0f; gstate_c.vpDepth = zScale * 2; // D3D doesn't like viewports partially outside the target, so we