nv50: support TXB and TXL
[mesa.git] / src / gallium / drivers / nv50 / nv50_program.c
index f0fe7e616844bac6b3d03bfad6fd8f629007bb7f..ddb049f391057ed70815e7865229a4e738d5b74e 100644 (file)
@@ -131,6 +131,9 @@ struct nv50_pc {
        struct nv50_reg *r_brdc;
        struct nv50_reg *r_dst[4];
 
+       struct nv50_reg reg_instances[16];
+       unsigned reg_instance_nr;
+
        unsigned interp_mode[32];
        /* perspective interpolation registers */
        struct nv50_reg *iv_p;
@@ -150,6 +153,19 @@ struct nv50_pc {
        boolean allow32;
 };
 
+static INLINE struct nv50_reg *
+reg_instance(struct nv50_pc *pc, struct nv50_reg *reg)
+{
+       struct nv50_reg *dup = NULL;
+       if (reg) {
+               assert(pc->reg_instance_nr < 16);
+               dup = &pc->reg_instances[pc->reg_instance_nr++];
+               *dup = *reg;
+               reg->mod = 0;
+       }
+       return dup;
+}
+
 static INLINE void
 ctor_reg(struct nv50_reg *reg, unsigned type, int index, int hw)
 {
@@ -898,7 +914,6 @@ static INLINE void
 emit_sub(struct nv50_pc *pc, struct nv50_reg *dst, struct nv50_reg *src0,
         struct nv50_reg *src1)
 {
-       assert(src0 != src1);
        src1->mod ^= NV50_MOD_NEG;
        emit_add(pc, dst, src0, src1);
        src1->mod ^= NV50_MOD_NEG;
@@ -967,7 +982,6 @@ static INLINE void
 emit_msb(struct nv50_pc *pc, struct nv50_reg *dst, struct nv50_reg *src0,
         struct nv50_reg *src1, struct nv50_reg *src2)
 {
-       assert(src2 != src0 && src2 != src1);
        src2->mod ^= NV50_MOD_NEG;
        emit_mad(pc, dst, src0, src1, src2);
        src2->mod ^= NV50_MOD_NEG;
@@ -1259,7 +1273,7 @@ emit_kil(struct nv50_pc *pc, struct nv50_reg *src)
 
 static void
 load_cube_tex_coords(struct nv50_pc *pc, struct nv50_reg *t[4],
-                    struct nv50_reg **src, boolean proj)
+                    struct nv50_reg **src, unsigned arg, boolean proj)
 {
        int mod[3] = { src[0]->mod, src[1]->mod, src[2]->mod };
 
@@ -1276,6 +1290,10 @@ load_cube_tex_coords(struct nv50_pc *pc, struct nv50_reg *t[4],
 
        if (proj && 0 /* looks more correct without this */)
                emit_mul(pc, t[2], t[2], src[3]);
+       else
+       if (arg == 4) /* there is no textureProj(samplerCubeShadow) */
+               emit_mov(pc, t[3], src[3]);
+
        emit_flop(pc, 0, t[2], t[2]);
 
        emit_mul(pc, t[0], src[0], t[2]);
@@ -1284,85 +1302,115 @@ load_cube_tex_coords(struct nv50_pc *pc, struct nv50_reg *t[4],
 }
 
 static void
-emit_tex(struct nv50_pc *pc, struct nv50_reg **dst, unsigned mask,
-        struct nv50_reg **src, unsigned unit, unsigned type, boolean proj)
+load_proj_tex_coords(struct nv50_pc *pc, struct nv50_reg *t[4],
+                    struct nv50_reg **src, unsigned dim, unsigned arg)
 {
-       struct nv50_reg *t[4];
-       struct nv50_program_exec *e;
+       unsigned c, mode;
+
+       if (src[0]->type == P_TEMP && src[0]->rhw != -1) {
+               mode = pc->interp_mode[src[0]->index] | INTERP_PERSPECTIVE;
+
+               t[3]->rhw = src[3]->rhw;
+               emit_interp(pc, t[3], NULL, (mode & INTERP_CENTROID));
+               emit_flop(pc, 0, t[3], t[3]);
 
-       unsigned c, mode, dim;
+               for (c = 0; c < dim; ++c) {
+                       t[c]->rhw = src[c]->rhw;
+                       emit_interp(pc, t[c], t[3], mode);
+               }
+               if (arg != dim) { /* depth reference value */
+                       t[dim]->rhw = src[2]->rhw;
+                       emit_interp(pc, t[dim], t[3], mode);
+               }
+       } else {
+               /* XXX: for some reason the blob sometimes uses MAD
+                * (mad f32 $rX $rY $rZ neg $r63)
+                */
+               emit_flop(pc, 0, t[3], src[3]);
+               for (c = 0; c < dim; ++c)
+                       emit_mul(pc, t[c], src[c], t[3]);
+               if (arg != dim) /* depth reference value */
+                       emit_mul(pc, t[dim], src[2], t[3]);
+       }
+}
 
+static INLINE void
+get_tex_dim(unsigned type, unsigned *dim, unsigned *arg)
+{
        switch (type) {
        case TGSI_TEXTURE_1D:
-               dim = 1;
+               *arg = *dim = 1;
+               break;
+       case TGSI_TEXTURE_SHADOW1D:
+               *dim = 1;
+               *arg = 2;
                break;
        case TGSI_TEXTURE_UNKNOWN:
        case TGSI_TEXTURE_2D:
-       case TGSI_TEXTURE_SHADOW1D: /* XXX: x, z */
        case TGSI_TEXTURE_RECT:
-               dim = 2;
+               *arg = *dim = 2;
+               break;
+       case TGSI_TEXTURE_SHADOW2D:
+       case TGSI_TEXTURE_SHADOWRECT:
+               *dim = 2;
+               *arg = 3;
                break;
        case TGSI_TEXTURE_3D:
        case TGSI_TEXTURE_CUBE:
-       case TGSI_TEXTURE_SHADOW2D:
-       case TGSI_TEXTURE_SHADOWRECT: /* XXX */
-               dim = 3;
+               *dim = *arg = 3;
                break;
        default:
                assert(0);
                break;
        }
+}
+
+static void
+emit_tex(struct nv50_pc *pc, struct nv50_reg **dst, unsigned mask,
+        struct nv50_reg **src, unsigned unit, unsigned type,
+        boolean proj, int bias_lod)
+{
+       struct nv50_reg *t[4];
+       struct nv50_program_exec *e;
+       unsigned c, dim, arg;
 
-       /* some cards need t[0]'s hw index to be a multiple of 4 */
+       /* t[i] must be within a single 128 bit super-reg */
        alloc_temp4(pc, t, 0);
 
+       e = exec(pc);
+       e->inst[0] = 0xf0000000;
+       set_long(pc, e);
+       set_dst(pc, t[0], e);
+
+       /* TIC and TSC binding indices (TSC is ignored as TSC_LINKED = TRUE): */
+       e->inst[0] |= (unit << 9) /* | (unit << 17) */;
+
+       /* live flag (don't set if TEX results affect input to another TEX): */
+       /* e->inst[0] |= 0x00000004; */
+
+       get_tex_dim(type, &dim, &arg);
+
        if (type == TGSI_TEXTURE_CUBE) {
-               load_cube_tex_coords(pc, t, src, proj);
+               e->inst[0] |= 0x08000000;
+               load_cube_tex_coords(pc, t, src, arg, proj);
        } else
-       if (proj) {
-               if (src[0]->type == P_TEMP && src[0]->rhw != -1) {
-                       mode = pc->interp_mode[src[0]->index];
-
-                       t[3]->rhw = src[3]->rhw;
-                       emit_interp(pc, t[3], NULL, (mode & INTERP_CENTROID));
-                       emit_flop(pc, 0, t[3], t[3]);
-
-                       for (c = 0; c < dim; c++) {
-                               t[c]->rhw = src[c]->rhw;
-                               emit_interp(pc, t[c], t[3],
-                                           (mode | INTERP_PERSPECTIVE));
-                       }
-               } else {
-                       emit_flop(pc, 0, t[3], src[3]);
-                       for (c = 0; c < dim; c++)
-                               emit_mul(pc, t[c], src[c], t[3]);
-
-                       /* XXX: for some reason the blob sometimes uses MAD:
-                        * emit_mad(pc, t[c], src[0][c], t[3], t[3])
-                        * pc->p->exec_tail->inst[1] |= 0x080fc000;
-                        */
-               }
-       } else {
+       if (proj)
+               load_proj_tex_coords(pc, t, src, dim, arg);
+       else {
                for (c = 0; c < dim; c++)
                        emit_mov(pc, t[c], src[c]);
+               if (arg != dim) /* depth reference value (always src.z here) */
+                       emit_mov(pc, t[dim], src[2]);
        }
 
-       e = exec(pc);
-       set_long(pc, e);
-       e->inst[0] |= 0xf0000000;
-       e->inst[1] |= 0x00000004;
-       set_dst(pc, t[0], e);
-       e->inst[0] |= (unit << 9);
-
-       if (dim == 2)
-               e->inst[0] |= 0x00400000;
-       else
-       if (dim == 3) {
-               e->inst[0] |= 0x00800000;
-               if (type == TGSI_TEXTURE_CUBE)
-                       e->inst[0] |= 0x08000000;
+       if (bias_lod) {
+               assert(arg < 4);
+               emit_mov(pc, t[arg++], src[3]);
+               e->inst[1] |= (bias_lod < 0) ? 0x20000000 : 0x40000000;
        }
 
+       e->inst[0] |= (arg - 1) << 22;
+
        e->inst[0] |= (mask & 0x3) << 25;
        e->inst[1] |= (mask & 0xc) << 12;
 
@@ -1515,8 +1563,6 @@ convert_to_long(struct nv50_pc *pc, struct nv50_program_exec *e)
 static boolean
 negate_supported(const struct tgsi_full_instruction *insn, int i)
 {
-       int s;
-
        switch (insn->Instruction.Opcode) {
        case TGSI_OPCODE_DDY:
        case TGSI_OPCODE_DP3:
@@ -1526,29 +1572,14 @@ negate_supported(const struct tgsi_full_instruction *insn, int i)
        case TGSI_OPCODE_ADD:
        case TGSI_OPCODE_SUB:
        case TGSI_OPCODE_MAD:
-               break;
+               return TRUE;
        case TGSI_OPCODE_POW:
                if (i == 1)
-                       break;
+                       return TRUE;
                return FALSE;
        default:
                return FALSE;
        }
-
-       /* Watch out for possible multiple uses of an nv50_reg, we
-        * can't use nv50_reg::neg in these cases.
-        */
-       for (s = 0; s < insn->Instruction.NumSrcRegs; ++s) {
-               if (s == i)
-                       continue;
-               if ((insn->Src[s].Register.Index ==
-                    insn->Src[i].Register.Index) &&
-                   (insn->Src[s].Register.File ==
-                    insn->Src[i].Register.File))
-                       return FALSE;
-       }
-
-       return TRUE;
 }
 
 /* Return a read mask for source registers deduced from opcode & write mask. */
@@ -1576,9 +1607,13 @@ nv50_tgsi_src_mask(const struct tgsi_full_instruction *insn, int c)
        case TGSI_OPCODE_RSQ:
        case TGSI_OPCODE_SCS:
                return 0x1;
+       case TGSI_OPCODE_IF:
+               return 0x1;
        case TGSI_OPCODE_LIT:
                return 0xb;
        case TGSI_OPCODE_TEX:
+       case TGSI_OPCODE_TXB:
+       case TGSI_OPCODE_TXL:
        case TGSI_OPCODE_TXP:
        {
                const struct tgsi_instruction_texture *tex;
@@ -1587,13 +1622,17 @@ nv50_tgsi_src_mask(const struct tgsi_full_instruction *insn, int c)
                tex = &insn->Texture;
 
                mask = 0x7;
-               if (insn->Instruction.Opcode == TGSI_OPCODE_TXP)
-                       mask |= 0x8;
+               if (insn->Instruction.Opcode != TGSI_OPCODE_TEX &&
+                   insn->Instruction.Opcode != TGSI_OPCODE_TXD)
+                       mask |= 0x8; /* bias, lod or proj */
 
                switch (tex->Texture) {
                case TGSI_TEXTURE_1D:
                        mask &= 0x9;
                        break;
+               case TGSI_TEXTURE_SHADOW1D:
+                       mask &= 0x5;
+                       break;
                case TGSI_TEXTURE_2D:
                        mask &= 0xb;
                        break;
@@ -1785,6 +1824,8 @@ nv50_tgsi_dst_revdep(unsigned op, int s, int c)
        case TGSI_OPCODE_LIT:
        case TGSI_OPCODE_SCS:
        case TGSI_OPCODE_TEX:
+       case TGSI_OPCODE_TXB:
+       case TGSI_OPCODE_TXL:
        case TGSI_OPCODE_TXP:
                /* these take care of dangerous swizzles themselves */
                return 0x0;
@@ -1882,7 +1923,8 @@ nv50_program_tx_insn(struct nv50_pc *pc,
 
                for (c = 0; c < 4; c++)
                        if (src_mask & (1 << c))
-                               src[i][c] = tgsi_src(pc, c, fs, neg_supp);
+                               src[i][c] = reg_instance(pc,
+                                       tgsi_src(pc, c, fs, neg_supp));
        }
 
        brdc = temp = pc->r_brdc;
@@ -2187,11 +2229,19 @@ nv50_program_tx_insn(struct nv50_pc *pc,
                break;
        case TGSI_OPCODE_TEX:
                emit_tex(pc, dst, mask, src[0], unit,
-                        inst->Texture.Texture, FALSE);
+                        inst->Texture.Texture, FALSE, 0);
+               break;
+       case TGSI_OPCODE_TXB:
+               emit_tex(pc, dst, mask, src[0], unit,
+                        inst->Texture.Texture, FALSE, -1);
+               break;
+       case TGSI_OPCODE_TXL:
+               emit_tex(pc, dst, mask, src[0], unit,
+                        inst->Texture.Texture, FALSE, 1);
                break;
        case TGSI_OPCODE_TXP:
                emit_tex(pc, dst, mask, src[0], unit,
-                        inst->Texture.Texture, TRUE);
+                        inst->Texture.Texture, TRUE, 0);
                break;
        case TGSI_OPCODE_TRUNC:
                for (c = 0; c < 4; c++) {
@@ -2249,16 +2299,14 @@ nv50_program_tx_insn(struct nv50_pc *pc,
                for (c = 0; c < 4; c++) {
                        if (!src[i][c])
                                continue;
-                       src[i][c]->mod = 0;
-                       if (src[i][c]->index == -1 && src[i][c]->type == P_IMMD)
-                               FREE(src[i][c]);
-                       else
                        if (src[i][c]->acc < 0 && src[i][c]->type == P_CONST)
                                FREE(src[i][c]); /* indirect constant */
                }
        }
 
        kill_temp_temp(pc);
+       pc->reg_instance_nr = 0;
+
        return TRUE;
 }