[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