mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-09-01 18:25:19 +02:00
Fix some issues with depth rounding, and depth clamp in D3D11
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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_) {
|
||||
|
||||
@@ -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_;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user