1bf215546Sopenharmony_ci/**************************************************************************
2bf215546Sopenharmony_ci *
3bf215546Sopenharmony_ci * Copyright 2009 Marek Olšák <maraeo@gmail.com>
4bf215546Sopenharmony_ci *
5bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
6bf215546Sopenharmony_ci * copy of this software and associated documentation files (the
7bf215546Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
8bf215546Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
9bf215546Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to
10bf215546Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
11bf215546Sopenharmony_ci * the following conditions:
12bf215546Sopenharmony_ci *
13bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the
14bf215546Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions
15bf215546Sopenharmony_ci * of the Software.
16bf215546Sopenharmony_ci *
17bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18bf215546Sopenharmony_ci * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19bf215546Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20bf215546Sopenharmony_ci * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
21bf215546Sopenharmony_ci * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22bf215546Sopenharmony_ci * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23bf215546Sopenharmony_ci * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24bf215546Sopenharmony_ci *
25bf215546Sopenharmony_ci **************************************************************************/
26bf215546Sopenharmony_ci
27bf215546Sopenharmony_ci/* This file contains the vertex shader tranformations for SW TCL needed
28bf215546Sopenharmony_ci * to overcome the limitations of the r300 rasterizer.
29bf215546Sopenharmony_ci *
30bf215546Sopenharmony_ci * Transformations:
31bf215546Sopenharmony_ci * 1) If the secondary color output is present, the primary color must be
32bf215546Sopenharmony_ci *    present too.
33bf215546Sopenharmony_ci * 2) If any back-face color output is present, there must be all 4 color
34bf215546Sopenharmony_ci *    outputs and missing ones must be inserted.
35bf215546Sopenharmony_ci * 3) Insert a trailing texcoord output containing a copy of POS, for WPOS.
36bf215546Sopenharmony_ci *
37bf215546Sopenharmony_ci * I know this code is cumbersome, but I don't know of any nicer way
38bf215546Sopenharmony_ci * of transforming TGSI shaders. ~ M.
39bf215546Sopenharmony_ci */
40bf215546Sopenharmony_ci
41bf215546Sopenharmony_ci#include "r300_vs.h"
42bf215546Sopenharmony_ci
43bf215546Sopenharmony_ci#include <stdio.h>
44bf215546Sopenharmony_ci
45bf215546Sopenharmony_ci#include "tgsi/tgsi_transform.h"
46bf215546Sopenharmony_ci#include "tgsi/tgsi_dump.h"
47bf215546Sopenharmony_ci
48bf215546Sopenharmony_ci#include "draw/draw_context.h"
49bf215546Sopenharmony_ci
50bf215546Sopenharmony_cistruct vs_transform_context {
51bf215546Sopenharmony_ci    struct tgsi_transform_context base;
52bf215546Sopenharmony_ci
53bf215546Sopenharmony_ci    boolean color_used[2];
54bf215546Sopenharmony_ci    boolean bcolor_used[2];
55bf215546Sopenharmony_ci
56bf215546Sopenharmony_ci    /* Index of the pos output, typically 0. */
57bf215546Sopenharmony_ci    unsigned pos_output;
58bf215546Sopenharmony_ci    /* Index of the pos temp where all writes of pos are redirected to. */
59bf215546Sopenharmony_ci    unsigned pos_temp;
60bf215546Sopenharmony_ci    /* The index of the last generic output, after which we insert a new
61bf215546Sopenharmony_ci     * output for WPOS. */
62bf215546Sopenharmony_ci    int last_generic;
63bf215546Sopenharmony_ci
64bf215546Sopenharmony_ci    unsigned num_outputs;
65bf215546Sopenharmony_ci    /* Used to shift output decl. indices when inserting new ones. */
66bf215546Sopenharmony_ci    unsigned decl_shift;
67bf215546Sopenharmony_ci    /* Used to remap writes to output decls if their indices changed. */
68bf215546Sopenharmony_ci    unsigned out_remap[32];
69bf215546Sopenharmony_ci
70bf215546Sopenharmony_ci    /* First instruction processed? */
71bf215546Sopenharmony_ci    boolean first_instruction;
72bf215546Sopenharmony_ci    /* End instruction processed? */
73bf215546Sopenharmony_ci    boolean end_instruction;
74bf215546Sopenharmony_ci
75bf215546Sopenharmony_ci    boolean temp_used[1024];
76bf215546Sopenharmony_ci};
77bf215546Sopenharmony_ci
78bf215546Sopenharmony_cistatic void emit_temp(struct tgsi_transform_context *ctx, unsigned reg)
79bf215546Sopenharmony_ci{
80bf215546Sopenharmony_ci    struct tgsi_full_declaration decl;
81bf215546Sopenharmony_ci
82bf215546Sopenharmony_ci    decl = tgsi_default_full_declaration();
83bf215546Sopenharmony_ci    decl.Declaration.File = TGSI_FILE_TEMPORARY;
84bf215546Sopenharmony_ci    decl.Range.First = decl.Range.Last = reg;
85bf215546Sopenharmony_ci    ctx->emit_declaration(ctx, &decl);
86bf215546Sopenharmony_ci}
87bf215546Sopenharmony_ci
88bf215546Sopenharmony_cistatic void emit_output(struct tgsi_transform_context *ctx,
89bf215546Sopenharmony_ci                        unsigned name, unsigned index, unsigned interp,
90bf215546Sopenharmony_ci                        unsigned reg)
91bf215546Sopenharmony_ci{
92bf215546Sopenharmony_ci    struct vs_transform_context *vsctx = (struct vs_transform_context *)ctx;
93bf215546Sopenharmony_ci    struct tgsi_full_declaration decl;
94bf215546Sopenharmony_ci
95bf215546Sopenharmony_ci    decl = tgsi_default_full_declaration();
96bf215546Sopenharmony_ci    decl.Declaration.File = TGSI_FILE_OUTPUT;
97bf215546Sopenharmony_ci    decl.Declaration.Interpolate = 1;
98bf215546Sopenharmony_ci    decl.Declaration.Semantic = TRUE;
99bf215546Sopenharmony_ci    decl.Semantic.Name = name;
100bf215546Sopenharmony_ci    decl.Semantic.Index = index;
101bf215546Sopenharmony_ci    decl.Range.First = decl.Range.Last = reg;
102bf215546Sopenharmony_ci    decl.Interp.Interpolate = interp;
103bf215546Sopenharmony_ci    ctx->emit_declaration(ctx, &decl);
104bf215546Sopenharmony_ci    ++vsctx->num_outputs;
105bf215546Sopenharmony_ci}
106bf215546Sopenharmony_ci
107bf215546Sopenharmony_cistatic void insert_output_before(struct tgsi_transform_context *ctx,
108bf215546Sopenharmony_ci                                 struct tgsi_full_declaration *before,
109bf215546Sopenharmony_ci                                 unsigned name, unsigned index, unsigned interp)
110bf215546Sopenharmony_ci{
111bf215546Sopenharmony_ci    struct vs_transform_context *vsctx = (struct vs_transform_context *)ctx;
112bf215546Sopenharmony_ci    unsigned i;
113bf215546Sopenharmony_ci
114bf215546Sopenharmony_ci    /* Make a place for the new output. */
115bf215546Sopenharmony_ci    for (i = before->Range.First; i < ARRAY_SIZE(vsctx->out_remap); i++) {
116bf215546Sopenharmony_ci        ++vsctx->out_remap[i];
117bf215546Sopenharmony_ci    }
118bf215546Sopenharmony_ci
119bf215546Sopenharmony_ci    /* Insert the new output. */
120bf215546Sopenharmony_ci    emit_output(ctx, name, index, interp,
121bf215546Sopenharmony_ci                before->Range.First + vsctx->decl_shift);
122bf215546Sopenharmony_ci
123bf215546Sopenharmony_ci    ++vsctx->decl_shift;
124bf215546Sopenharmony_ci}
125bf215546Sopenharmony_ci
126bf215546Sopenharmony_cistatic void insert_output_after(struct tgsi_transform_context *ctx,
127bf215546Sopenharmony_ci                                struct tgsi_full_declaration *after,
128bf215546Sopenharmony_ci                                unsigned name, unsigned index, unsigned interp)
129bf215546Sopenharmony_ci{
130bf215546Sopenharmony_ci    struct vs_transform_context *vsctx = (struct vs_transform_context *)ctx;
131bf215546Sopenharmony_ci    unsigned i;
132bf215546Sopenharmony_ci
133bf215546Sopenharmony_ci    /* Make a place for the new output. */
134bf215546Sopenharmony_ci    for (i = after->Range.First+1; i < ARRAY_SIZE(vsctx->out_remap); i++) {
135bf215546Sopenharmony_ci        ++vsctx->out_remap[i];
136bf215546Sopenharmony_ci    }
137bf215546Sopenharmony_ci
138bf215546Sopenharmony_ci    /* Insert the new output. */
139bf215546Sopenharmony_ci    emit_output(ctx, name, index, interp,
140bf215546Sopenharmony_ci                after->Range.First + 1);
141bf215546Sopenharmony_ci
142bf215546Sopenharmony_ci    ++vsctx->decl_shift;
143bf215546Sopenharmony_ci}
144bf215546Sopenharmony_ci
145bf215546Sopenharmony_cistatic void transform_decl(struct tgsi_transform_context *ctx,
146bf215546Sopenharmony_ci                           struct tgsi_full_declaration *decl)
147bf215546Sopenharmony_ci{
148bf215546Sopenharmony_ci    struct vs_transform_context *vsctx = (struct vs_transform_context *)ctx;
149bf215546Sopenharmony_ci    unsigned i;
150bf215546Sopenharmony_ci
151bf215546Sopenharmony_ci    if (decl->Declaration.File == TGSI_FILE_OUTPUT) {
152bf215546Sopenharmony_ci        switch (decl->Semantic.Name) {
153bf215546Sopenharmony_ci            case TGSI_SEMANTIC_POSITION:
154bf215546Sopenharmony_ci                vsctx->pos_output = decl->Range.First;
155bf215546Sopenharmony_ci                break;
156bf215546Sopenharmony_ci
157bf215546Sopenharmony_ci            case TGSI_SEMANTIC_COLOR:
158bf215546Sopenharmony_ci                assert(decl->Semantic.Index < 2);
159bf215546Sopenharmony_ci
160bf215546Sopenharmony_ci                /* We must rasterize the first color if the second one is
161bf215546Sopenharmony_ci                 * used, otherwise the rasterizer doesn't do the color
162bf215546Sopenharmony_ci                 * selection correctly. Declare it, but don't write to it. */
163bf215546Sopenharmony_ci                if (decl->Semantic.Index == 1 && !vsctx->color_used[0]) {
164bf215546Sopenharmony_ci                    insert_output_before(ctx, decl, TGSI_SEMANTIC_COLOR, 0,
165bf215546Sopenharmony_ci                                         TGSI_INTERPOLATE_LINEAR);
166bf215546Sopenharmony_ci                    vsctx->color_used[0] = TRUE;
167bf215546Sopenharmony_ci                }
168bf215546Sopenharmony_ci                break;
169bf215546Sopenharmony_ci
170bf215546Sopenharmony_ci            case TGSI_SEMANTIC_BCOLOR:
171bf215546Sopenharmony_ci                assert(decl->Semantic.Index < 2);
172bf215546Sopenharmony_ci
173bf215546Sopenharmony_ci                /* We must rasterize all 4 colors if back-face colors are
174bf215546Sopenharmony_ci                 * used, otherwise the rasterizer doesn't do the color
175bf215546Sopenharmony_ci                 * selection correctly. Declare it, but don't write to it. */
176bf215546Sopenharmony_ci                if (!vsctx->color_used[0]) {
177bf215546Sopenharmony_ci                    insert_output_before(ctx, decl, TGSI_SEMANTIC_COLOR, 0,
178bf215546Sopenharmony_ci                                         TGSI_INTERPOLATE_LINEAR);
179bf215546Sopenharmony_ci                    vsctx->color_used[0] = TRUE;
180bf215546Sopenharmony_ci                }
181bf215546Sopenharmony_ci                if (!vsctx->color_used[1]) {
182bf215546Sopenharmony_ci                    insert_output_before(ctx, decl, TGSI_SEMANTIC_COLOR, 1,
183bf215546Sopenharmony_ci                                         TGSI_INTERPOLATE_LINEAR);
184bf215546Sopenharmony_ci                    vsctx->color_used[1] = TRUE;
185bf215546Sopenharmony_ci                }
186bf215546Sopenharmony_ci                if (decl->Semantic.Index == 1 && !vsctx->bcolor_used[0]) {
187bf215546Sopenharmony_ci                    insert_output_before(ctx, decl, TGSI_SEMANTIC_BCOLOR, 0,
188bf215546Sopenharmony_ci                                         TGSI_INTERPOLATE_LINEAR);
189bf215546Sopenharmony_ci                    vsctx->bcolor_used[0] = TRUE;
190bf215546Sopenharmony_ci                }
191bf215546Sopenharmony_ci                break;
192bf215546Sopenharmony_ci
193bf215546Sopenharmony_ci            case TGSI_SEMANTIC_GENERIC:
194bf215546Sopenharmony_ci                vsctx->last_generic = MAX2(vsctx->last_generic, decl->Semantic.Index);
195bf215546Sopenharmony_ci                break;
196bf215546Sopenharmony_ci        }
197bf215546Sopenharmony_ci
198bf215546Sopenharmony_ci        /* Since we're inserting new outputs in between, the following outputs
199bf215546Sopenharmony_ci         * should be moved to the right so that they don't overlap with
200bf215546Sopenharmony_ci         * the newly added ones. */
201bf215546Sopenharmony_ci        decl->Range.First += vsctx->decl_shift;
202bf215546Sopenharmony_ci        decl->Range.Last += vsctx->decl_shift;
203bf215546Sopenharmony_ci
204bf215546Sopenharmony_ci        ++vsctx->num_outputs;
205bf215546Sopenharmony_ci    } else if (decl->Declaration.File == TGSI_FILE_TEMPORARY) {
206bf215546Sopenharmony_ci        for (i = decl->Range.First; i <= decl->Range.Last; i++) {
207bf215546Sopenharmony_ci           vsctx->temp_used[i] = TRUE;
208bf215546Sopenharmony_ci        }
209bf215546Sopenharmony_ci    }
210bf215546Sopenharmony_ci
211bf215546Sopenharmony_ci    ctx->emit_declaration(ctx, decl);
212bf215546Sopenharmony_ci
213bf215546Sopenharmony_ci    /* Insert BCOLOR1 if needed. */
214bf215546Sopenharmony_ci    if (decl->Declaration.File == TGSI_FILE_OUTPUT &&
215bf215546Sopenharmony_ci        decl->Semantic.Name == TGSI_SEMANTIC_BCOLOR &&
216bf215546Sopenharmony_ci        !vsctx->bcolor_used[1]) {
217bf215546Sopenharmony_ci        insert_output_after(ctx, decl, TGSI_SEMANTIC_BCOLOR, 1,
218bf215546Sopenharmony_ci                            TGSI_INTERPOLATE_LINEAR);
219bf215546Sopenharmony_ci    }
220bf215546Sopenharmony_ci}
221bf215546Sopenharmony_ci
222bf215546Sopenharmony_cistatic void transform_inst(struct tgsi_transform_context *ctx,
223bf215546Sopenharmony_ci                           struct tgsi_full_instruction *inst)
224bf215546Sopenharmony_ci{
225bf215546Sopenharmony_ci    struct vs_transform_context *vsctx = (struct vs_transform_context *) ctx;
226bf215546Sopenharmony_ci    struct tgsi_full_instruction new_inst;
227bf215546Sopenharmony_ci    unsigned i;
228bf215546Sopenharmony_ci
229bf215546Sopenharmony_ci    if (!vsctx->first_instruction) {
230bf215546Sopenharmony_ci        vsctx->first_instruction = TRUE;
231bf215546Sopenharmony_ci
232bf215546Sopenharmony_ci        /* Insert the generic output for WPOS. */
233bf215546Sopenharmony_ci        emit_output(ctx, TGSI_SEMANTIC_GENERIC, vsctx->last_generic + 1,
234bf215546Sopenharmony_ci                    TGSI_INTERPOLATE_PERSPECTIVE, vsctx->num_outputs);
235bf215546Sopenharmony_ci
236bf215546Sopenharmony_ci        /* Find a free temp for POSITION. */
237bf215546Sopenharmony_ci        for (i = 0; i < ARRAY_SIZE(vsctx->temp_used); i++) {
238bf215546Sopenharmony_ci            if (!vsctx->temp_used[i]) {
239bf215546Sopenharmony_ci                emit_temp(ctx, i);
240bf215546Sopenharmony_ci                vsctx->pos_temp = i;
241bf215546Sopenharmony_ci                break;
242bf215546Sopenharmony_ci            }
243bf215546Sopenharmony_ci        }
244bf215546Sopenharmony_ci    }
245bf215546Sopenharmony_ci
246bf215546Sopenharmony_ci    if (inst->Instruction.Opcode == TGSI_OPCODE_END) {
247bf215546Sopenharmony_ci        /* MOV OUT[pos_output], TEMP[pos_temp]; */
248bf215546Sopenharmony_ci        new_inst = tgsi_default_full_instruction();
249bf215546Sopenharmony_ci        new_inst.Instruction.Opcode = TGSI_OPCODE_MOV;
250bf215546Sopenharmony_ci        new_inst.Instruction.NumDstRegs = 1;
251bf215546Sopenharmony_ci        new_inst.Dst[0].Register.File = TGSI_FILE_OUTPUT;
252bf215546Sopenharmony_ci        new_inst.Dst[0].Register.Index = vsctx->pos_output;
253bf215546Sopenharmony_ci        new_inst.Dst[0].Register.WriteMask = TGSI_WRITEMASK_XYZW;
254bf215546Sopenharmony_ci        new_inst.Instruction.NumSrcRegs = 1;
255bf215546Sopenharmony_ci        new_inst.Src[0].Register.File = TGSI_FILE_TEMPORARY;
256bf215546Sopenharmony_ci        new_inst.Src[0].Register.Index = vsctx->pos_temp;
257bf215546Sopenharmony_ci        ctx->emit_instruction(ctx, &new_inst);
258bf215546Sopenharmony_ci
259bf215546Sopenharmony_ci        /* MOV OUT[n-1], TEMP[pos_temp]; */
260bf215546Sopenharmony_ci        new_inst = tgsi_default_full_instruction();
261bf215546Sopenharmony_ci        new_inst.Instruction.Opcode = TGSI_OPCODE_MOV;
262bf215546Sopenharmony_ci        new_inst.Instruction.NumDstRegs = 1;
263bf215546Sopenharmony_ci        new_inst.Dst[0].Register.File = TGSI_FILE_OUTPUT;
264bf215546Sopenharmony_ci        new_inst.Dst[0].Register.Index = vsctx->num_outputs - 1;
265bf215546Sopenharmony_ci        new_inst.Dst[0].Register.WriteMask = TGSI_WRITEMASK_XYZW;
266bf215546Sopenharmony_ci        new_inst.Instruction.NumSrcRegs = 1;
267bf215546Sopenharmony_ci        new_inst.Src[0].Register.File = TGSI_FILE_TEMPORARY;
268bf215546Sopenharmony_ci        new_inst.Src[0].Register.Index = vsctx->pos_temp;
269bf215546Sopenharmony_ci        ctx->emit_instruction(ctx, &new_inst);
270bf215546Sopenharmony_ci
271bf215546Sopenharmony_ci        vsctx->end_instruction = TRUE;
272bf215546Sopenharmony_ci    } else {
273bf215546Sopenharmony_ci        /* Not an END instruction. */
274bf215546Sopenharmony_ci        /* Fix writes to outputs. */
275bf215546Sopenharmony_ci        for (i = 0; i < inst->Instruction.NumDstRegs; i++) {
276bf215546Sopenharmony_ci            struct tgsi_full_dst_register *dst = &inst->Dst[i];
277bf215546Sopenharmony_ci            if (dst->Register.File == TGSI_FILE_OUTPUT) {
278bf215546Sopenharmony_ci                if (dst->Register.Index == vsctx->pos_output) {
279bf215546Sopenharmony_ci                    /* Replace writes to OUT[pos_output] with TEMP[pos_temp]. */
280bf215546Sopenharmony_ci                    dst->Register.File = TGSI_FILE_TEMPORARY;
281bf215546Sopenharmony_ci                    dst->Register.Index = vsctx->pos_temp;
282bf215546Sopenharmony_ci                } else {
283bf215546Sopenharmony_ci                    /* Not a position, good...
284bf215546Sopenharmony_ci                     * Since we were changing the indices of output decls,
285bf215546Sopenharmony_ci                     * we must redirect writes into them too. */
286bf215546Sopenharmony_ci                    dst->Register.Index = vsctx->out_remap[dst->Register.Index];
287bf215546Sopenharmony_ci                }
288bf215546Sopenharmony_ci            }
289bf215546Sopenharmony_ci        }
290bf215546Sopenharmony_ci
291bf215546Sopenharmony_ci        /* Inserting 2 instructions before the END opcode moves all following
292bf215546Sopenharmony_ci         * labels by 2. Subroutines are always after the END opcode so
293bf215546Sopenharmony_ci         * they're always moved. */
294bf215546Sopenharmony_ci        if (inst->Instruction.Opcode == TGSI_OPCODE_CAL) {
295bf215546Sopenharmony_ci            inst->Label.Label += 2;
296bf215546Sopenharmony_ci        }
297bf215546Sopenharmony_ci        /* The labels of the following opcodes are moved only after
298bf215546Sopenharmony_ci         * the END opcode. */
299bf215546Sopenharmony_ci        if (vsctx->end_instruction &&
300bf215546Sopenharmony_ci            (inst->Instruction.Opcode == TGSI_OPCODE_IF ||
301bf215546Sopenharmony_ci             inst->Instruction.Opcode == TGSI_OPCODE_ELSE ||
302bf215546Sopenharmony_ci             inst->Instruction.Opcode == TGSI_OPCODE_BGNLOOP ||
303bf215546Sopenharmony_ci             inst->Instruction.Opcode == TGSI_OPCODE_ENDLOOP)) {
304bf215546Sopenharmony_ci            inst->Label.Label += 2;
305bf215546Sopenharmony_ci        }
306bf215546Sopenharmony_ci    }
307bf215546Sopenharmony_ci
308bf215546Sopenharmony_ci    ctx->emit_instruction(ctx, inst);
309bf215546Sopenharmony_ci}
310bf215546Sopenharmony_ci
311bf215546Sopenharmony_civoid r300_draw_init_vertex_shader(struct r300_context *r300,
312bf215546Sopenharmony_ci                                  struct r300_vertex_shader *vs)
313bf215546Sopenharmony_ci{
314bf215546Sopenharmony_ci    struct draw_context *draw = r300->draw;
315bf215546Sopenharmony_ci    struct tgsi_shader_info info;
316bf215546Sopenharmony_ci    struct vs_transform_context transform;
317bf215546Sopenharmony_ci    const uint newLen = tgsi_num_tokens(vs->state.tokens) + 100;
318bf215546Sopenharmony_ci    struct pipe_shader_state new_vs = {
319bf215546Sopenharmony_ci        .type = PIPE_SHADER_IR_TGSI,
320bf215546Sopenharmony_ci        .tokens = tgsi_alloc_tokens(newLen)
321bf215546Sopenharmony_ci    };
322bf215546Sopenharmony_ci    unsigned i;
323bf215546Sopenharmony_ci
324bf215546Sopenharmony_ci    tgsi_scan_shader(vs->state.tokens, &info);
325bf215546Sopenharmony_ci
326bf215546Sopenharmony_ci    memset(&transform, 0, sizeof(transform));
327bf215546Sopenharmony_ci    for (i = 0; i < ARRAY_SIZE(transform.out_remap); i++) {
328bf215546Sopenharmony_ci        transform.out_remap[i] = i;
329bf215546Sopenharmony_ci    }
330bf215546Sopenharmony_ci    transform.last_generic = -1;
331bf215546Sopenharmony_ci    transform.base.transform_instruction = transform_inst;
332bf215546Sopenharmony_ci    transform.base.transform_declaration = transform_decl;
333bf215546Sopenharmony_ci
334bf215546Sopenharmony_ci    for (i = 0; i < info.num_outputs; i++) {
335bf215546Sopenharmony_ci        unsigned index = info.output_semantic_index[i];
336bf215546Sopenharmony_ci
337bf215546Sopenharmony_ci        switch (info.output_semantic_name[i]) {
338bf215546Sopenharmony_ci            case TGSI_SEMANTIC_COLOR:
339bf215546Sopenharmony_ci                assert(index < 2);
340bf215546Sopenharmony_ci                transform.color_used[index] = TRUE;
341bf215546Sopenharmony_ci                break;
342bf215546Sopenharmony_ci
343bf215546Sopenharmony_ci            case TGSI_SEMANTIC_BCOLOR:
344bf215546Sopenharmony_ci                assert(index < 2);
345bf215546Sopenharmony_ci                transform.bcolor_used[index] = TRUE;
346bf215546Sopenharmony_ci                break;
347bf215546Sopenharmony_ci        }
348bf215546Sopenharmony_ci    }
349bf215546Sopenharmony_ci
350bf215546Sopenharmony_ci    new_vs.tokens = tgsi_transform_shader(vs->state.tokens, newLen, &transform.base);
351bf215546Sopenharmony_ci    if (!new_vs.tokens)
352bf215546Sopenharmony_ci        return;
353bf215546Sopenharmony_ci
354bf215546Sopenharmony_ci#if 0
355bf215546Sopenharmony_ci    printf("----------------------------------------------\norig shader:\n");
356bf215546Sopenharmony_ci    tgsi_dump(vs->state.tokens, 0);
357bf215546Sopenharmony_ci    printf("----------------------------------------------\nnew shader:\n");
358bf215546Sopenharmony_ci    tgsi_dump(new_vs.tokens, 0);
359bf215546Sopenharmony_ci    printf("----------------------------------------------\n");
360bf215546Sopenharmony_ci#endif
361bf215546Sopenharmony_ci
362bf215546Sopenharmony_ci    /* Free old tokens. */
363bf215546Sopenharmony_ci    FREE((void*)vs->state.tokens);
364bf215546Sopenharmony_ci
365bf215546Sopenharmony_ci    vs->draw_vs = draw_create_vertex_shader(draw, &new_vs);
366bf215546Sopenharmony_ci
367bf215546Sopenharmony_ci    /* Instead of duplicating and freeing the tokens, copy the pointer directly. */
368bf215546Sopenharmony_ci    vs->state.tokens = new_vs.tokens;
369bf215546Sopenharmony_ci
370bf215546Sopenharmony_ci    /* Init the VS output table for the rasterizer. */
371bf215546Sopenharmony_ci    r300_init_vs_outputs(r300, vs);
372bf215546Sopenharmony_ci
373bf215546Sopenharmony_ci    /* Make the last generic be WPOS. */
374bf215546Sopenharmony_ci    vs->shader->outputs.wpos = vs->shader->outputs.generic[transform.last_generic + 1];
375bf215546Sopenharmony_ci    vs->shader->outputs.generic[transform.last_generic + 1] = ATTR_UNUSED;
376bf215546Sopenharmony_ci}
377