nv50: fix alphatest for non-blendable formats
authorIlia Mirkin <imirkin@alum.mit.edu>
Sun, 19 Jun 2016 20:57:50 +0000 (16:57 -0400)
committerIlia Mirkin <imirkin@alum.mit.edu>
Sat, 16 Jul 2016 15:45:30 +0000 (11:45 -0400)
The hardware can only do alphatest when using a blendable format. This
means that the various *16 norm formats didn't work with alphatest. It
appears that Talos Principle uses such formats, as well as alpha tests,
for some internal renders, which made them be incorrect. However this
does not appear to affect the final renders, but in a different game it
easily could.

The approach we take is that when alphatests are enabled and a suitable
format is used (which we anticipate is the vast minority of the time),
we insert code into the shader to perform the comparison and discard.
Once inserted, that code lives in the shader forever, and we re-upload
it each time the function changes with a fixed-up compare. To avoid
re-uploading too often, if we switch back to a blendable format, the
test is (effectively) disabled and the hw alphatest functionality is
used.

Signed-off-by: Ilia Mirkin <imirkin@alum.mit.edu>
14 files changed:
src/gallium/drivers/nouveau/codegen/nv50_ir_driver.h
src/gallium/drivers/nouveau/codegen/nv50_ir_emit_nv50.cpp
src/gallium/drivers/nouveau/codegen/nv50_ir_from_tgsi.cpp
src/gallium/drivers/nouveau/codegen/nv50_ir_peephole.cpp
src/gallium/drivers/nouveau/codegen/nv50_ir_target.cpp
src/gallium/drivers/nouveau/codegen/nv50_ir_target.h
src/gallium/drivers/nouveau/nv50/nv50_context.h
src/gallium/drivers/nouveau/nv50/nv50_program.c
src/gallium/drivers/nouveau/nv50/nv50_program.h
src/gallium/drivers/nouveau/nv50/nv50_shader_state.c
src/gallium/drivers/nouveau/nv50/nv50_state.c
src/gallium/drivers/nouveau/nv50/nv50_state_validate.c
src/gallium/drivers/nouveau/nv50/nv50_stateobj.h
src/gallium/drivers/nouveau/nvc0/nvc0_program.c

index b611519ade3732695ba0adf2e141027ff0cd1955..58a5d38a82c0429ff323473d677d550ff09c1679 100644 (file)
@@ -166,6 +166,7 @@ struct nv50_ir_prog_info
       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 */
@@ -206,7 +207,8 @@ extern void nv50_ir_relocate_code(void *relocData, uint32_t *code,
 
 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,
index 0fe399bdd8ebfa8f3ee106dc4db32672cccea2c2..7878f2fae48a4695275741a6e858f7de75696e20 100644 (file)
@@ -1265,6 +1265,28 @@ CodeEmitterNV50::emitISAD(const Instruction *i)
    }
 }
 
+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)
 {
@@ -1294,6 +1316,10 @@ 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
index 825a44f35fb6df503b2c05c6d5c495a56e1b75e6..61eb7f5c256c48f8407a0895993df769064f63b8 100644 (file)
@@ -1006,7 +1006,7 @@ bool Source::scanSource()
 
    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
@@ -3818,6 +3818,28 @@ Converter::handleUserClipPlanes()
 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))
index 32131884c5f3963925cc60d7fc844d17242913dc..3fc1abf18cdd0c894082d4f1ae477863809009e3 100644 (file)
@@ -196,6 +196,10 @@ LoadPropagation::checkSwapSrc01(Instruction *insn)
          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();
index 08aac00e53628a3048a9a8e22ef2ee3f77f78dad..7d7b31589514d65197da567bbb95c0eba8eb76fc 100644 (file)
@@ -506,14 +506,16 @@ nv50_ir_relocate_code(void *relocData, uint32_t *code,
 
 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);
 }
index 6bf1a5cff2307962711c4af9e9804a9688fc0eab..4a701f7cb9d24758aee2098a3dd55485dec02b16 100644 (file)
@@ -59,10 +59,11 @@ struct RelocInfo
 };
 
 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;
index cb94c8edc54c7d8669d4ed87630f4c84537006d4..cca44f5bb219787c972197b554fd6f15c200ddb2 100644 (file)
 /* 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)
 
index c764f5c57284614bfb48f0d0c5ca9e6536523079..2b66877c6797905959764dd6891b682623d72cf6 100644 (file)
@@ -334,6 +334,8 @@ nv50_program_translate(struct nv50_program *prog, uint16_t chipset,
    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;
@@ -488,7 +490,8 @@ nv50_program_upload_code(struct nv50_context *nv50, struct nv50_program *prog)
    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,
index 0a22e5bbbcf3a4de18dc0108941bfc4ea045c58b..fc9ada43624fcd8da28b50bca15ac6e7ec5124b5 100644 (file)
@@ -90,6 +90,7 @@ struct nv50_program {
       uint32_t colors; /* 0x1904 */
       uint8_t has_samplemask;
       uint8_t force_persample_interp;
+      uint8_t alphatest;
    } fp;
 
    struct {
index 23263945bdc8bcf8ffa2f050edcd085d0ec0a98f..d234748a0a0c011ccb07307a71a8061b83291baf 100644 (file)
@@ -174,6 +174,42 @@ nv50_fragprog_validate(struct nv50_context *nv50)
    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
index a84c9e27992451aefe711cbe86e6b04e2e2fd9ff..b6741140e50e534575042020de0e1cc3cdbb3d1c 100644 (file)
@@ -416,6 +416,11 @@ nv50_zsa_state_create(struct pipe_context *pipe,
       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;
 }
index 3a374a24867b29ab6e0c51db0c8379f1e5d63997..19181a9f496fef32153e44c9c8588869d331db38 100644 (file)
@@ -522,7 +522,8 @@ validate_list_3d[] = {
     { 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 },
index b8fa0f623f8f7496705d572e8c4a9aa1023ca1ad..9598b04e0f49eec693bffeed6e02021174747f05 100644 (file)
@@ -31,7 +31,7 @@ struct nv50_rasterizer_stateobj {
 struct nv50_zsa_stateobj {
    struct pipe_depth_stencil_alpha_state pipe;
    int size;
-   uint32_t state[34];
+   uint32_t state[38];
 };
 
 struct nv50_constbuf {
index 5b3caca2262d61026b64f95108ea9dc717a5f824..5fc27534c6f06ae024b7f6cce05abf848e4731ea 100644 (file)
@@ -748,7 +748,8 @@ nvc0_program_upload_code(struct nvc0_context *nvc0, struct nvc0_program *prog)
    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;