uint8_t auxCBSlot; /* driver constant buffer slot */
uint16_t ucpBase; /* base address for UCPs */
uint16_t drawInfoBase; /* base address for draw parameters */
+ uint16_t alphaRefBase; /* base address for alpha test values */
uint8_t pointSize; /* output index for PointSize */
uint8_t instanceId; /* system value index of InstanceID */
uint8_t vertexId; /* system value index of VertexID */
extern void
nv50_ir_apply_fixups(void *fixupData, uint32_t *code,
- bool force_per_sample, bool flatshade);
+ bool force_per_sample, bool flatshade,
+ uint8_t alphatest);
/* obtain code that will be shared among programs */
extern void nv50_ir_get_target_library(uint32_t chipset,
}
}
+static void
+alphatestSet(const FixupEntry *entry, uint32_t *code, const FixupData& data)
+{
+ int loc = entry->loc;
+ int enc;
+
+ switch (data.alphatest) {
+ case PIPE_FUNC_NEVER: enc = 0x0; break;
+ case PIPE_FUNC_LESS: enc = 0x1; break;
+ case PIPE_FUNC_EQUAL: enc = 0x2; break;
+ case PIPE_FUNC_LEQUAL: enc = 0x3; break;
+ case PIPE_FUNC_GREATER: enc = 0x4; break;
+ case PIPE_FUNC_NOTEQUAL: enc = 0x5; break;
+ case PIPE_FUNC_GEQUAL: enc = 0x6; break;
+ default:
+ case PIPE_FUNC_ALWAYS: enc = 0xf; break;
+ }
+
+ code[loc + 1] &= ~(0x1f << 14);
+ code[loc + 1] |= enc << 14;
+}
+
void
CodeEmitterNV50::emitSET(const Instruction *i)
{
if (i->src(1).mod.abs()) code[1] |= 0x00080000;
emitForm_MAD(i);
+
+ if (i->subOp == 1) {
+ addInterp(0, 0, alphatestSet);
+ }
}
void
if (info->type == PIPE_SHADER_FRAGMENT) {
info->prop.fp.writesDepth = scan.writes_z;
- info->prop.fp.usesDiscard = scan.uses_kill;
+ info->prop.fp.usesDiscard = scan.uses_kill || info->io.alphaRefBase;
} else
if (info->type == PIPE_SHADER_GEOMETRY) {
info->prop.gp.instanceCount = 1; // default value
void
Converter::exportOutputs()
{
+ if (info->io.alphaRefBase) {
+ for (unsigned int i = 0; i < info->numOutputs; ++i) {
+ if (info->out[i].sn != TGSI_SEMANTIC_COLOR ||
+ info->out[i].si != 0)
+ continue;
+ const unsigned int c = 3;
+ if (!oData.exists(sub.cur->values, i, c))
+ continue;
+ Value *val = oData.load(sub.cur->values, i, c, NULL);
+ if (!val)
+ continue;
+
+ Symbol *ref = mkSymbol(FILE_MEMORY_CONST, info->io.auxCBSlot,
+ TYPE_U32, info->io.alphaRefBase);
+ Value *pred = new_LValue(func, FILE_PREDICATE);
+ mkCmp(OP_SET, CC_TR, TYPE_U32, pred, TYPE_F32, val,
+ mkLoadv(TYPE_U32, ref, NULL))
+ ->subOp = 1;
+ mkOp(OP_DISCARD, TYPE_NONE, NULL)->setPredicate(CC_NOT_P, pred);
+ }
+ }
+
for (unsigned int i = 0; i < info->numOutputs; ++i) {
for (unsigned int c = 0; c < 4; ++c) {
if (!oData.exists(sub.cur->values, i, c))
return;
if (insn->src(1).getFile() != FILE_GPR)
return;
+ // This is the special OP_SET used for alphatesting, we can't reverse its
+ // arguments as that will confuse the fixup code.
+ if (insn->op == OP_SET && insn->subOp)
+ return;
Instruction *i0 = insn->getSrc(0)->getInsn();
Instruction *i1 = insn->getSrc(1)->getInsn();
void
nv50_ir_apply_fixups(void *fixupData, uint32_t *code,
- bool force_persample_interp, bool flatshade)
+ bool force_persample_interp, bool flatshade,
+ uint8_t alphatest)
{
nv50_ir::FixupInfo *info = reinterpret_cast<nv50_ir::FixupInfo *>(
fixupData);
// force_persample_interp: all non-flat -> per-sample
// flatshade: all color -> flat
- nv50_ir::FixupData data(force_persample_interp, flatshade);
+ // alphatest: PIPE_FUNC_* to use with alphatest
+ nv50_ir::FixupData data(force_persample_interp, flatshade, alphatest);
for (unsigned i = 0; i < info->count; ++i)
info->entry[i].apply(&info->entry[i], code, data);
}
};
struct FixupData {
- FixupData(bool force, bool flat) :
- force_persample_interp(force), flatshade(flat) {}
+ FixupData(bool force, bool flat, uint8_t alphatest) :
+ force_persample_interp(force), flatshade(flat), alphatest(alphatest) {}
bool force_persample_interp;
bool flatshade;
+ uint8_t alphatest;
};
struct FixupEntry;
/* Sample position pairs for the current output MS level */
#define NV50_CB_AUX_SAMPLE_OFFSET 0x300
#define NV50_CB_AUX_SAMPLE_OFFSET_SIZE (4 * 8 * 2)
-/* next spot: 0x340 */
+/* Alpha test ref value */
+#define NV50_CB_AUX_ALPHATEST_OFFSET 0x340
+#define NV50_CB_AUX_ALPHATEST_SIZE (4)
+/* next spot: 0x344 */
/* 4 32-bit floats for the vertex runout, put at the end */
#define NV50_CB_AUX_RUNOUT_OFFSET (NV50_CB_AUX_SIZE - 0x10)
info->io.auxCBSlot = 15;
info->io.ucpBase = NV50_CB_AUX_UCP_OFFSET;
info->io.genUserClip = prog->vp.clpd_nr;
+ if (prog->fp.alphatest)
+ info->io.alphaRefBase = NV50_CB_AUX_ALPHATEST_OFFSET;
info->io.suInfoBase = NV50_CB_AUX_TEX_MS_OFFSET;
info->io.sampleInfoBase = NV50_CB_AUX_SAMPLE_OFFSET;
if (prog->interps)
nv50_ir_apply_fixups(prog->interps, prog->code,
prog->fp.force_persample_interp,
- false /* flatshade */);
+ false /* flatshade */,
+ prog->fp.alphatest - 1);
nv50_sifc_linear_u8(&nv50->base, nv50->screen->code,
(prog_type << NV50_CODE_BO_SIZE_LOG2) + prog->code_base,
uint32_t colors; /* 0x1904 */
uint8_t has_samplemask;
uint8_t force_persample_interp;
+ uint8_t alphatest;
} fp;
struct {
struct nv50_program *fp = nv50->fragprog;
struct pipe_rasterizer_state *rast = &nv50->rast->pipe;
+ if (nv50->zsa && nv50->zsa->pipe.alpha.enabled) {
+ struct pipe_framebuffer_state *fb = &nv50->framebuffer;
+ bool blendable = fb->nr_cbufs == 0 || !fb->cbufs[0] ||
+ nv50->screen->base.base.is_format_supported(
+ &nv50->screen->base.base,
+ fb->cbufs[0]->format,
+ fb->cbufs[0]->texture->target,
+ fb->cbufs[0]->texture->nr_samples,
+ PIPE_BIND_BLENDABLE);
+ /* If we already have alphatest code, we have to keep updating
+ * it. However we only have to have different code if the current RT0 is
+ * non-blendable. Otherwise we just set it to always pass and use the
+ * hardware alpha test.
+ */
+ if (fp->fp.alphatest || !blendable) {
+ uint8_t alphatest = PIPE_FUNC_ALWAYS + 1;
+ if (!blendable)
+ alphatest = nv50->zsa->pipe.alpha.func + 1;
+ if (!fp->fp.alphatest)
+ nv50_program_destroy(nv50, fp);
+ else if (fp->mem && fp->fp.alphatest != alphatest)
+ nouveau_heap_free(&fp->mem);
+
+ fp->fp.alphatest = alphatest;
+ }
+ } else if (fp->fp.alphatest && fp->fp.alphatest != PIPE_FUNC_ALWAYS + 1) {
+ /* Alpha test is disabled but we have a shader where it's filled
+ * in. Make sure to reset the function to 'always', otherwise it'll end
+ * up discarding fragments incorrectly.
+ */
+ if (fp->mem)
+ nouveau_heap_free(&fp->mem);
+
+ fp->fp.alphatest = PIPE_FUNC_ALWAYS + 1;
+ }
+
if (fp->fp.force_persample_interp != rast->force_persample_interp) {
/* Force the program to be reuploaded, which will trigger interp fixups
* to get applied
SB_DATA (so, 0);
}
+ SB_BEGIN_3D(so, CB_ADDR, 1);
+ SB_DATA (so, NV50_CB_AUX_ALPHATEST_OFFSET << (8 - 2) | NV50_CB_AUX);
+ SB_BEGIN_3D(so, CB_DATA(0), 1);
+ SB_DATA (so, fui(cso->alpha.ref_value));
+
assert(so->size <= ARRAY_SIZE(so->state));
return (void *)so;
}
{ nv50_vertprog_validate, NV50_NEW_3D_VERTPROG },
{ nv50_gmtyprog_validate, NV50_NEW_3D_GMTYPROG },
{ nv50_fragprog_validate, NV50_NEW_3D_FRAGPROG | NV50_NEW_3D_RASTERIZER |
- NV50_NEW_3D_MIN_SAMPLES },
+ NV50_NEW_3D_MIN_SAMPLES | NV50_NEW_3D_ZSA |
+ NV50_NEW_3D_FRAMEBUFFER},
{ nv50_fp_linkage_validate, NV50_NEW_3D_FRAGPROG | NV50_NEW_3D_VERTPROG |
NV50_NEW_3D_GMTYPROG | NV50_NEW_3D_RASTERIZER },
{ nv50_gp_linkage_validate, NV50_NEW_3D_GMTYPROG | NV50_NEW_3D_VERTPROG },
struct nv50_zsa_stateobj {
struct pipe_depth_stencil_alpha_state pipe;
int size;
- uint32_t state[34];
+ uint32_t state[38];
};
struct nv50_constbuf {
if (prog->fixups) {
nv50_ir_apply_fixups(prog->fixups, prog->code,
prog->fp.force_persample_interp,
- prog->fp.flatshade);
+ prog->fp.flatshade,
+ 0 /* alphatest */);
for (int i = 0; i < 2; i++) {
unsigned mask = prog->fp.color_interp[i] >> 4;
unsigned interp = prog->fp.color_interp[i] & 3;