📄 s_stencil.c
字号:
*
*/
static GLboolean
stencil_and_ztest_span(GLcontext *ctx, struct sw_span *span, GLuint face)
{
struct gl_framebuffer *fb = ctx->DrawBuffer;
struct gl_renderbuffer *rb = fb->Attachment[BUFFER_STENCIL].Renderbuffer;
GLstencil stencilRow[MAX_WIDTH];
GLstencil *stencil;
const GLuint n = span->end;
const GLint x = span->x;
const GLint y = span->y;
GLubyte *mask = span->array->mask;
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);
}
#endif
stencil = rb->GetPointer(ctx, rb, x, y);
if (!stencil) {
rb->GetRow(ctx, rb, n, x, y, stencilRow);
stencil = stencilRow;
}
/*
* 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) {
/* all fragments failed the stencil test, we're done. */
span->writeAll = GL_FALSE;
return GL_FALSE;
}
/*
* Some fragments passed the stencil test, apply depth test to them
* and apply Zpass and Zfail stencil ops.
*/
if (ctx->Depth.Test == GL_FALSE) {
/*
* No depth buffer, just apply zpass stencil function to active pixels.
*/
apply_stencil_op( ctx, ctx->Stencil.ZPassFunc[face], face, n, stencil, mask );
}
else {
/*
* Perform depth buffering, then apply zpass or zfail stencil function.
*/
GLubyte passmask[MAX_WIDTH], failmask[MAX_WIDTH], oldmask[MAX_WIDTH];
GLuint i;
/* save the current mask bits */
_mesa_memcpy(oldmask, mask, n * sizeof(GLubyte));
/* apply the depth test */
_swrast_depth_test_span(ctx, span);
/* Set the stencil pass/fail flags according to result of depth testing.
* if oldmask[i] == 0 then
* Don't touch the stencil value
* else if oldmask[i] and newmask[i] then
* Depth test passed
* else
* assert(oldmask[i] && !newmask[i])
* Depth test failed
* endif
*/
for (i=0;i<n;i++) {
ASSERT(mask[i] == 0 || mask[i] == 1);
passmask[i] = oldmask[i] & mask[i];
failmask[i] = oldmask[i] & (mask[i] ^ 1);
}
/* 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 );
}
if (ctx->Stencil.ZPassFunc[face] != GL_KEEP) {
apply_stencil_op( ctx, ctx->Stencil.ZPassFunc[face], face,
n, stencil, passmask );
}
}
/*
* Write updated stencil values back into hardware stencil buffer.
*/
if (!rb->GetPointer(ctx, rb, 0, 0)) {
rb->PutRow(ctx, rb, n, x, y, stencil, mask);
}
span->writeAll = GL_FALSE;
return GL_TRUE; /* one or more fragments passed both tests */
}
/*
* 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( GLcontext *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->Attachment[BUFFER_STENCIL].Renderbuffer;
const GLstencil ref = ctx->Stencil.Ref[face];
const GLstencil wrtmask = ctx->Stencil.WriteMask[face];
const GLstencil invmask = (GLstencil) (~wrtmask);
GLuint i;
GLstencil *stencilStart = (GLubyte *) rb->Data;
const GLuint stride = rb->Width;
ASSERT(rb->GetPointer(ctx, rb, 0, 0));
ASSERT(sizeof(GLstencil) == 1);
switch (oper) {
case GL_KEEP:
/* do nothing */
break;
case GL_ZERO:
if (invmask==0) {
for (i=0;i<n;i++) {
if (mask[i]) {
GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
*sptr = 0;
}
}
}
else {
for (i=0;i<n;i++) {
if (mask[i]) {
GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
*sptr = (GLstencil) (invmask & *sptr);
}
}
}
break;
case GL_REPLACE:
if (invmask==0) {
for (i=0;i<n;i++) {
if (mask[i]) {
GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
*sptr = ref;
}
}
}
else {
for (i=0;i<n;i++) {
if (mask[i]) {
GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
*sptr = (GLstencil) ((invmask & *sptr ) | (wrtmask & ref));
}
}
}
break;
case GL_INCR:
if (invmask==0) {
for (i=0;i<n;i++) {
if (mask[i]) {
GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
if (*sptr < STENCIL_MAX) {
*sptr = (GLstencil) (*sptr + 1);
}
}
}
}
else {
for (i=0;i<n;i++) {
if (mask[i]) {
GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
if (*sptr < STENCIL_MAX) {
*sptr = (GLstencil) ((invmask & *sptr) | (wrtmask & (*sptr+1)));
}
}
}
}
break;
case GL_DECR:
if (invmask==0) {
for (i=0;i<n;i++) {
if (mask[i]) {
GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
if (*sptr>0) {
*sptr = (GLstencil) (*sptr - 1);
}
}
}
}
else {
for (i=0;i<n;i++) {
if (mask[i]) {
GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
if (*sptr>0) {
*sptr = (GLstencil) ((invmask & *sptr) | (wrtmask & (*sptr-1)));
}
}
}
}
break;
case GL_INCR_WRAP_EXT:
if (invmask==0) {
for (i=0;i<n;i++) {
if (mask[i]) {
GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
*sptr = (GLstencil) (*sptr + 1);
}
}
}
else {
for (i=0;i<n;i++) {
if (mask[i]) {
GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
*sptr = (GLstencil) ((invmask & *sptr) | (wrtmask & (*sptr+1)));
}
}
}
break;
case GL_DECR_WRAP_EXT:
if (invmask==0) {
for (i=0;i<n;i++) {
if (mask[i]) {
GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
*sptr = (GLstencil) (*sptr - 1);
}
}
}
else {
for (i=0;i<n;i++) {
if (mask[i]) {
GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
*sptr = (GLstencil) ((invmask & *sptr) | (wrtmask & (*sptr-1)));
}
}
}
break;
case GL_INVERT:
if (invmask==0) {
for (i=0;i<n;i++) {
if (mask[i]) {
GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
*sptr = (GLstencil) (~*sptr);
}
}
}
else {
for (i=0;i<n;i++) {
if (mask[i]) {
GLstencil *sptr = STENCIL_ADDRESS( x[i], y[i] );
*sptr = (GLstencil) ((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( GLcontext *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->Attachment[BUFFER_STENCIL].Renderbuffer;
GLubyte fail[MAX_WIDTH];
GLstencil r, s;
GLuint i;
GLboolean allfail = GL_FALSE;
const GLuint valueMask = ctx->Stencil.ValueMask[face];
const GLstencil *stencilStart = (GLstencil *) rb->Data;
const GLuint stride = rb->Width;
ASSERT(rb->GetPointer(ctx, rb, 0, 0));
ASSERT(sizeof(GLstencil) == 1);
/*
* 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 = (GLstencil) (ctx->Stencil.Ref[face] & valueMask);
for (i=0;i<n;i++) {
if (mask[i]) {
const GLstencil *sptr = STENCIL_ADDRESS(x[i],y[i]);
s = (GLstencil) (*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 = (GLstencil) (ctx->Stencil.Ref[face] & valueMask);
for (i=0;i<n;i++) {
if (mask[i]) {
const GLstencil *sptr = STENCIL_ADDRESS(x[i],y[i]);
s = (GLstencil) (*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 = (GLstencil) (ctx->Stencil.Ref[face] & valueMask);
for (i=0;i<n;i++) {
if (mask[i]) {
const GLstencil *sptr = STENCIL_ADDRESS(x[i],y[i]);
s = (GLstencil) (*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 = (GLstencil) (ctx->Stencil.Ref[face] & valueMask);
for (i=0;i<n;i++) {
if (mask[i]) {
const GLstencil *sptr = STENCIL_ADDRESS(x[i],y[i]);
s = (GLstencil) (*sptr & valueMask);
if (r >= s) {
/* passed */
fail[i] = 0;
}
else {
fail[i] = 1;
mask[i] = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -