Checkpoint: stencil roughly working, some bugs to fix...
authorBrian <brian.paul@tungstengraphics.com>
Wed, 11 Jul 2007 00:59:17 +0000 (18:59 -0600)
committerBrian <brian.paul@tungstengraphics.com>
Wed, 11 Jul 2007 00:59:17 +0000 (18:59 -0600)
src/mesa/drivers/x11/xm_surface.c
src/mesa/pipe/p_defines.h
src/mesa/pipe/softpipe/sp_context.c
src/mesa/pipe/softpipe/sp_quad.h
src/mesa/pipe/softpipe/sp_quad_depth_test.c
src/mesa/pipe/softpipe/sp_quad_stencil.c
src/mesa/pipe/softpipe/sp_surface.h

index 5158e42..2bef5e6 100644 (file)
@@ -244,6 +244,8 @@ xmesa_get_color_surface(GLcontext *ctx, GLuint buf)
 }
 
 
+
+
 static void
 read_quad_z(struct softpipe_surface *sps,
             GLint x, GLint y, GLuint zzzz[QUAD_SIZE])
@@ -296,6 +298,51 @@ create_z_surface(XMesaContext xmctx, struct gl_renderbuffer *rb)
    return xmsurf;
 }
 
+
+
+
+static void
+read_quad_stencil(struct softpipe_surface *sps,
+                  GLint x, GLint y, GLubyte ssss[QUAD_SIZE])
+{
+   struct xmesa_surface *xmsurf = xmesa_surface(sps);
+   struct gl_renderbuffer *rb = xmsurf->rb;
+   GET_CURRENT_CONTEXT(ctx);
+   rb->GetRow(ctx, rb, 2, x, y,     ssss);
+   rb->GetRow(ctx, rb, 2, x, y + 1, ssss + 2);
+}
+
+static void
+write_quad_stencil(struct softpipe_surface *sps,
+                   GLint x, GLint y, const GLubyte ssss[QUAD_SIZE])
+{
+   struct xmesa_surface *xmsurf = xmesa_surface(sps);
+   struct gl_renderbuffer *rb = xmsurf->rb;
+   GET_CURRENT_CONTEXT(ctx);
+   rb->PutRow(ctx, rb, 2, x, y,     ssss,     NULL);
+   rb->PutRow(ctx, rb, 2, x, y + 1, ssss + 2, NULL);
+}
+
+static struct xmesa_surface *
+create_stencil_surface(XMesaContext xmctx, struct gl_renderbuffer *rb)
+{
+   struct xmesa_surface *xmsurf;
+
+   xmsurf = CALLOC_STRUCT(xmesa_surface);
+   if (xmsurf) {
+      xmsurf->sps.surface.format = PIPE_FORMAT_S8;
+      xmsurf->sps.surface.width = rb->Width;
+      xmsurf->sps.surface.height = rb->Height;
+      xmsurf->sps.read_quad_stencil = read_quad_stencil;
+      xmsurf->sps.write_quad_stencil = write_quad_stencil;
+      xmsurf->rb = rb;
+   }
+   return xmsurf;
+}
+
+
+
+
 /**
  * Return a pipe_surface that wraps the current Z/depth buffer.
  * XXX this is pretty much a total hack until gl_renderbuffers and
@@ -327,6 +374,22 @@ xmesa_get_z_surface(GLcontext *ctx)
 struct pipe_surface *
 xmesa_get_stencil_surface(GLcontext *ctx)
 {
-   return NULL;
+   XMesaContext xmctx = XMESA_CONTEXT(ctx);
+   struct gl_renderbuffer *rb = ctx->DrawBuffer->_StencilBuffer;
+   static struct xmesa_surface *xms = NULL;
+
+   if (!rb)
+      return NULL;
+
+   if (!xms) {
+      xms = create_stencil_surface(xmctx, rb);
+   }
+   else if (xms->sps.surface.width != rb->Width ||
+            xms->sps.surface.height != rb->Height) {
+      free_surface(&xms->sps);
+      xms = create_stencil_surface(xmctx, rb);
+   }
+
+   return (struct pipe_surface *) &xms->sps.surface;
 }
 
index 7212040..fbdd015 100644 (file)
 #define PIPE_TEX_COMPARE_R_TO_TEXTURE  1
 
 /**
- * Texture/surface image formats
+ * Texture/surface image formats (preliminary)
  */
 #define PIPE_FORMAT_U_R8_G8_B8_A8   0  /**< ubyte[4] RGBA */
 #define PIPE_FORMAT_U_A8_R8_G8_B8   1  /**< ubyte[4] ARGB */
 #define PIPE_FORMAT_F_Z32           8  /**< float Z/depth */
 #define PIPE_FORMAT_YCBCR           9
 #define PIPE_FORMAT_YCBCR_REV      10
+#define PIPE_FORMAT_S8             11  /**< 8-bit stencil */
+#define PIPE_FORMAT_Z24_S8         12  /**< 24-bit Z + 8-bit stencil */
+
 
 /**
  * Texture typess
index 6afc0c1..5043460 100644 (file)
@@ -87,6 +87,7 @@ struct pipe_context *softpipe_create( void )
    softpipe->quad.alpha_test = sp_quad_alpha_test_stage(softpipe);
    softpipe->quad.blend = sp_quad_blend_stage(softpipe);
    softpipe->quad.depth_test = sp_quad_depth_test_stage(softpipe);
+   softpipe->quad.stencil_test = sp_quad_stencil_test_stage(softpipe);
    softpipe->quad.output = sp_quad_output_stage(softpipe);
 
    /*
index c09905c..df416df 100644 (file)
@@ -55,5 +55,6 @@ struct quad_stage *sp_quad_output_stage( struct softpipe_context *softpipe );
 
 void sp_build_quad_pipeline(struct softpipe_context *sp);
 
+void sp_depth_test_quad(struct quad_stage *qs, struct quad_header *quad);
 
 #endif /* SP_TILE_H */
index f7dc5c8..d47c4c4 100644 (file)
 #include "sp_quad.h"
 
 
-static void
-depth_test_quad(struct quad_stage *qs, struct quad_header *quad)
+/**
+ * Do depth testing for a quad.
+ * Not static since it's used by the stencil code.
+ */
+void
+sp_depth_test_quad(struct quad_stage *qs, struct quad_header *quad)
 {
    struct softpipe_context *softpipe = qs->softpipe;
    struct softpipe_surface *sps = softpipe_surface(softpipe->framebuffer.zbuf);
@@ -137,6 +141,13 @@ depth_test_quad(struct quad_stage *qs, struct quad_header *quad)
       /* write updated zquad to zbuffer */
       sps->write_quad_z(sps, quad->x0, quad->y0, bzzzz);
    }
+}
+
+
+static void
+depth_test_quad(struct quad_stage *qs, struct quad_header *quad)
+{
+   sp_depth_test_quad(qs, quad);
 
    if (quad->mask)
       qs->next->run(qs->next, quad);
index 9f59d09..5b59add 100644 (file)
 #include "pipe/p_defines.h"
 
 
-static void
-stencil_test_quad(struct quad_stage *qs, struct quad_header *quad)
-{
-   struct softpipe_context *softpipe = qs->softpipe;
-   struct softpipe_surface *sps = softpipe_surface(softpipe->framebuffer.zbuf);
-   GLuint bzzzz[QUAD_SIZE];  /**< Z values fetched from depth buffer */
-   GLuint qzzzz[QUAD_SIZE];  /**< Z values from the quad */
-   GLuint zmask = 0;
-   GLuint j;
-   GLfloat scale;
+/** Only 8-bit stencil supported */
+#define STENCIL_MAX 0xff
 
-   assert(sps); /* shouldn't get here if there's no zbuffer */
 
-   /*
-    * To increase efficiency, we should probably have multiple versions
-    * of this function that are specifically for Z16, Z32 and FP Z buffers.
-    * Try to effectively do that with codegen...
-    */
-   if (sps->surface.format == PIPE_FORMAT_U_Z16)
-      scale = 65535.0;
-   else
-      assert(0);  /* XXX fix this someday */
-
-   /*
-    * Convert quad's float depth values to int depth values.
-    * If the Z buffer stores integer values, we _have_ to do the depth
-    * compares with integers (not floats).  Otherwise, the float->int->float
-    * conversion of Z values (which isn't an identity function) will cause
-    * Z-fighting errors.
-    */
-   for (j = 0; j < QUAD_SIZE; j++) {
-      qzzzz[j] = (GLuint) (quad->outputs.depth[j] * scale);
-   }
+/**
+ * Do the basic stencil test (compare stencil buffer values against the
+ * reference value.
+ *
+ * \param stencilVals  the stencil values from the stencil buffer
+ * \param func  the stencil func (PIPE_FUNC_x)
+ * \param ref  the stencil reference value
+ * \param valMask  the stencil value mask indicating which bits of the stencil
+ *                 values and ref value are to be used.
+ * \return mask indicating which pixels passed the stencil test
+ */
+static GLbitfield
+do_stencil_test(const GLubyte stencilVals[QUAD_SIZE], GLuint func,
+                GLbitfield ref, GLbitfield valMask)
+{
+   GLbitfield passMask = 0x0;
+   GLuint j;
 
-   /* get zquad from zbuffer */
-   sps->read_quad_z(sps, quad->x0, quad->y0, bzzzz);
+   ref &= valMask;
 
-   switch (softpipe->depth_test.func) {
+   switch (func) {
    case PIPE_FUNC_NEVER:
-      /* zmask = 0 */
+      /* passMask = 0x0 */
       break;
    case PIPE_FUNC_LESS:
-      /* Note this is pretty much a single sse or cell instruction.  
-       * Like this:  quad->mask &= (quad->outputs.depth < zzzz);
-       */
       for (j = 0; j < QUAD_SIZE; j++) {
-        if (qzzzz[j] < bzzzz[j]) 
-           zmask |= 1 << j;
+         if ((stencilVals[j] & valMask) < ref) {
+            passMask |= (1 << j);
+         }
       }
       break;
    case PIPE_FUNC_EQUAL:
       for (j = 0; j < QUAD_SIZE; j++) {
-        if (qzzzz[j] == bzzzz[j]) 
-           zmask |= 1 << j;
+         if ((stencilVals[j] & valMask) == ref) {
+            passMask |= (1 << j);
+         }
       }
       break;
    case PIPE_FUNC_LEQUAL:
       for (j = 0; j < QUAD_SIZE; j++) {
-        if (qzzzz[j] <= bzzzz[j]) 
-           zmask |= (1 << j);
+         if ((stencilVals[j] & valMask) <= ref) {
+            passMask |= (1 << j);
+         }
       }
       break;
    case PIPE_FUNC_GREATER:
       for (j = 0; j < QUAD_SIZE; j++) {
-        if (qzzzz[j] > bzzzz[j]) 
-           zmask |= (1 << j);
+         if ((stencilVals[j] & valMask) > ref) {
+            passMask |= (1 << j);
+         }
       }
       break;
    case PIPE_FUNC_NOTEQUAL:
       for (j = 0; j < QUAD_SIZE; j++) {
-        if (qzzzz[j] != bzzzz[j]) 
-           zmask |= (1 << j);
+         if ((stencilVals[j] & valMask) != ref) {
+            passMask |= (1 << j);
+         }
       }
       break;
    case PIPE_FUNC_GEQUAL:
       for (j = 0; j < QUAD_SIZE; j++) {
-        if (qzzzz[j] >= bzzzz[j]) 
-           zmask |= (1 << j);
+         if ((stencilVals[j] & valMask) >= ref) {
+            passMask |= (1 << j);
+         }
       }
       break;
    case PIPE_FUNC_ALWAYS:
-      zmask = MASK_ALL;
+      passMask = MASK_ALL;
       break;
    default:
-      abort();
+      assert(0);
    }
 
-   quad->mask &= zmask;
+   return passMask;
+}
+
+
+/**
+ * Apply the stencil operator to stencil values.
+ *
+ * \param stencilVals  the stencil buffer values (read and written)
+ * \param mask  indicates which pixels to update
+ * \param op  the stencil operator (PIPE_STENCIL_OP_x)
+ * \param ref  the stencil reference value
+ * \param wrtMask  writemask controlling which bits are changed in the
+ *                 stencil values
+ */
+static void
+apply_stencil_op(GLubyte stencilVals[QUAD_SIZE],
+                 GLbitfield mask, GLuint op, GLubyte ref, GLubyte wrtMask)
+{
+   GLuint j;
+   GLubyte newstencil[QUAD_SIZE];
+
+   for (j = 0; j < QUAD_SIZE; j++) {
+      newstencil[j] = stencilVals[j];
+   }
 
-   if (softpipe->depth_test.writemask) {
-      
-      /* This is also efficient with sse / spe instructions: 
-       */
+   switch (op) {
+   case PIPE_STENCIL_OP_KEEP:
+      /* no-op */
+      break;
+   case PIPE_STENCIL_OP_ZERO:
       for (j = 0; j < QUAD_SIZE; j++) {
-        if (quad->mask & (1 << j)) {
-           bzzzz[j] = qzzzz[j];
-        }
+         if (mask & (1 << j)) {
+            newstencil[j] = 0;
+         }
       }
+      break;
+   case PIPE_STENCIL_OP_REPLACE:
+      for (j = 0; j < QUAD_SIZE; j++) {
+         if (mask & (1 << j)) {
+            newstencil[j] = ref;
+         }
+      }
+      break;
+   case PIPE_STENCIL_OP_INCR:
+      for (j = 0; j < QUAD_SIZE; j++) {
+         if (mask & (1 << j)) {
+            if (stencilVals[j] < STENCIL_MAX) {
+               newstencil[j] = stencilVals[j] + 1;
+            }
+         }
+      }
+      break;
+   case PIPE_STENCIL_OP_DECR:
+      for (j = 0; j < QUAD_SIZE; j++) {
+         if (mask & (1 << j)) {
+            if (stencilVals[j] > 0) {
+               newstencil[j] = stencilVals[j] - 1;
+            }
+         }
+      }
+      break;
+   case PIPE_STENCIL_OP_INCR_WRAP:
+      for (j = 0; j < QUAD_SIZE; j++) {
+         if (mask & (1 << j)) {
+            newstencil[j] = stencilVals[j] + 1;
+         }
+      }
+      break;
+   case PIPE_STENCIL_OP_DECR_WRAP:
+      for (j = 0; j < QUAD_SIZE; j++) {
+         if (mask & (1 << j)) {
+            newstencil[j] = stencilVals[j] - 1;
+         }
+      }
+      break;
+   case PIPE_STENCIL_OP_INVERT:
+      for (j = 0; j < QUAD_SIZE; j++) {
+         if (mask & (1 << j)) {
+            newstencil[j] = ~stencilVals[j];
+         }
+      }
+      break;
+   default:
+      assert(0);
+   }
 
-      /* write updated zquad to zbuffer */
-      sps->write_quad_z(sps, quad->x0, quad->y0, bzzzz);
+   /*
+    * update the stencil values
+    */
+   if (wrtMask != STENCIL_MAX) {
+      /* apply bit-wise stencil buffer writemask */
+      for (j = 0; j < QUAD_SIZE; j++) {
+         stencilVals[j] = (wrtMask & newstencil[j]) | (~wrtMask & stencilVals[j]);
+      }
    }
+   else {
+      for (j = 0; j < QUAD_SIZE; j++) {
+         stencilVals[j] = newstencil[j];
+      }
+   }
+}
+
+
+/**
+ * Do stencil (and depth) testing.  Stenciling depends on the outcome of
+ * depth testing.
+ */
+static void
+stencil_test_quad(struct quad_stage *qs, struct quad_header *quad)
+{
+   struct softpipe_context *softpipe = qs->softpipe;
+   struct softpipe_surface *s_surf = softpipe_surface(softpipe->framebuffer.sbuf);
+   GLuint func, zFailOp, zPassOp, failOp;
+   GLuint face = 0;
+   GLubyte ref, wrtMask, valMask;
+   GLubyte stencilVals[QUAD_SIZE];
+
+   /* choose front or back face function, operator, etc */
+   if (softpipe->stencil.back_enabled && face == 1) {
+      func = softpipe->stencil.back_func;
+      failOp = softpipe->stencil.back_fail_op;
+      zFailOp = softpipe->stencil.back_zfail_op;
+      zPassOp = softpipe->stencil.back_zpass_op;
+      ref = softpipe->stencil.ref_value[1];
+      wrtMask = softpipe->stencil.write_mask[1];
+      valMask = softpipe->stencil.value_mask[1];
+   }
+   else {
+      func = softpipe->stencil.front_func;
+      failOp = softpipe->stencil.front_fail_op;
+      zFailOp = softpipe->stencil.front_zfail_op;
+      zPassOp = softpipe->stencil.front_zpass_op;
+      ref = softpipe->stencil.ref_value[0];
+      wrtMask = softpipe->stencil.write_mask[0];
+      valMask = softpipe->stencil.value_mask[0];
+   }
+
+   assert(s_surf); /* shouldn't get here if there's no stencil buffer */
+   s_surf->read_quad_stencil(s_surf, quad->x0, quad->y0, stencilVals);
+
+   /* do the stencil test first */
+   {
+      GLbitfield passMask, failMask;
+      passMask = do_stencil_test(stencilVals, func, ref, valMask);
+      failMask = quad->mask & ~passMask;
+      quad->mask &= passMask;
+
+      if (failOp != PIPE_STENCIL_OP_KEEP) {
+         apply_stencil_op(stencilVals, failMask, failOp, ref, wrtMask);
+      }
+   }
+
+   if (!quad->mask)
+      return;
+
+   /* now the pixels that passed the stencil test are depth tested */
+   if (softpipe->depth_test.enabled) {
+      const GLbitfield origMask = quad->mask;
+
+      sp_depth_test_quad(qs, quad);  /* quad->mask is updated */
+
+      /* update stencil buffer values according to z pass/fail result */
+      if (zFailOp != PIPE_STENCIL_OP_KEEP) {
+         const GLbitfield failMask = origMask & ~quad->mask;
+         apply_stencil_op(stencilVals, failMask, zFailOp, ref, wrtMask);
+      }
+
+      if (zPassOp != PIPE_STENCIL_OP_KEEP) {
+         const GLbitfield passMask = origMask & quad->mask;
+         apply_stencil_op(stencilVals, passMask, zPassOp, ref, wrtMask);
+      }
+   }
+   else {
+      /* no depth test, apply Zpass operator to stencil buffer values */
+      apply_stencil_op(stencilVals, quad->mask, zPassOp, ref, wrtMask);
+   }
+
+   s_surf->write_quad_stencil(s_surf, quad->x0, quad->y0, stencilVals);
 
    if (quad->mask)
       qs->next->run(qs->next, quad);
index 450542a..3ba732c 100644 (file)
@@ -82,6 +82,11 @@ struct softpipe_surface {
                        GLint x, GLint y, GLuint zzzz[QUAD_SIZE]);
    void (*write_quad_z)(struct softpipe_surface *,
                         GLint x, GLint y, const GLuint zzzz[QUAD_SIZE]);
+
+   void (*read_quad_stencil)(struct softpipe_surface *,
+                             GLint x, GLint y, GLubyte ssss[QUAD_SIZE]);
+   void (*write_quad_stencil)(struct softpipe_surface *,
+                              GLint x, GLint y, const GLubyte ssss[QUAD_SIZE]);
 };