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 VMWARE 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];
56 /* Index of the pos output, typically 0. */
58 /* Index of the pos temp where all writes of pos are redirected to. */
60 /* The index of the last generic output, after which we insert a new
65 /* Used to shift output decl. indices when inserting new ones. */
67 /* Used to remap writes to output decls if their indices changed. */
68 unsigned out_remap
[32];
70 /* First instruction processed? */
71 boolean first_instruction
;
72 /* End instruction processed? */
73 boolean end_instruction
;
75 boolean temp_used
[1024];
78 static void emit_temp(struct tgsi_transform_context
*ctx
, unsigned reg
)
80 struct tgsi_full_declaration decl
;
82 decl
= tgsi_default_full_declaration();
83 decl
.Declaration
.File
= TGSI_FILE_TEMPORARY
;
84 decl
.Range
.First
= decl
.Range
.Last
= reg
;
85 ctx
->emit_declaration(ctx
, &decl
);
88 static void emit_output(struct tgsi_transform_context
*ctx
,
89 unsigned name
, unsigned index
, unsigned interp
,
92 struct vs_transform_context
*vsctx
= (struct vs_transform_context
*)ctx
;
93 struct tgsi_full_declaration decl
;
95 decl
= tgsi_default_full_declaration();
96 decl
.Declaration
.File
= TGSI_FILE_OUTPUT
;
97 decl
.Declaration
.Interpolate
= 1;
98 decl
.Declaration
.Semantic
= TRUE
;
99 decl
.Semantic
.Name
= name
;
100 decl
.Semantic
.Index
= index
;
101 decl
.Range
.First
= decl
.Range
.Last
= reg
;
102 decl
.Interp
.Interpolate
= interp
;
103 ctx
->emit_declaration(ctx
, &decl
);
104 ++vsctx
->num_outputs
;
107 static void insert_output_before(struct tgsi_transform_context
*ctx
,
108 struct tgsi_full_declaration
*before
,
109 unsigned name
, unsigned index
, unsigned interp
)
111 struct vs_transform_context
*vsctx
= (struct vs_transform_context
*)ctx
;
114 /* Make a place for the new output. */
115 for (i
= before
->Range
.First
; i
< ARRAY_SIZE(vsctx
->out_remap
); i
++) {
116 ++vsctx
->out_remap
[i
];
119 /* Insert the new output. */
120 emit_output(ctx
, name
, index
, interp
,
121 before
->Range
.First
+ vsctx
->decl_shift
);
126 static void insert_output_after(struct tgsi_transform_context
*ctx
,
127 struct tgsi_full_declaration
*after
,
128 unsigned name
, unsigned index
, unsigned interp
)
130 struct vs_transform_context
*vsctx
= (struct vs_transform_context
*)ctx
;
133 /* Make a place for the new output. */
134 for (i
= after
->Range
.First
+1; i
< ARRAY_SIZE(vsctx
->out_remap
); i
++) {
135 ++vsctx
->out_remap
[i
];
138 /* Insert the new output. */
139 emit_output(ctx
, name
, index
, interp
,
140 after
->Range
.First
+ 1);
145 static void transform_decl(struct tgsi_transform_context
*ctx
,
146 struct tgsi_full_declaration
*decl
)
148 struct vs_transform_context
*vsctx
= (struct vs_transform_context
*)ctx
;
151 if (decl
->Declaration
.File
== TGSI_FILE_OUTPUT
) {
152 switch (decl
->Semantic
.Name
) {
153 case TGSI_SEMANTIC_POSITION
:
154 vsctx
->pos_output
= decl
->Range
.First
;
157 case TGSI_SEMANTIC_COLOR
:
158 assert(decl
->Semantic
.Index
< 2);
160 /* We must rasterize the first color if the second one is
161 * used, otherwise the rasterizer doesn't do the color
162 * selection correctly. Declare it, but don't write to it. */
163 if (decl
->Semantic
.Index
== 1 && !vsctx
->color_used
[0]) {
164 insert_output_before(ctx
, decl
, TGSI_SEMANTIC_COLOR
, 0,
165 TGSI_INTERPOLATE_LINEAR
);
166 vsctx
->color_used
[0] = TRUE
;
170 case TGSI_SEMANTIC_BCOLOR
:
171 assert(decl
->Semantic
.Index
< 2);
173 /* We must rasterize all 4 colors if back-face colors are
174 * used, otherwise the rasterizer doesn't do the color
175 * selection correctly. Declare it, but don't write to it. */
176 if (!vsctx
->color_used
[0]) {
177 insert_output_before(ctx
, decl
, TGSI_SEMANTIC_COLOR
, 0,
178 TGSI_INTERPOLATE_LINEAR
);
179 vsctx
->color_used
[0] = TRUE
;
181 if (!vsctx
->color_used
[1]) {
182 insert_output_before(ctx
, decl
, TGSI_SEMANTIC_COLOR
, 1,
183 TGSI_INTERPOLATE_LINEAR
);
184 vsctx
->color_used
[1] = TRUE
;
186 if (decl
->Semantic
.Index
== 1 && !vsctx
->bcolor_used
[0]) {
187 insert_output_before(ctx
, decl
, TGSI_SEMANTIC_BCOLOR
, 0,
188 TGSI_INTERPOLATE_LINEAR
);
189 vsctx
->bcolor_used
[0] = TRUE
;
193 case TGSI_SEMANTIC_GENERIC
:
194 vsctx
->last_generic
= MAX2(vsctx
->last_generic
, decl
->Semantic
.Index
);
198 /* Since we're inserting new outputs in between, the following outputs
199 * should be moved to the right so that they don't overlap with
200 * the newly added ones. */
201 decl
->Range
.First
+= vsctx
->decl_shift
;
202 decl
->Range
.Last
+= vsctx
->decl_shift
;
204 ++vsctx
->num_outputs
;
205 } else if (decl
->Declaration
.File
== TGSI_FILE_TEMPORARY
) {
206 for (i
= decl
->Range
.First
; i
<= decl
->Range
.Last
; i
++) {
207 vsctx
->temp_used
[i
] = TRUE
;
211 ctx
->emit_declaration(ctx
, decl
);
213 /* Insert BCOLOR1 if needed. */
214 if (decl
->Declaration
.File
== TGSI_FILE_OUTPUT
&&
215 decl
->Semantic
.Name
== TGSI_SEMANTIC_BCOLOR
&&
216 !vsctx
->bcolor_used
[1]) {
217 insert_output_after(ctx
, decl
, TGSI_SEMANTIC_BCOLOR
, 1,
218 TGSI_INTERPOLATE_LINEAR
);
222 static void transform_inst(struct tgsi_transform_context
*ctx
,
223 struct tgsi_full_instruction
*inst
)
225 struct vs_transform_context
*vsctx
= (struct vs_transform_context
*) ctx
;
226 struct tgsi_full_instruction new_inst
;
229 if (!vsctx
->first_instruction
) {
230 vsctx
->first_instruction
= TRUE
;
232 /* Insert the generic output for WPOS. */
233 emit_output(ctx
, TGSI_SEMANTIC_GENERIC
, vsctx
->last_generic
+ 1,
234 TGSI_INTERPOLATE_PERSPECTIVE
, vsctx
->num_outputs
);
236 /* Find a free temp for POSITION. */
237 for (i
= 0; i
< ARRAY_SIZE(vsctx
->temp_used
); i
++) {
238 if (!vsctx
->temp_used
[i
]) {
246 if (inst
->Instruction
.Opcode
== TGSI_OPCODE_END
) {
247 /* MOV OUT[pos_output], TEMP[pos_temp]; */
248 new_inst
= tgsi_default_full_instruction();
249 new_inst
.Instruction
.Opcode
= TGSI_OPCODE_MOV
;
250 new_inst
.Instruction
.NumDstRegs
= 1;
251 new_inst
.Dst
[0].Register
.File
= TGSI_FILE_OUTPUT
;
252 new_inst
.Dst
[0].Register
.Index
= vsctx
->pos_output
;
253 new_inst
.Dst
[0].Register
.WriteMask
= TGSI_WRITEMASK_XYZW
;
254 new_inst
.Instruction
.NumSrcRegs
= 1;
255 new_inst
.Src
[0].Register
.File
= TGSI_FILE_TEMPORARY
;
256 new_inst
.Src
[0].Register
.Index
= vsctx
->pos_temp
;
257 ctx
->emit_instruction(ctx
, &new_inst
);
259 /* MOV OUT[n-1], TEMP[pos_temp]; */
260 new_inst
= tgsi_default_full_instruction();
261 new_inst
.Instruction
.Opcode
= TGSI_OPCODE_MOV
;
262 new_inst
.Instruction
.NumDstRegs
= 1;
263 new_inst
.Dst
[0].Register
.File
= TGSI_FILE_OUTPUT
;
264 new_inst
.Dst
[0].Register
.Index
= vsctx
->num_outputs
- 1;
265 new_inst
.Dst
[0].Register
.WriteMask
= TGSI_WRITEMASK_XYZW
;
266 new_inst
.Instruction
.NumSrcRegs
= 1;
267 new_inst
.Src
[0].Register
.File
= TGSI_FILE_TEMPORARY
;
268 new_inst
.Src
[0].Register
.Index
= vsctx
->pos_temp
;
269 ctx
->emit_instruction(ctx
, &new_inst
);
271 vsctx
->end_instruction
= TRUE
;
273 /* Not an END instruction. */
274 /* Fix writes to outputs. */
275 for (i
= 0; i
< inst
->Instruction
.NumDstRegs
; i
++) {
276 struct tgsi_full_dst_register
*dst
= &inst
->Dst
[i
];
277 if (dst
->Register
.File
== TGSI_FILE_OUTPUT
) {
278 if (dst
->Register
.Index
== vsctx
->pos_output
) {
279 /* Replace writes to OUT[pos_output] with TEMP[pos_temp]. */
280 dst
->Register
.File
= TGSI_FILE_TEMPORARY
;
281 dst
->Register
.Index
= vsctx
->pos_temp
;
283 /* Not a position, good...
284 * Since we were changing the indices of output decls,
285 * we must redirect writes into them too. */
286 dst
->Register
.Index
= vsctx
->out_remap
[dst
->Register
.Index
];
291 /* Inserting 2 instructions before the END opcode moves all following
292 * labels by 2. Subroutines are always after the END opcode so
293 * they're always moved. */
294 if (inst
->Instruction
.Opcode
== TGSI_OPCODE_CAL
) {
295 inst
->Label
.Label
+= 2;
297 /* The labels of the following opcodes are moved only after
299 if (vsctx
->end_instruction
&&
300 (inst
->Instruction
.Opcode
== TGSI_OPCODE_IF
||
301 inst
->Instruction
.Opcode
== TGSI_OPCODE_ELSE
||
302 inst
->Instruction
.Opcode
== TGSI_OPCODE_BGNLOOP
||
303 inst
->Instruction
.Opcode
== TGSI_OPCODE_ENDLOOP
)) {
304 inst
->Label
.Label
+= 2;
308 ctx
->emit_instruction(ctx
, inst
);
311 void r300_draw_init_vertex_shader(struct r300_context
*r300
,
312 struct r300_vertex_shader
*vs
)
314 struct draw_context
*draw
= r300
->draw
;
315 struct pipe_shader_state new_vs
;
316 struct tgsi_shader_info info
;
317 struct vs_transform_context transform
;
318 const uint newLen
= tgsi_num_tokens(vs
->state
.tokens
) + 100 /* XXX */;
321 tgsi_scan_shader(vs
->state
.tokens
, &info
);
323 new_vs
.tokens
= tgsi_alloc_tokens(newLen
);
324 if (new_vs
.tokens
== NULL
)
327 memset(&transform
, 0, sizeof(transform
));
328 for (i
= 0; i
< ARRAY_SIZE(transform
.out_remap
); i
++) {
329 transform
.out_remap
[i
] = i
;
331 transform
.last_generic
= -1;
332 transform
.base
.transform_instruction
= transform_inst
;
333 transform
.base
.transform_declaration
= transform_decl
;
335 for (i
= 0; i
< info
.num_outputs
; i
++) {
336 unsigned index
= info
.output_semantic_index
[i
];
338 switch (info
.output_semantic_name
[i
]) {
339 case TGSI_SEMANTIC_COLOR
:
341 transform
.color_used
[index
] = TRUE
;
344 case TGSI_SEMANTIC_BCOLOR
:
346 transform
.bcolor_used
[index
] = TRUE
;
351 tgsi_transform_shader(vs
->state
.tokens
,
352 (struct tgsi_token
*)new_vs
.tokens
,
353 newLen
, &transform
.base
);
356 printf("----------------------------------------------\norig shader:\n");
357 tgsi_dump(vs
->state
.tokens
, 0);
358 printf("----------------------------------------------\nnew shader:\n");
359 tgsi_dump(new_vs
.tokens
, 0);
360 printf("----------------------------------------------\n");
363 /* Free old tokens. */
364 FREE((void*)vs
->state
.tokens
);
366 vs
->draw_vs
= draw_create_vertex_shader(draw
, &new_vs
);
368 /* Instead of duplicating and freeing the tokens, copy the pointer directly. */
369 vs
->state
.tokens
= new_vs
.tokens
;
371 /* Init the VS output table for the rasterizer. */
372 r300_init_vs_outputs(r300
, vs
);
374 /* Make the last generic be WPOS. */
375 vs
->outputs
.wpos
= vs
->outputs
.generic
[transform
.last_generic
+ 1];
376 vs
->outputs
.generic
[transform
.last_generic
+ 1] = ATTR_UNUSED
;