1 /**************************************************************************
3 * Copyright 2008 VMware, Inc.
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 VMWARE 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"
55 #include "tgsi/tgsi_scan.h"
57 /** Approx number of new tokens for instructions in pstip_transform_inst() */
58 #define NUM_NEW_TOKENS 50
62 util_pstipple_update_stipple_texture(struct pipe_context
*pipe
,
63 struct pipe_resource
*tex
,
64 const uint32_t pattern
[32])
66 static const uint bit31
= 1 << 31;
67 struct pipe_transfer
*transfer
;
71 /* map texture memory */
72 data
= pipe_transfer_map(pipe
, tex
, 0, 0,
73 PIPE_TRANSFER_WRITE
, 0, 0, 32, 32, &transfer
);
77 * Note: 0 means keep the fragment, 255 means kill it.
78 * We'll negate the texel value and use KILL_IF 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
);
100 * Create a 32x32 alpha8 texture that encodes the given stipple pattern.
102 struct pipe_resource
*
103 util_pstipple_create_stipple_texture(struct pipe_context
*pipe
,
104 const uint32_t pattern
[32])
106 struct pipe_screen
*screen
= pipe
->screen
;
107 struct pipe_resource templat
, *tex
;
109 memset(&templat
, 0, sizeof(templat
));
110 templat
.target
= PIPE_TEXTURE_2D
;
111 templat
.format
= PIPE_FORMAT_A8_UNORM
;
112 templat
.last_level
= 0;
114 templat
.height0
= 32;
116 templat
.array_size
= 1;
117 templat
.bind
= PIPE_BIND_SAMPLER_VIEW
;
119 tex
= screen
->resource_create(screen
, &templat
);
122 util_pstipple_update_stipple_texture(pipe
, tex
, pattern
);
129 * Create sampler view to sample the stipple texture.
131 struct pipe_sampler_view
*
132 util_pstipple_create_sampler_view(struct pipe_context
*pipe
,
133 struct pipe_resource
*tex
)
135 struct pipe_sampler_view templat
, *sv
;
137 u_sampler_view_default_template(&templat
, tex
, tex
->format
);
138 sv
= pipe
->create_sampler_view(pipe
, tex
, &templat
);
145 * Create the sampler CSO that'll be used for stippling.
148 util_pstipple_create_sampler(struct pipe_context
*pipe
)
150 struct pipe_sampler_state templat
;
153 memset(&templat
, 0, sizeof(templat
));
154 templat
.wrap_s
= PIPE_TEX_WRAP_REPEAT
;
155 templat
.wrap_t
= PIPE_TEX_WRAP_REPEAT
;
156 templat
.wrap_r
= PIPE_TEX_WRAP_REPEAT
;
157 templat
.min_mip_filter
= PIPE_TEX_MIPFILTER_NONE
;
158 templat
.min_img_filter
= PIPE_TEX_FILTER_NEAREST
;
159 templat
.mag_img_filter
= PIPE_TEX_FILTER_NEAREST
;
160 templat
.normalized_coords
= 1;
161 templat
.min_lod
= 0.0f
;
162 templat
.max_lod
= 0.0f
;
164 s
= pipe
->create_sampler_state(pipe
, &templat
);
171 * Subclass of tgsi_transform_context, used for transforming the
172 * user's fragment shader to add the extra texture sample and fragment kill
175 struct pstip_transform_context
{
176 struct tgsi_transform_context base
;
177 struct tgsi_shader_info info
;
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
;
191 * TGSI declaration transform callback.
192 * Track samplers used, temps used, inputs used.
195 pstip_transform_decl(struct tgsi_transform_context
*ctx
,
196 struct tgsi_full_declaration
*decl
)
198 struct pstip_transform_context
*pctx
=
199 (struct pstip_transform_context
*) ctx
;
201 /* XXX we can use tgsi_shader_info instead of some of this */
203 if (decl
->Declaration
.File
== TGSI_FILE_SAMPLER
) {
205 for (i
= decl
->Range
.First
; i
<= decl
->Range
.Last
; i
++) {
206 pctx
->samplersUsed
|= 1 << i
;
209 else if (decl
->Declaration
.File
== TGSI_FILE_INPUT
) {
210 pctx
->maxInput
= MAX2(pctx
->maxInput
, (int) decl
->Range
.Last
);
211 if (decl
->Semantic
.Name
== TGSI_SEMANTIC_POSITION
)
212 pctx
->wincoordInput
= (int) decl
->Range
.First
;
214 else if (decl
->Declaration
.File
== TGSI_FILE_TEMPORARY
) {
216 for (i
= decl
->Range
.First
; i
<= decl
->Range
.Last
; i
++) {
217 pctx
->tempsUsed
|= (1 << i
);
221 ctx
->emit_declaration(ctx
, decl
);
226 pstip_transform_immed(struct tgsi_transform_context
*ctx
,
227 struct tgsi_full_immediate
*immed
)
229 struct pstip_transform_context
*pctx
=
230 (struct pstip_transform_context
*) ctx
;
236 * Find the lowest zero bit in the given word, or -1 if bitfield is all ones.
239 free_bit(uint bitfield
)
241 return ffs(~bitfield
) - 1;
246 * TGSI instruction transform callback.
247 * Before the first instruction, insert our new code to sample the
248 * stipple texture (using the fragment coord register) then kill the
249 * fragment if the stipple texture bit is off.
252 * declare new registers
253 * MUL texTemp, INPUT[wincoord], 1/32;
254 * TEX texTemp, texTemp, sampler;
255 * KILL_IF -texTemp; # if -texTemp < 0, kill fragment
256 * [...original code...]
259 pstip_transform_inst(struct tgsi_transform_context
*ctx
,
260 struct tgsi_full_instruction
*inst
)
262 struct pstip_transform_context
*pctx
=
263 (struct pstip_transform_context
*) ctx
;
265 if (pctx
->firstInstruction
) {
266 /* emit our new declarations before the first instruction */
268 struct tgsi_full_declaration decl
;
269 struct tgsi_full_instruction newInst
;
273 /* find free texture sampler */
274 pctx
->freeSampler
= free_bit(pctx
->samplersUsed
);
275 if (pctx
->freeSampler
>= PIPE_MAX_SAMPLERS
)
276 pctx
->freeSampler
= PIPE_MAX_SAMPLERS
- 1;
278 if (pctx
->wincoordInput
< 0)
279 wincoordInput
= pctx
->maxInput
+ 1;
281 wincoordInput
= pctx
->wincoordInput
;
283 /* find one free temp register */
284 for (i
= 0; i
< 32; i
++) {
285 if ((pctx
->tempsUsed
& (1 << i
)) == 0) {
286 /* found a free temp */
287 if (pctx
->texTemp
< 0)
293 assert(pctx
->texTemp
>= 0);
295 if (pctx
->wincoordInput
< 0) {
296 /* declare new position input reg */
297 decl
= tgsi_default_full_declaration();
298 decl
.Declaration
.File
= TGSI_FILE_INPUT
;
299 decl
.Declaration
.Interpolate
= 1;
300 decl
.Declaration
.Semantic
= 1;
301 decl
.Semantic
.Name
= TGSI_SEMANTIC_POSITION
;
302 decl
.Semantic
.Index
= 0;
304 decl
.Range
.Last
= wincoordInput
;
305 decl
.Interp
.Interpolate
= TGSI_INTERPOLATE_LINEAR
;
306 ctx
->emit_declaration(ctx
, &decl
);
309 /* declare new sampler */
310 decl
= tgsi_default_full_declaration();
311 decl
.Declaration
.File
= TGSI_FILE_SAMPLER
;
313 decl
.Range
.Last
= pctx
->freeSampler
;
314 ctx
->emit_declaration(ctx
, &decl
);
316 /* declare new temp regs */
317 decl
= tgsi_default_full_declaration();
318 decl
.Declaration
.File
= TGSI_FILE_TEMPORARY
;
320 decl
.Range
.Last
= pctx
->texTemp
;
321 ctx
->emit_declaration(ctx
, &decl
);
323 /* emit immediate = {1/32, 1/32, 1, 1}
324 * The index/position of this immediate will be pctx->numImmed
327 static const float value
[4] = { 1.0/32, 1.0/32, 1.0, 1.0 };
328 struct tgsi_full_immediate immed
;
330 immed
= tgsi_default_full_immediate();
331 immed
.Immediate
.NrTokens
= 1 + size
; /* one for the token itself */
332 immed
.u
[0].Float
= value
[0];
333 immed
.u
[1].Float
= value
[1];
334 immed
.u
[2].Float
= value
[2];
335 immed
.u
[3].Float
= value
[3];
336 ctx
->emit_immediate(ctx
, &immed
);
339 pctx
->firstInstruction
= FALSE
;
343 * Insert new MUL/TEX/KILL_IF instructions at start of program
344 * Take gl_FragCoord, divide by 32 (stipple size), sample the
345 * texture and kill fragment if needed.
347 * We'd like to use non-normalized texcoords to index into a RECT
348 * texture, but we can only use REPEAT wrap mode with normalized
352 /* XXX invert wincoord if origin isn't lower-left... */
354 /* MUL texTemp, INPUT[wincoord], 1/32; */
355 newInst
= tgsi_default_full_instruction();
356 newInst
.Instruction
.Opcode
= TGSI_OPCODE_MUL
;
357 newInst
.Instruction
.NumDstRegs
= 1;
358 newInst
.Dst
[0].Register
.File
= TGSI_FILE_TEMPORARY
;
359 newInst
.Dst
[0].Register
.Index
= pctx
->texTemp
;
360 newInst
.Instruction
.NumSrcRegs
= 2;
361 newInst
.Src
[0].Register
.File
= TGSI_FILE_INPUT
;
362 newInst
.Src
[0].Register
.Index
= wincoordInput
;
363 newInst
.Src
[1].Register
.File
= TGSI_FILE_IMMEDIATE
;
364 newInst
.Src
[1].Register
.Index
= pctx
->numImmed
;
365 ctx
->emit_instruction(ctx
, &newInst
);
367 /* TEX texTemp, texTemp, sampler; */
368 newInst
= tgsi_default_full_instruction();
369 newInst
.Instruction
.Opcode
= TGSI_OPCODE_TEX
;
370 newInst
.Instruction
.NumDstRegs
= 1;
371 newInst
.Dst
[0].Register
.File
= TGSI_FILE_TEMPORARY
;
372 newInst
.Dst
[0].Register
.Index
= pctx
->texTemp
;
373 newInst
.Instruction
.NumSrcRegs
= 2;
374 newInst
.Instruction
.Texture
= TRUE
;
375 newInst
.Texture
.Texture
= TGSI_TEXTURE_2D
;
376 newInst
.Src
[0].Register
.File
= TGSI_FILE_TEMPORARY
;
377 newInst
.Src
[0].Register
.Index
= pctx
->texTemp
;
378 newInst
.Src
[1].Register
.File
= TGSI_FILE_SAMPLER
;
379 newInst
.Src
[1].Register
.Index
= pctx
->freeSampler
;
380 ctx
->emit_instruction(ctx
, &newInst
);
382 /* KILL_IF -texTemp; # if -texTemp < 0, kill fragment */
383 newInst
= tgsi_default_full_instruction();
384 newInst
.Instruction
.Opcode
= TGSI_OPCODE_KILL_IF
;
385 newInst
.Instruction
.NumDstRegs
= 0;
386 newInst
.Instruction
.NumSrcRegs
= 1;
387 newInst
.Src
[0].Register
.File
= TGSI_FILE_TEMPORARY
;
388 newInst
.Src
[0].Register
.Index
= pctx
->texTemp
;
389 newInst
.Src
[0].Register
.Negate
= 1;
390 ctx
->emit_instruction(ctx
, &newInst
);
393 /* emit this instruction */
394 ctx
->emit_instruction(ctx
, inst
);
399 * Given a fragment shader, return a new fragment shader which
400 * samples a stipple texture and executes KILL.
402 struct pipe_shader_state
*
403 util_pstipple_create_fragment_shader(struct pipe_context
*pipe
,
404 struct pipe_shader_state
*fs
,
405 unsigned *samplerUnitOut
)
407 struct pipe_shader_state
*new_fs
;
408 struct pstip_transform_context transform
;
409 const uint newLen
= tgsi_num_tokens(fs
->tokens
) + NUM_NEW_TOKENS
;
412 new_fs
= MALLOC(sizeof(*new_fs
));
416 new_fs
->tokens
= tgsi_alloc_tokens(newLen
);
417 if (!new_fs
->tokens
) {
422 /* Setup shader transformation info/context.
424 memset(&transform
, 0, sizeof(transform
));
425 transform
.wincoordInput
= -1;
426 transform
.maxInput
= -1;
427 transform
.texTemp
= -1;
428 transform
.firstInstruction
= TRUE
;
429 transform
.coordOrigin
= TGSI_FS_COORD_ORIGIN_UPPER_LEFT
;
430 transform
.base
.transform_instruction
= pstip_transform_inst
;
431 transform
.base
.transform_declaration
= pstip_transform_decl
;
432 transform
.base
.transform_immediate
= pstip_transform_immed
;
434 tgsi_scan_shader(fs
->tokens
, &transform
.info
);
436 /* find fragment coordinate origin property */
437 for (i
= 0; i
< transform
.info
.num_properties
; i
++) {
438 if (transform
.info
.properties
[i
].name
== TGSI_PROPERTY_FS_COORD_ORIGIN
)
439 transform
.coordOrigin
= transform
.info
.properties
[i
].data
[0];
442 tgsi_transform_shader(fs
->tokens
,
443 (struct tgsi_token
*) new_fs
->tokens
,
444 newLen
, &transform
.base
);
447 tgsi_dump(fs
->tokens
, 0);
448 tgsi_dump(new_fs
->tokens
, 0);
451 assert(transform
.freeSampler
< PIPE_MAX_SAMPLERS
);
452 *samplerUnitOut
= transform
.freeSampler
;