diff --git a/GPU/Common/ShaderUniforms.cpp b/GPU/Common/ShaderUniforms.cpp index e92448c7ed..20dd1e1b14 100644 --- a/GPU/Common/ShaderUniforms.cpp +++ b/GPU/Common/ShaderUniforms.cpp @@ -34,7 +34,7 @@ void BaseUpdateUniforms(UB_VS_FS_Base *ub, uint64_t dirtyUniforms, bool flipView Uint8x3ToInt4_Alpha(ub->colorTestMask, gstate.getColorTestMask(), gstate.getAlphaTestMask()); } if (dirtyUniforms & DIRTY_FOGCOLOR) { - Uint8x3ToFloat4(ub->fogColor, gstate.fogcolor); + Uint8x3ToFloat4_Alpha(ub->fogColor, gstate.fogcolor, NAN); } if (dirtyUniforms & DIRTY_SHADERBLEND) { Uint8x3ToFloat4(ub->blendFixA, gstate.getFixA()); diff --git a/GPU/Common/ShaderUniforms.h b/GPU/Common/ShaderUniforms.h index 55f08f7658..7db09bd724 100644 --- a/GPU/Common/ShaderUniforms.h +++ b/GPU/Common/ShaderUniforms.h @@ -59,7 +59,7 @@ R"( mat4 proj_mtx; int spline_count_v; int spline_type_u; int spline_type_v; - vec3 fogcolor; + vec3 fogcolor; float nanValue; vec3 texenv; ivec4 alphacolorref; ivec4 alphacolormask; @@ -84,7 +84,7 @@ R"( float4x4 u_proj; int u_spline_count_v; int u_spline_type_u; int u_spline_type_v; - float3 u_fogcolor; + float3 u_fogcolor; float nanValue; float3 u_texenv; uint4 u_alphacolorref; uint4 u_alphacolormask; diff --git a/GPU/Directx9/VertexShaderGeneratorDX9.cpp b/GPU/Directx9/VertexShaderGeneratorDX9.cpp index 01fd322a5e..4313123d73 100644 --- a/GPU/Directx9/VertexShaderGeneratorDX9.cpp +++ b/GPU/Directx9/VertexShaderGeneratorDX9.cpp @@ -37,6 +37,16 @@ namespace DX9 { +// The PSP does not have a proper triangle clipper, but it does have a guard band and can rasterize rather large +// triangles that go outside the viewport. However, there are limits, and it will drop triangles that are very +// large. Some games appear to draw broken geometry, probably game bugs that were never discovered because the PSP +// would drop the geometry, including Parappa The Rapper in an obscure case and Outrun. Try to get rid of those +// triangles by setting the W of one of the vertices to NaN if they are discovered. +const bool guardBandCulling = true; +// Not sure what a good value for this is, it should probably depend on the framebuffer size. +// Let's be conservative. +const float guardBand = 64.0f; + static const char * const boneWeightAttrDecl[9] = { "#ERROR#", "float a_w1:TEXCOORD1;\n", @@ -344,6 +354,7 @@ void GenerateVertexShaderHLSL(const ShaderID &id, char *buffer, ShaderLanguage l } } + WRITE(p, "VS_OUT main(VS_IN In) {\n"); WRITE(p, " VS_OUT Out;\n"); if (!useHWTransform) { @@ -369,26 +380,32 @@ void GenerateVertexShaderHLSL(const ShaderID &id, char *buffer, ShaderLanguage l } if (lang == HLSL_D3D11 || lang == HLSL_D3D11_LEVEL9) { if (gstate.isModeThrough()) { - WRITE(p, " Out.gl_Position = mul(u_proj_through, float4(In.position.xyz, 1.0));\n"); + WRITE(p, " float4 outPos = mul(u_proj_through, float4(In.position.xyz, 1.0));\n"); } else { if (gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) { - WRITE(p, " Out.gl_Position = depthRoundZVP(mul(u_proj, float4(In.position.xyz, 1.0)));\n"); + WRITE(p, " float4 outPos = depthRoundZVP(mul(u_proj, float4(In.position.xyz, 1.0)));\n"); } else { - WRITE(p, " Out.gl_Position = mul(u_proj, float4(In.position.xyz, 1.0));\n"); + WRITE(p, " float4 outPos = mul(u_proj, float4(In.position.xyz, 1.0));\n"); } } } else { if (gstate.isModeThrough()) { - WRITE(p, " Out.gl_Position = mul(float4(In.position.xyz, 1.0), u_proj_through);\n"); + WRITE(p, " float4 outPos = mul(float4(In.position.xyz, 1.0), u_proj_through);\n"); } else { if (gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) { - WRITE(p, " Out.gl_Position = depthRoundZVP(mul(float4(In.position.xyz, 1.0), u_proj));\n"); + WRITE(p, " float4 outPos = depthRoundZVP(mul(float4(In.position.xyz, 1.0), u_proj));\n"); } else { - WRITE(p, " Out.gl_Position = mul(float4(In.position.xyz, 1.0), u_proj);\n"); + WRITE(p, " float4 outPos = mul(float4(In.position.xyz, 1.0), u_proj);\n"); } } } - } else { + if (lang != HLSL_DX9 && guardBandCulling) { + // Guard band culling + WRITE(p, " float2 projPos = outPos.xy / outPos.w;\n"); + WRITE(p, " if (abs(projPos.x) > %f || abs(projPos.y) > %f) outPos.w = nanValue;\n", guardBand, guardBand); + } + WRITE(p, " Out.gl_Position = outPos;\n"); + } else { // Step 1: World Transform / Skinning if (!enableBones) { // Hardware tessellation @@ -574,18 +591,23 @@ void GenerateVertexShaderHLSL(const ShaderID &id, char *buffer, ShaderLanguage l if (lang == HLSL_D3D11 || lang == HLSL_D3D11_LEVEL9) { // Final view and projection transforms. if (gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) { - WRITE(p, " Out.gl_Position = depthRoundZVP(mul(u_proj, viewPos));\n"); + WRITE(p, " float4 outPos = depthRoundZVP(mul(u_proj, viewPos));\n"); } else { - WRITE(p, " Out.gl_Position = mul(u_proj, viewPos);\n"); + WRITE(p, " float4 outPos = mul(u_proj, viewPos);\n"); } } else { // Final view and projection transforms. if (gstate_c.Supports(GPU_ROUND_DEPTH_TO_16BIT)) { - WRITE(p, " Out.gl_Position = depthRoundZVP(mul(viewPos, u_proj));\n"); + WRITE(p, " float4 outPos = depthRoundZVP(mul(viewPos, u_proj));\n"); } else { - WRITE(p, " Out.gl_Position = mul(viewPos, u_proj);\n"); + WRITE(p, " float4 outPos = mul(viewPos, u_proj);\n"); } } + if (lang != HLSL_DX9 && guardBandCulling) { + WRITE(p, " float2 projPos = outPos.xy / outPos.w;\n"); + WRITE(p, " if (abs(projPos.x) > %f || abs(projPos.y) > %f) outPos.w = nanValue;\n", guardBand, guardBand); + } + WRITE(p, " Out.gl_Position = outPos;\n"); // TODO: Declare variables for dots for shade mapping if needed.