[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