Track two sets of back-face stencil state

Track separate back-face stencil state for OpenGL 2.0 /
GL_ATI_separate_stencil and GL_EXT_stencil_two_side.  This allows all
three to be enabled in a driver.  One set of state is set via the 2.0
or ATI functions and is used when STENCIL_TEST_TWO_SIDE_EXT is
disabled.  The other is set by StencilFunc and StencilOp when the
active stencil face is set to BACK.  The GL_EXT_stencil_two_side spec has
more details.

http://opengl.org/registry/specs/EXT/stencil_two_side.txt
(cherry picked from commit dde7cb9628)
This commit is contained in:
Ian Romanick 2009-01-09 15:43:17 -08:00 committed by Ian Romanick
parent a6f7e909a7
commit 842b5164d2
9 changed files with 128 additions and 104 deletions

View file

@ -84,6 +84,7 @@ static void
cc_unit_populate_key(struct brw_context *brw, struct brw_cc_unit_key *key)
{
struct gl_stencil_attrib *stencil = brw->attribs.Stencil;
const unsigned back = stencil->_BackFace;
memset(key, 0, sizeof(*key));
@ -100,13 +101,13 @@ cc_unit_populate_key(struct brw_context *brw, struct brw_cc_unit_key *key)
key->stencil_test_mask[0] = stencil->ValueMask[0];
}
if (key->stencil_two_side) {
key->stencil_func[1] = stencil->Function[1];
key->stencil_fail_op[1] = stencil->FailFunc[1];
key->stencil_pass_depth_fail_op[1] = stencil->ZFailFunc[1];
key->stencil_pass_depth_pass_op[1] = stencil->ZPassFunc[1];
key->stencil_ref[1] = stencil->Ref[1];
key->stencil_write_mask[1] = stencil->WriteMask[1];
key->stencil_test_mask[1] = stencil->ValueMask[1];
key->stencil_func[1] = stencil->Function[back];
key->stencil_fail_op[1] = stencil->FailFunc[back];
key->stencil_pass_depth_fail_op[1] = stencil->ZFailFunc[back];
key->stencil_pass_depth_pass_op[1] = stencil->ZPassFunc[back];
key->stencil_ref[1] = stencil->Ref[back];
key->stencil_write_mask[1] = stencil->WriteMask[back];
key->stencil_test_mask[1] = stencil->ValueMask[back];
}
if (brw->attribs.Color->_LogicOpEnabled)

View file

@ -189,8 +189,7 @@ static void brw_wm_populate_key( struct brw_context *brw,
lookup |= IZ_STENCIL_TEST_ENABLE_BIT;
if (brw->attribs.Stencil->WriteMask[0] ||
(brw->attribs.Stencil->_TestTwoSide &&
brw->attribs.Stencil->WriteMask[1]))
brw->attribs.Stencil->WriteMask[brw->attribs.Stencil->_BackFace])
lookup |= IZ_STENCIL_WRITE_ENABLE_BIT;
}

View file

@ -347,6 +347,8 @@ static GLboolean r300RunRender(GLcontext * ctx,
static int r300Fallback(GLcontext * ctx)
{
r300ContextPtr r300 = R300_CONTEXT(ctx);
const unsigned back = ctx->Stencil._BackFace;
/* Do we need to use new-style shaders?
* Also is there a better way to do this? */
if (r300->radeon.radeonScreen->chip_family >= CHIP_FAMILY_RV515) {
@ -371,12 +373,14 @@ static int r300Fallback(GLcontext * ctx)
FALLBACK_IF(ctx->RenderMode != GL_RENDER);
FALLBACK_IF(ctx->Stencil._TestTwoSide
&& (ctx->Stencil.Ref[0] != ctx->Stencil.Ref[1]
|| ctx->Stencil.ValueMask[0] !=
ctx->Stencil.ValueMask[1]
|| ctx->Stencil.WriteMask[0] !=
ctx->Stencil.WriteMask[1]));
/* If GL_EXT_stencil_two_side is disabled, this fallback check can
* be removed.
*/
FALLBACK_IF(ctx->Stencil.Ref[0] != ctx->Stencil.Ref[back]
|| ctx->Stencil.ValueMask[0] !=
ctx->Stencil.ValueMask[back]
|| ctx->Stencil.WriteMask[0] !=
ctx->Stencil.WriteMask[back]);
if (ctx->Extensions.NV_point_sprite || ctx->Extensions.ARB_point_sprite)
FALLBACK_IF(ctx->Point.PointSprite);

View file

@ -974,15 +974,9 @@ static void r300StencilFuncSeparate(GLcontext * ctx, GLenum face,
{
r300ContextPtr rmesa = R300_CONTEXT(ctx);
GLuint refmask =
(((ctx->Stencil.
Ref[0] & 0xff) << R300_STENCILREF_SHIFT) | ((ctx->
Stencil.
ValueMask
[0] &
0xff)
<<
R300_STENCILMASK_SHIFT));
((ctx->Stencil.Ref[0] & 0xff) << R300_STENCILREF_SHIFT)
| ((ctx->Stencil.ValueMask[0] & 0xff) << R300_STENCILMASK_SHIFT);
const unsigned back = ctx->Stencil._BackFace;
GLuint flag;
R300_STATECHANGE(rmesa, zs);
@ -1000,8 +994,7 @@ static void r300StencilFuncSeparate(GLcontext * ctx, GLenum face,
rmesa->hw.zs.cmd[R300_ZS_CNTL_1] |=
(flag << R300_S_FRONT_FUNC_SHIFT);
if (ctx->Stencil._TestTwoSide)
flag = translate_func(ctx->Stencil.Function[1]);
flag = translate_func(ctx->Stencil.Function[back]);
rmesa->hw.zs.cmd[R300_ZS_CNTL_1] |=
(flag << R300_S_BACK_FUNC_SHIFT);
@ -1026,6 +1019,7 @@ static void r300StencilOpSeparate(GLcontext * ctx, GLenum face,
GLenum fail, GLenum zfail, GLenum zpass)
{
r300ContextPtr rmesa = R300_CONTEXT(ctx);
const unsigned back = ctx->Stencil._BackFace;
R300_STATECHANGE(rmesa, zs);
/* It is easier to mask what's left.. */
@ -1042,23 +1036,13 @@ static void r300StencilOpSeparate(GLcontext * ctx, GLenum face,
| (translate_stencil_op(ctx->Stencil.ZPassFunc[0]) <<
R300_S_FRONT_ZPASS_OP_SHIFT);
if (ctx->Stencil._TestTwoSide) {
rmesa->hw.zs.cmd[R300_ZS_CNTL_1] |=
(translate_stencil_op(ctx->Stencil.FailFunc[1]) <<
R300_S_BACK_SFAIL_OP_SHIFT)
| (translate_stencil_op(ctx->Stencil.ZFailFunc[1]) <<
R300_S_BACK_ZFAIL_OP_SHIFT)
| (translate_stencil_op(ctx->Stencil.ZPassFunc[1]) <<
R300_S_BACK_ZPASS_OP_SHIFT);
} else {
rmesa->hw.zs.cmd[R300_ZS_CNTL_1] |=
(translate_stencil_op(ctx->Stencil.FailFunc[0]) <<
R300_S_BACK_SFAIL_OP_SHIFT)
| (translate_stencil_op(ctx->Stencil.ZFailFunc[0]) <<
R300_S_BACK_ZFAIL_OP_SHIFT)
| (translate_stencil_op(ctx->Stencil.ZPassFunc[0]) <<
R300_S_BACK_ZPASS_OP_SHIFT);
}
rmesa->hw.zs.cmd[R300_ZS_CNTL_1] |=
(translate_stencil_op(ctx->Stencil.FailFunc[back]) <<
R300_S_BACK_SFAIL_OP_SHIFT)
| (translate_stencil_op(ctx->Stencil.ZFailFunc[back]) <<
R300_S_BACK_ZFAIL_OP_SHIFT)
| (translate_stencil_op(ctx->Stencil.ZPassFunc[back]) <<
R300_S_BACK_ZPASS_OP_SHIFT);
}
/* =============================================================

View file

@ -922,10 +922,13 @@ _mesa_set_enable(GLcontext *ctx, GLenum cap, GLboolean state)
return;
FLUSH_VERTICES(ctx, _NEW_STENCIL);
ctx->Stencil.TestTwoSide = state;
if (state)
if (state) {
ctx->Stencil._BackFace = 2;
ctx->_TriangleCaps |= DD_TRI_TWOSTENCIL;
else
} else {
ctx->Stencil._BackFace = 1;
ctx->_TriangleCaps &= ~DD_TRI_TWOSTENCIL;
}
break;
#if FEATURE_ARB_fragment_program

View file

@ -82,7 +82,16 @@ compute_version(const GLcontext *ctx)
ctx->Extensions.ARB_vertex_shader &&
ctx->Extensions.ARB_fragment_shader &&
ctx->Extensions.ARB_texture_non_power_of_two &&
ctx->Extensions.EXT_blend_equation_separate);
ctx->Extensions.EXT_blend_equation_separate &&
/* Technically, 2.0 requires the functionality
* of the EXT version. Enable 2.0 if either
* extension is available, and assume that a
* driver that only exposes the ATI extension
* will fallback to software when necessary.
*/
(ctx->Extensions.EXT_stencil_two_side
|| ctx->Extensions.ATI_separate_stencil));
const GLboolean ver_2_1 = (ver_2_0 &&
ctx->Extensions.ARB_shading_language_120 &&
ctx->Extensions.EXT_pixel_buffer_object &&

View file

@ -1108,20 +1108,34 @@ struct gl_scissor_attrib
/**
* Stencil attribute group (GL_STENCIL_BUFFER_BIT).
*
* Three sets of stencil data are tracked so that OpenGL 2.0,
* GL_EXT_stencil_two_side, and GL_ATI_separate_stencil can all be supported
* simultaneously. In each of the stencil state arrays, element 0 corresponds
* to GL_FRONT. Element 1 corresponds to the OpenGL 2.0 /
* GL_ATI_separate_stencil GL_BACK state. Element 2 corresponds to the
* GL_EXT_stencil_two_side GL_BACK state.
*
* The derived value \c _BackFace is either 1 or 2 depending on whether or
* not GL_STENCIL_TEST_TWO_SIDE_EXT is enabled.
*
* The derived value \c _TestTwoSide is set when the front-face and back-face
* stencil state are different.
*/
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) */
GLubyte ActiveFace; /**< GL_EXT_stencil_two_side (0 or 2) */
GLboolean _TestTwoSide;
GLenum Function[2]; /**< Stencil function */
GLenum FailFunc[2]; /**< Fail function */
GLenum ZPassFunc[2]; /**< Depth buffer pass function */
GLenum ZFailFunc[2]; /**< Depth buffer fail function */
GLint Ref[2]; /**< Reference value */
GLuint ValueMask[2]; /**< Value mask */
GLuint WriteMask[2]; /**< Write mask */
GLubyte _BackFace;
GLenum Function[3]; /**< Stencil function */
GLenum FailFunc[3]; /**< Fail function */
GLenum ZPassFunc[3]; /**< Depth buffer pass function */
GLenum ZFailFunc[3]; /**< Depth buffer fail function */
GLint Ref[3]; /**< Reference value */
GLuint ValueMask[3]; /**< Value mask */
GLuint WriteMask[3]; /**< Write mask */
GLuint Clear; /**< Clear value */
};

View file

@ -27,21 +27,6 @@
* \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.
*
* Also, note that GL_ATI_separate_stencil is different as well:
* glStencilFuncSeparateATI(GLenum frontfunc, GLenum backfunc, ...) vs.
* glStencilFuncSeparate(GLenum face, GLenum func, ...).
*/
@ -198,6 +183,7 @@ _mesa_StencilFunc( GLenum func, GLint ref, GLuint mask )
{
GET_CURRENT_CONTEXT(ctx);
const GLint stencilMax = (1 << ctx->DrawBuffer->Visual.stencilBits) - 1;
const GLint face = ctx->Stencil.ActiveFace;
ASSERT_OUTSIDE_BEGIN_END(ctx);
if (!validate_stencil_func(ctx, func)) {
@ -207,9 +193,7 @@ _mesa_StencilFunc( GLenum func, GLint ref, GLuint mask )
ref = CLAMP( ref, 0, stencilMax );
if (ctx->Extensions.EXT_stencil_two_side) {
/* only set active face state */
const GLint face = ctx->Stencil.ActiveFace;
if (face != 0) {
if (ctx->Stencil.Function[face] == func &&
ctx->Stencil.ValueMask[face] == mask &&
ctx->Stencil.Ref[face] == ref)
@ -218,9 +202,12 @@ _mesa_StencilFunc( GLenum func, GLint ref, GLuint mask )
ctx->Stencil.Function[face] = func;
ctx->Stencil.Ref[face] = ref;
ctx->Stencil.ValueMask[face] = mask;
if (ctx->Driver.StencilFuncSeparate) {
ctx->Driver.StencilFuncSeparate(ctx, face ? GL_BACK : GL_FRONT,
func, ref, mask);
/* Only propagate the change to the driver if EXT_stencil_two_side
* is enabled.
*/
if (ctx->Driver.StencilFuncSeparate && ctx->Stencil.TestTwoSide) {
ctx->Driver.StencilFuncSeparate(ctx, GL_BACK, func, ref, mask);
}
}
else {
@ -237,7 +224,9 @@ _mesa_StencilFunc( GLenum func, GLint ref, GLuint mask )
ctx->Stencil.Ref[0] = ctx->Stencil.Ref[1] = ref;
ctx->Stencil.ValueMask[0] = ctx->Stencil.ValueMask[1] = mask;
if (ctx->Driver.StencilFuncSeparate) {
ctx->Driver.StencilFuncSeparate(ctx, GL_FRONT_AND_BACK,
ctx->Driver.StencilFuncSeparate(ctx,
((ctx->Stencil.TestTwoSide)
? GL_FRONT : GL_FRONT_AND_BACK),
func, ref, mask);
}
}
@ -259,17 +248,23 @@ void GLAPIENTRY
_mesa_StencilMask( GLuint mask )
{
GET_CURRENT_CONTEXT(ctx);
const GLint face = ctx->Stencil.ActiveFace;
ASSERT_OUTSIDE_BEGIN_END(ctx);
if (ctx->Extensions.EXT_stencil_two_side) {
/* only set active face state */
const GLint face = ctx->Stencil.ActiveFace;
if (face != 0) {
/* Only modify the EXT_stencil_two_side back-face state.
*/
if (ctx->Stencil.WriteMask[face] == mask)
return;
FLUSH_VERTICES(ctx, _NEW_STENCIL);
ctx->Stencil.WriteMask[face] = mask;
if (ctx->Driver.StencilMaskSeparate) {
ctx->Driver.StencilMaskSeparate(ctx, face ? GL_BACK : GL_FRONT, mask);
/* Only propagate the change to the driver if EXT_stencil_two_side
* is enabled.
*/
if (ctx->Driver.StencilMaskSeparate && ctx->Stencil.TestTwoSide) {
ctx->Driver.StencilMaskSeparate(ctx, GL_BACK, mask);
}
}
else {
@ -280,7 +275,10 @@ _mesa_StencilMask( GLuint mask )
FLUSH_VERTICES(ctx, _NEW_STENCIL);
ctx->Stencil.WriteMask[0] = ctx->Stencil.WriteMask[1] = mask;
if (ctx->Driver.StencilMaskSeparate) {
ctx->Driver.StencilMaskSeparate(ctx, GL_FRONT_AND_BACK, mask);
ctx->Driver.StencilMaskSeparate(ctx,
((ctx->Stencil.TestTwoSide)
? GL_FRONT : GL_FRONT_AND_BACK),
mask);
}
}
}
@ -304,6 +302,8 @@ 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);
if (!validate_stencil_op(ctx, fail)) {
@ -319,9 +319,8 @@ _mesa_StencilOp(GLenum fail, GLenum zfail, GLenum zpass)
return;
}
if (ctx->Extensions.EXT_stencil_two_side) {
if (face != 0) {
/* 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)
@ -330,9 +329,12 @@ _mesa_StencilOp(GLenum fail, GLenum zfail, GLenum zpass)
ctx->Stencil.ZFailFunc[face] = zfail;
ctx->Stencil.ZPassFunc[face] = zpass;
ctx->Stencil.FailFunc[face] = fail;
if (ctx->Driver.StencilOpSeparate) {
ctx->Driver.StencilOpSeparate(ctx, face ? GL_BACK : GL_FRONT,
fail, zfail, zpass);
/* Only propagate the change to the driver if EXT_stencil_two_side
* is enabled.
*/
if (ctx->Driver.StencilOpSeparate && ctx->Stencil.TestTwoSide) {
ctx->Driver.StencilOpSeparate(ctx, GL_BACK, fail, zfail, zpass);
}
}
else {
@ -349,7 +351,9 @@ _mesa_StencilOp(GLenum fail, GLenum zfail, GLenum zpass)
ctx->Stencil.ZPassFunc[0] = ctx->Stencil.ZPassFunc[1] = zpass;
ctx->Stencil.FailFunc[0] = ctx->Stencil.FailFunc[1] = fail;
if (ctx->Driver.StencilOpSeparate) {
ctx->Driver.StencilOpSeparate(ctx, GL_FRONT_AND_BACK,
ctx->Driver.StencilOpSeparate(ctx,
((ctx->Stencil.TestTwoSide)
? GL_FRONT : GL_FRONT_AND_BACK),
fail, zfail, zpass);
}
}
@ -372,7 +376,7 @@ _mesa_ActiveStencilFaceEXT(GLenum face)
if (face == GL_FRONT || face == GL_BACK) {
FLUSH_VERTICES(ctx, _NEW_STENCIL);
ctx->Stencil.ActiveFace = (face == GL_FRONT) ? 0 : 1;
ctx->Stencil.ActiveFace = (face == GL_FRONT) ? 0 : 2;
}
else {
_mesa_error(ctx, GL_INVALID_ENUM, "glActiveStencilFaceEXT(face)");
@ -513,19 +517,16 @@ _mesa_StencilMaskSeparate(GLenum face, GLuint mask)
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]);
}
const GLint face = ctx->Stencil._BackFace;
ctx->Stencil._TestTwoSide =
(ctx->Stencil.Function[0] != ctx->Stencil.Function[face] ||
ctx->Stencil.FailFunc[0] != ctx->Stencil.FailFunc[face] ||
ctx->Stencil.ZPassFunc[0] != ctx->Stencil.ZPassFunc[face] ||
ctx->Stencil.ZFailFunc[0] != ctx->Stencil.ZFailFunc[face] ||
ctx->Stencil.Ref[0] != ctx->Stencil.Ref[face] ||
ctx->Stencil.ValueMask[0] != ctx->Stencil.ValueMask[face] ||
ctx->Stencil.WriteMask[0] != ctx->Stencil.WriteMask[face]);
}
@ -544,17 +545,24 @@ _mesa_init_stencil(GLcontext *ctx)
ctx->Stencil.ActiveFace = 0; /* 0 = GL_FRONT, 1 = GL_BACK */
ctx->Stencil.Function[0] = GL_ALWAYS;
ctx->Stencil.Function[1] = GL_ALWAYS;
ctx->Stencil.Function[2] = GL_ALWAYS;
ctx->Stencil.FailFunc[0] = GL_KEEP;
ctx->Stencil.FailFunc[1] = GL_KEEP;
ctx->Stencil.FailFunc[2] = GL_KEEP;
ctx->Stencil.ZPassFunc[0] = GL_KEEP;
ctx->Stencil.ZPassFunc[1] = GL_KEEP;
ctx->Stencil.ZPassFunc[2] = GL_KEEP;
ctx->Stencil.ZFailFunc[0] = GL_KEEP;
ctx->Stencil.ZFailFunc[1] = GL_KEEP;
ctx->Stencil.ZFailFunc[2] = GL_KEEP;
ctx->Stencil.Ref[0] = 0;
ctx->Stencil.Ref[1] = 0;
ctx->Stencil.Ref[2] = 0;
ctx->Stencil.ValueMask[0] = ~0U;
ctx->Stencil.ValueMask[1] = ~0U;
ctx->Stencil.ValueMask[2] = ~0U;
ctx->Stencil.WriteMask[0] = ~0U;
ctx->Stencil.WriteMask[1] = ~0U;
ctx->Stencil.WriteMask[2] = ~0U;
ctx->Stencil.Clear = 0;
}

View file

@ -997,10 +997,12 @@ stencil_and_ztest_pixels( GLcontext *ctx, SWspan *span, GLuint face )
GLboolean
_swrast_stencil_and_ztest_span(GLcontext *ctx, SWspan *span)
{
const GLuint face = (span->facing == 0) ? 0 : ctx->Stencil._BackFace;
if (span->arrayMask & SPAN_XY)
return stencil_and_ztest_pixels(ctx, span, span->facing);
return stencil_and_ztest_pixels(ctx, span, face);
else
return stencil_and_ztest_span(ctx, span, span->facing);
return stencil_and_ztest_span(ctx, span, face);
}