• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
util_pstipple_update_stipple_texture(struct pipe_context * pipe,struct pipe_resource * tex,const uint32_t pattern[32])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    transfer = pipe_get_transfer(pipe, tex, 0, 0,
73                                 PIPE_TRANSFER_WRITE, 0, 0, 32, 32);
74    data = pipe->transfer_map(pipe, transfer);
75 
76    /*
77     * Load alpha texture.
78     * Note: 0 means keep the fragment, 255 means kill it.
79     * We'll negate the texel value and use KILP which kills if value
80     * is negative.
81     */
82    for (i = 0; i < 32; i++) {
83       for (j = 0; j < 32; j++) {
84          if (pattern[i] & (bit31 >> j)) {
85             /* fragment "on" */
86             data[i * transfer->stride + j] = 0;
87          }
88          else {
89             /* fragment "off" */
90             data[i * transfer->stride + j] = 255;
91          }
92       }
93    }
94 
95    /* unmap */
96    pipe->transfer_unmap(pipe, transfer);
97    pipe->transfer_destroy(pipe, transfer);
98 }
99 
100 
101 /**
102  * Create a 32x32 alpha8 texture that encodes the given stipple pattern.
103  */
104 struct pipe_resource *
util_pstipple_create_stipple_texture(struct pipe_context * pipe,const uint32_t pattern[32])105 util_pstipple_create_stipple_texture(struct pipe_context *pipe,
106                                      const uint32_t pattern[32])
107 {
108    struct pipe_screen *screen = pipe->screen;
109    struct pipe_resource templat, *tex;
110 
111    memset(&templat, 0, sizeof(templat));
112    templat.target = PIPE_TEXTURE_2D;
113    templat.format = PIPE_FORMAT_A8_UNORM;
114    templat.last_level = 0;
115    templat.width0 = 32;
116    templat.height0 = 32;
117    templat.depth0 = 1;
118    templat.array_size = 1;
119    templat.bind = PIPE_BIND_SAMPLER_VIEW;
120 
121    tex = screen->resource_create(screen, &templat);
122 
123    if (tex)
124       util_pstipple_update_stipple_texture(pipe, tex, pattern);
125 
126    return tex;
127 }
128 
129 
130 /**
131  * Create sampler view to sample the stipple texture.
132  */
133 struct pipe_sampler_view *
util_pstipple_create_sampler_view(struct pipe_context * pipe,struct pipe_resource * tex)134 util_pstipple_create_sampler_view(struct pipe_context *pipe,
135                                   struct pipe_resource *tex)
136 {
137    struct pipe_sampler_view templat, *sv;
138 
139    u_sampler_view_default_template(&templat, tex, tex->format);
140    sv = pipe->create_sampler_view(pipe, tex, &templat);
141 
142    return sv;
143 }
144 
145 
146 /**
147  * Create the sampler CSO that'll be used for stippling.
148  */
149 void *
util_pstipple_create_sampler(struct pipe_context * pipe)150 util_pstipple_create_sampler(struct pipe_context *pipe)
151 {
152    struct pipe_sampler_state templat;
153    void *s;
154 
155    memset(&templat, 0, sizeof(templat));
156    templat.wrap_s = PIPE_TEX_WRAP_REPEAT;
157    templat.wrap_t = PIPE_TEX_WRAP_REPEAT;
158    templat.wrap_r = PIPE_TEX_WRAP_REPEAT;
159    templat.min_mip_filter = PIPE_TEX_MIPFILTER_NONE;
160    templat.min_img_filter = PIPE_TEX_FILTER_NEAREST;
161    templat.mag_img_filter = PIPE_TEX_FILTER_NEAREST;
162    templat.normalized_coords = 1;
163    templat.min_lod = 0.0f;
164    templat.max_lod = 0.0f;
165 
166    s = pipe->create_sampler_state(pipe, &templat);
167    return s;
168 }
169 
170 
171 
172 /**
173  * Subclass of tgsi_transform_context, used for transforming the
174  * user's fragment shader to add the extra texture sample and fragment kill
175  * instructions.
176  */
177 struct pstip_transform_context {
178    struct tgsi_transform_context base;
179    struct tgsi_shader_info info;
180    uint tempsUsed;  /**< bitmask */
181    int wincoordInput;
182    int maxInput;
183    uint samplersUsed;  /**< bitfield of samplers used */
184    int freeSampler;  /** an available sampler for the pstipple */
185    int texTemp;  /**< temp registers */
186    int numImmed;
187    boolean firstInstruction;
188    uint coordOrigin;
189 };
190 
191 
192 /**
193  * TGSI declaration transform callback.
194  * Track samplers used, temps used, inputs used.
195  */
196 static void
pstip_transform_decl(struct tgsi_transform_context * ctx,struct tgsi_full_declaration * decl)197 pstip_transform_decl(struct tgsi_transform_context *ctx,
198                      struct tgsi_full_declaration *decl)
199 {
200    struct pstip_transform_context *pctx =
201       (struct pstip_transform_context *) ctx;
202 
203    /* XXX we can use tgsi_shader_info instead of some of this */
204 
205    if (decl->Declaration.File == TGSI_FILE_SAMPLER) {
206       uint i;
207       for (i = decl->Range.First; i <= decl->Range.Last; i++) {
208          pctx->samplersUsed |= 1 << i;
209       }
210    }
211    else if (decl->Declaration.File == TGSI_FILE_INPUT) {
212       pctx->maxInput = MAX2(pctx->maxInput, (int) decl->Range.Last);
213       if (decl->Semantic.Name == TGSI_SEMANTIC_POSITION)
214          pctx->wincoordInput = (int) decl->Range.First;
215    }
216    else if (decl->Declaration.File == TGSI_FILE_TEMPORARY) {
217       uint i;
218       for (i = decl->Range.First; i <= decl->Range.Last; i++) {
219          pctx->tempsUsed |= (1 << i);
220       }
221    }
222 
223    ctx->emit_declaration(ctx, decl);
224 }
225 
226 
227 static void
pstip_transform_immed(struct tgsi_transform_context * ctx,struct tgsi_full_immediate * immed)228 pstip_transform_immed(struct tgsi_transform_context *ctx,
229                       struct tgsi_full_immediate *immed)
230 {
231    struct pstip_transform_context *pctx =
232       (struct pstip_transform_context *) ctx;
233    pctx->numImmed++;
234 }
235 
236 
237 /**
238  * Find the lowest zero bit in the given word, or -1 if bitfield is all ones.
239  */
240 static int
free_bit(uint bitfield)241 free_bit(uint bitfield)
242 {
243    return ffs(~bitfield) - 1;
244 }
245 
246 
247 /**
248  * TGSI instruction transform callback.
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.
252  *
253  * Insert:
254  *   declare new registers
255  *   MUL texTemp, INPUT[wincoord], 1/32;
256  *   TEX texTemp, texTemp, sampler;
257  *   KIL -texTemp;   # if -texTemp < 0, KILL fragment
258  *   [...original code...]
259  */
260 static void
pstip_transform_inst(struct tgsi_transform_context * ctx,struct tgsi_full_instruction * inst)261 pstip_transform_inst(struct tgsi_transform_context *ctx,
262                      struct tgsi_full_instruction *inst)
263 {
264    struct pstip_transform_context *pctx =
265       (struct pstip_transform_context *) ctx;
266 
267    if (pctx->firstInstruction) {
268       /* emit our new declarations before the first instruction */
269 
270       struct tgsi_full_declaration decl;
271       struct tgsi_full_instruction newInst;
272       uint i;
273       int wincoordInput;
274 
275       /* find free texture sampler */
276       pctx->freeSampler = free_bit(pctx->samplersUsed);
277       if (pctx->freeSampler >= PIPE_MAX_SAMPLERS)
278          pctx->freeSampler = PIPE_MAX_SAMPLERS - 1;
279 
280       if (pctx->wincoordInput < 0)
281          wincoordInput = pctx->maxInput + 1;
282       else
283          wincoordInput = pctx->wincoordInput;
284 
285       /* find one free temp register */
286       for (i = 0; i < 32; i++) {
287          if ((pctx->tempsUsed & (1 << i)) == 0) {
288             /* found a free temp */
289             if (pctx->texTemp < 0)
290                pctx->texTemp  = i;
291             else
292                break;
293          }
294       }
295       assert(pctx->texTemp >= 0);
296 
297       if (pctx->wincoordInput < 0) {
298          /* declare new position input reg */
299          decl = tgsi_default_full_declaration();
300          decl.Declaration.File = TGSI_FILE_INPUT;
301          decl.Declaration.Interpolate = 1;
302          decl.Declaration.Semantic = 1;
303          decl.Semantic.Name = TGSI_SEMANTIC_POSITION;
304          decl.Semantic.Index = 0;
305          decl.Range.First =
306             decl.Range.Last = wincoordInput;
307          decl.Interp.Interpolate = TGSI_INTERPOLATE_LINEAR;
308          ctx->emit_declaration(ctx, &decl);
309       }
310 
311       /* declare new sampler */
312       decl = tgsi_default_full_declaration();
313       decl.Declaration.File = TGSI_FILE_SAMPLER;
314       decl.Range.First =
315       decl.Range.Last = pctx->freeSampler;
316       ctx->emit_declaration(ctx, &decl);
317 
318       /* declare new temp regs */
319       decl = tgsi_default_full_declaration();
320       decl.Declaration.File = TGSI_FILE_TEMPORARY;
321       decl.Range.First =
322       decl.Range.Last = pctx->texTemp;
323       ctx->emit_declaration(ctx, &decl);
324 
325       /* emit immediate = {1/32, 1/32, 1, 1}
326        * The index/position of this immediate will be pctx->numImmed
327        */
328       {
329          static const float value[4] = { 1.0/32, 1.0/32, 1.0, 1.0 };
330          struct tgsi_full_immediate immed;
331          uint size = 4;
332          immed = tgsi_default_full_immediate();
333          immed.Immediate.NrTokens = 1 + size; /* one for the token itself */
334          immed.u[0].Float = value[0];
335          immed.u[1].Float = value[1];
336          immed.u[2].Float = value[2];
337          immed.u[3].Float = value[3];
338          ctx->emit_immediate(ctx, &immed);
339       }
340 
341       pctx->firstInstruction = FALSE;
342 
343 
344       /*
345        * Insert new MUL/TEX/KILP instructions at start of program
346        * Take gl_FragCoord, divide by 32 (stipple size), sample the
347        * texture and kill fragment if needed.
348        *
349        * We'd like to use non-normalized texcoords to index into a RECT
350        * texture, but we can only use REPEAT wrap mode with normalized
351        * texcoords.  Darn.
352        */
353 
354       /* XXX invert wincoord if origin isn't lower-left... */
355 
356       /* MUL texTemp, INPUT[wincoord], 1/32; */
357       newInst = tgsi_default_full_instruction();
358       newInst.Instruction.Opcode = TGSI_OPCODE_MUL;
359       newInst.Instruction.NumDstRegs = 1;
360       newInst.Dst[0].Register.File = TGSI_FILE_TEMPORARY;
361       newInst.Dst[0].Register.Index = pctx->texTemp;
362       newInst.Instruction.NumSrcRegs = 2;
363       newInst.Src[0].Register.File = TGSI_FILE_INPUT;
364       newInst.Src[0].Register.Index = wincoordInput;
365       newInst.Src[1].Register.File = TGSI_FILE_IMMEDIATE;
366       newInst.Src[1].Register.Index = pctx->numImmed;
367       ctx->emit_instruction(ctx, &newInst);
368 
369       /* TEX texTemp, texTemp, sampler; */
370       newInst = tgsi_default_full_instruction();
371       newInst.Instruction.Opcode = TGSI_OPCODE_TEX;
372       newInst.Instruction.NumDstRegs = 1;
373       newInst.Dst[0].Register.File = TGSI_FILE_TEMPORARY;
374       newInst.Dst[0].Register.Index = pctx->texTemp;
375       newInst.Instruction.NumSrcRegs = 2;
376       newInst.Instruction.Texture = TRUE;
377       newInst.Texture.Texture = TGSI_TEXTURE_2D;
378       newInst.Src[0].Register.File = TGSI_FILE_TEMPORARY;
379       newInst.Src[0].Register.Index = pctx->texTemp;
380       newInst.Src[1].Register.File = TGSI_FILE_SAMPLER;
381       newInst.Src[1].Register.Index = pctx->freeSampler;
382       ctx->emit_instruction(ctx, &newInst);
383 
384       /* KIL -texTemp;   # if -texTemp < 0, KILL fragment */
385       newInst = tgsi_default_full_instruction();
386       newInst.Instruction.Opcode = TGSI_OPCODE_KIL;
387       newInst.Instruction.NumDstRegs = 0;
388       newInst.Instruction.NumSrcRegs = 1;
389       newInst.Src[0].Register.File = TGSI_FILE_TEMPORARY;
390       newInst.Src[0].Register.Index = pctx->texTemp;
391       newInst.Src[0].Register.Negate = 1;
392       ctx->emit_instruction(ctx, &newInst);
393    }
394 
395    /* emit this instruction */
396    ctx->emit_instruction(ctx, inst);
397 }
398 
399 
400 /**
401  * Given a fragment shader, return a new fragment shader which
402  * samples a stipple texture and executes KILL.
403  */
404 struct pipe_shader_state *
util_pstipple_create_fragment_shader(struct pipe_context * pipe,struct pipe_shader_state * fs,unsigned * samplerUnitOut)405 util_pstipple_create_fragment_shader(struct pipe_context *pipe,
406                                      struct pipe_shader_state *fs,
407                                      unsigned *samplerUnitOut)
408 {
409    struct pipe_shader_state *new_fs;
410    struct pstip_transform_context transform;
411    const uint newLen = tgsi_num_tokens(fs->tokens) + NUM_NEW_TOKENS;
412    unsigned i;
413 
414    new_fs = MALLOC(sizeof(*new_fs));
415    if (!new_fs)
416       return NULL;
417 
418    new_fs->tokens = tgsi_alloc_tokens(newLen);
419    if (!new_fs->tokens) {
420       FREE(new_fs);
421       return NULL;
422    }
423 
424    /* Setup shader transformation info/context.
425     */
426    memset(&transform, 0, sizeof(transform));
427    transform.wincoordInput = -1;
428    transform.maxInput = -1;
429    transform.texTemp = -1;
430    transform.firstInstruction = TRUE;
431    transform.coordOrigin = TGSI_FS_COORD_ORIGIN_UPPER_LEFT;
432    transform.base.transform_instruction = pstip_transform_inst;
433    transform.base.transform_declaration = pstip_transform_decl;
434    transform.base.transform_immediate = pstip_transform_immed;
435 
436    tgsi_scan_shader(fs->tokens, &transform.info);
437 
438    /* find fragment coordinate origin property */
439    for (i = 0; i < transform.info.num_properties; i++) {
440       if (transform.info.properties[i].name == TGSI_PROPERTY_FS_COORD_ORIGIN)
441          transform.coordOrigin = transform.info.properties[i].data[0];
442    }
443 
444    tgsi_transform_shader(fs->tokens,
445                          (struct tgsi_token *) new_fs->tokens,
446                          newLen, &transform.base);
447 
448 #if 0 /* DEBUG */
449    tgsi_dump(fs->tokens, 0);
450    tgsi_dump(new_fs->tokens, 0);
451 #endif
452 
453    assert(transform.freeSampler < PIPE_MAX_SAMPLERS);
454    *samplerUnitOut = transform.freeSampler;
455 
456    return new_fs;
457 }
458 
459