diff options
Diffstat (limited to 'mesalib/src/mesa/swrast/s_stencil.c')
-rw-r--r-- | mesalib/src/mesa/swrast/s_stencil.c | 1298 |
1 files changed, 346 insertions, 952 deletions
diff --git a/mesalib/src/mesa/swrast/s_stencil.c b/mesalib/src/mesa/swrast/s_stencil.c index 101ee5056..dbcbd2be0 100644 --- a/mesalib/src/mesa/swrast/s_stencil.c +++ b/mesalib/src/mesa/swrast/s_stencil.c @@ -26,6 +26,8 @@ #include "main/glheader.h" #include "main/context.h" #include "main/imports.h" +#include "main/format_pack.h" +#include "main/format_unpack.h" #include "s_context.h" #include "s_depth.h" @@ -50,193 +52,171 @@ ENDIF */ + +/** + * 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;i<n;i++) { - if (mask[i]) { - stencil[i] = 0; - } - } - } - else { - for (i=0;i<n;i++) { - if (mask[i]) { - stencil[i] = (GLubyte) (stencil[i] & invmask); - } - } - } - break; - case GL_REPLACE: - if (invmask==0) { - for (i=0;i<n;i++) { - if (mask[i]) { - stencil[i] = ref; - } - } - } - else { - for (i=0;i<n;i++) { - if (mask[i]) { - GLubyte s = stencil[i]; - stencil[i] = (GLubyte) ((invmask & s ) | (wrtmask & ref)); - } - } - } - break; - case GL_INCR: - if (invmask==0) { - for (i=0;i<n;i++) { - if (mask[i]) { - GLubyte s = stencil[i]; - if (s < stencilMax) { - stencil[i] = (GLubyte) (s+1); - } - } - } - } - else { - for (i=0;i<n;i++) { - if (mask[i]) { - /* VERIFY logic of adding 1 to a write-masked value */ - GLubyte s = stencil[i]; - if (s < stencilMax) { - stencil[i] = (GLubyte) ((invmask & s) | (wrtmask & (s+1))); - } - } - } - } - break; - case GL_DECR: - if (invmask==0) { - for (i=0;i<n;i++) { - if (mask[i]) { - GLubyte s = stencil[i]; - if (s>0) { - stencil[i] = (GLubyte) (s-1); - } - } - } - } - else { - for (i=0;i<n;i++) { - if (mask[i]) { - /* VERIFY logic of subtracting 1 to a write-masked value */ - GLubyte s = stencil[i]; - if (s>0) { - stencil[i] = (GLubyte) ((invmask & s) | (wrtmask & (s-1))); - } - } - } - } - break; - case GL_INCR_WRAP_EXT: - if (invmask==0) { - for (i=0;i<n;i++) { - if (mask[i]) { - stencil[i]++; - } - } - } - else { - for (i=0;i<n;i++) { - if (mask[i]) { - GLubyte s = stencil[i]; - stencil[i] = (GLubyte) ((invmask & s) | (wrtmask & (s+1))); - } - } - } - break; - case GL_DECR_WRAP_EXT: - if (invmask==0) { - for (i=0;i<n;i++) { - if (mask[i]) { - stencil[i]--; - } - } - } - else { - for (i=0;i<n;i++) { - if (mask[i]) { - GLubyte s = stencil[i]; - stencil[i] = (GLubyte) ((invmask & s) | (wrtmask & (s-1))); - } - } - } - break; - case GL_INVERT: - if (invmask==0) { - for (i=0;i<n;i++) { - if (mask[i]) { - GLubyte s = stencil[i]; - stencil[i] = (GLubyte) ~s; - } - } - } - else { - for (i=0;i<n;i++) { - if (mask[i]) { - GLubyte s = stencil[i]; - stencil[i] = (GLubyte) ((invmask & s) | (wrtmask & ~s)); - } - } - } - break; - default: - _mesa_problem(ctx, "Bad stencil op in apply_stencil_op"); + case GL_KEEP: + /* do nothing */ + break; + case GL_ZERO: + /* replace stencil buf values with zero */ + STENCIL_OP(0); + break; + case GL_REPLACE: + /* replace stencil buf values with ref value */ + STENCIL_OP(ref); + break; + case GL_INCR: + /* increment stencil buf values, with clamping */ + STENCIL_OP(clamp(s + 1)); + break; + case GL_DECR: + /* increment stencil buf values, with clamping */ + STENCIL_OP(clamp(s - 1)); + break; + case GL_INCR_WRAP_EXT: + /* increment stencil buf values, without clamping */ + STENCIL_OP(s + 1); + break; + case GL_DECR_WRAP_EXT: + /* increment stencil buf values, without clamping */ + STENCIL_OP(s - 1); + break; + case GL_INVERT: + /* replace stencil buf values with inverted value */ + STENCIL_OP(~s); + break; + default: + _mesa_problem(ctx, "Bad stencil op in apply_stencil_op"); } } +#define STENCIL_TEST(FUNC) \ + for (i = j = 0; i < n; i++, j += stride) { \ + if (mask[i]) { \ + s = (GLubyte) (stencil[j] & valueMask); \ + if (FUNC) { \ + /* stencil pass */ \ + fail[i] = 0; \ + } \ + else { \ + /* stencil fail */ \ + fail[i] = 1; \ + mask[i] = 0; \ + } \ + } \ + else { \ + fail[i] = 0; \ + } \ + } + + /** * Apply stencil test to an array of stencil values (before depth buffering). - * Input: face - 0 or 1 for front or back-face polygons - * n - number of pixels in the array - * stencil - array of [n] stencil values - * mask - array [n] of flag: 0=skip the pixel, 1=stencil the pixel - * Output: mask - pixels which fail the stencil test will have their - * mask flag set to 0. - * stencil - updated stencil values (where the test passed) - * Return: GL_FALSE = all pixels failed, GL_TRUE = zero or more pixels passed. + * For the values that fail, we'll apply the GL_STENCIL_FAIL operator to + * the stencil values. + * + * @param face 0 or 1 for front or back-face polygons + * @param n number of pixels in the array + * @param stencil array of [n] stencil values (in/out) + * @param mask array [n] of flag: 0=skip the pixel, 1=stencil the pixel, + * values are set to zero where the stencil test fails. + * @param stride stride between stencil values + * @return GL_FALSE = all pixels failed, GL_TRUE = zero or more pixels passed. */ static GLboolean -do_stencil_test( struct gl_context *ctx, GLuint face, GLuint n, GLubyte stencil[], - GLubyte mask[] ) +do_stencil_test(struct gl_context *ctx, GLuint face, GLuint n, + GLubyte stencil[], GLubyte mask[], GLint stride) { GLubyte fail[MAX_WIDTH]; GLboolean allfail = GL_FALSE; - GLuint i; + GLuint i, j; const GLuint valueMask = ctx->Stencil.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: @@ -247,140 +227,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<n;i++) { - if (mask[i]) { - mask[i] = 0; - fail[i] = 1; - } - else { - fail[i] = 0; - } - } - allfail = GL_TRUE; - break; - case GL_LESS: - for (i=0;i<n;i++) { - if (mask[i]) { - s = (GLubyte) (stencil[i] & valueMask); - if (r < s) { - /* passed */ - fail[i] = 0; - } - else { - fail[i] = 1; - mask[i] = 0; - } - } - else { - fail[i] = 0; - } - } - break; - case GL_LEQUAL: - for (i=0;i<n;i++) { - if (mask[i]) { - s = (GLubyte) (stencil[i] & valueMask); - if (r <= s) { - /* pass */ - fail[i] = 0; - } - else { - fail[i] = 1; - mask[i] = 0; - } - } - else { - fail[i] = 0; - } - } - break; - case GL_GREATER: - for (i=0;i<n;i++) { - if (mask[i]) { - s = (GLubyte) (stencil[i] & valueMask); - if (r > s) { - /* passed */ - fail[i] = 0; - } - else { - fail[i] = 1; - mask[i] = 0; - } - } - else { - fail[i] = 0; - } - } - break; - case GL_GEQUAL: - for (i=0;i<n;i++) { - if (mask[i]) { - s = (GLubyte) (stencil[i] & valueMask); - if (r >= s) { - /* passed */ - fail[i] = 0; - } - else { - fail[i] = 1; - mask[i] = 0; - } - } - else { - fail[i] = 0; - } - } - break; - case GL_EQUAL: - for (i=0;i<n;i++) { - if (mask[i]) { - s = (GLubyte) (stencil[i] & valueMask); - if (r == s) { - /* passed */ - fail[i] = 0; - } - else { - fail[i] = 1; - mask[i] = 0; - } - } - else { - fail[i] = 0; - } - } - break; - case GL_NOTEQUAL: - for (i=0;i<n;i++) { - if (mask[i]) { - s = (GLubyte) (stencil[i] & valueMask); - if (r != s) { - /* passed */ - fail[i] = 0; - } - else { - fail[i] = 1; - mask[i] = 0; - } - } - else { - fail[i] = 0; - } - } - break; - case GL_ALWAYS: - /* always pass */ - for (i=0;i<n;i++) { - fail[i] = 0; - } - break; - default: - _mesa_problem(ctx, "Bad stencil func in gl_stencil_span"); - return 0; + case GL_NEVER: + STENCIL_TEST(0); + allfail = GL_TRUE; + break; + case GL_LESS: + STENCIL_TEST(ref < s); + break; + case GL_LEQUAL: + STENCIL_TEST(ref <= s); + break; + case GL_GREATER: + STENCIL_TEST(ref > 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; @@ -406,54 +285,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[]) { - 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; - GLubyte *mask = span->array->mask; + const GLint w = rb->Width, h = rb->Height; + const GLubyte *map = (const GLubyte *) rb->Data; + GLuint i; - 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 (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]); + } + } } -#endif +} + + +/** + * 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; - stencil = (GLubyte *) rb->GetPointer(ctx, rb, x, y); - if (!stencil) { - rb->GetRow(ctx, rb, n, x, y, stencilRow); - stencil = stencilRow; + for (i = 0; i < count; i++) { + if (x[i] >= 0 && y[i] >= 0 && x[i] < w && y[i] < h) { + GLubyte *dst = _swrast_pixel_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->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; + + 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; + } + 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 = _swrast_pixel_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; } @@ -463,11 +388,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 { /* @@ -476,29 +402,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; @@ -508,530 +433,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;i<n;i++) { - if (mask[i]) { - GLubyte *sptr = STENCIL_ADDRESS( x[i], y[i] ); - *sptr = 0; - } - } - } - else { - for (i=0;i<n;i++) { - if (mask[i]) { - GLubyte *sptr = STENCIL_ADDRESS( x[i], y[i] ); - *sptr = (GLubyte) (invmask & *sptr); - } - } - } - break; - case GL_REPLACE: - if (invmask==0) { - for (i=0;i<n;i++) { - if (mask[i]) { - GLubyte *sptr = STENCIL_ADDRESS( x[i], y[i] ); - *sptr = ref; - } - } - } - else { - for (i=0;i<n;i++) { - if (mask[i]) { - GLubyte *sptr = STENCIL_ADDRESS( x[i], y[i] ); - *sptr = (GLubyte) ((invmask & *sptr ) | (wrtmask & ref)); - } - } - } - break; - case GL_INCR: - if (invmask==0) { - for (i=0;i<n;i++) { - if (mask[i]) { - GLubyte *sptr = STENCIL_ADDRESS( x[i], y[i] ); - if (*sptr < stencilMax) { - *sptr = (GLubyte) (*sptr + 1); - } - } - } - } - else { - for (i=0;i<n;i++) { - if (mask[i]) { - GLubyte *sptr = STENCIL_ADDRESS( x[i], y[i] ); - if (*sptr < stencilMax) { - *sptr = (GLubyte) ((invmask & *sptr) | (wrtmask & (*sptr+1))); - } - } - } - } - break; - case GL_DECR: - if (invmask==0) { - for (i=0;i<n;i++) { - if (mask[i]) { - GLubyte *sptr = STENCIL_ADDRESS( x[i], y[i] ); - if (*sptr>0) { - *sptr = (GLubyte) (*sptr - 1); - } - } - } - } - else { - for (i=0;i<n;i++) { - if (mask[i]) { - GLubyte *sptr = STENCIL_ADDRESS( x[i], y[i] ); - if (*sptr>0) { - *sptr = (GLubyte) ((invmask & *sptr) | (wrtmask & (*sptr-1))); - } - } - } - } - break; - case GL_INCR_WRAP_EXT: - if (invmask==0) { - for (i=0;i<n;i++) { - if (mask[i]) { - GLubyte *sptr = STENCIL_ADDRESS( x[i], y[i] ); - *sptr = (GLubyte) (*sptr + 1); - } - } - } - else { - for (i=0;i<n;i++) { - if (mask[i]) { - GLubyte *sptr = STENCIL_ADDRESS( x[i], y[i] ); - *sptr = (GLubyte) ((invmask & *sptr) | (wrtmask & (*sptr+1))); - } - } - } - break; - case GL_DECR_WRAP_EXT: - if (invmask==0) { - for (i=0;i<n;i++) { - if (mask[i]) { - GLubyte *sptr = STENCIL_ADDRESS( x[i], y[i] ); - *sptr = (GLubyte) (*sptr - 1); - } - } - } - else { - for (i=0;i<n;i++) { - if (mask[i]) { - GLubyte *sptr = STENCIL_ADDRESS( x[i], y[i] ); - *sptr = (GLubyte) ((invmask & *sptr) | (wrtmask & (*sptr-1))); - } - } - } - break; - case GL_INVERT: - if (invmask==0) { - for (i=0;i<n;i++) { - if (mask[i]) { - GLubyte *sptr = STENCIL_ADDRESS( x[i], y[i] ); - *sptr = (GLubyte) (~*sptr); - } - } - } - else { - for (i=0;i<n;i++) { - if (mask[i]) { - GLubyte *sptr = STENCIL_ADDRESS( x[i], y[i] ); - *sptr = (GLubyte) ((invmask & *sptr) | (wrtmask & ~*sptr)); - } - } - } - break; - default: - _mesa_problem(ctx, "Bad stencilop in apply_stencil_op_to_pixels"); - } -} - - - -/** - * Apply stencil test to an array of pixels before depth buffering. - * - * \note Used for software stencil buffer only. - * Input: n - number of pixels in the span - * x, y - array of [n] pixels to stencil - * mask - array [n] of flag: 0=skip the pixel, 1=stencil the pixel - * Output: mask - pixels which fail the stencil test will have their - * mask flag set to 0. - * \return GL_FALSE = all pixels failed, GL_TRUE = zero or more pixels passed. - */ -static GLboolean -stencil_test_pixels( struct gl_context *ctx, GLuint face, GLuint n, - const GLint x[], const GLint y[], GLubyte mask[] ) -{ - const struct gl_framebuffer *fb = ctx->DrawBuffer; - 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;i<n;i++) { - if (mask[i]) { - mask[i] = 0; - fail[i] = 1; - } - else { - fail[i] = 0; - } - } - allfail = GL_TRUE; - break; - case GL_LESS: - r = (GLubyte) (ctx->Stencil.Ref[face] & valueMask); - for (i=0;i<n;i++) { - if (mask[i]) { - const GLubyte *sptr = STENCIL_ADDRESS(x[i],y[i]); - s = (GLubyte) (*sptr & valueMask); - if (r < s) { - /* passed */ - fail[i] = 0; - } - else { - fail[i] = 1; - mask[i] = 0; - } - } - else { - fail[i] = 0; - } - } - break; - case GL_LEQUAL: - r = (GLubyte) (ctx->Stencil.Ref[face] & valueMask); - for (i=0;i<n;i++) { - if (mask[i]) { - const GLubyte *sptr = STENCIL_ADDRESS(x[i],y[i]); - s = (GLubyte) (*sptr & valueMask); - if (r <= s) { - /* pass */ - fail[i] = 0; - } - else { - fail[i] = 1; - mask[i] = 0; - } - } - else { - fail[i] = 0; - } - } - break; - case GL_GREATER: - r = (GLubyte) (ctx->Stencil.Ref[face] & valueMask); - for (i=0;i<n;i++) { - if (mask[i]) { - const GLubyte *sptr = STENCIL_ADDRESS(x[i],y[i]); - s = (GLubyte) (*sptr & valueMask); - if (r > 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<n;i++) { - if (mask[i]) { - const GLubyte *sptr = STENCIL_ADDRESS(x[i],y[i]); - s = (GLubyte) (*sptr & valueMask); - if (r >= 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;i<n;i++) { - if (mask[i]) { - const GLubyte *sptr = STENCIL_ADDRESS(x[i],y[i]); - s = (GLubyte) (*sptr & valueMask); - if (r == s) { - /* passed */ - fail[i] = 0; - } - else { - fail[i] = 1; - mask[i] = 0; - } - } - else { - fail[i] = 0; - } - } - break; - case GL_NOTEQUAL: - r = (GLubyte) (ctx->Stencil.Ref[face] & valueMask); - for (i=0;i<n;i++) { - if (mask[i]) { - const GLubyte *sptr = STENCIL_ADDRESS(x[i],y[i]); - s = (GLubyte) (*sptr & valueMask); - if (r != s) { - /* passed */ - fail[i] = 0; - } - else { - fail[i] = 1; - mask[i] = 0; - } - } - else { - fail[i] = 0; - } - } - break; - case GL_ALWAYS: - /* always pass */ - for (i=0;i<n;i++) { - fail[i] = 0; - } - break; - default: - _mesa_problem(ctx, "Bad stencil func in gl_stencil_pixels"); - return 0; - } - - if (ctx->Stencil.FailFunc[face] != GL_KEEP) { - apply_stencil_op_to_pixels( ctx, n, x, y, ctx->Stencil.FailFunc[face], - face, fail ); - } - - return !allfail; -} - - - - -/** - * 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); - _swrast_get_values(ctx, rb, n, x, y, stencil, sizeof(GLubyte)); - - 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); -} - - -#if 0 -GLuint -clip_span(GLuint bufferWidth, GLuint bufferHeight, - GLint x, GLint y, GLuint *count) -{ - GLuint n = *count; - GLuint skipPixels = 0; - - if (y < 0 || y >= bufferHeight || x + n <= 0 || x >= bufferWidth) { - /* totally out of bounds */ - n = 0; - } - else { - /* left clip */ - if (x < 0) { - skipPixels = -x; - x = 0; - n -= skipPixels; - } - /* right clip */ - if (x + n > bufferWidth) { - GLint dx = x + n - bufferWidth; - n -= dx; - } - } - - *count = n; - - return skipPixels; -} -#endif - /** * Return a span of stencil values from the stencil buffer. @@ -1044,6 +445,8 @@ void _swrast_read_stencil_span(struct gl_context *ctx, struct gl_renderbuffer *rb, GLint n, GLint x, GLint y, GLubyte stencil[]) { + GLubyte *src; + if (y < 0 || y >= (GLint) rb->Height || x + n <= 0 || x >= (GLint) rb->Width) { /* span is completely outside framebuffer */ @@ -1064,7 +467,8 @@ _swrast_read_stencil_span(struct gl_context *ctx, struct gl_renderbuffer *rb, return; } - rb->GetRow(ctx, rb, n, x, y, stencil); + src = _swrast_pixel_address(rb, x, y); + _mesa_unpack_ubyte_stencil_row(rb->Format, n, src, stencil); } @@ -1082,9 +486,10 @@ _swrast_write_stencil_span(struct gl_context *ctx, GLint n, GLint x, GLint y, const GLubyte stencil[] ) { struct gl_framebuffer *fb = ctx->DrawBuffer; - struct gl_renderbuffer *rb = fb->_StencilBuffer; + struct gl_renderbuffer *rb = fb->Attachment[BUFFER_STENCIL].Renderbuffer; const GLuint stencilMax = (1 << fb->Visual.stencilBits) - 1; const GLuint stencilMask = ctx->Stencil.WriteMask[0]; + GLubyte *stencilBuf; if (y < 0 || y >= (GLint) rb->Height || x + n <= 0 || x >= (GLint) rb->Width) { @@ -1105,140 +510,129 @@ _swrast_write_stencil_span(struct gl_context *ctx, GLint n, GLint x, GLint y, return; } + stencilBuf = _swrast_pixel_address(rb, x, y); + if ((stencilMask & stencilMax) != stencilMax) { /* need to apply writemask */ GLubyte destVals[MAX_WIDTH], newVals[MAX_WIDTH]; GLint i; - rb->GetRow(ctx, rb, n, x, y, destVals); + + _mesa_unpack_ubyte_stencil_row(rb->Format, n, stencilBuf, destVals); for (i = 0; i < n; i++) { newVals[i] = (stencil[i] & stencilMask) | (destVals[i] & ~stencilMask); } - rb->PutRow(ctx, rb, n, x, y, newVals, NULL); + _mesa_pack_ubyte_stencil_row(rb->Format, n, newVals, stencilBuf); } else { - rb->PutRow(ctx, rb, n, x, y, stencil, NULL); + _mesa_pack_ubyte_stencil_row(rb->Format, n, stencil, stencilBuf); } } /** - * Clear the stencil buffer. + * Clear the stencil buffer. If the buffer is a combined + * depth+stencil buffer, only the stencil bits will be touched. */ void -_swrast_clear_stencil_buffer( struct gl_context *ctx, struct gl_renderbuffer *rb ) +_swrast_clear_stencil_buffer(struct gl_context *ctx) { + struct gl_renderbuffer *rb = + ctx->DrawBuffer->Attachment[BUFFER_STENCIL].Renderbuffer; const GLubyte stencilBits = ctx->DrawBuffer->Visual.stencilBits; - const GLuint mask = ctx->Stencil.WriteMask[0]; - const GLuint invMask = ~mask; - const GLuint clearVal = (ctx->Stencil.Clear & mask); + const GLuint writeMask = ctx->Stencil.WriteMask[0]; const GLuint stencilMax = (1 << stencilBits) - 1; GLint x, y, width, height; + GLubyte *map; + GLint rowStride, i, j; + GLbitfield mapMode; - if (!rb || mask == 0) + if (!rb || writeMask == 0) return; - ASSERT(rb->DataType == GL_UNSIGNED_BYTE || - rb->DataType == GL_UNSIGNED_SHORT); - - ASSERT(rb->_BaseFormat == GL_STENCIL_INDEX); - /* compute region to clear */ x = ctx->DrawBuffer->_Xmin; y = ctx->DrawBuffer->_Ymin; width = ctx->DrawBuffer->_Xmax - ctx->DrawBuffer->_Xmin; height = ctx->DrawBuffer->_Ymax - ctx->DrawBuffer->_Ymin; - if (rb->GetPointer(ctx, rb, 0, 0)) { - /* Direct buffer access */ - if ((mask & stencilMax) != stencilMax) { - /* need to mask the clear */ - if (rb->DataType == GL_UNSIGNED_BYTE) { - GLint i, j; - for (i = 0; i < height; i++) { - GLubyte *stencil = (GLubyte*) rb->GetPointer(ctx, rb, x, y + i); - for (j = 0; j < width; j++) { - stencil[j] = (stencil[j] & invMask) | clearVal; - } - } - } - else { - GLint i, j; + mapMode = GL_MAP_WRITE_BIT; + if ((writeMask & stencilMax) != stencilMax) { + /* need to mask stencil values */ + mapMode |= GL_MAP_READ_BIT; + } + else if (_mesa_get_format_bits(rb->Format, GL_DEPTH_BITS) > 0) { + /* combined depth+stencil, need to mask Z values */ + mapMode |= GL_MAP_READ_BIT; + } + + ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, + mapMode, &map, &rowStride); + if (!map) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "glClear(stencil)"); + return; + } + + switch (rb->Format) { + case MESA_FORMAT_S8: + { + GLubyte clear = ctx->Stencil.Clear & writeMask & 0xff; + GLubyte mask = (~writeMask) & 0xff; + if (mask != 0) { + /* masked clear */ for (i = 0; i < height; i++) { - GLushort *stencil = (GLushort*) rb->GetPointer(ctx, rb, x, y + i); + GLubyte *row = map; for (j = 0; j < width; j++) { - stencil[j] = (stencil[j] & invMask) | clearVal; + row[j] = (row[j] & mask) | clear; } + map += rowStride; } } - } - else { - /* no bit masking */ - if (width == (GLint) rb->Width && rb->DataType == GL_UNSIGNED_BYTE) { - /* optimized case */ - /* Note: bottom-to-top raster assumed! */ - GLubyte *stencil = (GLubyte *) rb->GetPointer(ctx, rb, x, y); - GLuint len = width * height * sizeof(GLubyte); - memset(stencil, clearVal, len); + else if (rowStride == width) { + /* clear whole buffer */ + memset(map, clear, width * height); } else { - /* general case */ - GLint i; + /* clear scissored */ for (i = 0; i < height; i++) { - GLvoid *stencil = rb->GetPointer(ctx, rb, x, y + i); - if (rb->DataType == GL_UNSIGNED_BYTE) { - memset(stencil, clearVal, width); - } - else { - _mesa_memset16((short unsigned int*) stencil, clearVal, width); - } + memset(map, clear, width); + map += rowStride; } } } - } - else { - /* no direct access */ - if ((mask & stencilMax) != stencilMax) { - /* need to mask the clear */ - if (rb->DataType == GL_UNSIGNED_BYTE) { - GLint i, j; - for (i = 0; i < height; i++) { - GLubyte stencil[MAX_WIDTH]; - rb->GetRow(ctx, rb, width, x, y + i, stencil); - for (j = 0; j < width; j++) { - stencil[j] = (stencil[j] & invMask) | clearVal; - } - rb->PutRow(ctx, rb, width, x, y + i, stencil, NULL); - } - } - else { - GLint i, j; - for (i = 0; i < height; i++) { - GLushort stencil[MAX_WIDTH]; - rb->GetRow(ctx, rb, width, x, y + i, stencil); - for (j = 0; j < width; j++) { - stencil[j] = (stencil[j] & invMask) | clearVal; - } - rb->PutRow(ctx, rb, width, x, y + i, stencil, NULL); + break; + case MESA_FORMAT_S8_Z24: + { + GLuint clear = (ctx->Stencil.Clear & writeMask & 0xff) << 24; + GLuint mask = (((~writeMask) & 0xff) << 24) | 0xffffff; + for (i = 0; i < height; i++) { + GLuint *row = (GLuint *) map; + for (j = 0; j < width; j++) { + row[j] = (row[j] & mask) | clear; } + map += rowStride; } } - else { - /* no bit masking */ - const GLubyte clear8 = (GLubyte) clearVal; - const GLushort clear16 = (GLushort) clearVal; - const void *clear; - GLint i; - if (rb->DataType == GL_UNSIGNED_BYTE) { - clear = &clear8; - } - else { - clear = &clear16; - } + break; + case MESA_FORMAT_Z24_S8: + { + GLuint clear = ctx->Stencil.Clear & writeMask & 0xff; + GLuint mask = 0xffffff00 | ((~writeMask) & 0xff); for (i = 0; i < height; i++) { - rb->PutMonoRow(ctx, rb, width, x, y + i, clear, NULL); + GLuint *row = (GLuint *) map; + for (j = 0; j < width; j++) { + row[j] = (row[j] & mask) | clear; + } + map += rowStride; } } + break; + default: + _mesa_problem(ctx, "Unexpected stencil buffer format %s" + " in _swrast_clear_stencil_buffer()", + _mesa_get_format_name(rb->Format)); } + + ctx->Driver.UnmapRenderbuffer(ctx, rb); } |