Files
third_party_mesa3d/src/mesa/pipe/softpipe/sp_quad_stencil.c
Zack Rusin d6ac959833 Combing depth and stencil objects and making them immutable.
Converting depth and stencil objects into a single state object
(d3d10 like) and making it immutable.
2007-09-18 06:31:22 -04:00

294 lines
7.9 KiB
C

/**
* \brief Quad stencil testing
*/
#include "sp_context.h"
#include "sp_headers.h"
#include "sp_surface.h"
#include "sp_quad.h"
#include "pipe/p_defines.h"
#include "pipe/p_util.h"
/** Only 8-bit stencil supported */
#define STENCIL_MAX 0xff
/**
* 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 unsigned
do_stencil_test(const ubyte stencilVals[QUAD_SIZE], unsigned func,
unsigned ref, unsigned valMask)
{
unsigned passMask = 0x0;
unsigned j;
ref &= valMask;
switch (func) {
case PIPE_FUNC_NEVER:
/* passMask = 0x0 */
break;
case PIPE_FUNC_LESS:
for (j = 0; j < QUAD_SIZE; j++) {
if ((stencilVals[j] & valMask) < ref) {
passMask |= (1 << j);
}
}
break;
case PIPE_FUNC_EQUAL:
for (j = 0; j < QUAD_SIZE; j++) {
if ((stencilVals[j] & valMask) == ref) {
passMask |= (1 << j);
}
}
break;
case PIPE_FUNC_LEQUAL:
for (j = 0; j < QUAD_SIZE; j++) {
if ((stencilVals[j] & valMask) <= ref) {
passMask |= (1 << j);
}
}
break;
case PIPE_FUNC_GREATER:
for (j = 0; j < QUAD_SIZE; j++) {
if ((stencilVals[j] & valMask) > ref) {
passMask |= (1 << j);
}
}
break;
case PIPE_FUNC_NOTEQUAL:
for (j = 0; j < QUAD_SIZE; j++) {
if ((stencilVals[j] & valMask) != ref) {
passMask |= (1 << j);
}
}
break;
case PIPE_FUNC_GEQUAL:
for (j = 0; j < QUAD_SIZE; j++) {
if ((stencilVals[j] & valMask) >= ref) {
passMask |= (1 << j);
}
}
break;
case PIPE_FUNC_ALWAYS:
passMask = MASK_ALL;
break;
default:
assert(0);
}
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(ubyte stencilVals[QUAD_SIZE],
unsigned mask, unsigned op, ubyte ref, ubyte wrtMask)
{
unsigned j;
ubyte newstencil[QUAD_SIZE];
for (j = 0; j < QUAD_SIZE; j++) {
newstencil[j] = stencilVals[j];
}
switch (op) {
case PIPE_STENCIL_OP_KEEP:
/* no-op */
break;
case PIPE_STENCIL_OP_ZERO:
for (j = 0; j < QUAD_SIZE; 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);
}
/*
* 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);
unsigned func, zFailOp, zPassOp, failOp;
ubyte ref, wrtMask, valMask;
ubyte stencilVals[QUAD_SIZE];
/* choose front or back face function, operator, etc */
/* XXX we could do these initializations once per primitive */
if (softpipe->depth_stencil->stencil.back_enabled && quad->facing) {
func = softpipe->depth_stencil->stencil.back_func;
failOp = softpipe->depth_stencil->stencil.back_fail_op;
zFailOp = softpipe->depth_stencil->stencil.back_zfail_op;
zPassOp = softpipe->depth_stencil->stencil.back_zpass_op;
ref = softpipe->depth_stencil->stencil.ref_value[1];
wrtMask = softpipe->depth_stencil->stencil.write_mask[1];
valMask = softpipe->depth_stencil->stencil.value_mask[1];
}
else {
func = softpipe->depth_stencil->stencil.front_func;
failOp = softpipe->depth_stencil->stencil.front_fail_op;
zFailOp = softpipe->depth_stencil->stencil.front_zfail_op;
zPassOp = softpipe->depth_stencil->stencil.front_zpass_op;
ref = softpipe->depth_stencil->stencil.ref_value[0];
wrtMask = softpipe->depth_stencil->stencil.write_mask[0];
valMask = softpipe->depth_stencil->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 */
{
unsigned 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) {
/* now the pixels that passed the stencil test are depth tested */
if (softpipe->depth_stencil->depth.enabled) {
const unsigned 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 unsigned failMask = origMask & ~quad->mask;
apply_stencil_op(stencilVals, failMask, zFailOp, ref, wrtMask);
}
if (zPassOp != PIPE_STENCIL_OP_KEEP) {
const unsigned 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);
}
static void stencil_begin(struct quad_stage *qs)
{
if (qs->next)
qs->next->begin(qs->next);
}
struct quad_stage *sp_quad_stencil_test_stage( struct softpipe_context *softpipe )
{
struct quad_stage *stage = CALLOC_STRUCT(quad_stage);
stage->softpipe = softpipe;
stage->begin = stencil_begin;
stage->run = stencil_test_quad;
return stage;
}