r300g/swtcl: fix secondary color and back-face color outputs
authorMarek Olšák <maraeo@gmail.com>
Thu, 27 May 2010 17:11:56 +0000 (19:11 +0200)
committerMarek Olšák <maraeo@gmail.com>
Thu, 27 May 2010 18:56:32 +0000 (20:56 +0200)
These piglit tests have been fixed:
- bgra-sec-color-pointer
- glsl-routing

See comments at the beginning of r300_vs_draw.c

WPOS is implemented too but it doesn't work yet. I'm still working on it.

src/gallium/drivers/r300/Makefile
src/gallium/drivers/r300/SConscript
src/gallium/drivers/r300/r300_state.c
src/gallium/drivers/r300/r300_state_derived.c
src/gallium/drivers/r300/r300_vs.h
src/gallium/drivers/r300/r300_vs_draw.c [new file with mode: 0644]

index d3cd6bef96e6935db36a8a0eff0d3ff5ffd2e082..6bb82e5ed0880f0826cac548283635c60181117c 100644 (file)
@@ -21,6 +21,7 @@ C_SOURCES = \
        r300_state_derived.c \
        r300_state_invariant.c \
        r300_vs.c \
+       r300_vs_draw.c \
        r300_texture.c \
        r300_tgsi_to_rc.c \
        r300_transfer.c
index 3921085d76a22e60fe9403be513cf30c6fb2ef26..eb3e1d365e975572ee793b05d2adbea2477a48b5 100644 (file)
@@ -31,6 +31,7 @@ r300 = env.ConvenienceLibrary(
         'r300_state_derived.c',
         'r300_state_invariant.c',
         'r300_vs.c',
+        'r300_vs_draw.c',
         'r300_texture.c',
         'r300_tgsi_to_rc.c',
         'r300_transfer.c',
index 80346e0c79152040622efdc0f594d97fa372adc2..d7657218270b3cf92d526d77436802b5024012e7 100644 (file)
@@ -1412,12 +1412,11 @@ static void* r300_create_vs_state(struct pipe_context* pipe,
     vs->state = *shader;
     vs->state.tokens = tgsi_dup_tokens(shader->tokens);
 
-    r300_init_vs_outputs(vs);
-
     if (r300->screen->caps.has_tcl) {
+        r300_init_vs_outputs(vs);
         r300_translate_vertex_shader(r300, vs);
     } else {
-        vs->draw_vs = draw_create_vertex_shader(r300->draw, shader);
+        r300_draw_init_vertex_shader(r300->draw, vs);
     }
 
     return vs;
index 7583862a1a4baed83a9033fdef0a7744b3231ad8..20f24e8b04b44570ef174d49a6d8530fd5013f3f 100644 (file)
@@ -90,7 +90,13 @@ static void r300_draw_emit_all_attribs(struct r300_context* r300)
         }
     }
 
-    /* XXX Back-face colors. */
+    /* Back-face colors. */
+    for (i = 0; i < ATTR_COLOR_COUNT; i++) {
+        if (vs_outputs->bcolor[i] != ATTR_UNUSED) {
+            r300_draw_emit_attrib(r300, EMIT_4F, INTERP_LINEAR,
+                                  vs_outputs->bcolor[i]);
+        }
+    }
 
     /* Texture coordinates. */
     /* Only 8 generic vertex attributes can be used. If there are more,
@@ -110,6 +116,14 @@ static void r300_draw_emit_all_attribs(struct r300_context* r300)
                               vs_outputs->fog);
         gen_count++;
     }
+
+    /* WPOS. */
+    if (r300_fs(r300)->shader->inputs.wpos != ATTR_UNUSED && gen_count < 8) {
+        DBG(r300, DBG_DRAW, "draw_emit_attrib: WPOS, index: %i\n",
+            vs_outputs->wpos);
+        r300_draw_emit_attrib(r300, EMIT_4F, INTERP_PERSPECTIVE,
+                              vs_outputs->wpos);
+    }
 }
 
 /* Update the PSC tables for SW TCL, using Draw. */
@@ -129,7 +143,7 @@ static void r300_swtcl_vertex_psc(struct r300_context *r300)
     attrib_count = vinfo->num_attribs;
     DBG(r300, DBG_DRAW, "r300: attrib count: %d\n", attrib_count);
     for (i = 0; i < attrib_count; i++) {
-        DBG(r300, DBG_DRAW, "r300: attrib: offset %d, interp %d, size %d,"
+        DBG(r300, DBG_DRAW, "r300: attrib: index %d, interp %d, emit %d,"
                " vs_output_tab %d\n", vinfo->attrib[i].src_index,
                vinfo->attrib[i].interp_mode, vinfo->attrib[i].emit,
                vs_output_tab[i]);
index 31890d78caf076b501e4f6564bbc07624ffb3528..170de6c79dbbbc0ddcbef18b92d3a3764746c9fd 100644 (file)
@@ -60,4 +60,8 @@ void r300_init_vs_outputs(struct r300_vertex_shader *vs);
 
 void r300_translate_vertex_shader(struct r300_context *r300,
                                   struct r300_vertex_shader *vs);
+
+void r300_draw_init_vertex_shader(struct draw_context *draw,
+                                  struct r300_vertex_shader *vs);
+
 #endif /* R300_VS_H */
diff --git a/src/gallium/drivers/r300/r300_vs_draw.c b/src/gallium/drivers/r300/r300_vs_draw.c
new file mode 100644 (file)
index 0000000..5858492
--- /dev/null
@@ -0,0 +1,358 @@
+/**************************************************************************
+ * 
+ * Copyright 2009 Marek Olšák <maraeo@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * 
+ **************************************************************************/
+
+/* This file contains the vertex shader tranformations for SW TCL needed
+ * to overcome the limitations of the r300 rasterizer.
+ *
+ * Transformations:
+ * 1) If the secondary color output is present, the primary color must be
+ *    inserted before it.
+ * 2) If any back-face color output is present, there must be all 4 color
+ *    outputs and missing ones must be inserted.
+ * 3) Insert a trailing texcoord output containing a copy of POS, for WPOS.
+ *
+ * I know this code is cumbersome, but I don't know of any nicer way
+ * of transforming TGSI shaders. ~ M.
+ */
+
+#include "r300_vs.h"
+
+#include <stdio.h>
+
+#include "tgsi/tgsi_transform.h"
+#include "tgsi/tgsi_dump.h"
+
+#include "draw/draw_context.h"
+
+struct vs_transform_context {
+    struct tgsi_transform_context base;
+
+    boolean color_used[2];
+    boolean bcolor_used[2];
+    boolean temp_used[128];
+
+    /* Index of the pos output, typically 0. */
+    unsigned pos_output;
+    /* Index of the pos temp where all writes of pos are redirected to. */
+    unsigned pos_temp;
+    /* The index of the last generic output, after which we insert a new
+     * output for WPOS. */
+    int last_generic;
+
+    unsigned num_outputs;
+    /* Used to shift output decl. indices when inserting new ones. */
+    unsigned decl_shift;
+    /* Used to remap writes to output decls if their indices changed. */
+    unsigned out_remap[32];
+
+    /* First instruction processed? */
+    boolean first_instruction;
+    /* End instruction processed? */
+    boolean end_instruction;
+};
+
+static void emit_temp(struct tgsi_transform_context *ctx, unsigned reg)
+{
+    struct tgsi_full_declaration decl;
+
+    decl = tgsi_default_full_declaration();
+    decl.Declaration.File = TGSI_FILE_TEMPORARY;
+    decl.Range.First = decl.Range.Last = reg;
+    ctx->emit_declaration(ctx, &decl);
+}
+
+static void emit_output(struct tgsi_transform_context *ctx,
+                        unsigned name, unsigned index, unsigned interp,
+                        unsigned reg)
+{
+    struct vs_transform_context *vsctx = (struct vs_transform_context *)ctx;
+    struct tgsi_full_declaration decl;
+
+    decl = tgsi_default_full_declaration();
+    decl.Declaration.File = TGSI_FILE_OUTPUT;
+    decl.Declaration.Interpolate = interp;
+    decl.Declaration.Semantic = TRUE;
+    decl.Semantic.Name = name;
+    decl.Semantic.Index = index;
+    decl.Range.First = decl.Range.Last = reg;
+    ctx->emit_declaration(ctx, &decl);
+    ++vsctx->num_outputs;
+}
+
+static void insert_output(struct tgsi_transform_context *ctx,
+                          struct tgsi_full_declaration *before,
+                          unsigned name, unsigned index, unsigned interp)
+{
+    struct vs_transform_context *vsctx = (struct vs_transform_context *)ctx;
+    unsigned i;
+
+    /* Make a place for the new output. */
+    for (i = before->Range.First; i < Elements(vsctx->out_remap); i++) {
+        ++vsctx->out_remap[i];
+    }
+
+    /* Insert the new output. */
+    emit_output(ctx, name, index, interp, before->Range.First);
+
+    ++vsctx->decl_shift;
+}
+
+static void insert_trailing_bcolor(struct tgsi_transform_context *ctx,
+                                   struct tgsi_full_declaration *before)
+{
+    struct vs_transform_context *vsctx = (struct vs_transform_context *)ctx;
+
+    /* If BCOLOR0 is used, make sure BCOLOR1 is present too. Otherwise
+     * the rasterizer doesn't do the color selection correctly. */
+    if (vsctx->bcolor_used[0] && !vsctx->bcolor_used[1]) {
+        if (before) {
+            insert_output(ctx, before, TGSI_SEMANTIC_BCOLOR, 1,
+                          TGSI_INTERPOLATE_LINEAR);
+        } else {
+            emit_output(ctx, TGSI_SEMANTIC_BCOLOR, 1,
+                        TGSI_INTERPOLATE_LINEAR, vsctx->num_outputs);
+        }
+        vsctx->bcolor_used[1] = TRUE;
+    }
+}
+
+static void transform_decl(struct tgsi_transform_context *ctx,
+                           struct tgsi_full_declaration *decl)
+{
+    struct vs_transform_context *vsctx = (struct vs_transform_context *)ctx;
+    unsigned i;
+
+    if (decl->Declaration.File == TGSI_FILE_OUTPUT) {
+        switch (decl->Semantic.Name) {
+            case TGSI_SEMANTIC_POSITION:
+                vsctx->pos_output = decl->Range.First;
+                break;
+
+            case TGSI_SEMANTIC_COLOR:
+                assert(decl->Semantic.Index < 2);
+                vsctx->color_used[decl->Semantic.Index] = TRUE;
+
+                /* We must rasterize the first color if the second one is
+                 * used, otherwise the rasterizer doesn't do the color
+                 * selection correctly. Declare it, but don't write to it. */
+                if (decl->Semantic.Index == 1 && !vsctx->color_used[0]) {
+                    insert_output(ctx, decl, TGSI_SEMANTIC_COLOR, 0,
+                                  TGSI_INTERPOLATE_LINEAR);
+                    vsctx->color_used[0] = TRUE;
+                }
+                break;
+
+            case TGSI_SEMANTIC_BCOLOR:
+                assert(decl->Semantic.Index < 2);
+                vsctx->bcolor_used[decl->Semantic.Index] = TRUE;
+
+                /* We must rasterize all 4 colors if back-face colors are
+                 * used, otherwise the rasterizer doesn't do the color
+                 * selection correctly. Declare it, but don't write to it. */
+                if (!vsctx->color_used[0]) {
+                    insert_output(ctx, decl, TGSI_SEMANTIC_COLOR, 0,
+                                  TGSI_INTERPOLATE_LINEAR);
+                    vsctx->color_used[0] = TRUE;
+                }
+                if (!vsctx->color_used[1]) {
+                    insert_output(ctx, decl, TGSI_SEMANTIC_COLOR, 1,
+                                  TGSI_INTERPOLATE_LINEAR);
+                    vsctx->color_used[1] = TRUE;
+                }
+                if (decl->Semantic.Index == 1 && !vsctx->bcolor_used[0]) {
+                    insert_output(ctx, decl, TGSI_SEMANTIC_BCOLOR, 0,
+                                  TGSI_INTERPOLATE_LINEAR);
+                    vsctx->color_used[2] = TRUE;
+                }
+                /* One more case is handled in insert_trailing_bcolor. */
+                break;
+
+            case TGSI_SEMANTIC_GENERIC:
+                vsctx->last_generic = MAX2(vsctx->last_generic, decl->Semantic.Index);
+                break;
+        }
+
+        if (decl->Semantic.Name != TGSI_SEMANTIC_BCOLOR) {
+            /* Insert it as soon as possible. */
+            insert_trailing_bcolor(ctx, decl);
+        }
+
+        /* Since we're inserting new outputs in between, the following outputs
+         * should be moved to the right so that they don't overlap with
+         * the newly added ones. */
+        decl->Range.First += vsctx->decl_shift;
+        decl->Range.Last += vsctx->decl_shift;
+
+        ++vsctx->num_outputs;
+    } else if (decl->Declaration.File == TGSI_FILE_TEMPORARY) {
+        for (i = decl->Range.First; i <= decl->Range.Last; i++) {
+           vsctx->temp_used[i] = TRUE;
+        }
+    }
+
+    ctx->emit_declaration(ctx, decl);
+}
+
+static void transform_inst(struct tgsi_transform_context *ctx,
+                           struct tgsi_full_instruction *inst)
+{
+    struct vs_transform_context *vsctx = (struct vs_transform_context *) ctx;
+    struct tgsi_full_instruction new_inst;
+    unsigned i;
+
+    if (!vsctx->first_instruction) {
+        vsctx->first_instruction = TRUE;
+
+        /* The trailing BCOLOR should be inserted before the code
+         * if it hasn't already been done so. */
+        insert_trailing_bcolor(ctx, NULL);
+
+        /* Insert the generic output for WPOS. */
+        emit_output(ctx, TGSI_SEMANTIC_GENERIC, vsctx->last_generic + 1,
+                    TGSI_INTERPOLATE_PERSPECTIVE, vsctx->num_outputs);
+
+        /* Find a free temp for POSITION. */
+        for (i = 0; i < Elements(vsctx->temp_used); i++) {
+            if (!vsctx->temp_used[i]) {
+                emit_temp(ctx, i);
+                vsctx->pos_temp = i;
+                break;
+            }
+        }
+    }
+
+    if (inst->Instruction.Opcode == TGSI_OPCODE_END) {
+        /* MOV OUT[pos_output], TEMP[pos_temp]; */
+        new_inst = tgsi_default_full_instruction();
+        new_inst.Instruction.Opcode = TGSI_OPCODE_MOV;
+        new_inst.Instruction.NumDstRegs = 1;
+        new_inst.Dst[0].Register.File = TGSI_FILE_OUTPUT;
+        new_inst.Dst[0].Register.Index = vsctx->pos_output;
+        new_inst.Dst[0].Register.WriteMask = TGSI_WRITEMASK_XYZW;
+        new_inst.Instruction.NumSrcRegs = 1;
+        new_inst.Src[0].Register.File = TGSI_FILE_TEMPORARY;
+        new_inst.Src[0].Register.Index = vsctx->pos_temp;
+        ctx->emit_instruction(ctx, &new_inst);
+
+        /* MOV OUT[n-1], TEMP[pos_temp]; */
+        new_inst = tgsi_default_full_instruction();
+        new_inst.Instruction.Opcode = TGSI_OPCODE_MOV;
+        new_inst.Instruction.NumDstRegs = 1;
+        new_inst.Dst[0].Register.File = TGSI_FILE_OUTPUT;
+        new_inst.Dst[0].Register.Index = vsctx->num_outputs - 1;
+        new_inst.Dst[0].Register.WriteMask = TGSI_WRITEMASK_XYZW;
+        new_inst.Instruction.NumSrcRegs = 1;
+        new_inst.Src[0].Register.File = TGSI_FILE_TEMPORARY;
+        new_inst.Src[0].Register.Index = vsctx->pos_temp;
+        ctx->emit_instruction(ctx, &new_inst);
+
+        vsctx->end_instruction = TRUE;
+    } else {
+        /* Not an END instruction. */
+        /* Fix writes to outputs. */
+        for (i = 0; i < inst->Instruction.NumDstRegs; i++) {
+            struct tgsi_full_dst_register *dst = &inst->Dst[i];
+            if (dst->Register.File == TGSI_FILE_OUTPUT) {
+                if (dst->Register.Index == vsctx->pos_output) {
+                    /* Replace writes to OUT[pos_output] with TEMP[pos_temp]. */
+                    dst->Register.File = TGSI_FILE_TEMPORARY;
+                    dst->Register.Index = vsctx->pos_temp;
+                } else {
+                    /* Not a position, good...
+                     * Since we were changing the indices of output decls,
+                     * we must redirect writes into them too. */
+                    dst->Register.Index = vsctx->out_remap[dst->Register.Index];
+                }
+            }
+        }
+
+        /* Inserting 2 instructions before the END opcode moves all following
+         * labels by 2. Subroutines are always after the END opcode so
+         * they're always moved. */
+        if (inst->Instruction.Opcode == TGSI_OPCODE_CAL) {
+            inst->Label.Label += 2;
+        }
+        /* The labels of the following opcodes are moved only after
+         * the END opcode. */
+        if (vsctx->end_instruction &&
+            (inst->Instruction.Opcode == TGSI_OPCODE_IF ||
+             inst->Instruction.Opcode == TGSI_OPCODE_ELSE ||
+             inst->Instruction.Opcode == TGSI_OPCODE_BGNLOOP ||
+             inst->Instruction.Opcode == TGSI_OPCODE_ENDLOOP)) {
+            inst->Label.Label += 2;
+        }
+    }
+
+    ctx->emit_instruction(ctx, inst);
+}
+
+void r300_draw_init_vertex_shader(struct draw_context *draw,
+                                  struct r300_vertex_shader *vs)
+{
+    struct pipe_shader_state new_vs;
+    struct vs_transform_context transform;
+    const uint newLen = tgsi_num_tokens(vs->state.tokens) + 100 /* XXX */;
+    unsigned i;
+
+    new_vs.tokens = tgsi_alloc_tokens(newLen);
+    if (new_vs.tokens == NULL)
+        return;
+
+    memset(&transform, 0, sizeof(transform));
+    for (i = 0; i < Elements(transform.out_remap); i++) {
+        transform.out_remap[i] = i;
+    }
+    transform.last_generic = -1;
+    transform.base.transform_instruction = transform_inst;
+    transform.base.transform_declaration = transform_decl;
+
+    tgsi_transform_shader(vs->state.tokens,
+                          (struct tgsi_token*)new_vs.tokens,
+                          newLen, &transform.base);
+
+#if 0
+    printf("----------------------------------------------\norig shader:\n");
+    tgsi_dump(vs->state.tokens, 0);
+    printf("----------------------------------------------\nnew shader:\n");
+    tgsi_dump(new_vs.tokens, 0);
+    printf("----------------------------------------------\n");
+#endif
+
+    /* Free old tokens. */
+    FREE((void*)vs->state.tokens);
+
+    vs->draw_vs = draw_create_vertex_shader(draw, &new_vs);
+
+    /* Instead of duplicating and freeing the tokens, copy the pointer directly. */
+    vs->state.tokens = new_vs.tokens;
+
+    /* Init the VS output table for the rasterizer. */
+    r300_init_vs_outputs(vs);
+
+    /**/
+    vs->outputs.generic[transform.last_generic + 1] = ATTR_UNUSED;
+    vs->outputs.wpos -= 1;
+}