diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h index 61e4bcd8e16..63ef9892d9c 100644 --- a/filament/backend/include/backend/DriverEnums.h +++ b/filament/backend/include/backend/DriverEnums.h @@ -787,11 +787,9 @@ struct RasterState { using DepthFunc = backend::SamplerCompareFunc; using BlendEquation = backend::BlendEquation; using BlendFunction = backend::BlendFunction; - using StencilFunction = backend::SamplerCompareFunc; - using StencilOperation = backend::StencilOperation; RasterState() noexcept { // NOLINT - static_assert(sizeof(RasterState) == sizeof(uint64_t), + static_assert(sizeof(RasterState) == sizeof(uint32_t), "RasterState size not what was intended"); culling = CullingMode::BACK; blendEquationRGB = BlendEquation::ADD; @@ -800,10 +798,6 @@ struct RasterState { blendFunctionSrcAlpha = BlendFunction::ONE; blendFunctionDstRGB = BlendFunction::ZERO; blendFunctionDstAlpha = BlendFunction::ZERO; - stencilFunc = StencilFunction::A; - stencilOpStencilFail = StencilOperation::KEEP; - stencilOpDepthFail = StencilOperation::KEEP; - stencilOpDepthStencilPass = StencilOperation::KEEP; } bool operator == (RasterState rhs) const noexcept { return u == rhs.u; } @@ -862,26 +856,10 @@ struct RasterState { //! whether front face winding direction must be inverted bool inverseFrontFaces : 1; // 31 - //! Whether stencil-buffer writes are enabled - bool stencilWrite : 1; // 32 - //! Stencil reference value - uint8_t stencilRef : 8; // 40 - //! Stencil test function - StencilFunction stencilFunc : 3; // 43 - //! Stencil operation when stencil test fails - StencilOperation stencilOpStencilFail : 3; // 46 - //! padding, must be 0 - uint8_t padding0 : 2; // 48 - //! Stencil operation when stencil test passes but depth test fails - StencilOperation stencilOpDepthFail : 3; // 51 - //! Stencil operation when both stencil and depth test pass - StencilOperation stencilOpDepthStencilPass : 3; // 54 - //! padding, must be 0 - uint8_t padding1 : 2; // 56 //! padding, must be 0 - uint8_t padding2 : 8; // 64 + uint8_t padding : 1; // 32 }; - uint64_t u = 0; + uint32_t u = 0; }; }; @@ -975,6 +953,45 @@ struct PolygonOffset { float constant = 0; // units in GL-speak }; +struct StencilState { + using StencilFunction = SamplerCompareFunc; + + StencilState() noexcept : referenceValue(0u), stencilWrite(false) { + static_assert(sizeof(StencilState) == 4u, "StencilState size not what was intended"); + frontBack.u = 0u; + frontBack.stencilFunc = StencilFunction::A; + } + + bool operator==(StencilState rhs) const noexcept { + return frontBack.u == rhs.frontBack.u && + referenceValue == rhs.referenceValue && + stencilWrite == rhs.stencilWrite; + } + bool operator!=(StencilState rhs) const noexcept { return !(*this == rhs); } + + union StencilOperations { + struct { + //! Stencil test function + StencilFunction stencilFunc : 3; + + //! Stencil operation when stencil test fails + StencilOperation stencilOpStencilFail : 3; + + //! Stencil operation when stencil test passes but depth test fails + StencilOperation stencilOpDepthFail : 3; + + //! Stencil operation when both stencil and depth test pass + StencilOperation stencilOpDepthStencilPass : 3; + }; + uint16_t u = 0; + }; + + // TODO: separate out front and back stencil states. + StencilOperations frontBack; + uint8_t referenceValue; + //! Whether stencil-buffer writes are enabled + bool stencilWrite; +}; using FrameScheduledCallback = void(*)(PresentCallable callable, void* user); diff --git a/filament/backend/include/backend/PipelineState.h b/filament/backend/include/backend/PipelineState.h index 6238988e3c0..8b31c5f3dfd 100644 --- a/filament/backend/include/backend/PipelineState.h +++ b/filament/backend/include/backend/PipelineState.h @@ -31,6 +31,7 @@ namespace filament::backend { struct PipelineState { Handle program; RasterState rasterState; + StencilState stencilState; PolygonOffset polygonOffset; Viewport scissor{ 0, 0, (uint32_t)std::numeric_limits::max(), diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 61ee022f5d5..f67762c1577 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -1234,12 +1234,13 @@ depthState.depthWriteEnabled = rs.depthWrite; } if (stencilAttachment) { - depthState.stencilCompare = getMetalCompareFunction(rs.stencilFunc); - depthState.stencilOperationDepthStencilPass = getMetalStencilOperation(rs.stencilOpDepthStencilPass); - depthState.stencilOperationDepthFail = getMetalStencilOperation(rs.stencilOpDepthFail); - depthState.stencilOperationStencilFail = getMetalStencilOperation(rs.stencilOpStencilFail); - depthState.stencilWriteEnabled = rs.stencilWrite; - [mContext->currentRenderPassEncoder setStencilReferenceValue:rs.stencilRef]; + const auto& ss = ps.stencilState; + depthState.stencilCompare = getMetalCompareFunction(ss.frontBack.stencilFunc); + depthState.stencilOperationDepthStencilPass = getMetalStencilOperation(ss.frontBack.stencilOpDepthStencilPass); + depthState.stencilOperationDepthFail = getMetalStencilOperation(ss.frontBack.stencilOpDepthFail); + depthState.stencilOperationStencilFail = getMetalStencilOperation(ss.frontBack.stencilOpStencilFail); + depthState.stencilWriteEnabled = ss.stencilWrite; + [mContext->currentRenderPassEncoder setStencilReferenceValue:ss.referenceValue]; } mContext->depthStencilState.updateState(depthState); if (mContext->depthStencilState.stateChanged()) { diff --git a/filament/backend/src/metal/MetalEnums.h b/filament/backend/src/metal/MetalEnums.h index 3e7de0022d0..a739ba8201d 100644 --- a/filament/backend/src/metal/MetalEnums.h +++ b/filament/backend/src/metal/MetalEnums.h @@ -52,7 +52,7 @@ constexpr inline MTLCompareFunction getMetalCompareFunction(RasterState::DepthFu } } -constexpr inline MTLStencilOperation getMetalStencilOperation(RasterState::StencilOperation op) { +constexpr inline MTLStencilOperation getMetalStencilOperation(StencilOperation op) { switch (op) { case StencilOperation::KEEP: return MTLStencilOperationKeep; case StencilOperation::ZERO: return MTLStencilOperationZero; diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 3ce23aff7ae..551693c4f54 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -300,19 +300,6 @@ void OpenGLDriver::setRasterStateSlow(RasterState rs) noexcept { gl.depthMask(GLboolean(rs.depthWrite)); } - // stencil test / operation - if (rs.stencilFunc == RasterState::StencilFunction::A && !rs.stencilWrite) { - gl.disable(GL_STENCIL_TEST); - } else { - gl.enable(GL_STENCIL_TEST); - gl.stencilFunc(getStencilFunc(rs.stencilFunc), rs.stencilRef, ~GLuint(0)); - gl.stencilOp(getStencilOp(rs.stencilOpStencilFail), - getStencilOp(rs.stencilOpDepthFail), - getStencilOp(rs.stencilOpDepthStencilPass)); - GLuint stencilMask = rs.stencilWrite ? ~GLuint(0) : 0x00; - gl.stencilMask(stencilMask); - } - // write masks gl.colorMask(GLboolean(rs.colorWrite)); @@ -324,6 +311,24 @@ void OpenGLDriver::setRasterStateSlow(RasterState rs) noexcept { } } +void OpenGLDriver::setStencilStateSlow(StencilState ss) noexcept { + mStencilState = ss; + auto& gl = mContext; + + // stencil test / operation + if (ss.frontBack.stencilFunc == StencilState::StencilFunction::A && !ss.stencilWrite) { + gl.disable(GL_STENCIL_TEST); + } else { + gl.enable(GL_STENCIL_TEST); + gl.stencilFunc(getStencilFunc(ss.frontBack.stencilFunc), ss.referenceValue, ~GLuint(0)); + gl.stencilOp(getStencilOp(ss.frontBack.stencilOpStencilFail), + getStencilOp(ss.frontBack.stencilOpDepthFail), + getStencilOp(ss.frontBack.stencilOpDepthStencilPass)); + GLuint stencilMask = ss.stencilWrite ? ~GLuint(0) : 0x00; + gl.stencilMask(stencilMask); + } +} + // ------------------------------------------------------------------------------------------------ // Creating driver objects // ------------------------------------------------------------------------------------------------ @@ -2884,6 +2889,7 @@ UTILS_NOINLINE void OpenGLDriver::clearWithRasterPipe(TargetBufferFlags clearFlags, math::float4 const& linearColor, GLfloat depth, GLint stencil) noexcept { RasterState rs(mRasterState); + StencilState ss(mStencilState); if (any(clearFlags & TargetBufferFlags::COLOR_ALL)) { rs.colorWrite = true; @@ -2891,12 +2897,13 @@ void OpenGLDriver::clearWithRasterPipe(TargetBufferFlags clearFlags, if (any(clearFlags & TargetBufferFlags::DEPTH)) { rs.depthWrite = true; } - if (any(clearFlags & TargetBufferFlags::STENCIL)) { - rs.stencilWrite = true; - } - if (any(clearFlags)) { + if (any(clearFlags & (TargetBufferFlags::COLOR_ALL | TargetBufferFlags::DEPTH))) { setRasterState(rs); } + if (any(clearFlags & TargetBufferFlags::STENCIL)) { + ss.stencilWrite = true; + setStencilState(ss); + } if (any(clearFlags & TargetBufferFlags::COLOR0)) { glClearBufferfv(GL_COLOR, 0, linearColor.v); @@ -3056,6 +3063,7 @@ void OpenGLDriver::draw(PipelineState state, Handle rph, uint } setRasterState(state.rasterState); + setStencilState(state.stencilState); gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant); diff --git a/filament/backend/src/opengl/OpenGLDriver.h b/filament/backend/src/opengl/OpenGLDriver.h index 1f3d7321e43..7c954f4f4ac 100644 --- a/filament/backend/src/opengl/OpenGLDriver.h +++ b/filament/backend/src/opengl/OpenGLDriver.h @@ -286,6 +286,13 @@ class OpenGLDriver final : public DriverBase { } } + void setStencilStateSlow(StencilState ss) noexcept; + void setStencilState(StencilState ss) noexcept { + if (UTILS_UNLIKELY(ss != mStencilState)) { + setStencilStateSlow(ss); + } + } + void setTextureData(GLTexture* t, uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, @@ -337,6 +344,7 @@ class OpenGLDriver final : public DriverBase { GLRenderTarget const* rt, TargetBufferFlags buffers) noexcept; RasterState mRasterState; + StencilState mStencilState; // state required to represent the current render pass Handle mRenderPassTarget; diff --git a/filament/backend/test/test_StencilBuffer.cpp b/filament/backend/test/test_StencilBuffer.cpp index 713db0c04b9..ad9791d0caf 100644 --- a/filament/backend/test/test_StencilBuffer.cpp +++ b/filament/backend/test/test_StencilBuffer.cpp @@ -106,8 +106,8 @@ class BasicStencilBufferTest : public BackendTest { ps.program = program; ps.rasterState.colorWrite = false; ps.rasterState.depthWrite = false; - ps.rasterState.stencilWrite = true; - ps.rasterState.stencilOpDepthStencilPass = StencilOperation::INCR; + ps.stencilState.stencilWrite = true; + ps.stencilState.frontBack.stencilOpDepthStencilPass = StencilOperation::INCR; api.makeCurrent(swapChain, swapChain); api.beginFrame(0, 0); @@ -120,10 +120,10 @@ class BasicStencilBufferTest : public BackendTest { params.flags.clear = TargetBufferFlags::NONE; params.flags.discardStart = TargetBufferFlags::NONE; ps.rasterState.colorWrite = true; - ps.rasterState.stencilWrite = false; - ps.rasterState.stencilOpDepthStencilPass = StencilOperation::KEEP; - ps.rasterState.stencilFunc = RasterState::StencilFunction::E; - ps.rasterState.stencilRef = 0u; + ps.stencilState.stencilWrite = false; + ps.stencilState.frontBack.stencilOpDepthStencilPass = StencilOperation::KEEP; + ps.stencilState.frontBack.stencilFunc = StencilState::StencilFunction::E; + ps.stencilState.referenceValue = 0u; api.beginRenderPass(renderTarget, params); api.draw(ps, triangle.getRenderPrimitive(), 1); @@ -233,8 +233,8 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) { ps.program = program; ps.rasterState.colorWrite = false; ps.rasterState.depthWrite = false; - ps.rasterState.stencilWrite = true; - ps.rasterState.stencilOpDepthStencilPass = StencilOperation::INCR; + ps.stencilState.stencilWrite = true; + ps.stencilState.frontBack.stencilOpDepthStencilPass = StencilOperation::INCR; api.makeCurrent(swapChain, swapChain); api.beginFrame(0, 0); @@ -249,10 +249,10 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) { params.flags.discardEnd = TargetBufferFlags::STENCIL; params.clearColor = math::float4(0.0f, 0.0f, 1.0f, 1.0f); ps.rasterState.colorWrite = true; - ps.rasterState.stencilWrite = false; - ps.rasterState.stencilOpDepthStencilPass = StencilOperation::KEEP; - ps.rasterState.stencilFunc = RasterState::StencilFunction::E; - ps.rasterState.stencilRef = 0u; + ps.stencilState.stencilWrite = false; + ps.stencilState.frontBack.stencilOpDepthStencilPass = StencilOperation::KEEP; + ps.stencilState.frontBack.stencilFunc = StencilState::StencilFunction::E; + ps.stencilState.referenceValue = 0u; api.beginRenderPass(renderTarget1, params); api.draw(ps, triangle.getRenderPrimitive(), 1);