diff --git a/GPU/Common/FragmentShaderGenerator.cpp b/GPU/Common/FragmentShaderGenerator.cpp index c8d40987fc..8e53e80a5d 100644 --- a/GPU/Common/FragmentShaderGenerator.cpp +++ b/GPU/Common/FragmentShaderGenerator.cpp @@ -186,7 +186,7 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu } bool needFragCoord = readFramebufferTex || gstate_c.Use(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT); - bool writeDepth = (gstate_c.Use(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT) && !forceDepthWritesOff) || fsDepthClamp; + bool writeDepth = (gstate_c.Use(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT) || fsDepthClamp) && !forceDepthWritesOff; // TODO: We could have a separate mechanism to support more ops using the shader blending mechanism, // on hardware that can do proper bit math in fragment shaders. @@ -1180,36 +1180,26 @@ bool GenerateFragmentShader(const FShaderID &id, char *buffer, const ShaderLangu if (fsMinmaxDiscard) { // See the vertex shader generator for the explanation for this. WRITE(p, " float clipZ = floor(projZ * 0.5 + 0.5) * 2.0;\n"); - WRITE(p, " if (u_minZmaxZ.x > 0 && clipZ < u_minZmaxZ.x) DISCARD;\n"); - WRITE(p, " if (u_minZmaxZ.y < 65535 && clipZ > u_minZmaxZ.y) DISCARD;\n"); + WRITE(p, " if (u_minZmaxZ.x > 0.0 && clipZ < u_minZmaxZ.x) DISCARD;\n"); + WRITE(p, " if (u_minZmaxZ.y < 65535.0 && clipZ > u_minZmaxZ.y) DISCARD;\n"); } - if (gstate_c.Use(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT)) { - DepthScaleFactors depthScale = GetDepthScaleFactors(gstate_c.UseFlags()); - - const double scale = depthScale.ScaleU16(); - - if (fsDepthClamp) { - WRITE(p, " highp float z = clamp(projZ, 0.0, 65536.0) / 65536.0;\n"); - } else { - WRITE(p, " highp float z = gl_FragCoord.z;\n"); + if (writeDepth) { + if (gstate_c.Use(GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT)) { + if (fsDepthClamp) { + WRITE(p, " gl_FragDepth = clamp(floor(projZ), 0.0, 65536.0) / 65536.0;\n"); + } else { + WRITE(p, " gl_FragDepth = floor(gl_FragCoord.z) / 65536.0;\n"); + } + } else if (fsDepthClamp) { + WRITE(p, " gl_FragDepth = clamp(projZ, 0.0, 65536.0) / 65536.0;\n"); + } else if (useDiscardStencilBugWorkaround) { + // Adreno and some Mali drivers apply early frag tests even with discard in the shader, + // when only stencil is used. The exact situation seems to vary by driver. + // Writing depth prevents the bug for both vendors, even with depth_unchanged specified. + // This doesn't make a ton of sense, but empirically does work. + WRITE(p, " gl_FragDepth = gl_FragCoord.z;\n"); } - // We center the depth with an offset, but only its fraction matters. - // When (DepthSliceFactor() - 1) is odd, it will be 0.5, otherwise 0. - if (((int)(depthScale.Scale() - 1.0f) & 1) == 1) { - WRITE(p, " z = (floor((z * %f) - (1.0 / 2.0)) + (1.0 / 2.0)) * (1.0 / %f);\n", scale, scale); - } else { - WRITE(p, " z = floor(z * %f) * (1.0 / %f);\n", scale, scale); - } - WRITE(p, " gl_FragDepth = z;\n"); - } else if (fsDepthClamp) { - WRITE(p, " gl_FragDepth = clamp(projZ, 0.0, 65536.0) / 65536.0;\n"); - } else if (useDiscardStencilBugWorkaround) { - // Adreno and some Mali drivers apply early frag tests even with discard in the shader, - // when only stencil is used. The exact situation seems to vary by driver. - // Writing depth prevents the bug for both vendors, even with depth_unchanged specified. - // This doesn't make a ton of sense, but empirically does work. - WRITE(p, " gl_FragDepth = gl_FragCoord.z;\n"); } if (compat.shaderLanguage == HLSL_D3D11) { diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index ee1c7dd129..9236bdcb87 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -299,20 +299,20 @@ inline u32 SanitizeBlendMode(GEBlendMode mode) { // 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 ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pipelineState, const Draw::Bugs &bugs, bool useHwTransform, ClipInfoFlags clipInfoFlags) { +void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pipelineState, const Draw::Bugs &bugs, ClipInfoFlags clipInfoFlags) { FShaderID id; bool isModeThrough = gstate.isModeThrough(); - // We exclude hwTransform mode here, although we could absolutely do this in hwtransform as well, because we detect - // draws that needs this and use software transform as the fallback for them. That logic will have to change if we change that. // NOTE: This check MUST be identical to the one in ComputeVertexShaderID, otherwise we might get mismatches between VS and FS and end up with no shader at all. - if (!useHwTransform && !isModeThrough) { + if (!isModeThrough) { if (clipInfoFlags & ClipInfoFlags::DepthClampFragment) { id.SetBit(FS_BIT_DEPTH_CLAMP); } if (clipInfoFlags & ClipInfoFlags::MinMaxZDiscard) { id.SetBit(FS_BIT_MINMAX_DISCARD); } + } else { + _dbg_assert_(0 == (clipInfoFlags & (ClipInfoFlags::DepthClampFragment | ClipInfoFlags::MinMaxZDiscard))); } if (gstate.isModeClear()) { diff --git a/GPU/Common/ShaderId.h b/GPU/Common/ShaderId.h index dd8efd111e..d91679f374 100644 --- a/GPU/Common/ShaderId.h +++ b/GPU/Common/ShaderId.h @@ -18,7 +18,7 @@ inline bool needFragmentMinMaxClipping() { inline bool needFragmentDepthClamp() { // If gstate.isDepthClipEnabled is false, clamping does not happen, instead fragments are culled as normal. - return (gstate.getDepthRangeMin() == 0 || gstate.getDepthRangeMax() == 0xFFFF) && gstate.isDepthClipEnabled() && !gstate_c.Use(GPU_USE_DEPTH_CLAMP); + return (gstate.getDepthRangeMin() == 0 || gstate.getDepthRangeMax() == 0xFFFF) && gstate.isDepthClipEnabled(); } // VS_BIT_LIGHT_UBERSHADER indicates that some groups of these will be @@ -268,7 +268,7 @@ void ComputeVertexShaderID(VShaderID *id, u32 vertType, bool useHWTransform, boo std::string VertexShaderDesc(const VShaderID &id); struct ComputedPipelineState; -void ComputeFragmentShaderID(FShaderID *id, const ComputedPipelineState &pipelineState, const Draw::Bugs &bugs, bool useHwTransform, ClipInfoFlags clipInfoFlags); +void ComputeFragmentShaderID(FShaderID *id, const ComputedPipelineState &pipelineState, const Draw::Bugs &bugs, ClipInfoFlags clipInfoFlags); std::string FragmentShaderDesc(const FShaderID &id); // For sanity checking. diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index 73efef4727..9d10071333 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -294,8 +294,10 @@ void DrawEngineD3D11::Flush() { // Always use software for flat shading to fix the provoking index. bool tess = gstate_c.submitType == SubmitType::HW_BEZIER || gstate_c.submitType == SubmitType::HW_SPLINE; bool useHWTransform = CanUseHardwareTransform(prim) && (tess || gstate.getShadeMode() != GE_SHADE_FLAT); - if (clipInfoFlags_ & ClipInfoFlags::SoftClipCull) { - useHWTransform = false; + if (clipInfoFlags_ & ClipInfoFlags::Valid) { + if (clipInfoFlags_ & ClipInfoFlags::SoftClipCull) { + useHWTransform = false; + } } if (useHWTransform != lastUseHwTransform_) { diff --git a/GPU/D3D11/ShaderManagerD3D11.cpp b/GPU/D3D11/ShaderManagerD3D11.cpp index ebb5b86c8d..52b006add9 100644 --- a/GPU/D3D11/ShaderManagerD3D11.cpp +++ b/GPU/D3D11/ShaderManagerD3D11.cpp @@ -199,7 +199,7 @@ void ShaderManagerD3D11::GetShaders(int prim, u32 vertexType, D3D11VertexShader if (gstate_c.IsDirty(DIRTY_FRAGMENTSHADER_STATE)) { gstate_c.Clean(DIRTY_FRAGMENTSHADER_STATE); - ComputeFragmentShaderID(&FSID, pipelineState, draw_->GetBugs(), useHWTransform, clipInfoFlags); + ComputeFragmentShaderID(&FSID, pipelineState, draw_->GetBugs(), clipInfoFlags); } else { FSID = lastFSID_; } diff --git a/GPU/D3D11/StateMappingD3D11.cpp b/GPU/D3D11/StateMappingD3D11.cpp index d8901700ca..d035f96a2b 100644 --- a/GPU/D3D11/StateMappingD3D11.cpp +++ b/GPU/D3D11/StateMappingD3D11.cpp @@ -230,7 +230,8 @@ void DrawEngineD3D11::ApplyDrawState(int prim) { } else { if (gstate.getDepthRangeMin() == 0 || gstate.getDepthRangeMax() == 65535) { // We get some extra clamping behavior if clipping is enabled. - keys_.raster.depthClipEnable = !(gstate.isDepthClipEnabled() && !gstate_c.Use(GPU_USE_DEPTH_CLAMP)); + const bool clamp = gstate.isDepthClipEnabled() && gstate_c.Use(GPU_USE_DEPTH_CLAMP); + keys_.raster.depthClipEnable = !clamp; } else { // We just want to clip in this case, the clamp would be clipped anyway. keys_.raster.depthClipEnable = 1; diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index e328b61d46..c2d9fa035a 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -254,8 +254,10 @@ void DrawEngineGLES::Flush() { GEPrimitiveType prim = prevPrim_; bool useHWTransform = CanUseHardwareTransform(prim); - if (clipInfoFlags_ & ClipInfoFlags::SoftClipCull) { - useHWTransform = false; + if (clipInfoFlags_ & ClipInfoFlags::Valid) { + if (clipInfoFlags_ & ClipInfoFlags::SoftClipCull) { + useHWTransform = false; + } } if (useHWTransform != lastUseHwTransform_) { @@ -315,7 +317,7 @@ void DrawEngineGLES::Flush() { ApplyDrawState(prim); ApplyDrawStateLate(false, 0); - LinkedShader *program = shaderManager_->ApplyFragmentShader(vsid, vshader, pipelineState_, true, clipInfoFlags_); + LinkedShader *program = shaderManager_->ApplyFragmentShader(vsid, vshader, pipelineState_, clipInfoFlags_); GLRInputLayout *inputLayout = SetupDecFmtForDraw(dec_->GetDecVtxFmt()); if (useElements) { render_->DrawIndexed(inputLayout, @@ -401,7 +403,7 @@ void DrawEngineGLES::Flush() { ApplyDrawState(prim); ApplyDrawStateLate(result.setStencil, result.stencilValue); - LinkedShader *linked = shaderManager_->ApplyFragmentShader(vsid, vshader, pipelineState_, false, clipInfoFlags_); + LinkedShader *linked = shaderManager_->ApplyFragmentShader(vsid, vshader, pipelineState_, clipInfoFlags_); if (!linked) { // Not much we can do here. Let's skip drawing. goto bail; diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index ba16090e4c..a17c4ca316 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -808,7 +808,7 @@ Shader *ShaderManagerGLES::ApplyVertexShader(bool useHWTransform, bool useHWTess return vs; } -LinkedShader *ShaderManagerGLES::ApplyFragmentShader(VShaderID VSID, Shader *vs, const ComputedPipelineState &pipelineState, bool useHwTransform, ClipInfoFlags clipInfoFlags) { +LinkedShader *ShaderManagerGLES::ApplyFragmentShader(VShaderID VSID, Shader *vs, const ComputedPipelineState &pipelineState, ClipInfoFlags clipInfoFlags) { uint64_t dirty = gstate_c.GetDirtyUniforms(); if (dirty) { if (lastShader_) @@ -820,7 +820,7 @@ LinkedShader *ShaderManagerGLES::ApplyFragmentShader(VShaderID VSID, Shader *vs, FShaderID FSID; if (gstate_c.IsDirty(DIRTY_FRAGMENTSHADER_STATE)) { gstate_c.Clean(DIRTY_FRAGMENTSHADER_STATE); - ComputeFragmentShaderID(&FSID, pipelineState, draw_->GetBugs(), useHwTransform, clipInfoFlags); + ComputeFragmentShaderID(&FSID, pipelineState, draw_->GetBugs(), clipInfoFlags); } else { FSID = lastFSID_; } diff --git a/GPU/GLES/ShaderManagerGLES.h b/GPU/GLES/ShaderManagerGLES.h index 8851d44aee..f3bd32ea29 100644 --- a/GPU/GLES/ShaderManagerGLES.h +++ b/GPU/GLES/ShaderManagerGLES.h @@ -173,7 +173,7 @@ public: // This is the old ApplyShader split into two parts, because of annoying information dependencies. // If you call ApplyVertexShader, you MUST call ApplyFragmentShader soon afterwards. Shader *ApplyVertexShader(bool useHWTransform, bool useHWTessellation, u32 vertexType, bool weightsAsFloat, bool useSkinInDecode, ClipInfoFlags clipInfoFlags, VShaderID *VSID); - LinkedShader *ApplyFragmentShader(VShaderID VSID, Shader *vs, const ComputedPipelineState &pipelineState, bool useHWTransform, ClipInfoFlags clipInfoFlags); + LinkedShader *ApplyFragmentShader(VShaderID VSID, Shader *vs, const ComputedPipelineState &pipelineState, ClipInfoFlags clipInfoFlags); void DeviceLost() override; void DeviceRestore(Draw::DrawContext *draw) override; diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 310c731cc7..fb31a0576d 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -241,8 +241,10 @@ void DrawEngineVulkan::Flush() { provokingVertexOk = true; } bool useHWTransform = CanUseHardwareTransform(prim) && provokingVertexOk; - if (clipInfoFlags_ & ClipInfoFlags::SoftClipCull) { - useHWTransform = false; + if (clipInfoFlags_ & ClipInfoFlags::Valid) { + if (clipInfoFlags_ & ClipInfoFlags::SoftClipCull) { + useHWTransform = false; + } } // Is this still needed? diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index d9fbf410fc..413f0bfef4 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -284,7 +284,7 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertexType, VulkanVertexShade VulkanFragmentShader *fs = nullptr; if (gstate_c.IsDirty(DIRTY_FRAGMENTSHADER_STATE)) { gstate_c.Clean(DIRTY_FRAGMENTSHADER_STATE); - ComputeFragmentShaderID(&FSID, pipelineState, draw_->GetBugs(), useHWTransform, clipInfoFlags); + ComputeFragmentShaderID(&FSID, pipelineState, draw_->GetBugs(), clipInfoFlags); recomputedFS = true; if (FSID == lastFSID_) { _dbg_assert_(lastFShader_ != nullptr); diff --git a/assets/compat.ini b/assets/compat.ini index d0a904f5cb..2e9e076abc 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -135,6 +135,9 @@ NPJH50625 = true ULJM08069 = true NPJH50625 = true +# Kidou Keisatsu +ULJS00026 = true + [DepthRangeHack] # Phantasy Star Portable 2 and Infinity as well as some FromSoftware titles both use viewport depth outside [0, 1]. # This gets clamped in our current implementation, but attempts to fix it run into