1 /**************************************************************************
3 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
4 * Copyright 2010 VMware, Inc.
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sub license, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
15 * The above copyright notice and this permission notice (including the
16 * next paragraph) shall be included in all copies or substantial portions
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
23 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 **************************************************************************/
30 * Polygon stipple helper module. Drivers/GPUs which don't support polygon
31 * stipple natively can use this module to simulate it.
33 * Basically, modify fragment shader to sample the 32x32 stipple pattern
34 * texture and do a fragment kill for the 'off' bits.
36 * This was originally a 'draw' module stage, but since we don't need
37 * vertex window coords or anything, it can be a stand-alone utility module.
43 #include "pipe/p_context.h"
44 #include "pipe/p_defines.h"
45 #include "pipe/p_shader_tokens.h"
46 #include "util/u_inlines.h"
48 #include "util/u_format.h"
49 #include "util/u_memory.h"
50 #include "util/u_pstipple.h"
51 #include "util/u_sampler.h"
53 #include "tgsi/tgsi_transform.h"
54 #include "tgsi/tgsi_dump.h"
56 /** Approx number of new tokens for instructions in pstip_transform_inst() */
57 #define NUM_NEW_TOKENS 50
61 util_pstipple_update_stipple_texture(struct pipe_context
*pipe
,
62 struct pipe_resource
*tex
,
63 const uint32_t pattern
[32])
65 static const uint bit31
= 1 << 31;
66 struct pipe_transfer
*transfer
;
70 /* map texture memory */
71 transfer
= pipe_get_transfer(pipe
, tex
, 0, 0,
72 PIPE_TRANSFER_WRITE
, 0, 0, 32, 32);
73 data
= pipe
->transfer_map(pipe
, transfer
);
77 * Note: 0 means keep the fragment, 255 means kill it.
78 * We'll negate the texel value and use KILP which kills if value
81 for (i
= 0; i
< 32; i
++) {
82 for (j
= 0; j
< 32; j
++) {
83 if (pattern
[i
] & (bit31
>> j
)) {
85 data
[i
* transfer
->stride
+ j
] = 0;
89 data
[i
* transfer
->stride
+ j
] = 255;
95 pipe
->transfer_unmap(pipe
, transfer
);
96 pipe
->transfer_destroy(pipe
, transfer
);
101 * Create a 32x32 alpha8 texture that encodes the given stipple pattern.
103 struct pipe_resource
*
104 util_pstipple_create_stipple_texture(struct pipe_context
*pipe
,
105 const uint32_t pattern
[32])
107 struct pipe_screen
*screen
= pipe
->screen
;
108 struct pipe_resource templat
, *tex
;
110 memset(&templat
, 0, sizeof(templat
));
111 templat
.target
= PIPE_TEXTURE_2D
;
112 templat
.format
= PIPE_FORMAT_A8_UNORM
;
113 templat
.last_level
= 0;
115 templat
.height0
= 32;
117 templat
.array_size
= 1;
118 templat
.bind
= PIPE_BIND_SAMPLER_VIEW
;
120 tex
= screen
->resource_create(screen
, &templat
);
123 util_pstipple_update_stipple_texture(pipe
, tex
, pattern
);
130 * Create sampler view to sample the stipple texture.
132 struct pipe_sampler_view
*
133 util_pstipple_create_sampler_view(struct pipe_context
*pipe
,
134 struct pipe_resource
*tex
)
136 struct pipe_sampler_view templat
, *sv
;
138 u_sampler_view_default_template(&templat
, tex
, tex
->format
);
139 sv
= pipe
->create_sampler_view(pipe
, tex
, &templat
);
146 * Create the sampler CSO that'll be used for stippling.
149 util_pstipple_create_sampler(struct pipe_context
*pipe
)
151 struct pipe_sampler_state templat
;
154 memset(&templat
, 0, sizeof(templat
));
155 templat
.wrap_s
= PIPE_TEX_WRAP_REPEAT
;
156 templat
.wrap_t
= PIPE_TEX_WRAP_REPEAT
;
157 templat
.wrap_r
= PIPE_TEX_WRAP_REPEAT
;
158 templat
.min_mip_filter
= PIPE_TEX_MIPFILTER_NONE
;
159 templat
.min_img_filter
= PIPE_TEX_FILTER_NEAREST
;
160 templat
.mag_img_filter
= PIPE_TEX_FILTER_NEAREST
;
161 templat
.normalized_coords
= 1;
162 templat
.min_lod
= 0.0f
;
163 templat
.max_lod
= 0.0f
;
165 s
= pipe
->create_sampler_state(pipe
, &templat
);
172 * Subclass of tgsi_transform_context, used for transforming the
173 * user's fragment shader to add the extra texture sample and fragment kill
176 struct pstip_transform_context
{
177 struct tgsi_transform_context base
;
178 uint tempsUsed
; /**< bitmask */
181 uint samplersUsed
; /**< bitfield of samplers used */
182 int freeSampler
; /** an available sampler for the pstipple */
183 int texTemp
; /**< temp registers */
185 boolean firstInstruction
;
190 * TGSI declaration transform callback.
191 * Look for a free sampler, a free input attrib, and two free temp regs.
194 pstip_transform_decl(struct tgsi_transform_context
*ctx
,
195 struct tgsi_full_declaration
*decl
)
197 struct pstip_transform_context
*pctx
=
198 (struct pstip_transform_context
*) ctx
;
200 if (decl
->Declaration
.File
== TGSI_FILE_SAMPLER
) {
202 for (i
= decl
->Range
.First
;
203 i
<= decl
->Range
.Last
; i
++) {
204 pctx
->samplersUsed
|= 1 << i
;
207 else if (decl
->Declaration
.File
== TGSI_FILE_INPUT
) {
208 pctx
->maxInput
= MAX2(pctx
->maxInput
, (int) decl
->Range
.Last
);
209 if (decl
->Semantic
.Name
== TGSI_SEMANTIC_POSITION
)
210 pctx
->wincoordInput
= (int) decl
->Range
.First
;
212 else if (decl
->Declaration
.File
== TGSI_FILE_TEMPORARY
) {
214 for (i
= decl
->Range
.First
;
215 i
<= decl
->Range
.Last
; i
++) {
216 pctx
->tempsUsed
|= (1 << i
);
220 ctx
->emit_declaration(ctx
, decl
);
225 pstip_transform_immed(struct tgsi_transform_context
*ctx
,
226 struct tgsi_full_immediate
*immed
)
228 struct pstip_transform_context
*pctx
=
229 (struct pstip_transform_context
*) ctx
;
235 * Find the lowest zero bit in the given word, or -1 if bitfield is all ones.
238 free_bit(uint bitfield
)
240 return ffs(~bitfield
) - 1;
245 * TGSI instruction transform callback.
246 * Replace writes to result.color w/ a temp reg.
247 * Upon END instruction, insert texture sampling code for antialiasing.
250 pstip_transform_inst(struct tgsi_transform_context
*ctx
,
251 struct tgsi_full_instruction
*inst
)
253 struct pstip_transform_context
*pctx
=
254 (struct pstip_transform_context
*) ctx
;
256 if (pctx
->firstInstruction
) {
257 /* emit our new declarations before the first instruction */
259 struct tgsi_full_declaration decl
;
260 struct tgsi_full_instruction newInst
;
264 /* find free sampler */
265 pctx
->freeSampler
= free_bit(pctx
->samplersUsed
);
266 if (pctx
->freeSampler
>= PIPE_MAX_SAMPLERS
)
267 pctx
->freeSampler
= PIPE_MAX_SAMPLERS
- 1;
269 if (pctx
->wincoordInput
< 0)
270 wincoordInput
= pctx
->maxInput
+ 1;
272 wincoordInput
= pctx
->wincoordInput
;
274 /* find one free temp reg */
275 for (i
= 0; i
< 32; i
++) {
276 if ((pctx
->tempsUsed
& (1 << i
)) == 0) {
277 /* found a free temp */
278 if (pctx
->texTemp
< 0)
284 assert(pctx
->texTemp
>= 0);
286 if (pctx
->wincoordInput
< 0) {
287 /* declare new position input reg */
288 decl
= tgsi_default_full_declaration();
289 decl
.Declaration
.File
= TGSI_FILE_INPUT
;
290 decl
.Declaration
.Interpolate
= TGSI_INTERPOLATE_LINEAR
;
291 decl
.Declaration
.Semantic
= 1;
292 decl
.Semantic
.Name
= TGSI_SEMANTIC_POSITION
;
293 decl
.Semantic
.Index
= 0;
295 decl
.Range
.Last
= wincoordInput
;
296 ctx
->emit_declaration(ctx
, &decl
);
299 /* declare new sampler */
300 decl
= tgsi_default_full_declaration();
301 decl
.Declaration
.File
= TGSI_FILE_SAMPLER
;
303 decl
.Range
.Last
= pctx
->freeSampler
;
304 ctx
->emit_declaration(ctx
, &decl
);
306 /* declare new temp regs */
307 decl
= tgsi_default_full_declaration();
308 decl
.Declaration
.File
= TGSI_FILE_TEMPORARY
;
310 decl
.Range
.Last
= pctx
->texTemp
;
311 ctx
->emit_declaration(ctx
, &decl
);
313 /* emit immediate = {1/32, 1/32, 1, 1}
314 * The index/position of this immediate will be pctx->numImmed
317 static const float value
[4] = { 1.0/32, 1.0/32, 1.0, 1.0 };
318 struct tgsi_full_immediate immed
;
320 immed
= tgsi_default_full_immediate();
321 immed
.Immediate
.NrTokens
= 1 + size
; /* one for the token itself */
322 immed
.u
[0].Float
= value
[0];
323 immed
.u
[1].Float
= value
[1];
324 immed
.u
[2].Float
= value
[2];
325 immed
.u
[3].Float
= value
[3];
326 ctx
->emit_immediate(ctx
, &immed
);
329 pctx
->firstInstruction
= FALSE
;
333 * Insert new MUL/TEX/KILP instructions at start of program
334 * Take gl_FragCoord, divide by 32 (stipple size), sample the
335 * texture and kill fragment if needed.
337 * We'd like to use non-normalized texcoords to index into a RECT
338 * texture, but we can only use REPEAT wrap mode with normalized
342 /* XXX invert wincoord if origin isn't lower-left... */
344 /* MUL texTemp, INPUT[wincoord], 1/32; */
345 newInst
= tgsi_default_full_instruction();
346 newInst
.Instruction
.Opcode
= TGSI_OPCODE_MUL
;
347 newInst
.Instruction
.NumDstRegs
= 1;
348 newInst
.Dst
[0].Register
.File
= TGSI_FILE_TEMPORARY
;
349 newInst
.Dst
[0].Register
.Index
= pctx
->texTemp
;
350 newInst
.Instruction
.NumSrcRegs
= 2;
351 newInst
.Src
[0].Register
.File
= TGSI_FILE_INPUT
;
352 newInst
.Src
[0].Register
.Index
= wincoordInput
;
353 newInst
.Src
[1].Register
.File
= TGSI_FILE_IMMEDIATE
;
354 newInst
.Src
[1].Register
.Index
= pctx
->numImmed
;
355 ctx
->emit_instruction(ctx
, &newInst
);
357 /* TEX texTemp, texTemp, sampler; */
358 newInst
= tgsi_default_full_instruction();
359 newInst
.Instruction
.Opcode
= TGSI_OPCODE_TEX
;
360 newInst
.Instruction
.NumDstRegs
= 1;
361 newInst
.Dst
[0].Register
.File
= TGSI_FILE_TEMPORARY
;
362 newInst
.Dst
[0].Register
.Index
= pctx
->texTemp
;
363 newInst
.Instruction
.NumSrcRegs
= 2;
364 newInst
.Instruction
.Texture
= TRUE
;
365 newInst
.Texture
.Texture
= TGSI_TEXTURE_2D
;
366 newInst
.Src
[0].Register
.File
= TGSI_FILE_TEMPORARY
;
367 newInst
.Src
[0].Register
.Index
= pctx
->texTemp
;
368 newInst
.Src
[1].Register
.File
= TGSI_FILE_SAMPLER
;
369 newInst
.Src
[1].Register
.Index
= pctx
->freeSampler
;
370 ctx
->emit_instruction(ctx
, &newInst
);
372 /* KIL -texTemp; # if -texTemp < 0, KILL fragment */
373 newInst
= tgsi_default_full_instruction();
374 newInst
.Instruction
.Opcode
= TGSI_OPCODE_KIL
;
375 newInst
.Instruction
.NumDstRegs
= 0;
376 newInst
.Instruction
.NumSrcRegs
= 1;
377 newInst
.Src
[0].Register
.File
= TGSI_FILE_TEMPORARY
;
378 newInst
.Src
[0].Register
.Index
= pctx
->texTemp
;
379 newInst
.Src
[0].Register
.Negate
= 1;
380 ctx
->emit_instruction(ctx
, &newInst
);
383 /* emit this instruction */
384 ctx
->emit_instruction(ctx
, inst
);
389 * Given a fragment shader, return a new fragment shader which
390 * samples a stipple texture and executes KILL.
392 struct pipe_shader_state
*
393 util_pstipple_create_fragment_shader(struct pipe_context
*pipe
,
394 struct pipe_shader_state
*fs
,
395 unsigned *samplerUnitOut
)
397 struct pipe_shader_state
*new_fs
;
398 struct pstip_transform_context transform
;
399 const uint newLen
= tgsi_num_tokens(fs
->tokens
) + NUM_NEW_TOKENS
;
401 new_fs
= MALLOC(sizeof(*new_fs
));
405 new_fs
->tokens
= tgsi_alloc_tokens(newLen
);
406 if (!new_fs
->tokens
) {
411 memset(&transform
, 0, sizeof(transform
));
412 transform
.wincoordInput
= -1;
413 transform
.maxInput
= -1;
414 transform
.texTemp
= -1;
415 transform
.firstInstruction
= TRUE
;
416 transform
.base
.transform_instruction
= pstip_transform_inst
;
417 transform
.base
.transform_declaration
= pstip_transform_decl
;
418 transform
.base
.transform_immediate
= pstip_transform_immed
;
420 tgsi_transform_shader(fs
->tokens
,
421 (struct tgsi_token
*) new_fs
->tokens
,
422 newLen
, &transform
.base
);
425 tgsi_dump(fs
->tokens
, 0);
426 tgsi_dump(pstip_fs
.tokens
, 0);
429 assert(transform
.freeSampler
< PIPE_MAX_SAMPLERS
);
430 *samplerUnitOut
= transform
.freeSampler
;