--- /dev/null
+/**************************************************************************
+ *
+ * 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;
+}