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

Roland Scheidegger sroland at vmware.com
Fri Dec 19 14:35:06 PST 2014


I'm not a fan of using semantics for consts (fwiw I think it would
probably be a good idea to use different namespaces for system values
and inputs too but that's another topic).
I don't quite share the concerns about possibly indirectly addressing
such a const though (because if const buffers are used this isn't a
problem).

As for the implementation, it is a bit lacking though I don't know if
you care. Because you are supposed to do that in the format of the
(first) render target. And your results won't match that very well
(especially a problem probably if you use exact comparisons). llvmpipe
has some code to do exactly that (though only works for 8bit unorm
alpha). I suppose there could be different ways to do it (at least for
most functions except equal/not equal you could probably get away with
adjusting the alpha ref value sligthly to account for the rounding you'd
get when converting to render target output - I'd guess for the exact
comparisons you could do something similar possibly, use a range and
combine two comparisons).

Roland

Am 19.12.2014 um 20:11 schrieb Rob Clark:
> 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
>  {
> 



More information about the mesa-dev mailing list