From: Rob Clark <robcl...@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 <robcl...@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 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/mesa-dev