[Mesa-dev] [PATCH] tgsi: lowering support for alpha test

Rob Clark robdclark at gmail.com
Fri Dec 19 11:11:39 PST 2014


From: Rob Clark <robclark at freedesktop.org>

This emulates alpha-test with a compare + KILL_IF.  The alpha-ref value
is passed to the shader via constant tagged with new ALPHAREF semantic.
For example:

  FRAG
  PROPERTY FS_COLOR0_WRITES_ALL_CBUFS 1
  DCL IN[0], COLOR, COLOR
  DCL OUT[0], COLOR
    0: MOV OUT[0], IN[0]
    1: END

with alpha-func PIPE_FUNC_LESS, becomes:

  FRAG
  PROPERTY FS_COLOR0_WRITES_ALL_CBUFS 1
  DCL IN[0], COLOR, COLOR
  DCL OUT[0], COLOR
  IMM[0] FLT32 {    0.0000,     1.0000,   128.0000,     0.0000}
  DCL TEMP[0]
  DCL TEMP[1]
  DCL CONST[0], ALPHAREF
    0: MOV TEMP[1], IN[0]
    1: SLT TEMP[0].x, TEMP[1].wwww, CONST[0].xxxx
    2: KILL_IF TEMP[0].xxxx
    3: MOV OUT[0], TEMP[1]
    4: END

Signed-off-by: Rob Clark <robclark at freedesktop.org>
---
 src/gallium/auxiliary/tgsi/tgsi_lowering.c | 170 ++++++++++++++++++++++++++++-
 src/gallium/auxiliary/tgsi/tgsi_lowering.h |   7 ++
 src/gallium/auxiliary/tgsi/tgsi_strings.c  |   1 +
 src/gallium/include/pipe/p_shader_tokens.h |   3 +-
 4 files changed, 177 insertions(+), 4 deletions(-)

diff --git a/src/gallium/auxiliary/tgsi/tgsi_lowering.c b/src/gallium/auxiliary/tgsi/tgsi_lowering.c
index dee6c41..4f1da29 100644
--- a/src/gallium/auxiliary/tgsi/tgsi_lowering.c
+++ b/src/gallium/auxiliary/tgsi/tgsi_lowering.c
@@ -37,15 +37,22 @@ struct tgsi_lowering_context {
    struct tgsi_transform_context base;
    const struct tgsi_lowering_config *config;
    struct tgsi_shader_info *info;
+
+   bool alpha_test;
+   unsigned colorout;
+   unsigned colortmp;   /* tmp register to hold original color out */
+   unsigned alpharef;   /* const slot used to hold alpharef value */
+
    unsigned two_side_colors;
    unsigned two_side_idx[PIPE_MAX_SHADER_INPUTS];
    unsigned color_base;  /* base register for chosen COLOR/BCOLOR's */
    int face_idx;
+
    unsigned numtmp;
    struct {
       struct tgsi_full_src_register src;
       struct tgsi_full_dst_register dst;
-   } tmp[2];
+   } tmp[3];
 #define A 0
 #define B 1
    struct tgsi_full_src_register imm;
@@ -1146,6 +1153,128 @@ transform_samp(struct tgsi_transform_context *tctx,
    return 0;
 }
 
+/* Alpha-test emulation:
+ * Redirects writes to COLOR output to a temporary and appends
+ * extra instructions to do conditional kill based on output
+ * alpha value
+ */
+#define ALPHATEST_GROW (                        \
+      3 +         /* CONST[], ALPHAREF */       \
+      NINST(2) +  /* SEQ/SNE/SLE/SGT/SLT */     \
+      NINST(1) +  /* KILL_IF */                 \
+      NINST(1)    /* MOV */                     \
+      )
+#define ALPHATEST_TMP  1
+
+static void
+emit_alphatest_decls(struct tgsi_transform_context *tctx)
+{
+   struct tgsi_lowering_context *ctx = tgsi_lowering_context(tctx);
+   struct tgsi_full_declaration decl;
+
+   /* NOTE: the temporaries we need are handled in emit_decls() */
+
+   decl = tgsi_default_full_declaration();
+   decl.Declaration.File = TGSI_FILE_CONSTANT;
+   decl.Declaration.Semantic = true;
+   decl.Range.First = decl.Range.Last = ctx->alpharef;
+   decl.Semantic.Name = TGSI_SEMANTIC_ALPHAREF;
+   decl.Semantic.Index = 0;
+   tctx->emit_declaration(tctx, &decl);
+}
+
+static void
+emit_alphatest_instrs(struct tgsi_transform_context *tctx)
+{
+   struct tgsi_lowering_context *ctx = tgsi_lowering_context(tctx);
+   struct tgsi_full_instruction new_inst;
+   int c = ctx->colortmp;
+
+   if (ctx->config->alpha_func == PIPE_FUNC_NEVER) {
+      /* KILL */
+      new_inst = tgsi_default_full_instruction();
+      new_inst.Instruction.Opcode = TGSI_OPCODE_KILL;
+      new_inst.Instruction.NumDstRegs = 0;
+      new_inst.Instruction.NumSrcRegs = 0;
+      tctx->emit_instruction(tctx, &new_inst);
+   } else {
+      unsigned opcode;
+
+      switch (ctx->config->alpha_func) {
+      case PIPE_FUNC_LESS:
+         opcode = TGSI_OPCODE_SLT;
+         break;
+      case PIPE_FUNC_EQUAL:
+         opcode = TGSI_OPCODE_SEQ;
+         break;
+      case PIPE_FUNC_LEQUAL:
+         opcode = TGSI_OPCODE_SLE;
+         break;
+      case PIPE_FUNC_GREATER:
+         opcode = TGSI_OPCODE_SGT;
+         break;
+      case PIPE_FUNC_NOTEQUAL:
+         opcode = TGSI_OPCODE_SNE;
+         break;
+      case PIPE_FUNC_GEQUAL:
+         opcode = TGSI_OPCODE_SGE;
+         break;
+      default:
+         assert(0);
+         return;
+      }
+
+      /* SEQ/SNE/SGE/SLE/SGT/SLT tmpA.x, tmpColor.w, alpharef.x */
+      new_inst = tgsi_default_full_instruction();
+      new_inst.Instruction.Opcode = opcode;
+      new_inst.Instruction.NumDstRegs = 1;
+      reg_dst(&new_inst.Dst[0], &ctx->tmp[A].dst, TGSI_WRITEMASK_X);
+      new_inst.Instruction.NumSrcRegs = 2;
+      reg_src(&new_inst.Src[0], &ctx->tmp[c].src, SWIZ(W, W, W, W));
+      new_inst.Src[1].Register.File = TGSI_FILE_CONSTANT;
+      new_inst.Src[1].Register.Index = ctx->alpharef;
+      new_inst.Src[1].Register.SwizzleX = TGSI_SWIZZLE_X;
+      new_inst.Src[1].Register.SwizzleY = TGSI_SWIZZLE_X;
+      new_inst.Src[1].Register.SwizzleZ = TGSI_SWIZZLE_X;
+      new_inst.Src[1].Register.SwizzleW = TGSI_SWIZZLE_X;
+      tctx->emit_instruction(tctx, &new_inst);
+
+      /* KILL_IF tmpA.x */
+      new_inst = tgsi_default_full_instruction();
+      new_inst.Instruction.Opcode = TGSI_OPCODE_KILL_IF;
+      new_inst.Instruction.NumDstRegs = 0;
+      new_inst.Instruction.NumSrcRegs = 1;
+      reg_src(&new_inst.Src[0], &ctx->tmp[A].src, SWIZ(X, _, _, _));
+      tctx->emit_instruction(tctx, &new_inst);
+   }
+
+   /* MOV OUT[color], tmpColor */
+   /* (would be nice if we could create_mov() here..) */
+   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 = ctx->colorout;
+   new_inst.Dst[0].Register.WriteMask = TGSI_WRITEMASK_XYZW;
+   new_inst.Instruction.NumSrcRegs = 1;
+   reg_src(&new_inst.Src[0], &ctx->tmp[c].src, SWIZ(X, Y, Z, W));
+   tctx->emit_instruction(tctx, &new_inst);
+}
+
+static void
+rename_color_outputs(struct tgsi_lowering_context *ctx,
+                    struct tgsi_full_instruction *inst)
+{
+   unsigned i;
+   for (i = 0; i < inst->Instruction.NumDstRegs; i++) {
+      struct tgsi_dst_register *dst = &inst->Dst[i].Register;
+      if ((dst->File == TGSI_FILE_OUTPUT) &&
+          (dst->Index == ctx->colorout)) {
+         *dst = ctx->tmp[ctx->colortmp].dst.Register;
+      }
+   }
+}
+
 /* Two-sided color emulation:
  * For each COLOR input, create a corresponding BCOLOR input, plus
  * CMP instruction to select front or back color based on FACE
@@ -1288,6 +1417,9 @@ emit_decls(struct tgsi_transform_context *tctx)
 
    if (ctx->two_side_colors)
       emit_twoside(tctx);
+
+   if (ctx->alpha_test)
+      emit_alphatest_decls(tctx);
 }
 
 static void
@@ -1307,7 +1439,6 @@ rename_color_inputs(struct tgsi_lowering_context *ctx,
          }
       }
    }
-
 }
 
 static void
@@ -1327,6 +1458,12 @@ transform_instr(struct tgsi_transform_context *tctx,
    if (ctx->two_side_colors)
       rename_color_inputs(ctx, inst);
 
+   /* if emulating alpha-test, we need to re-write some dst
+    * registers:
+    */
+   if (ctx->alpha_test)
+      rename_color_outputs(ctx, inst);
+
    switch (inst->Instruction.Opcode) {
    case TGSI_OPCODE_DST:
       if (!ctx->config->lower_DST)
@@ -1406,6 +1543,10 @@ transform_instr(struct tgsi_transform_context *tctx,
       if (transform_samp(tctx, inst))
          goto skip;
       break;
+   case TGSI_OPCODE_END:
+      if (ctx->alpha_test)
+         emit_alphatest_instrs(tctx);
+      goto skip;   /* emit the actual END instruction itself */
    default:
    skip:
       tctx->emit_instruction(tctx, inst);
@@ -1452,6 +1593,20 @@ tgsi_transform_lowering(const struct tgsi_lowering_config *config,
       }
    }
 
+   if ((info->processor == TGSI_PROCESSOR_FRAGMENT) &&
+       config->lower_alpha_test &&
+       (config->alpha_func != PIPE_FUNC_ALWAYS)) {
+      int i;
+      ctx.alpha_test = true;
+      for (i = 0; i < info->file_max[TGSI_FILE_OUTPUT]; i++) {
+         /* TODO not sure what to do in case of MRT */
+         if (info->output_semantic_name[i] == TGSI_SEMANTIC_COLOR) {
+             ctx.colorout = i;
+             break;
+         }
+      }
+   }
+
    ctx.saturate = config->saturate_r | config->saturate_s | config->saturate_t;
 
 #define OPCS(x) ((config->lower_ ## x) ? info->opcode_count[TGSI_OPCODE_ ## x] : 0)
@@ -1472,7 +1627,8 @@ tgsi_transform_lowering(const struct tgsi_lowering_config *config,
          OPCS(DP2A) ||
          OPCS(TXP) ||
          ctx.two_side_colors ||
-         ctx.saturate))
+         ctx.saturate ||
+         ctx.alpha_test))
       return NULL;
 
 #if 0  /* debug */
@@ -1555,6 +1711,14 @@ tgsi_transform_lowering(const struct tgsi_lowering_config *config,
       numtmp = MAX2(numtmp, SAMP_TMP);
    }
 
+   if (ctx.alpha_test) {
+      newlen += ALPHATEST_GROW;
+      numtmp = MAX2(numtmp, ALPHATEST_TMP);
+      /* and one more tmp to hold temporary color output: */
+      ctx.colortmp = numtmp++;
+      ctx.alpharef = info->file_max[TGSI_FILE_CONSTANT] + 1;
+   }
+
    /* specifically don't include two_side_colors temps in the count: */
    ctx.numtmp = numtmp;
 
diff --git a/src/gallium/auxiliary/tgsi/tgsi_lowering.h b/src/gallium/auxiliary/tgsi/tgsi_lowering.h
index 52c204f..3a95c82 100644
--- a/src/gallium/auxiliary/tgsi/tgsi_lowering.h
+++ b/src/gallium/auxiliary/tgsi/tgsi_lowering.h
@@ -69,6 +69,13 @@ struct tgsi_lowering_config
    unsigned lower_DP2:1;
    unsigned lower_DP2A:1;
 
+   /* If lowering alpha-test, a constant w/ the semantic
+    * ALPHAREF is inserted.  The driver should pass in the
+    * actual alpha-ref value via this constant (.x coord)
+    */
+   unsigned lower_alpha_test:1;
+   unsigned alpha_func:3;    /* PIPE_FUNC_x */
+
    /* bitmask of (1 << TGSI_TEXTURE_type): */
    unsigned lower_TXP;
 
diff --git a/src/gallium/auxiliary/tgsi/tgsi_strings.c b/src/gallium/auxiliary/tgsi/tgsi_strings.c
index bd97544..b696838 100644
--- a/src/gallium/auxiliary/tgsi/tgsi_strings.c
+++ b/src/gallium/auxiliary/tgsi/tgsi_strings.c
@@ -88,6 +88,7 @@ const char *tgsi_semantic_names[TGSI_SEMANTIC_COUNT] =
    "INVOCATIONID",
    "VERTEXID_NOBASE",
    "BASEVERTEX",
+   "ALPHAREF",
 };
 
 const char *tgsi_texture_names[TGSI_TEXTURE_COUNT] =
diff --git a/src/gallium/include/pipe/p_shader_tokens.h b/src/gallium/include/pipe/p_shader_tokens.h
index 442b67b..bf54c3e 100644
--- a/src/gallium/include/pipe/p_shader_tokens.h
+++ b/src/gallium/include/pipe/p_shader_tokens.h
@@ -178,7 +178,8 @@ struct tgsi_declaration_interp
 #define TGSI_SEMANTIC_INVOCATIONID 27
 #define TGSI_SEMANTIC_VERTEXID_NOBASE 28
 #define TGSI_SEMANTIC_BASEVERTEX 29
-#define TGSI_SEMANTIC_COUNT      30 /**< number of semantic values */
+#define TGSI_SEMANTIC_ALPHAREF   30
+#define TGSI_SEMANTIC_COUNT      31 /**< number of semantic values */
 
 struct tgsi_declaration_semantic
 {
-- 
2.1.0



More information about the mesa-dev mailing list