Metal: Support depthRange near > far Metal doesn't natively support near > far depthRange, need to emulate it via shader. Bug: angleproject:2634 Change-Id: I1885cad3467478a0dcc4b25b7d41f980b9e03103 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1919282 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: Jonah Ryan-Davis <jonahr@google.com>
diff --git a/src/compiler/translator/TranslatorMetal.cpp b/src/compiler/translator/TranslatorMetal.cpp index 5c9d7f3..ce4e9ea 100644 --- a/src/compiler/translator/TranslatorMetal.cpp +++ b/src/compiler/translator/TranslatorMetal.cpp
@@ -96,4 +96,32 @@ return true; } +// Metal needs to inverse the depth if depthRange is is reverse order, i.e. depth near > depth far +// This is achieved by multiply the depth value with scale value stored in +// driver uniform's depthRange.reserved +bool TranslatorMetal::transformDepthBeforeCorrection(TIntermBlock *root, + const TVariable *driverUniforms) +{ + // Create a symbol reference to "gl_Position" + const TVariable *position = BuiltInVariable::gl_Position(); + TIntermSymbol *positionRef = new TIntermSymbol(position); + + // Create a swizzle to "gl_Position.z" + TVector<int> swizzleOffsetZ = {2}; + TIntermSwizzle *positionZ = new TIntermSwizzle(positionRef, swizzleOffsetZ); + + // Create a ref to "depthRange.reserved" + TIntermBinary *viewportZScale = getDriverUniformDepthRangeReservedFieldRef(driverUniforms); + + // Create the expression "gl_Position.z * depthRange.reserved". + TIntermBinary *zScale = new TIntermBinary(EOpMul, positionZ->deepCopy(), viewportZScale); + + // Create the assignment "gl_Position.z = gl_Position.z * depthRange.reserved" + TIntermTyped *positionZLHS = positionZ->deepCopy(); + TIntermBinary *assignment = new TIntermBinary(TOperator::EOpAssign, positionZLHS, zScale); + + // Append the assignment as a statement at the end of the shader. + return RunAtTheEndOfShader(this, root, assignment, &getSymbolTable()); +} + } // namespace sh
diff --git a/src/compiler/translator/TranslatorMetal.h b/src/compiler/translator/TranslatorMetal.h index f70294a..d571e31 100644 --- a/src/compiler/translator/TranslatorMetal.h +++ b/src/compiler/translator/TranslatorMetal.h
@@ -29,6 +29,9 @@ ANGLE_NO_DISCARD bool translate(TIntermBlock *root, ShCompileOptions compileOptions, PerformanceDiagnostics *perfDiagnostics) override; + + ANGLE_NO_DISCARD bool transformDepthBeforeCorrection(TIntermBlock *root, + const TVariable *driverUniforms) override; }; } // namespace sh
diff --git a/src/compiler/translator/TranslatorVulkan.cpp b/src/compiler/translator/TranslatorVulkan.cpp index 84e7fdd..98ba3b1 100644 --- a/src/compiler/translator/TranslatorVulkan.cpp +++ b/src/compiler/translator/TranslatorVulkan.cpp
@@ -351,8 +351,9 @@ depthRangeParamsFields->push_back(new TField(new TType(EbtFloat, EbpHigh, EvqGlobal, 1, 1), ImmutableString("diff"), TSourceLoc(), SymbolType::AngleInternal)); + // This additional field might be used by subclass such as TranslatorMetal. depthRangeParamsFields->push_back(new TField(new TType(EbtFloat, EbpHigh, EvqGlobal, 1, 1), - ImmutableString("dummyPacker"), TSourceLoc(), + ImmutableString("reserved"), TSourceLoc(), SymbolType::AngleInternal)); TStructure *emulatedDepthRangeParams = new TStructure( symbolTable, kEmulatedDepthRangeParams, depthRangeParamsFields, SymbolType::AngleInternal); @@ -938,6 +939,10 @@ } // Append depth range translation to main. + if (!transformDepthBeforeCorrection(root, driverUniforms)) + { + return false; + } if (!AppendVertexShaderDepthCorrectionToMain(this, root, &getSymbolTable())) { return false; @@ -1001,4 +1006,12 @@ return CreateDriverUniformRef(driverUniforms, kNegViewportYScale); } +TIntermBinary *TranslatorVulkan::getDriverUniformDepthRangeReservedFieldRef( + const TVariable *driverUniforms) const +{ + TIntermBinary *depthRange = CreateDriverUniformRef(driverUniforms, kDepthRange); + + return new TIntermBinary(EOpIndexDirectStruct, depthRange, CreateIndexNode(3)); +} + } // namespace sh
diff --git a/src/compiler/translator/TranslatorVulkan.h b/src/compiler/translator/TranslatorVulkan.h index 121ebd4..6b3c71a 100644 --- a/src/compiler/translator/TranslatorVulkan.h +++ b/src/compiler/translator/TranslatorVulkan.h
@@ -31,6 +31,8 @@ bool shouldFlattenPragmaStdglInvariantAll() override; TIntermBinary *getDriverUniformNegViewportYScaleRef(const TVariable *driverUniforms) const; + TIntermBinary *getDriverUniformDepthRangeReservedFieldRef( + const TVariable *driverUniforms) const; // Subclass can call this method to transform the AST before writing the final output. // See TranslatorMetal.cpp. ANGLE_NO_DISCARD bool translateImpl(TIntermBlock *root, @@ -38,6 +40,14 @@ PerformanceDiagnostics *perfDiagnostics, const TVariable **driverUniformsOut, TOutputVulkanGLSL *outputGLSL); + + // Give subclass such as TranslatorMetal a chance to do depth transform before + // TranslatorVulkan apply its own transform. + ANGLE_NO_DISCARD virtual bool transformDepthBeforeCorrection(TIntermBlock *root, + const TVariable *driverUniforms) + { + return true; + } }; } // namespace sh
diff --git a/src/libANGLE/renderer/metal/ContextMtl.h b/src/libANGLE/renderer/metal/ContextMtl.h index 2202ba1..df78323 100644 --- a/src/libANGLE/renderer/metal/ContextMtl.h +++ b/src/libANGLE/renderer/metal/ContextMtl.h
@@ -385,7 +385,7 @@ int32_t xfbBufferOffsets[4]; uint32_t acbBufferOffsets[4]; - // We'll use x, y, z for near / far / diff respectively. + // We'll use x, y, z, w for near / far / diff / zscale respectively. float depthRange[4]; };
diff --git a/src/libANGLE/renderer/metal/ContextMtl.mm b/src/libANGLE/renderer/metal/ContextMtl.mm index 84a2c0b..a0e89d5 100644 --- a/src/libANGLE/renderer/metal/ContextMtl.mm +++ b/src/libANGLE/renderer/metal/ContextMtl.mm
@@ -85,6 +85,11 @@ return angle::Result::Continue; } + +bool NeedToInvertDepthRange(float near, float far) +{ + return near > far; +} } // namespace ContextMtl::ContextMtl(const gl::State &state, gl::ErrorSet *errorSet, DisplayMtl *display) @@ -1205,6 +1210,12 @@ void ContextMtl::updateDepthRange(float nearPlane, float farPlane) { + if (NeedToInvertDepthRange(nearPlane, farPlane)) + { + // We also need to invert the depth in shader later by using scale value stored in driver + // uniform depthRange.reserved + std::swap(nearPlane, farPlane); + } mViewport.znear = nearPlane; mViewport.zfar = farPlane; mDirtyBits.set(DIRTY_BIT_VIEWPORT); @@ -1572,6 +1583,7 @@ mDriverUniforms.depthRange[0] = depthRangeNear; mDriverUniforms.depthRange[1] = depthRangeFar; mDriverUniforms.depthRange[2] = depthRangeDiff; + mDriverUniforms.depthRange[3] = NeedToInvertDepthRange(depthRangeNear, depthRangeFar) ? -1 : 1; ASSERT(mRenderEncoder.valid()); mRenderEncoder.setFragmentData(mDriverUniforms, mtl::kDriverUniformsBindingIndex);