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
= 1u << 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
|= 1u << 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 STATIC_ASSERT(sizeof(pctx
->samplersUsed
) * 8 >= PIPE_MAX_SAMPLERS
);
271 /* find free texture sampler */
272 pctx
->freeSampler
= free_bit(pctx
->samplersUsed
);
273 if (pctx
->freeSampler
< 0 || pctx
->freeSampler
>= PIPE_MAX_SAMPLERS
)
274 pctx
->freeSampler
= PIPE_MAX_SAMPLERS
- 1;
276 if (pctx
->wincoordInput
< 0)
277 wincoordInput
= pctx
->maxInput
+ 1;
279 wincoordInput
= pctx
->wincoordInput
;
281 if (pctx
->wincoordInput
< 0) {
282 struct tgsi_full_declaration decl
;
284 decl
= tgsi_default_full_declaration();
285 /* declare new position input reg */
286 decl
.Declaration
.File
= pctx
->wincoordFile
;
287 decl
.Declaration
.Semantic
= 1;
288 decl
.Semantic
.Name
= TGSI_SEMANTIC_POSITION
;
290 decl
.Range
.Last
= wincoordInput
;
292 if (pctx
->wincoordFile
== TGSI_FILE_INPUT
) {
293 decl
.Declaration
.Interpolate
= 1;
294 decl
.Interp
.Interpolate
= TGSI_INTERPOLATE_LINEAR
;
297 ctx
->emit_declaration(ctx
, &decl
);
300 sampIdx
= pctx
->hasFixedUnit
? pctx
->fixedUnit
: pctx
->freeSampler
;
302 /* declare new sampler */
303 tgsi_transform_sampler_decl(ctx
, sampIdx
);
305 /* if the src shader has SVIEW decl's for each SAMP decl, we
306 * need to continue the trend and ensure there is a matching
307 * SVIEW for the new SAMP we just created
309 if (pctx
->info
.file_max
[TGSI_FILE_SAMPLER_VIEW
] != -1) {
310 tgsi_transform_sampler_view_decl(ctx
,
313 TGSI_RETURN_TYPE_FLOAT
);
316 /* Declare temp[0] reg if not already declared.
317 * We can always use temp[0] since this code is before
318 * the rest of the shader.
321 if ((pctx
->tempsUsed
& (1 << texTemp
)) == 0) {
322 tgsi_transform_temp_decl(ctx
, texTemp
);
325 /* emit immediate = {1/32, 1/32, 1, 1}
326 * The index/position of this immediate will be pctx->numImmed
328 tgsi_transform_immediate_decl(ctx
, 1.0/32.0, 1.0/32.0, 1.0, 1.0);
331 * Insert new MUL/TEX/KILL_IF instructions at start of program
332 * Take gl_FragCoord, divide by 32 (stipple size), sample the
333 * texture and kill fragment if needed.
335 * We'd like to use non-normalized texcoords to index into a RECT
336 * texture, but we can only use REPEAT wrap mode with normalized
340 /* XXX invert wincoord if origin isn't lower-left... */
342 /* MUL texTemp, INPUT[wincoord], 1/32; */
343 tgsi_transform_op2_inst(ctx
, TGSI_OPCODE_MUL
,
344 TGSI_FILE_TEMPORARY
, texTemp
,
346 pctx
->wincoordFile
, wincoordInput
,
347 TGSI_FILE_IMMEDIATE
, pctx
->numImmed
, false);
349 /* TEX texTemp, texTemp, sampler, 2D; */
350 tgsi_transform_tex_inst(ctx
,
351 TGSI_FILE_TEMPORARY
, texTemp
,
352 TGSI_FILE_TEMPORARY
, texTemp
,
353 TGSI_TEXTURE_2D
, sampIdx
);
355 /* KILL_IF -texTemp; # if -texTemp < 0, kill fragment */
356 tgsi_transform_kill_inst(ctx
,
357 TGSI_FILE_TEMPORARY
, texTemp
,
358 TGSI_SWIZZLE_W
, TRUE
);
363 * Given a fragment shader, return a new fragment shader which
364 * samples a stipple texture and executes KILL.
366 * \param samplerUnitOut returns the index of the sampler unit which
367 * will be used to sample the stipple texture;
368 * if NULL, the fixed unit is used
369 * \param fixedUnit fixed texture unit used for the stipple texture
370 * \param wincoordFile TGSI_FILE_INPUT or TGSI_FILE_SYSTEM_VALUE,
371 * depending on which one is supported by the driver
372 * for TGSI_SEMANTIC_POSITION in the fragment shader
375 util_pstipple_create_fragment_shader(const struct tgsi_token
*tokens
,
376 unsigned *samplerUnitOut
,
378 unsigned wincoordFile
)
380 struct pstip_transform_context transform
;
381 const uint newLen
= tgsi_num_tokens(tokens
) + NUM_NEW_TOKENS
;
382 struct tgsi_token
*new_tokens
;
384 new_tokens
= tgsi_alloc_tokens(newLen
);
389 /* Setup shader transformation info/context.
391 memset(&transform
, 0, sizeof(transform
));
392 transform
.wincoordInput
= -1;
393 transform
.wincoordFile
= wincoordFile
;
394 transform
.maxInput
= -1;
395 transform
.coordOrigin
= TGSI_FS_COORD_ORIGIN_UPPER_LEFT
;
396 transform
.hasFixedUnit
= !samplerUnitOut
;
397 transform
.fixedUnit
= fixedUnit
;
398 transform
.base
.prolog
= pstip_transform_prolog
;
399 transform
.base
.transform_declaration
= pstip_transform_decl
;
400 transform
.base
.transform_immediate
= pstip_transform_immed
;
402 tgsi_scan_shader(tokens
, &transform
.info
);
404 transform
.coordOrigin
=
405 transform
.info
.properties
[TGSI_PROPERTY_FS_COORD_ORIGIN
];
407 tgsi_transform_shader(tokens
, new_tokens
, newLen
, &transform
.base
);
410 tgsi_dump(fs
->tokens
, 0);
411 tgsi_dump(new_fs
->tokens
, 0);
414 if (samplerUnitOut
) {
415 assert(transform
.freeSampler
< PIPE_MAX_SAMPLERS
);
416 *samplerUnitOut
= transform
.freeSampler
;