Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
3770 | Serge | 1 | /************************************************************************** |
2 | * |
||
3 | * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas. |
||
4 | * Copyright 2010 VMware, Inc. |
||
5 | * All Rights Reserved. |
||
6 | * |
||
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: |
||
14 | * |
||
15 | * The above copyright notice and this permission notice (including the |
||
16 | * next paragraph) shall be included in all copies or substantial portions |
||
17 | * of the Software. |
||
18 | * |
||
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. |
||
26 | * |
||
27 | **************************************************************************/ |
||
28 | |||
29 | /** |
||
30 | * Polygon stipple helper module. Drivers/GPUs which don't support polygon |
||
31 | * stipple natively can use this module to simulate it. |
||
32 | * |
||
33 | * Basically, modify fragment shader to sample the 32x32 stipple pattern |
||
34 | * texture and do a fragment kill for the 'off' bits. |
||
35 | * |
||
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. |
||
38 | * |
||
39 | * Authors: Brian Paul |
||
40 | */ |
||
41 | |||
42 | |||
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" |
||
47 | |||
48 | #include "util/u_format.h" |
||
49 | #include "util/u_memory.h" |
||
50 | #include "util/u_pstipple.h" |
||
51 | #include "util/u_sampler.h" |
||
52 | |||
53 | #include "tgsi/tgsi_transform.h" |
||
54 | #include "tgsi/tgsi_dump.h" |
||
55 | #include "tgsi/tgsi_scan.h" |
||
56 | |||
57 | /** Approx number of new tokens for instructions in pstip_transform_inst() */ |
||
58 | #define NUM_NEW_TOKENS 50 |
||
59 | |||
60 | |||
61 | static void |
||
62 | util_pstipple_update_stipple_texture(struct pipe_context *pipe, |
||
63 | struct pipe_resource *tex, |
||
64 | const uint32_t pattern[32]) |
||
65 | { |
||
66 | static const uint bit31 = 1 << 31; |
||
67 | struct pipe_transfer *transfer; |
||
68 | ubyte *data; |
||
69 | int i, j; |
||
70 | |||
71 | /* map texture memory */ |
||
72 | data = pipe_transfer_map(pipe, tex, 0, 0, |
||
73 | PIPE_TRANSFER_WRITE, 0, 0, 32, 32, &transfer); |
||
74 | |||
75 | /* |
||
76 | * Load alpha texture. |
||
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 |
||
79 | * is negative. |
||
80 | */ |
||
81 | for (i = 0; i < 32; i++) { |
||
82 | for (j = 0; j < 32; j++) { |
||
83 | if (pattern[i] & (bit31 >> j)) { |
||
84 | /* fragment "on" */ |
||
85 | data[i * transfer->stride + j] = 0; |
||
86 | } |
||
87 | else { |
||
88 | /* fragment "off" */ |
||
89 | data[i * transfer->stride + j] = 255; |
||
90 | } |
||
91 | } |
||
92 | } |
||
93 | |||
94 | /* unmap */ |
||
95 | pipe->transfer_unmap(pipe, transfer); |
||
96 | } |
||
97 | |||
98 | |||
99 | /** |
||
100 | * Create a 32x32 alpha8 texture that encodes the given stipple pattern. |
||
101 | */ |
||
102 | struct pipe_resource * |
||
103 | util_pstipple_create_stipple_texture(struct pipe_context *pipe, |
||
104 | const uint32_t pattern[32]) |
||
105 | { |
||
106 | struct pipe_screen *screen = pipe->screen; |
||
107 | struct pipe_resource templat, *tex; |
||
108 | |||
109 | memset(&templat, 0, sizeof(templat)); |
||
110 | templat.target = PIPE_TEXTURE_2D; |
||
111 | templat.format = PIPE_FORMAT_A8_UNORM; |
||
112 | templat.last_level = 0; |
||
113 | templat.width0 = 32; |
||
114 | templat.height0 = 32; |
||
115 | templat.depth0 = 1; |
||
116 | templat.array_size = 1; |
||
117 | templat.bind = PIPE_BIND_SAMPLER_VIEW; |
||
118 | |||
119 | tex = screen->resource_create(screen, &templat); |
||
120 | |||
121 | if (tex) |
||
122 | util_pstipple_update_stipple_texture(pipe, tex, pattern); |
||
123 | |||
124 | return tex; |
||
125 | } |
||
126 | |||
127 | |||
128 | /** |
||
129 | * Create sampler view to sample the stipple texture. |
||
130 | */ |
||
131 | struct pipe_sampler_view * |
||
132 | util_pstipple_create_sampler_view(struct pipe_context *pipe, |
||
133 | struct pipe_resource *tex) |
||
134 | { |
||
135 | struct pipe_sampler_view templat, *sv; |
||
136 | |||
137 | u_sampler_view_default_template(&templat, tex, tex->format); |
||
138 | sv = pipe->create_sampler_view(pipe, tex, &templat); |
||
139 | |||
140 | return sv; |
||
141 | } |
||
142 | |||
143 | |||
144 | /** |
||
145 | * Create the sampler CSO that'll be used for stippling. |
||
146 | */ |
||
147 | void * |
||
148 | util_pstipple_create_sampler(struct pipe_context *pipe) |
||
149 | { |
||
150 | struct pipe_sampler_state templat; |
||
151 | void *s; |
||
152 | |||
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; |
||
163 | |||
164 | s = pipe->create_sampler_state(pipe, &templat); |
||
165 | return s; |
||
166 | } |
||
167 | |||
168 | |||
169 | |||
170 | /** |
||
171 | * Subclass of tgsi_transform_context, used for transforming the |
||
172 | * user's fragment shader to add the extra texture sample and fragment kill |
||
173 | * instructions. |
||
174 | */ |
||
175 | struct pstip_transform_context { |
||
176 | struct tgsi_transform_context base; |
||
177 | struct tgsi_shader_info info; |
||
178 | uint tempsUsed; /**< bitmask */ |
||
179 | int wincoordInput; |
||
180 | int maxInput; |
||
181 | uint samplersUsed; /**< bitfield of samplers used */ |
||
182 | int freeSampler; /** an available sampler for the pstipple */ |
||
183 | int texTemp; /**< temp registers */ |
||
184 | int numImmed; |
||
185 | boolean firstInstruction; |
||
186 | uint coordOrigin; |
||
187 | }; |
||
188 | |||
189 | |||
190 | /** |
||
191 | * TGSI declaration transform callback. |
||
192 | * Track samplers used, temps used, inputs used. |
||
193 | */ |
||
194 | static void |
||
195 | pstip_transform_decl(struct tgsi_transform_context *ctx, |
||
196 | struct tgsi_full_declaration *decl) |
||
197 | { |
||
198 | struct pstip_transform_context *pctx = |
||
199 | (struct pstip_transform_context *) ctx; |
||
200 | |||
201 | /* XXX we can use tgsi_shader_info instead of some of this */ |
||
202 | |||
203 | if (decl->Declaration.File == TGSI_FILE_SAMPLER) { |
||
204 | uint i; |
||
205 | for (i = decl->Range.First; i <= decl->Range.Last; i++) { |
||
206 | pctx->samplersUsed |= 1 << i; |
||
207 | } |
||
208 | } |
||
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; |
||
213 | } |
||
214 | else if (decl->Declaration.File == TGSI_FILE_TEMPORARY) { |
||
215 | uint i; |
||
216 | for (i = decl->Range.First; i <= decl->Range.Last; i++) { |
||
217 | pctx->tempsUsed |= (1 << i); |
||
218 | } |
||
219 | } |
||
220 | |||
221 | ctx->emit_declaration(ctx, decl); |
||
222 | } |
||
223 | |||
224 | |||
225 | static void |
||
226 | pstip_transform_immed(struct tgsi_transform_context *ctx, |
||
227 | struct tgsi_full_immediate *immed) |
||
228 | { |
||
229 | struct pstip_transform_context *pctx = |
||
230 | (struct pstip_transform_context *) ctx; |
||
231 | pctx->numImmed++; |
||
232 | } |
||
233 | |||
234 | |||
235 | /** |
||
236 | * Find the lowest zero bit in the given word, or -1 if bitfield is all ones. |
||
237 | */ |
||
238 | static int |
||
239 | free_bit(uint bitfield) |
||
240 | { |
||
241 | return ffs(~bitfield) - 1; |
||
242 | } |
||
243 | |||
244 | |||
245 | /** |
||
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. |
||
250 | * |
||
251 | * Insert: |
||
252 | * declare new registers |
||
253 | * MUL texTemp, INPUT[wincoord], 1/32; |
||
254 | * TEX texTemp, texTemp, sampler; |
||
255 | * KIL -texTemp; # if -texTemp < 0, KILL fragment |
||
256 | * [...original code...] |
||
257 | */ |
||
258 | static void |
||
259 | pstip_transform_inst(struct tgsi_transform_context *ctx, |
||
260 | struct tgsi_full_instruction *inst) |
||
261 | { |
||
262 | struct pstip_transform_context *pctx = |
||
263 | (struct pstip_transform_context *) ctx; |
||
264 | |||
265 | if (pctx->firstInstruction) { |
||
266 | /* emit our new declarations before the first instruction */ |
||
267 | |||
268 | struct tgsi_full_declaration decl; |
||
269 | struct tgsi_full_instruction newInst; |
||
270 | uint i; |
||
271 | int wincoordInput; |
||
272 | |||
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; |
||
277 | |||
278 | if (pctx->wincoordInput < 0) |
||
279 | wincoordInput = pctx->maxInput + 1; |
||
280 | else |
||
281 | wincoordInput = pctx->wincoordInput; |
||
282 | |||
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) |
||
288 | pctx->texTemp = i; |
||
289 | else |
||
290 | break; |
||
291 | } |
||
292 | } |
||
293 | assert(pctx->texTemp >= 0); |
||
294 | |||
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; |
||
303 | decl.Range.First = |
||
304 | decl.Range.Last = wincoordInput; |
||
305 | decl.Interp.Interpolate = TGSI_INTERPOLATE_LINEAR; |
||
306 | ctx->emit_declaration(ctx, &decl); |
||
307 | } |
||
308 | |||
309 | /* declare new sampler */ |
||
310 | decl = tgsi_default_full_declaration(); |
||
311 | decl.Declaration.File = TGSI_FILE_SAMPLER; |
||
312 | decl.Range.First = |
||
313 | decl.Range.Last = pctx->freeSampler; |
||
314 | ctx->emit_declaration(ctx, &decl); |
||
315 | |||
316 | /* declare new temp regs */ |
||
317 | decl = tgsi_default_full_declaration(); |
||
318 | decl.Declaration.File = TGSI_FILE_TEMPORARY; |
||
319 | decl.Range.First = |
||
320 | decl.Range.Last = pctx->texTemp; |
||
321 | ctx->emit_declaration(ctx, &decl); |
||
322 | |||
323 | /* emit immediate = {1/32, 1/32, 1, 1} |
||
324 | * The index/position of this immediate will be pctx->numImmed |
||
325 | */ |
||
326 | { |
||
327 | static const float value[4] = { 1.0/32, 1.0/32, 1.0, 1.0 }; |
||
328 | struct tgsi_full_immediate immed; |
||
329 | uint size = 4; |
||
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); |
||
337 | } |
||
338 | |||
339 | pctx->firstInstruction = FALSE; |
||
340 | |||
341 | |||
342 | /* |
||
343 | * Insert new MUL/TEX/KILP instructions at start of program |
||
344 | * Take gl_FragCoord, divide by 32 (stipple size), sample the |
||
345 | * texture and kill fragment if needed. |
||
346 | * |
||
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 |
||
349 | * texcoords. Darn. |
||
350 | */ |
||
351 | |||
352 | /* XXX invert wincoord if origin isn't lower-left... */ |
||
353 | |||
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); |
||
366 | |||
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); |
||
381 | |||
382 | /* KIL -texTemp; # if -texTemp < 0, KILL fragment */ |
||
383 | newInst = tgsi_default_full_instruction(); |
||
384 | newInst.Instruction.Opcode = TGSI_OPCODE_KIL; |
||
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); |
||
391 | } |
||
392 | |||
393 | /* emit this instruction */ |
||
394 | ctx->emit_instruction(ctx, inst); |
||
395 | } |
||
396 | |||
397 | |||
398 | /** |
||
399 | * Given a fragment shader, return a new fragment shader which |
||
400 | * samples a stipple texture and executes KILL. |
||
401 | */ |
||
402 | struct pipe_shader_state * |
||
403 | util_pstipple_create_fragment_shader(struct pipe_context *pipe, |
||
404 | struct pipe_shader_state *fs, |
||
405 | unsigned *samplerUnitOut) |
||
406 | { |
||
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; |
||
410 | unsigned i; |
||
411 | |||
412 | new_fs = MALLOC(sizeof(*new_fs)); |
||
413 | if (!new_fs) |
||
414 | return NULL; |
||
415 | |||
416 | new_fs->tokens = tgsi_alloc_tokens(newLen); |
||
417 | if (!new_fs->tokens) { |
||
418 | FREE(new_fs); |
||
419 | return NULL; |
||
420 | } |
||
421 | |||
422 | /* Setup shader transformation info/context. |
||
423 | */ |
||
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; |
||
433 | |||
434 | tgsi_scan_shader(fs->tokens, &transform.info); |
||
435 | |||
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]; |
||
440 | } |
||
441 | |||
442 | tgsi_transform_shader(fs->tokens, |
||
443 | (struct tgsi_token *) new_fs->tokens, |
||
444 | newLen, &transform.base); |
||
445 | |||
446 | #if 0 /* DEBUG */ |
||
447 | tgsi_dump(fs->tokens, 0); |
||
448 | tgsi_dump(new_fs->tokens, 0); |
||
449 | #endif |
||
450 | |||
451 | assert(transform.freeSampler < PIPE_MAX_SAMPLERS); |
||
452 | *samplerUnitOut = transform.freeSampler; |
||
453 | |||
454 | return new_fs; |
||
455 | }>>>>>><>>>>><>=>><>=>>>>>>><> |
||
456 |