1 /**************************************************************************
3 * Copyright 2009 Marek Olšák <maraeo@gmail.com>
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sub license, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial portions
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
21 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 **************************************************************************/
27 /* This file contains the vertex shader tranformations for SW TCL needed
28 * to overcome the limitations of the r300 rasterizer.
31 * 1) If the secondary color output is present, the primary color must be
33 * 2) If any back-face color output is present, there must be all 4 color
34 * outputs and missing ones must be inserted.
35 * 3) Insert a trailing texcoord output containing a copy of POS, for WPOS.
37 * I know this code is cumbersome, but I don't know of any nicer way
38 * of transforming TGSI shaders. ~ M.
45 #include "tgsi/tgsi_transform.h"
46 #include "tgsi/tgsi_dump.h"
48 #include "draw/draw_context.h"
50 struct vs_transform_context
{
51 struct tgsi_transform_context base
;
53 boolean color_used
[2];
54 boolean bcolor_used
[2];
55 boolean temp_used
[128];
57 /* Index of the pos output, typically 0. */
59 /* Index of the pos temp where all writes of pos are redirected to. */
61 /* The index of the last generic output, after which we insert a new
66 /* Used to shift output decl. indices when inserting new ones. */
68 /* Used to remap writes to output decls if their indices changed. */
69 unsigned out_remap
[32];
71 /* First instruction processed? */
72 boolean first_instruction
;
73 /* End instruction processed? */
74 boolean end_instruction
;
77 static void emit_temp(struct tgsi_transform_context
*ctx
, unsigned reg
)
79 struct tgsi_full_declaration decl
;
81 decl
= tgsi_default_full_declaration();
82 decl
.Declaration
.File
= TGSI_FILE_TEMPORARY
;
83 decl
.Range
.First
= decl
.Range
.Last
= reg
;
84 ctx
->emit_declaration(ctx
, &decl
);
87 static void emit_output(struct tgsi_transform_context
*ctx
,
88 unsigned name
, unsigned index
, unsigned interp
,
91 struct vs_transform_context
*vsctx
= (struct vs_transform_context
*)ctx
;
92 struct tgsi_full_declaration decl
;
94 decl
= tgsi_default_full_declaration();
95 decl
.Declaration
.File
= TGSI_FILE_OUTPUT
;
96 decl
.Declaration
.Interpolate
= interp
;
97 decl
.Declaration
.Semantic
= TRUE
;
98 decl
.Semantic
.Name
= name
;
99 decl
.Semantic
.Index
= index
;
100 decl
.Range
.First
= decl
.Range
.Last
= reg
;
101 ctx
->emit_declaration(ctx
, &decl
);
102 ++vsctx
->num_outputs
;
105 static void insert_output(struct tgsi_transform_context
*ctx
,
106 struct tgsi_full_declaration
*before
,
107 unsigned name
, unsigned index
, unsigned interp
)
109 struct vs_transform_context
*vsctx
= (struct vs_transform_context
*)ctx
;
112 /* Make a place for the new output. */
113 for (i
= before
->Range
.First
; i
< Elements(vsctx
->out_remap
); i
++) {
114 ++vsctx
->out_remap
[i
];
117 /* Insert the new output. */
118 emit_output(ctx
, name
, index
, interp
, before
->Range
.First
);
123 static void insert_trailing_bcolor(struct tgsi_transform_context
*ctx
,
124 struct tgsi_full_declaration
*before
)
126 struct vs_transform_context
*vsctx
= (struct vs_transform_context
*)ctx
;
128 /* If BCOLOR0 is used, make sure BCOLOR1 is present too. Otherwise
129 * the rasterizer doesn't do the color selection correctly. */
130 if (vsctx
->bcolor_used
[0] && !vsctx
->bcolor_used
[1]) {
132 insert_output(ctx
, before
, TGSI_SEMANTIC_BCOLOR
, 1,
133 TGSI_INTERPOLATE_LINEAR
);
135 emit_output(ctx
, TGSI_SEMANTIC_BCOLOR
, 1,
136 TGSI_INTERPOLATE_LINEAR
, vsctx
->num_outputs
);
138 vsctx
->bcolor_used
[1] = TRUE
;
142 static void transform_decl(struct tgsi_transform_context
*ctx
,
143 struct tgsi_full_declaration
*decl
)
145 struct vs_transform_context
*vsctx
= (struct vs_transform_context
*)ctx
;
148 if (decl
->Declaration
.File
== TGSI_FILE_OUTPUT
) {
149 switch (decl
->Semantic
.Name
) {
150 case TGSI_SEMANTIC_POSITION
:
151 vsctx
->pos_output
= decl
->Range
.First
;
154 case TGSI_SEMANTIC_COLOR
:
155 assert(decl
->Semantic
.Index
< 2);
156 vsctx
->color_used
[decl
->Semantic
.Index
] = TRUE
;
158 /* We must rasterize the first color if the second one is
159 * used, otherwise the rasterizer doesn't do the color
160 * selection correctly. Declare it, but don't write to it. */
161 if (decl
->Semantic
.Index
== 1 && !vsctx
->color_used
[0]) {
162 insert_output(ctx
, decl
, TGSI_SEMANTIC_COLOR
, 0,
163 TGSI_INTERPOLATE_LINEAR
);
164 vsctx
->color_used
[0] = TRUE
;
168 case TGSI_SEMANTIC_BCOLOR
:
169 assert(decl
->Semantic
.Index
< 2);
170 vsctx
->bcolor_used
[decl
->Semantic
.Index
] = TRUE
;
172 /* We must rasterize all 4 colors if back-face colors are
173 * used, otherwise the rasterizer doesn't do the color
174 * selection correctly. Declare it, but don't write to it. */
175 if (!vsctx
->color_used
[0]) {
176 insert_output(ctx
, decl
, TGSI_SEMANTIC_COLOR
, 0,
177 TGSI_INTERPOLATE_LINEAR
);
178 vsctx
->color_used
[0] = TRUE
;
180 if (!vsctx
->color_used
[1]) {
181 insert_output(ctx
, decl
, TGSI_SEMANTIC_COLOR
, 1,
182 TGSI_INTERPOLATE_LINEAR
);
183 vsctx
->color_used
[1] = TRUE
;
185 if (decl
->Semantic
.Index
== 1 && !vsctx
->bcolor_used
[0]) {
186 insert_output(ctx
, decl
, TGSI_SEMANTIC_BCOLOR
, 0,
187 TGSI_INTERPOLATE_LINEAR
);
188 vsctx
->bcolor_used
[0] = TRUE
;
190 /* One more case is handled in insert_trailing_bcolor. */
193 case TGSI_SEMANTIC_GENERIC
:
194 vsctx
->last_generic
= MAX2(vsctx
->last_generic
, decl
->Semantic
.Index
);
198 if (decl
->Semantic
.Name
!= TGSI_SEMANTIC_BCOLOR
) {
199 /* Insert it as soon as possible. */
200 insert_trailing_bcolor(ctx
, decl
);
203 /* Since we're inserting new outputs in between, the following outputs
204 * should be moved to the right so that they don't overlap with
205 * the newly added ones. */
206 decl
->Range
.First
+= vsctx
->decl_shift
;
207 decl
->Range
.Last
+= vsctx
->decl_shift
;
209 ++vsctx
->num_outputs
;
210 } else if (decl
->Declaration
.File
== TGSI_FILE_TEMPORARY
) {
211 for (i
= decl
->Range
.First
; i
<= decl
->Range
.Last
; i
++) {
212 vsctx
->temp_used
[i
] = TRUE
;
216 ctx
->emit_declaration(ctx
, decl
);
219 static void transform_inst(struct tgsi_transform_context
*ctx
,
220 struct tgsi_full_instruction
*inst
)
222 struct vs_transform_context
*vsctx
= (struct vs_transform_context
*) ctx
;
223 struct tgsi_full_instruction new_inst
;
226 if (!vsctx
->first_instruction
) {
227 vsctx
->first_instruction
= TRUE
;
229 /* The trailing BCOLOR should be inserted before the code
230 * if it hasn't already been done so. */
231 insert_trailing_bcolor(ctx
, NULL
);
233 /* Insert the generic output for WPOS. */
234 emit_output(ctx
, TGSI_SEMANTIC_GENERIC
, vsctx
->last_generic
+ 1,
235 TGSI_INTERPOLATE_PERSPECTIVE
, vsctx
->num_outputs
);
237 /* Find a free temp for POSITION. */
238 for (i
= 0; i
< Elements(vsctx
->temp_used
); i
++) {
239 if (!vsctx
->temp_used
[i
]) {
247 if (inst
->Instruction
.Opcode
== TGSI_OPCODE_END
) {
248 /* MOV OUT[pos_output], TEMP[pos_temp]; */
249 new_inst
= tgsi_default_full_instruction();
250 new_inst
.Instruction
.Opcode
= TGSI_OPCODE_MOV
;
251 new_inst
.Instruction
.NumDstRegs
= 1;
252 new_inst
.Dst
[0].Register
.File
= TGSI_FILE_OUTPUT
;
253 new_inst
.Dst
[0].Register
.Index
= vsctx
->pos_output
;
254 new_inst
.Dst
[0].Register
.WriteMask
= TGSI_WRITEMASK_XYZW
;
255 new_inst
.Instruction
.NumSrcRegs
= 1;
256 new_inst
.Src
[0].Register
.File
= TGSI_FILE_TEMPORARY
;
257 new_inst
.Src
[0].Register
.Index
= vsctx
->pos_temp
;
258 ctx
->emit_instruction(ctx
, &new_inst
);
260 /* MOV OUT[n-1], TEMP[pos_temp]; */
261 new_inst
= tgsi_default_full_instruction();
262 new_inst
.Instruction
.Opcode
= TGSI_OPCODE_MOV
;
263 new_inst
.Instruction
.NumDstRegs
= 1;
264 new_inst
.Dst
[0].Register
.File
= TGSI_FILE_OUTPUT
;
265 new_inst
.Dst
[0].Register
.Index
= vsctx
->num_outputs
- 1;
266 new_inst
.Dst
[0].Register
.WriteMask
= TGSI_WRITEMASK_XYZW
;
267 new_inst
.Instruction
.NumSrcRegs
= 1;
268 new_inst
.Src
[0].Register
.File
= TGSI_FILE_TEMPORARY
;
269 new_inst
.Src
[0].Register
.Index
= vsctx
->pos_temp
;
270 ctx
->emit_instruction(ctx
, &new_inst
);
272 vsctx
->end_instruction
= TRUE
;
274 /* Not an END instruction. */
275 /* Fix writes to outputs. */
276 for (i
= 0; i
< inst
->Instruction
.NumDstRegs
; i
++) {
277 struct tgsi_full_dst_register
*dst
= &inst
->Dst
[i
];
278 if (dst
->Register
.File
== TGSI_FILE_OUTPUT
) {
279 if (dst
->Register
.Index
== vsctx
->pos_output
) {
280 /* Replace writes to OUT[pos_output] with TEMP[pos_temp]. */
281 dst
->Register
.File
= TGSI_FILE_TEMPORARY
;
282 dst
->Register
.Index
= vsctx
->pos_temp
;
284 /* Not a position, good...
285 * Since we were changing the indices of output decls,
286 * we must redirect writes into them too. */
287 dst
->Register
.Index
= vsctx
->out_remap
[dst
->Register
.Index
];
292 /* Inserting 2 instructions before the END opcode moves all following
293 * labels by 2. Subroutines are always after the END opcode so
294 * they're always moved. */
295 if (inst
->Instruction
.Opcode
== TGSI_OPCODE_CAL
) {
296 inst
->Label
.Label
+= 2;
298 /* The labels of the following opcodes are moved only after
300 if (vsctx
->end_instruction
&&
301 (inst
->Instruction
.Opcode
== TGSI_OPCODE_IF
||
302 inst
->Instruction
.Opcode
== TGSI_OPCODE_ELSE
||
303 inst
->Instruction
.Opcode
== TGSI_OPCODE_BGNLOOP
||
304 inst
->Instruction
.Opcode
== TGSI_OPCODE_ENDLOOP
)) {
305 inst
->Label
.Label
+= 2;
309 ctx
->emit_instruction(ctx
, inst
);
312 void r300_draw_init_vertex_shader(struct draw_context
*draw
,
313 struct r300_vertex_shader
*vs
)
315 struct pipe_shader_state new_vs
;
316 struct vs_transform_context transform
;
317 const uint newLen
= tgsi_num_tokens(vs
->state
.tokens
) + 100 /* XXX */;
320 new_vs
.tokens
= tgsi_alloc_tokens(newLen
);
321 if (new_vs
.tokens
== NULL
)
324 memset(&transform
, 0, sizeof(transform
));
325 for (i
= 0; i
< Elements(transform
.out_remap
); i
++) {
326 transform
.out_remap
[i
] = i
;
328 transform
.last_generic
= -1;
329 transform
.base
.transform_instruction
= transform_inst
;
330 transform
.base
.transform_declaration
= transform_decl
;
332 tgsi_transform_shader(vs
->state
.tokens
,
333 (struct tgsi_token
*)new_vs
.tokens
,
334 newLen
, &transform
.base
);
337 printf("----------------------------------------------\norig shader:\n");
338 tgsi_dump(vs
->state
.tokens
, 0);
339 printf("----------------------------------------------\nnew shader:\n");
340 tgsi_dump(new_vs
.tokens
, 0);
341 printf("----------------------------------------------\n");
344 /* Free old tokens. */
345 FREE((void*)vs
->state
.tokens
);
347 vs
->draw_vs
= draw_create_vertex_shader(draw
, &new_vs
);
349 /* Instead of duplicating and freeing the tokens, copy the pointer directly. */
350 vs
->state
.tokens
= new_vs
.tokens
;
352 /* Init the VS output table for the rasterizer. */
353 r300_init_vs_outputs(vs
);
355 /* Make the last generic be WPOS. */
356 vs
->outputs
.wpos
= vs
->outputs
.generic
[transform
.last_generic
+ 1];
357 vs
->outputs
.generic
[transform
.last_generic
+ 1] = ATTR_UNUSED
;