From 42c34efd23d7ad05df9f3c71f7d52dd259e179d8 Mon Sep 17 00:00:00 2001 From: Brian Paul Date: Tue, 13 Sep 2005 02:59:53 +0000 Subject: OpenGL 2.0's two-sided stencil feature wasn't implemented correctly. See comment near top of stencil.c for info about OpenGL 2.0 vs. GL_EXT_stencil_two_side. --- src/mesa/main/attrib.c | 33 ++++---- src/mesa/main/extensions.c | 2 +- src/mesa/main/mtypes.h | 1 + src/mesa/main/state.c | 3 + src/mesa/main/stencil.c | 161 +++++++++++++++++++++++++++--------- src/mesa/main/stencil.h | 6 +- src/mesa/swrast/s_stencil.c | 2 +- src/mesa/swrast_setup/ss_triangle.c | 2 +- 8 files changed, 154 insertions(+), 56 deletions(-) diff --git a/src/mesa/main/attrib.c b/src/mesa/main/attrib.c index c58ff98b12..a9786db27a 100644 --- a/src/mesa/main/attrib.c +++ b/src/mesa/main/attrib.c @@ -1070,26 +1070,31 @@ _mesa_PopAttrib(void) break; case GL_STENCIL_BUFFER_BIT: { - GLint face; const struct gl_stencil_attrib *stencil; stencil = (const struct gl_stencil_attrib *) attr->data; _mesa_set_enable(ctx, GL_STENCIL_TEST, stencil->Enabled); _mesa_ClearStencil(stencil->Clear); - face = stencil->ActiveFace; if (ctx->Extensions.EXT_stencil_two_side) { - _mesa_set_enable(ctx, GL_STENCIL_TEST_TWO_SIDE_EXT, stencil->TestTwoSide); - face ^= 1; + _mesa_set_enable(ctx, GL_STENCIL_TEST_TWO_SIDE_EXT, + stencil->TestTwoSide); + _mesa_ActiveStencilFaceEXT(stencil->ActiveFace + ? GL_BACK : GL_FRONT); } - do { - _mesa_ActiveStencilFaceEXT(face); - _mesa_StencilFunc(stencil->Function[face], stencil->Ref[face], - stencil->ValueMask[face]); - _mesa_StencilMask(stencil->WriteMask[face]); - _mesa_StencilOp(stencil->FailFunc[face], - stencil->ZFailFunc[face], - stencil->ZPassFunc[face]); - face ^= 1; - } while (face != (stencil->ActiveFace ^ 1)); + /* front state */ + _mesa_StencilFunc(stencil->Function[0], stencil->Ref[0], + stencil->ValueMask[0]); + _mesa_StencilMask(stencil->WriteMask[0]); + _mesa_StencilOp(stencil->FailFunc[0], + stencil->ZFailFunc[0], + stencil->ZPassFunc[0]); + /* back state */ + _mesa_StencilFuncSeparate(GL_BACK, stencil->Function[1], + stencil->Ref[1], + stencil->ValueMask[1]); + _mesa_StencilMaskSeparate(GL_BACK, stencil->WriteMask[1]); + _mesa_StencilOpSeparate(GL_BACK, stencil->FailFunc[1], + stencil->ZFailFunc[1], + stencil->ZPassFunc[1]); } break; case GL_TRANSFORM_BIT: diff --git a/src/mesa/main/extensions.c b/src/mesa/main/extensions.c index d761704e77..006f55ccca 100644 --- a/src/mesa/main/extensions.c +++ b/src/mesa/main/extensions.c @@ -376,7 +376,7 @@ _mesa_enable_2_0_extensions(GLcontext *ctx) #if 0 && FEATURE_ARB_shading_language_100 ctx->Extensions.ARB_shading_language_100 = GL_TRUE; #endif - ctx->Extensions.EXT_stencil_two_side = GL_TRUE; + ctx->Extensions.EXT_stencil_two_side = GL_FALSE; /* yes, turn it off */ #if 0 && FEATURE_ARB_vertex_shader ctx->Extensions.ARB_vertex_shader = GL_TRUE; #endif diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h index 30c90c6744..8868aac280 100644 --- a/src/mesa/main/mtypes.h +++ b/src/mesa/main/mtypes.h @@ -1033,6 +1033,7 @@ struct gl_stencil_attrib GLboolean Enabled; /**< Enabled flag */ GLboolean TestTwoSide; /**< GL_EXT_stencil_two_side */ GLubyte ActiveFace; /**< GL_EXT_stencil_two_side (0 or 1) */ + GLboolean _TestTwoSide; GLenum Function[2]; /**< Stencil function */ GLenum FailFunc[2]; /**< Fail function */ GLenum ZPassFunc[2]; /**< Depth buffer pass function */ diff --git a/src/mesa/main/state.c b/src/mesa/main/state.c index 6fc770eb3b..509ee42a02 100644 --- a/src/mesa/main/state.c +++ b/src/mesa/main/state.c @@ -993,6 +993,9 @@ _mesa_update_state( GLcontext *ctx ) if (new_state & _NEW_LIGHT) _mesa_update_lighting( ctx ); + if (new_state & _NEW_STENCIL) + _mesa_update_stencil( ctx ); + if (new_state & _IMAGE_NEW_TRANSFER_STATE) _mesa_update_pixel( ctx, new_state ); diff --git a/src/mesa/main/stencil.c b/src/mesa/main/stencil.c index 3ce45a90f7..1adfac2dbc 100644 --- a/src/mesa/main/stencil.c +++ b/src/mesa/main/stencil.c @@ -1,8 +1,3 @@ -/** - * \file stencil.c - * Stencil operations. - */ - /* * Mesa 3-D graphics library * Version: 6.5 @@ -28,6 +23,24 @@ */ +/** + * \file stencil.c + * Stencil operations. + * + * Note: There's an incompatibility between GL_EXT_stencil_two_side and + * OpenGL 2.0's two-sided stencil feature. + * + * With GL_EXT_stencil_two_side, calling glStencilOp/Func/Mask() only the + * front OR back face state (as set by glActiveStencilFaceEXT) is set. + * + * But with OpenGL 2.0, calling glStencilOp/Func/Mask() sets BOTH the + * front AND back state. + * + * So either we advertise the GL_EXT_stencil_two_side extension, or OpenGL + * 2.0, but not both. + */ + + #include "glheader.h" #include "imports.h" #include "context.h" @@ -60,7 +73,7 @@ _mesa_ClearStencil( GLint s ) ctx->Stencil.Clear = (GLuint) s; if (ctx->Driver.ClearStencil) { - (*ctx->Driver.ClearStencil)( ctx, s ); + ctx->Driver.ClearStencil( ctx, s ); } } @@ -82,7 +95,6 @@ void GLAPIENTRY _mesa_StencilFunc( GLenum func, GLint ref, GLuint mask ) { GET_CURRENT_CONTEXT(ctx); - const GLint face = ctx->Stencil.ActiveFace; GLint maxref; ASSERT_OUTSIDE_BEGIN_END(ctx); @@ -104,18 +116,35 @@ _mesa_StencilFunc( GLenum func, GLint ref, GLuint mask ) maxref = (1 << STENCIL_BITS) - 1; ref = CLAMP( ref, 0, maxref ); - if (ctx->Stencil.Function[face] == func && - ctx->Stencil.ValueMask[face] == mask && - ctx->Stencil.Ref[face] == ref) - return; - - FLUSH_VERTICES(ctx, _NEW_STENCIL); - ctx->Stencil.Function[face] = func; - ctx->Stencil.Ref[face] = ref; - ctx->Stencil.ValueMask[face] = mask; + if (ctx->Extensions.EXT_stencil_two_side) { + /* only set active face state */ + const GLint face = ctx->Stencil.ActiveFace; + if (ctx->Stencil.Function[face] == func && + ctx->Stencil.ValueMask[face] == mask && + ctx->Stencil.Ref[face] == ref) + return; + FLUSH_VERTICES(ctx, _NEW_STENCIL); + ctx->Stencil.Function[face] = func; + ctx->Stencil.Ref[face] = ref; + ctx->Stencil.ValueMask[face] = mask; + } + else { + /* set both front and back state */ + if (ctx->Stencil.Function[0] == func && + ctx->Stencil.Function[1] == func && + ctx->Stencil.ValueMask[0] == mask && + ctx->Stencil.ValueMask[1] == mask && + ctx->Stencil.Ref[0] == ref && + ctx->Stencil.Ref[1] == ref) + return; + FLUSH_VERTICES(ctx, _NEW_STENCIL); + ctx->Stencil.Function[0] = ctx->Stencil.Function[1] = func; + ctx->Stencil.Ref[0] = ctx->Stencil.Ref[1] = ref; + ctx->Stencil.ValueMask[0] = ctx->Stencil.ValueMask[1] = mask; + } if (ctx->Driver.StencilFunc) { - (*ctx->Driver.StencilFunc)( ctx, func, ref, mask ); + ctx->Driver.StencilFunc( ctx, func, ref, mask ); } } @@ -135,17 +164,27 @@ void GLAPIENTRY _mesa_StencilMask( GLuint mask ) { GET_CURRENT_CONTEXT(ctx); - const GLint face = ctx->Stencil.ActiveFace; ASSERT_OUTSIDE_BEGIN_END(ctx); - if (ctx->Stencil.WriteMask[face] == mask) - return; - - FLUSH_VERTICES(ctx, _NEW_STENCIL); - ctx->Stencil.WriteMask[face] = mask; + if (ctx->Extensions.EXT_stencil_two_side) { + /* only set active face state */ + const GLint face = ctx->Stencil.ActiveFace; + if (ctx->Stencil.WriteMask[face] == mask) + return; + FLUSH_VERTICES(ctx, _NEW_STENCIL); + ctx->Stencil.WriteMask[face] = mask; + } + else { + /* set both front and back state */ + if (ctx->Stencil.WriteMask[0] == mask && + ctx->Stencil.WriteMask[1] == mask) + return; + FLUSH_VERTICES(ctx, _NEW_STENCIL); + ctx->Stencil.WriteMask[0] = ctx->Stencil.WriteMask[1] = mask; + } if (ctx->Driver.StencilMask) { - (*ctx->Driver.StencilMask)( ctx, mask ); + ctx->Driver.StencilMask( ctx, mask ); } } @@ -168,7 +207,6 @@ void GLAPIENTRY _mesa_StencilOp(GLenum fail, GLenum zfail, GLenum zpass) { GET_CURRENT_CONTEXT(ctx); - const GLint face = ctx->Stencil.ActiveFace; ASSERT_OUTSIDE_BEGIN_END(ctx); switch (fail) { @@ -226,18 +264,35 @@ _mesa_StencilOp(GLenum fail, GLenum zfail, GLenum zpass) return; } - if (ctx->Stencil.ZFailFunc[face] == zfail && - ctx->Stencil.ZPassFunc[face] == zpass && - ctx->Stencil.FailFunc[face] == fail) - return; - - FLUSH_VERTICES(ctx, _NEW_STENCIL); - ctx->Stencil.ZFailFunc[face] = zfail; - ctx->Stencil.ZPassFunc[face] = zpass; - ctx->Stencil.FailFunc[face] = fail; + if (ctx->Extensions.EXT_stencil_two_side) { + /* only set active face state */ + const GLint face = ctx->Stencil.ActiveFace; + if (ctx->Stencil.ZFailFunc[face] == zfail && + ctx->Stencil.ZPassFunc[face] == zpass && + ctx->Stencil.FailFunc[face] == fail) + return; + FLUSH_VERTICES(ctx, _NEW_STENCIL); + ctx->Stencil.ZFailFunc[face] = zfail; + ctx->Stencil.ZPassFunc[face] = zpass; + ctx->Stencil.FailFunc[face] = fail; + } + else { + /* set both front and back state */ + if (ctx->Stencil.ZFailFunc[0] == zfail && + ctx->Stencil.ZFailFunc[1] == zfail && + ctx->Stencil.ZPassFunc[0] == zpass && + ctx->Stencil.ZPassFunc[1] == zpass && + ctx->Stencil.FailFunc[0] == fail && + ctx->Stencil.FailFunc[1] == fail) + return; + FLUSH_VERTICES(ctx, _NEW_STENCIL); + ctx->Stencil.ZFailFunc[0] = ctx->Stencil.ZFailFunc[1] = zfail; + ctx->Stencil.ZPassFunc[0] = ctx->Stencil.ZPassFunc[1] = zpass; + ctx->Stencil.FailFunc[0] = ctx->Stencil.FailFunc[1] = fail; + } if (ctx->Driver.StencilOp) { - (*ctx->Driver.StencilOp)(ctx, fail, zfail, zpass); + ctx->Driver.StencilOp(ctx, fail, zfail, zpass); } } @@ -251,13 +306,22 @@ _mesa_ActiveStencilFaceEXT(GLenum face) GET_CURRENT_CONTEXT(ctx); ASSERT_OUTSIDE_BEGIN_END(ctx); + if (!ctx->Extensions.EXT_stencil_two_side) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glActiveStencilFaceEXT"); + return; + } + if (face == GL_FRONT || face == GL_BACK) { FLUSH_VERTICES(ctx, _NEW_STENCIL); ctx->Stencil.ActiveFace = (face == GL_FRONT) ? 0 : 1; } + else { + _mesa_error(ctx, GL_INVALID_ENUM, "glActiveStencilFaceEXT(face)"); + return; + } if (ctx->Driver.ActiveStencilFace) { - (*ctx->Driver.ActiveStencilFace)( ctx, (GLuint) ctx->Stencil.ActiveFace ); + ctx->Driver.ActiveStencilFace(ctx, (GLuint) ctx->Stencil.ActiveFace); } } #endif @@ -432,6 +496,28 @@ _mesa_StencilMaskSeparate(GLenum face, GLuint mask) } +/** + * Update derived stencil state. + */ +void +_mesa_update_stencil(GLcontext *ctx) +{ + if (ctx->Extensions.EXT_stencil_two_side) { + ctx->Stencil._TestTwoSide = ctx->Stencil.TestTwoSide; + } + else { + ctx->Stencil._TestTwoSide = + (ctx->Stencil.Function[0] != ctx->Stencil.Function[1] || + ctx->Stencil.FailFunc[0] != ctx->Stencil.FailFunc[1] || + ctx->Stencil.ZPassFunc[0] != ctx->Stencil.ZPassFunc[1] || + ctx->Stencil.ZFailFunc[0] != ctx->Stencil.ZFailFunc[1] || + ctx->Stencil.Ref[0] != ctx->Stencil.Ref[1] || + ctx->Stencil.ValueMask[0] != ctx->Stencil.ValueMask[1] || + ctx->Stencil.WriteMask[0] != ctx->Stencil.WriteMask[1]); + } +} + + /** * Initialize the context stipple state. * @@ -440,9 +526,8 @@ _mesa_StencilMaskSeparate(GLenum face, GLuint mask) * Initializes __GLcontextRec::Stencil attribute group. */ void -_mesa_init_stencil( GLcontext * ctx ) +_mesa_init_stencil(GLcontext *ctx) { - /* Stencil group */ ctx->Stencil.Enabled = GL_FALSE; ctx->Stencil.TestTwoSide = GL_FALSE; ctx->Stencil.ActiveFace = 0; /* 0 = GL_FRONT, 1 = GL_BACK */ diff --git a/src/mesa/main/stencil.h b/src/mesa/main/stencil.h index ec2927cfd2..27c6362023 100644 --- a/src/mesa/main/stencil.h +++ b/src/mesa/main/stencil.h @@ -5,7 +5,7 @@ /* * Mesa 3-D graphics library - * Version: 6.3 + * Version: 6.5 * * Copyright (C) 1999-2005 Brian Paul All Rights Reserved. * @@ -67,6 +67,10 @@ extern void GLAPIENTRY _mesa_StencilMaskSeparate(GLenum face, GLuint mask); +extern void +_mesa_update_stencil(GLcontext *ctx); + + extern void _mesa_init_stencil( GLcontext * ctx ); diff --git a/src/mesa/swrast/s_stencil.c b/src/mesa/swrast/s_stencil.c index e45f8eabb2..3e1b28740a 100644 --- a/src/mesa/swrast/s_stencil.c +++ b/src/mesa/swrast/s_stencil.c @@ -1004,7 +1004,7 @@ GLboolean _swrast_stencil_and_ztest_span(GLcontext *ctx, struct sw_span *span) { /* span->facing can only be non-zero if using two-sided stencil */ - ASSERT(ctx->Stencil.TestTwoSide || span->facing == 0); + ASSERT(ctx->Stencil._TestTwoSide || span->facing == 0); if (span->arrayMask & SPAN_XY) return stencil_and_ztest_pixels(ctx, span, span->facing); else diff --git a/src/mesa/swrast_setup/ss_triangle.c b/src/mesa/swrast_setup/ss_triangle.c index d37bdf71cb..09244d9c4b 100644 --- a/src/mesa/swrast_setup/ss_triangle.c +++ b/src/mesa/swrast_setup/ss_triangle.c @@ -299,7 +299,7 @@ void _swsetup_choose_trifuncs( GLcontext *ctx ) */ if (ctx->Polygon.FrontMode != GL_FILL || ctx->Polygon.BackMode != GL_FILL || - (ctx->Stencil.Enabled && ctx->Stencil.TestTwoSide)) + (ctx->Stencil.Enabled && ctx->Stencil._TestTwoSide)) ind |= SS_UNFILLED_BIT; if (ctx->Visual.rgbMode) -- cgit v1.2.3