From d00d39a016893979d9b4ca61f6eaa570ccafb0b5 Mon Sep 17 00:00:00 2001 From: Brian Paul Date: Sat, 24 Dec 2011 08:54:26 -0700 Subject: [PATCH] swrast: rewrite stencil test code Stop using the deprecated renderbuffer Get/Put Row/Values functions. Consolidate code paths, etc. The file is nearly half the size it used to be! Reviewed-by: Eric Anholt --- src/mesa/swrast/s_stencil.c | 1117 ++++++++--------------------------- 1 file changed, 259 insertions(+), 858 deletions(-) diff --git a/src/mesa/swrast/s_stencil.c b/src/mesa/swrast/s_stencil.c index dff8f46ca9c..18f44966ca2 100644 --- a/src/mesa/swrast/s_stencil.c +++ b/src/mesa/swrast/s_stencil.c @@ -65,194 +65,170 @@ get_stencil_address(struct gl_renderbuffer *rb, GLint x, GLint y) } +/** + * Compute/return the offset of the stencil value in a pixel. + * For example, if the format is Z24+S8, the position of the stencil bits + * within the 4-byte pixel will be either 0 or 3. + */ +static GLint +get_stencil_offset(gl_format format) +{ + const GLubyte one = 1; + GLubyte pixel[MAX_PIXEL_BYTES]; + GLint bpp = _mesa_get_format_bytes(format); + GLint i; + + assert(_mesa_get_format_bits(format, GL_STENCIL_BITS) == 8); + memset(pixel, 0, sizeof(pixel)); + _mesa_pack_ubyte_stencil_row(format, 1, &one, pixel); + + for (i = 0; i < bpp; i++) { + if (pixel[i]) + return i; + } + + _mesa_problem(NULL, "get_stencil_offset() failed\n"); + return 0; +} + + +/** Clamp the stencil value to [0, 255] */ +static inline GLubyte +clamp(GLint val) +{ + if (val < 0) + return 0; + else if (val > 255) + return 255; + else + return val; +} + + +#define STENCIL_OP(NEW_VAL) \ + if (invmask == 0) { \ + for (i = j = 0; i < n; i++, j += stride) { \ + if (mask[i]) { \ + GLubyte s = stencil[j]; \ + (void) s; \ + stencil[j] = (GLubyte) (NEW_VAL); \ + } \ + } \ + } \ + else { \ + for (i = j = 0; i < n; i++, j += stride) { \ + if (mask[i]) { \ + GLubyte s = stencil[j]; \ + stencil[j] = (GLubyte) ((invmask & s) | (wrtmask & (NEW_VAL))); \ + } \ + } \ + } + /** * Apply the given stencil operator to the array of stencil values. * Don't touch stencil[i] if mask[i] is zero. - * Input: n - size of stencil array - * oper - the stencil buffer operator - * face - 0 or 1 for front or back face operation - * stencil - array of stencil values - * mask - array [n] of flag: 1=apply operator, 0=don't apply operator - * Output: stencil - modified values + * @param n number of stencil values + * @param oper the stencil buffer operator + * @param face 0 or 1 for front or back face operation + * @param stencil array of stencil values (in/out) + * @param mask array [n] of flag: 1=apply operator, 0=don't apply operator + * @param stride stride between stencil values */ static void -apply_stencil_op( const struct gl_context *ctx, GLenum oper, GLuint face, - GLuint n, GLubyte stencil[], const GLubyte mask[] ) +apply_stencil_op(const struct gl_context *ctx, GLenum oper, GLuint face, + GLuint n, GLubyte stencil[], const GLubyte mask[], + GLint stride) { const GLubyte ref = ctx->Stencil.Ref[face]; const GLubyte wrtmask = ctx->Stencil.WriteMask[face]; const GLubyte invmask = (GLubyte) (~wrtmask); - const GLubyte stencilMax = (1 << ctx->DrawBuffer->Visual.stencilBits) - 1; - GLuint i; + GLuint i, j; switch (oper) { - case GL_KEEP: - /* do nothing */ - break; - case GL_ZERO: - if (invmask==0) { - for (i=0;i0) { - stencil[i] = (GLubyte) (s-1); - } - } - } - } - else { - for (i=0;i0) { - stencil[i] = (GLubyte) ((invmask & s) | (wrtmask & (s-1))); - } - } - } - } - break; - case GL_INCR_WRAP_EXT: - if (invmask==0) { - for (i=0;iStencil.ValueMask[face]; - const GLubyte r = (GLubyte) (ctx->Stencil.Ref[face] & valueMask); + const GLubyte ref = (GLubyte) (ctx->Stencil.Ref[face] & valueMask); GLubyte s; - ASSERT(n <= MAX_WIDTH); - /* * Perform stencil test. The results of this operation are stored * in the fail[] array: @@ -263,140 +239,39 @@ do_stencil_test( struct gl_context *ctx, GLuint face, GLuint n, GLubyte stencil[ * ENDIF */ switch (ctx->Stencil.Function[face]) { - case GL_NEVER: - /* never pass; always fail */ - for (i=0;i s) { - /* passed */ - fail[i] = 0; - } - else { - fail[i] = 1; - mask[i] = 0; - } - } - else { - fail[i] = 0; - } - } - break; - case GL_GEQUAL: - for (i=0;i= s) { - /* passed */ - fail[i] = 0; - } - else { - fail[i] = 1; - mask[i] = 0; - } - } - else { - fail[i] = 0; - } - } - break; - case GL_EQUAL: - for (i=0;i s); + break; + case GL_GEQUAL: + STENCIL_TEST(ref >= s); + break; + case GL_EQUAL: + STENCIL_TEST(ref == s); + break; + case GL_NOTEQUAL: + STENCIL_TEST(ref != s); + break; + case GL_ALWAYS: + STENCIL_TEST(1); + break; + default: + _mesa_problem(ctx, "Bad stencil func in gl_stencil_span"); + return 0; } if (ctx->Stencil.FailFunc[face] != GL_KEEP) { - apply_stencil_op( ctx, ctx->Stencil.FailFunc[face], face, n, stencil, fail ); + apply_stencil_op(ctx, ctx->Stencil.FailFunc[face], face, n, stencil, + fail, stride); } return !allfail; @@ -422,54 +297,100 @@ compute_pass_fail_masks(GLuint n, const GLubyte origMask[], /** - * Apply stencil and depth testing to the span of pixels. - * Both software and hardware stencil buffers are acceptable. - * Input: n - number of pixels in the span - * x, y - location of leftmost pixel in span - * z - array [n] of z values - * mask - array [n] of flags (1=test this pixel, 0=skip the pixel) - * Output: mask - array [n] of flags (1=stencil and depth test passed) - * Return: GL_FALSE - all fragments failed the testing - * GL_TRUE - one or more fragments passed the testing - * + * Get 8-bit stencil values from random locations in the stencil buffer. */ -static GLboolean -stencil_and_ztest_span(struct gl_context *ctx, SWspan *span, GLuint face) +static void +get_s8_values(struct gl_context *ctx, struct gl_renderbuffer *rb, + GLuint count, const GLint x[], const GLint y[], + GLubyte stencil[]) +{ + const GLint w = rb->Width, h = rb->Height; + const GLubyte *map = (const GLubyte *) rb->Data; + GLuint i; + + if (rb->Format == MESA_FORMAT_S8) { + const GLuint rowStride = rb->RowStride; + for (i = 0; i < count; i++) { + if (x[i] >= 0 && y[i] >= 0 && x[i] < w && y[i] < h) { + stencil[i] = *(map + y[i] * rowStride + x[i]); + } + } + } + else { + const GLuint bpp = _mesa_get_format_bytes(rb->Format); + const GLuint rowStride = rb->RowStride * bpp; + for (i = 0; i < count; i++) { + if (x[i] >= 0 && y[i] >= 0 && x[i] < w && y[i] < h) { + const GLubyte *src = map + y[i] * rowStride + x[i] * bpp; + _mesa_unpack_ubyte_stencil_row(rb->Format, 1, src, &stencil[i]); + } + } + } +} + + +/** + * Put 8-bit stencil values at random locations into the stencil buffer. + */ +static void +put_s8_values(struct gl_context *ctx, struct gl_renderbuffer *rb, + GLuint count, const GLint x[], const GLint y[], + const GLubyte stencil[]) +{ + const GLint w = rb->Width, h = rb->Height; + GLuint i; + + for (i = 0; i < count; i++) { + if (x[i] >= 0 && y[i] >= 0 && x[i] < w && y[i] < h) { + GLubyte *dst = get_stencil_address(rb, x[i], y[i]); + _mesa_pack_ubyte_stencil_row(rb->Format, 1, &stencil[i], dst); + } + } +} + + +/** + * /return GL_TRUE = one or more fragments passed, + * GL_FALSE = all fragments failed. + */ +GLboolean +_swrast_stencil_and_ztest_span(struct gl_context *ctx, SWspan *span) { struct gl_framebuffer *fb = ctx->DrawBuffer; - struct gl_renderbuffer *rb = fb->_StencilBuffer; - GLubyte stencilRow[MAX_WIDTH]; - GLubyte *stencil; - const GLuint n = span->end; - const GLint x = span->x; - const GLint y = span->y; + struct gl_renderbuffer *rb = fb->Attachment[BUFFER_STENCIL].Renderbuffer; + const GLint stencilOffset = get_stencil_offset(rb->Format); + const GLint stencilStride = _mesa_get_format_bytes(rb->Format); + const GLuint face = (span->facing == 0) ? 0 : ctx->Stencil._BackFace; + const GLuint count = span->end; GLubyte *mask = span->array->mask; + GLubyte stencilTemp[MAX_WIDTH]; + GLubyte *stencilBuf; - ASSERT((span->arrayMask & SPAN_XY) == 0); - ASSERT(ctx->Stencil.Enabled); - ASSERT(n <= MAX_WIDTH); -#ifdef DEBUG - if (ctx->Depth.Test) { - ASSERT(span->arrayMask & SPAN_Z); + if (span->arrayMask & SPAN_XY) { + /* read stencil values from random locations */ + get_s8_values(ctx, rb, count, span->array->x, span->array->y, + stencilTemp); + stencilBuf = stencilTemp; } -#endif - - stencil = (GLubyte *) rb->GetPointer(ctx, rb, x, y); - if (!stencil) { - rb->GetRow(ctx, rb, n, x, y, stencilRow); - stencil = stencilRow; + else { + /* Processing a horizontal run of pixels. Since stencil is always + * 8 bits for all MESA_FORMATs, we just need to use the right offset + * and stride to access them. + */ + stencilBuf = get_stencil_address(rb, span->x, span->y) + stencilOffset; } /* * Apply the stencil test to the fragments. * failMask[i] is 1 if the stencil test failed. */ - if (do_stencil_test( ctx, face, n, stencil, mask ) == GL_FALSE) { + if (!do_stencil_test(ctx, face, count, stencilBuf, mask, stencilStride)) { /* all fragments failed the stencil test, we're done. */ span->writeAll = GL_FALSE; - if (!rb->GetPointer(ctx, rb, 0, 0)) { - /* put updated stencil values into buffer */ - rb->PutRow(ctx, rb, n, x, y, stencil, NULL); + if (span->arrayMask & SPAN_XY) { + /* need to write the updated stencil values back to the buffer */ + put_s8_values(ctx, rb, count, span->array->x, span->array->y, + stencilTemp); } return GL_FALSE; } @@ -479,11 +400,12 @@ stencil_and_ztest_span(struct gl_context *ctx, SWspan *span, GLuint face) * and apply Zpass and Zfail stencil ops. */ if (ctx->Depth.Test == GL_FALSE || - ctx->DrawBuffer->_DepthBuffer == NULL) { + ctx->DrawBuffer->Attachment[BUFFER_DEPTH].Renderbuffer == NULL) { /* * No depth buffer, just apply zpass stencil function to active pixels. */ - apply_stencil_op( ctx, ctx->Stencil.ZPassFunc[face], face, n, stencil, mask ); + apply_stencil_op(ctx, ctx->Stencil.ZPassFunc[face], face, count, + stencilBuf, mask, stencilStride); } else { /* @@ -492,29 +414,28 @@ stencil_and_ztest_span(struct gl_context *ctx, SWspan *span, GLuint face) GLubyte passMask[MAX_WIDTH], failMask[MAX_WIDTH], origMask[MAX_WIDTH]; /* save the current mask bits */ - memcpy(origMask, mask, n * sizeof(GLubyte)); + memcpy(origMask, mask, count * sizeof(GLubyte)); /* apply the depth test */ _swrast_depth_test_span(ctx, span); - compute_pass_fail_masks(n, origMask, mask, passMask, failMask); + compute_pass_fail_masks(count, origMask, mask, passMask, failMask); /* apply the pass and fail operations */ if (ctx->Stencil.ZFailFunc[face] != GL_KEEP) { - apply_stencil_op( ctx, ctx->Stencil.ZFailFunc[face], face, - n, stencil, failMask ); + apply_stencil_op(ctx, ctx->Stencil.ZFailFunc[face], face, + count, stencilBuf, failMask, stencilStride); } if (ctx->Stencil.ZPassFunc[face] != GL_KEEP) { - apply_stencil_op( ctx, ctx->Stencil.ZPassFunc[face], face, - n, stencil, passMask ); + apply_stencil_op(ctx, ctx->Stencil.ZPassFunc[face], face, + count, stencilBuf, passMask, stencilStride); } } - /* - * Write updated stencil values back into hardware stencil buffer. - */ - if (!rb->GetPointer(ctx, rb, 0, 0)) { - rb->PutRow(ctx, rb, n, x, y, stencil, NULL); + /* Write updated stencil values back into hardware stencil buffer */ + if (span->arrayMask & SPAN_XY) { + put_s8_values(ctx, rb, count, span->array->x, span->array->y, + stencilBuf); } span->writeAll = GL_FALSE; @@ -524,526 +445,6 @@ stencil_and_ztest_span(struct gl_context *ctx, SWspan *span, GLuint face) -/* - * Return the address of a stencil buffer value given the window coords: - */ -#define STENCIL_ADDRESS(X, Y) (stencilStart + (Y) * stride + (X)) - - - -/** - * Apply the given stencil operator for each pixel in the array whose - * mask flag is set. - * \note This is for software stencil buffers only. - * Input: n - number of pixels in the span - * x, y - array of [n] pixels - * operator - the stencil buffer operator - * mask - array [n] of flag: 1=apply operator, 0=don't apply operator - */ -static void -apply_stencil_op_to_pixels( struct gl_context *ctx, - GLuint n, const GLint x[], const GLint y[], - GLenum oper, GLuint face, const GLubyte mask[] ) -{ - struct gl_framebuffer *fb = ctx->DrawBuffer; - struct gl_renderbuffer *rb = fb->_StencilBuffer; - const GLubyte stencilMax = (1 << fb->Visual.stencilBits) - 1; - const GLubyte ref = ctx->Stencil.Ref[face]; - const GLubyte wrtmask = ctx->Stencil.WriteMask[face]; - const GLubyte invmask = (GLubyte) (~wrtmask); - GLuint i; - GLubyte *stencilStart = (GLubyte *) rb->Data; - const GLuint stride = rb->Width; - - ASSERT(rb->GetPointer(ctx, rb, 0, 0)); - - switch (oper) { - case GL_KEEP: - /* do nothing */ - break; - case GL_ZERO: - if (invmask==0) { - for (i=0;i0) { - *sptr = (GLubyte) (*sptr - 1); - } - } - } - } - else { - for (i=0;i0) { - *sptr = (GLubyte) ((invmask & *sptr) | (wrtmask & (*sptr-1))); - } - } - } - } - break; - case GL_INCR_WRAP_EXT: - if (invmask==0) { - for (i=0;iDrawBuffer; - struct gl_renderbuffer *rb = fb->_StencilBuffer; - GLubyte fail[MAX_WIDTH]; - GLubyte r, s; - GLuint i; - GLboolean allfail = GL_FALSE; - const GLuint valueMask = ctx->Stencil.ValueMask[face]; - const GLubyte *stencilStart = (GLubyte *) rb->Data; - const GLuint stride = rb->Width; - - ASSERT(rb->GetPointer(ctx, rb, 0, 0)); - - /* - * Perform stencil test. The results of this operation are stored - * in the fail[] array: - * IF fail[i] is non-zero THEN - * the stencil fail operator is to be applied - * ELSE - * the stencil fail operator is not to be applied - * ENDIF - */ - - switch (ctx->Stencil.Function[face]) { - case GL_NEVER: - /* always fail */ - for (i=0;iStencil.Ref[face] & valueMask); - for (i=0;iStencil.Ref[face] & valueMask); - for (i=0;iStencil.Ref[face] & valueMask); - for (i=0;i s) { - /* passed */ - fail[i] = 0; - } - else { - fail[i] = 1; - mask[i] = 0; - } - } - else { - fail[i] = 0; - } - } - break; - case GL_GEQUAL: - r = (GLubyte) (ctx->Stencil.Ref[face] & valueMask); - for (i=0;i= s) { - /* passed */ - fail[i] = 0; - } - else { - fail[i] = 1; - mask[i] = 0; - } - } - else { - fail[i] = 0; - } - } - break; - case GL_EQUAL: - r = (GLubyte) (ctx->Stencil.Ref[face] & valueMask); - for (i=0;iStencil.Ref[face] & valueMask); - for (i=0;iStencil.FailFunc[face] != GL_KEEP) { - apply_stencil_op_to_pixels( ctx, n, x, y, ctx->Stencil.FailFunc[face], - face, fail ); - } - - return !allfail; -} - - - -static void -get_s8_values(struct gl_context *ctx, struct gl_renderbuffer *rb, - GLuint count, const GLint x[], const GLint y[], - GLubyte stencil[]) -{ - const GLint w = rb->Width, h = rb->Height; - const GLubyte *map = (const GLubyte *) rb->Data; - GLuint i; - - if (rb->Format == MESA_FORMAT_S8) { - const GLuint rowStride = rb->RowStride; - for (i = 0; i < count; i++) { - if (x[i] >= 0 && y[i] >= 0 && x[i] < w && y[i] < h) { - stencil[i] = *(map + y[i] * rowStride + x[i]); - } - } - } - else { - const GLuint bpp = _mesa_get_format_bytes(rb->Format); - const GLuint rowStride = rb->RowStride * bpp; - for (i = 0; i < count; i++) { - if (x[i] >= 0 && y[i] >= 0 && x[i] < w && y[i] < h) { - const GLubyte *src = map + y[i] * rowStride + x[i] * bpp; - _mesa_unpack_ubyte_stencil_row(rb->Format, 1, src, &stencil[i]); - } - } - } -} - - -/** - * Apply stencil and depth testing to an array of pixels. - * This is used both for software and hardware stencil buffers. - * - * The comments in this function are a bit sparse but the code is - * almost identical to stencil_and_ztest_span(), which is well - * commented. - * - * Input: n - number of pixels in the array - * x, y - array of [n] pixel positions - * z - array [n] of z values - * mask - array [n] of flags (1=test this pixel, 0=skip the pixel) - * Output: mask - array [n] of flags (1=stencil and depth test passed) - * Return: GL_FALSE - all fragments failed the testing - * GL_TRUE - one or more fragments passed the testing - */ -static GLboolean -stencil_and_ztest_pixels( struct gl_context *ctx, SWspan *span, GLuint face ) -{ - GLubyte passMask[MAX_WIDTH], failMask[MAX_WIDTH], origMask[MAX_WIDTH]; - struct gl_framebuffer *fb = ctx->DrawBuffer; - struct gl_renderbuffer *rb = fb->_StencilBuffer; - const GLuint n = span->end; - const GLint *x = span->array->x; - const GLint *y = span->array->y; - GLubyte *mask = span->array->mask; - - ASSERT(span->arrayMask & SPAN_XY); - ASSERT(ctx->Stencil.Enabled); - ASSERT(n <= MAX_WIDTH); - - if (!rb->GetPointer(ctx, rb, 0, 0)) { - /* No direct access */ - GLubyte stencil[MAX_WIDTH]; - - ASSERT(rb->DataType == GL_UNSIGNED_BYTE); - get_s8_values(ctx, rb, n, x, y, stencil); - - memcpy(origMask, mask, n * sizeof(GLubyte)); - - (void) do_stencil_test(ctx, face, n, stencil, mask); - - if (ctx->Depth.Test == GL_FALSE) { - apply_stencil_op(ctx, ctx->Stencil.ZPassFunc[face], face, - n, stencil, mask); - } - else { - GLubyte tmpMask[MAX_WIDTH]; - memcpy(tmpMask, mask, n * sizeof(GLubyte)); - - _swrast_depth_test_span(ctx, span); - - compute_pass_fail_masks(n, tmpMask, mask, passMask, failMask); - - if (ctx->Stencil.ZFailFunc[face] != GL_KEEP) { - apply_stencil_op(ctx, ctx->Stencil.ZFailFunc[face], face, - n, stencil, failMask); - } - if (ctx->Stencil.ZPassFunc[face] != GL_KEEP) { - apply_stencil_op(ctx, ctx->Stencil.ZPassFunc[face], face, - n, stencil, passMask); - } - } - - /* Write updated stencil values into hardware stencil buffer */ - rb->PutValues(ctx, rb, n, x, y, stencil, origMask); - - return GL_TRUE; - } - else { - /* Direct access to stencil buffer */ - - if (stencil_test_pixels(ctx, face, n, x, y, mask) == GL_FALSE) { - /* all fragments failed the stencil test, we're done. */ - return GL_FALSE; - } - - if (ctx->Depth.Test==GL_FALSE) { - apply_stencil_op_to_pixels(ctx, n, x, y, - ctx->Stencil.ZPassFunc[face], face, mask); - } - else { - memcpy(origMask, mask, n * sizeof(GLubyte)); - - _swrast_depth_test_span(ctx, span); - - compute_pass_fail_masks(n, origMask, mask, passMask, failMask); - - if (ctx->Stencil.ZFailFunc[face] != GL_KEEP) { - apply_stencil_op_to_pixels(ctx, n, x, y, - ctx->Stencil.ZFailFunc[face], - face, failMask); - } - if (ctx->Stencil.ZPassFunc[face] != GL_KEEP) { - apply_stencil_op_to_pixels(ctx, n, x, y, - ctx->Stencil.ZPassFunc[face], - face, passMask); - } - } - - return GL_TRUE; /* one or more fragments passed both tests */ - } -} - - -/** - * /return GL_TRUE = one or more fragments passed, - * GL_FALSE = all fragments failed. - */ -GLboolean -_swrast_stencil_and_ztest_span(struct gl_context *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, face); - else - return stencil_and_ztest_span(ctx, span, face); -} - /** * Return a span of stencil values from the stencil buffer. -- 2.30.2