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 53
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 */
180 unsigned wincoordFile
;
182 uint samplersUsed
; /**< bitfield of samplers used */
183 int freeSampler
; /** an available sampler for the pstipple */
192 * TGSI declaration transform callback.
193 * Track samplers used, temps used, inputs used.
196 pstip_transform_decl(struct tgsi_transform_context
*ctx
,
197 struct tgsi_full_declaration
*decl
)
199 struct pstip_transform_context
*pctx
=
200 (struct pstip_transform_context
*) ctx
;
202 /* XXX we can use tgsi_shader_info instead of some of this */
204 if (decl
->Declaration
.File
== TGSI_FILE_SAMPLER
) {
206 for (i
= decl
->Range
.First
; i
<= decl
->Range
.Last
; i
++) {
207 pctx
->samplersUsed
|= 1 << i
;
210 else if (decl
->Declaration
.File
== pctx
->wincoordFile
) {
211 pctx
->maxInput
= MAX2(pctx
->maxInput
, (int) decl
->Range
.Last
);
212 if (decl
->Semantic
.Name
== TGSI_SEMANTIC_POSITION
)
213 pctx
->wincoordInput
= (int) decl
->Range
.First
;
215 else if (decl
->Declaration
.File
== TGSI_FILE_TEMPORARY
) {
217 for (i
= decl
->Range
.First
; i
<= decl
->Range
.Last
; i
++) {
218 pctx
->tempsUsed
|= (1 << i
);
222 ctx
->emit_declaration(ctx
, decl
);
227 pstip_transform_immed(struct tgsi_transform_context
*ctx
,
228 struct tgsi_full_immediate
*immed
)
230 struct pstip_transform_context
*pctx
=
231 (struct pstip_transform_context
*) ctx
;
233 ctx
->emit_immediate(ctx
, immed
);
238 * Find the lowest zero bit in the given word, or -1 if bitfield is all ones.
241 free_bit(uint bitfield
)
243 return ffs(~bitfield
) - 1;
248 * TGSI transform prolog
249 * Before the first instruction, insert our new code to sample the
250 * stipple texture (using the fragment coord register) then kill the
251 * fragment if the stipple texture bit is off.
254 * declare new registers
255 * MUL texTemp, INPUT[wincoord], 1/32;
256 * TEX texTemp, texTemp, sampler;
257 * KILL_IF -texTemp; # if -texTemp < 0, kill fragment
258 * [...original code...]
261 pstip_transform_prolog(struct tgsi_transform_context
*ctx
)
263 struct pstip_transform_context
*pctx
=
264 (struct pstip_transform_context
*) ctx
;
269 /* find free texture sampler */
270 pctx
->freeSampler
= free_bit(pctx
->samplersUsed
);
271 if (pctx
->freeSampler
>= PIPE_MAX_SAMPLERS
)
272 pctx
->freeSampler
= PIPE_MAX_SAMPLERS
- 1;
274 if (pctx
->wincoordInput
< 0)
275 wincoordInput
= pctx
->maxInput
+ 1;
277 wincoordInput
= pctx
->wincoordInput
;
279 if (pctx
->wincoordInput
< 0) {
280 struct tgsi_full_declaration decl
;
282 decl
= tgsi_default_full_declaration();
283 /* declare new position input reg */
284 decl
.Declaration
.File
= pctx
->wincoordFile
;
285 decl
.Declaration
.Semantic
= 1;
286 decl
.Semantic
.Name
= TGSI_SEMANTIC_POSITION
;
288 decl
.Range
.Last
= wincoordInput
;
290 if (pctx
->wincoordFile
== TGSI_FILE_INPUT
) {
291 decl
.Declaration
.Interpolate
= 1;
292 decl
.Interp
.Interpolate
= TGSI_INTERPOLATE_LINEAR
;
295 ctx
->emit_declaration(ctx
, &decl
);
298 sampIdx
= pctx
->hasFixedUnit
? pctx
->fixedUnit
: pctx
->freeSampler
;
300 /* declare new sampler */
301 tgsi_transform_sampler_decl(ctx
, sampIdx
);
303 /* if the src shader has SVIEW decl's for each SAMP decl, we
304 * need to continue the trend and ensure there is a matching
305 * SVIEW for the new SAMP we just created
307 if (pctx
->info
.file_max
[TGSI_FILE_SAMPLER_VIEW
] != -1) {
308 tgsi_transform_sampler_view_decl(ctx
,
311 TGSI_RETURN_TYPE_FLOAT
);
314 /* Declare temp[0] reg if not already declared.
315 * We can always use temp[0] since this code is before
316 * the rest of the shader.
319 if ((pctx
->tempsUsed
& (1 << texTemp
)) == 0) {
320 tgsi_transform_temp_decl(ctx
, texTemp
);
323 /* emit immediate = {1/32, 1/32, 1, 1}
324 * The index/position of this immediate will be pctx->numImmed
326 tgsi_transform_immediate_decl(ctx
, 1.0/32.0, 1.0/32.0, 1.0, 1.0);
329 * Insert new MUL/TEX/KILL_IF instructions at start of program
330 * Take gl_FragCoord, divide by 32 (stipple size), sample the
331 * texture and kill fragment if needed.
333 * We'd like to use non-normalized texcoords to index into a RECT
334 * texture, but we can only use REPEAT wrap mode with normalized
338 /* XXX invert wincoord if origin isn't lower-left... */
340 /* MUL texTemp, INPUT[wincoord], 1/32; */
341 tgsi_transform_op2_inst(ctx
, TGSI_OPCODE_MUL
,
342 TGSI_FILE_TEMPORARY
, texTemp
,
344 pctx
->wincoordFile
, wincoordInput
,
345 TGSI_FILE_IMMEDIATE
, pctx
->numImmed
);
347 /* TEX texTemp, texTemp, sampler; */
348 tgsi_transform_tex_2d_inst(ctx
,
349 TGSI_FILE_TEMPORARY
, texTemp
,
350 TGSI_FILE_TEMPORARY
, texTemp
,
353 /* KILL_IF -texTemp; # if -texTemp < 0, kill fragment */
354 tgsi_transform_kill_inst(ctx
,
355 TGSI_FILE_TEMPORARY
, texTemp
,
356 TGSI_SWIZZLE_W
, TRUE
);
361 * Given a fragment shader, return a new fragment shader which
362 * samples a stipple texture and executes KILL.
364 * \param samplerUnitOut returns the index of the sampler unit which
365 * will be used to sample the stipple texture;
366 * if NULL, the fixed unit is used
367 * \param fixedUnit fixed texture unit used for the stipple texture
368 * \param wincoordFile TGSI_FILE_INPUT or TGSI_FILE_SYSTEM_VALUE,
369 * depending on which one is supported by the driver
370 * for TGSI_SEMANTIC_POSITION in the fragment shader
373 util_pstipple_create_fragment_shader(const struct tgsi_token
*tokens
,
374 unsigned *samplerUnitOut
,
376 unsigned wincoordFile
)
378 struct pstip_transform_context transform
;
379 const uint newLen
= tgsi_num_tokens(tokens
) + NUM_NEW_TOKENS
;
380 struct tgsi_token
*new_tokens
;
382 new_tokens
= tgsi_alloc_tokens(newLen
);
387 /* Setup shader transformation info/context.
389 memset(&transform
, 0, sizeof(transform
));
390 transform
.wincoordInput
= -1;
391 transform
.wincoordFile
= wincoordFile
;
392 transform
.maxInput
= -1;
393 transform
.coordOrigin
= TGSI_FS_COORD_ORIGIN_UPPER_LEFT
;
394 transform
.hasFixedUnit
= !samplerUnitOut
;
395 transform
.fixedUnit
= fixedUnit
;
396 transform
.base
.prolog
= pstip_transform_prolog
;
397 transform
.base
.transform_declaration
= pstip_transform_decl
;
398 transform
.base
.transform_immediate
= pstip_transform_immed
;
400 tgsi_scan_shader(tokens
, &transform
.info
);
402 transform
.coordOrigin
=
403 transform
.info
.properties
[TGSI_PROPERTY_FS_COORD_ORIGIN
];
405 tgsi_transform_shader(tokens
, new_tokens
, newLen
, &transform
.base
);
408 tgsi_dump(fs
->tokens
, 0);
409 tgsi_dump(new_fs
->tokens
, 0);
412 if (samplerUnitOut
) {
413 assert(transform
.freeSampler
< PIPE_MAX_SAMPLERS
);
414 *samplerUnitOut
= transform
.freeSampler
;