3 * \brief Quad stencil testing
7 #include "sp_context.h"
9 #include "sp_surface.h"
10 #include "sp_tile_cache.h"
11 #include "sp_quad_pipe.h"
12 #include "pipe/p_defines.h"
13 #include "util/u_memory.h"
16 /** Only 8-bit stencil supported */
17 #define STENCIL_MAX 0xff
21 * Do the basic stencil test (compare stencil buffer values against the
24 * \param stencilVals the stencil values from the stencil buffer
25 * \param func the stencil func (PIPE_FUNC_x)
26 * \param ref the stencil reference value
27 * \param valMask the stencil value mask indicating which bits of the stencil
28 * values and ref value are to be used.
29 * \return mask indicating which pixels passed the stencil test
32 do_stencil_test(const ubyte stencilVals
[QUAD_SIZE
], unsigned func
,
33 unsigned ref
, unsigned valMask
)
35 unsigned passMask
= 0x0;
45 for (j
= 0; j
< QUAD_SIZE
; j
++) {
46 if (ref
< (stencilVals
[j
] & valMask
)) {
52 for (j
= 0; j
< QUAD_SIZE
; j
++) {
53 if (ref
== (stencilVals
[j
] & valMask
)) {
58 case PIPE_FUNC_LEQUAL
:
59 for (j
= 0; j
< QUAD_SIZE
; j
++) {
60 if (ref
<= (stencilVals
[j
] & valMask
)) {
65 case PIPE_FUNC_GREATER
:
66 for (j
= 0; j
< QUAD_SIZE
; j
++) {
67 if (ref
> (stencilVals
[j
] & valMask
)) {
72 case PIPE_FUNC_NOTEQUAL
:
73 for (j
= 0; j
< QUAD_SIZE
; j
++) {
74 if (ref
!= (stencilVals
[j
] & valMask
)) {
79 case PIPE_FUNC_GEQUAL
:
80 for (j
= 0; j
< QUAD_SIZE
; j
++) {
81 if (ref
>= (stencilVals
[j
] & valMask
)) {
86 case PIPE_FUNC_ALWAYS
:
98 * Apply the stencil operator to stencil values.
100 * \param stencilVals the stencil buffer values (read and written)
101 * \param mask indicates which pixels to update
102 * \param op the stencil operator (PIPE_STENCIL_OP_x)
103 * \param ref the stencil reference value
104 * \param wrtMask writemask controlling which bits are changed in the
108 apply_stencil_op(ubyte stencilVals
[QUAD_SIZE
],
109 unsigned mask
, unsigned op
, ubyte ref
, ubyte wrtMask
)
112 ubyte newstencil
[QUAD_SIZE
];
114 for (j
= 0; j
< QUAD_SIZE
; j
++) {
115 newstencil
[j
] = stencilVals
[j
];
119 case PIPE_STENCIL_OP_KEEP
:
122 case PIPE_STENCIL_OP_ZERO
:
123 for (j
= 0; j
< QUAD_SIZE
; j
++) {
124 if (mask
& (1 << j
)) {
129 case PIPE_STENCIL_OP_REPLACE
:
130 for (j
= 0; j
< QUAD_SIZE
; j
++) {
131 if (mask
& (1 << j
)) {
136 case PIPE_STENCIL_OP_INCR
:
137 for (j
= 0; j
< QUAD_SIZE
; j
++) {
138 if (mask
& (1 << j
)) {
139 if (stencilVals
[j
] < STENCIL_MAX
) {
140 newstencil
[j
] = stencilVals
[j
] + 1;
145 case PIPE_STENCIL_OP_DECR
:
146 for (j
= 0; j
< QUAD_SIZE
; j
++) {
147 if (mask
& (1 << j
)) {
148 if (stencilVals
[j
] > 0) {
149 newstencil
[j
] = stencilVals
[j
] - 1;
154 case PIPE_STENCIL_OP_INCR_WRAP
:
155 for (j
= 0; j
< QUAD_SIZE
; j
++) {
156 if (mask
& (1 << j
)) {
157 newstencil
[j
] = stencilVals
[j
] + 1;
161 case PIPE_STENCIL_OP_DECR_WRAP
:
162 for (j
= 0; j
< QUAD_SIZE
; j
++) {
163 if (mask
& (1 << j
)) {
164 newstencil
[j
] = stencilVals
[j
] - 1;
168 case PIPE_STENCIL_OP_INVERT
:
169 for (j
= 0; j
< QUAD_SIZE
; j
++) {
170 if (mask
& (1 << j
)) {
171 newstencil
[j
] = ~stencilVals
[j
];
180 * update the stencil values
182 if (wrtMask
!= STENCIL_MAX
) {
183 /* apply bit-wise stencil buffer writemask */
184 for (j
= 0; j
< QUAD_SIZE
; j
++) {
185 stencilVals
[j
] = (wrtMask
& newstencil
[j
]) | (~wrtMask
& stencilVals
[j
]);
189 for (j
= 0; j
< QUAD_SIZE
; j
++) {
190 stencilVals
[j
] = newstencil
[j
];
197 * Do stencil (and depth) testing. Stenciling depends on the outcome of
201 stencil_test_quad(struct quad_stage
*qs
, struct quad_header
*quad
)
203 struct softpipe_context
*softpipe
= qs
->softpipe
;
204 struct pipe_surface
*ps
= softpipe
->framebuffer
.zsbuf
;
205 unsigned func
, zFailOp
, zPassOp
, failOp
;
206 ubyte ref
, wrtMask
, valMask
;
207 ubyte stencilVals
[QUAD_SIZE
];
208 struct softpipe_cached_tile
*tile
209 = sp_get_cached_tile(softpipe
, softpipe
->zsbuf_cache
, quad
->input
.x0
, quad
->input
.y0
);
211 uint face
= quad
->input
.facing
;
213 if (!softpipe
->depth_stencil
->stencil
[1].enabled
) {
214 /* single-sided stencil test, use front (face=0) state */
218 /* choose front or back face function, operator, etc */
219 /* XXX we could do these initializations once per primitive */
220 func
= softpipe
->depth_stencil
->stencil
[face
].func
;
221 failOp
= softpipe
->depth_stencil
->stencil
[face
].fail_op
;
222 zFailOp
= softpipe
->depth_stencil
->stencil
[face
].zfail_op
;
223 zPassOp
= softpipe
->depth_stencil
->stencil
[face
].zpass_op
;
224 ref
= softpipe
->depth_stencil
->stencil
[face
].ref_value
;
225 wrtMask
= softpipe
->depth_stencil
->stencil
[face
].writemask
;
226 valMask
= softpipe
->depth_stencil
->stencil
[face
].valuemask
;
228 assert(ps
); /* shouldn't get here if there's no stencil buffer */
230 /* get stencil values from cached tile */
231 switch (ps
->format
) {
232 case PIPE_FORMAT_S8Z24_UNORM
:
233 for (j
= 0; j
< QUAD_SIZE
; j
++) {
234 int x
= quad
->input
.x0
% TILE_SIZE
+ (j
& 1);
235 int y
= quad
->input
.y0
% TILE_SIZE
+ (j
>> 1);
236 stencilVals
[j
] = tile
->data
.depth32
[y
][x
] >> 24;
239 case PIPE_FORMAT_Z24S8_UNORM
:
240 for (j
= 0; j
< QUAD_SIZE
; j
++) {
241 int x
= quad
->input
.x0
% TILE_SIZE
+ (j
& 1);
242 int y
= quad
->input
.y0
% TILE_SIZE
+ (j
>> 1);
243 stencilVals
[j
] = tile
->data
.depth32
[y
][x
] & 0xff;
246 case PIPE_FORMAT_S8_UNORM
:
247 for (j
= 0; j
< QUAD_SIZE
; j
++) {
248 int x
= quad
->input
.x0
% TILE_SIZE
+ (j
& 1);
249 int y
= quad
->input
.y0
% TILE_SIZE
+ (j
>> 1);
250 stencilVals
[j
] = tile
->data
.stencil8
[y
][x
];
257 /* do the stencil test first */
259 unsigned passMask
, failMask
;
260 passMask
= do_stencil_test(stencilVals
, func
, ref
, valMask
);
261 failMask
= quad
->inout
.mask
& ~passMask
;
262 quad
->inout
.mask
&= passMask
;
264 if (failOp
!= PIPE_STENCIL_OP_KEEP
) {
265 apply_stencil_op(stencilVals
, failMask
, failOp
, ref
, wrtMask
);
269 if (quad
->inout
.mask
) {
270 /* now the pixels that passed the stencil test are depth tested */
271 if (softpipe
->depth_stencil
->depth
.enabled
) {
272 const unsigned origMask
= quad
->inout
.mask
;
274 sp_depth_test_quad(qs
, quad
); /* quad->mask is updated */
276 /* update stencil buffer values according to z pass/fail result */
277 if (zFailOp
!= PIPE_STENCIL_OP_KEEP
) {
278 const unsigned failMask
= origMask
& ~quad
->inout
.mask
;
279 apply_stencil_op(stencilVals
, failMask
, zFailOp
, ref
, wrtMask
);
282 if (zPassOp
!= PIPE_STENCIL_OP_KEEP
) {
283 const unsigned passMask
= origMask
& quad
->inout
.mask
;
284 apply_stencil_op(stencilVals
, passMask
, zPassOp
, ref
, wrtMask
);
288 /* no depth test, apply Zpass operator to stencil buffer values */
289 apply_stencil_op(stencilVals
, quad
->inout
.mask
, zPassOp
, ref
, wrtMask
);
294 /* put new stencil values into cached tile */
295 switch (ps
->format
) {
296 case PIPE_FORMAT_S8Z24_UNORM
:
297 for (j
= 0; j
< QUAD_SIZE
; j
++) {
298 int x
= quad
->input
.x0
% TILE_SIZE
+ (j
& 1);
299 int y
= quad
->input
.y0
% TILE_SIZE
+ (j
>> 1);
300 uint s8z24
= tile
->data
.depth32
[y
][x
];
301 s8z24
= (stencilVals
[j
] << 24) | (s8z24
& 0xffffff);
302 tile
->data
.depth32
[y
][x
] = s8z24
;
305 case PIPE_FORMAT_Z24S8_UNORM
:
306 for (j
= 0; j
< QUAD_SIZE
; j
++) {
307 int x
= quad
->input
.x0
% TILE_SIZE
+ (j
& 1);
308 int y
= quad
->input
.y0
% TILE_SIZE
+ (j
>> 1);
309 uint z24s8
= tile
->data
.depth32
[y
][x
];
310 z24s8
= (z24s8
& 0xffffff00) | stencilVals
[j
];
311 tile
->data
.depth32
[y
][x
] = z24s8
;
314 case PIPE_FORMAT_S8_UNORM
:
315 for (j
= 0; j
< QUAD_SIZE
; j
++) {
316 int x
= quad
->input
.x0
% TILE_SIZE
+ (j
& 1);
317 int y
= quad
->input
.y0
% TILE_SIZE
+ (j
>> 1);
318 tile
->data
.stencil8
[y
][x
] = stencilVals
[j
];
325 if (quad
->inout
.mask
)
326 qs
->next
->run(qs
->next
, quad
);
330 static void stencil_begin(struct quad_stage
*qs
)
332 qs
->next
->begin(qs
->next
);
336 static void stencil_destroy(struct quad_stage
*qs
)
342 struct quad_stage
*sp_quad_stencil_test_stage( struct softpipe_context
*softpipe
)
344 struct quad_stage
*stage
= CALLOC_STRUCT(quad_stage
);
346 stage
->softpipe
= softpipe
;
347 stage
->begin
= stencil_begin
;
348 stage
->run
= stencil_test_quad
;
349 stage
->destroy
= stencil_destroy
;