nv50: import current "state of the art" nv50 code
authorBen Skeggs <skeggsb@gmail.com>
Sun, 1 Jun 2008 12:41:40 +0000 (22:41 +1000)
committerBen Skeggs <skeggsb@gmail.com>
Sun, 29 Jun 2008 05:46:13 +0000 (15:46 +1000)
src/gallium/drivers/nv50/Makefile
src/gallium/drivers/nv50/nv50_context.h
src/gallium/drivers/nv50/nv50_program.c [new file with mode: 0644]
src/gallium/drivers/nv50/nv50_screen.c
src/gallium/drivers/nv50/nv50_screen.h
src/gallium/drivers/nv50/nv50_state.c
src/gallium/drivers/nv50/nv50_state.h
src/gallium/drivers/nv50/nv50_state_validate.c
src/gallium/drivers/nv50/nv50_vbo.c

index a62a4d961bc4616771fadbf81d4a07b77ade810f..2cc6e9de287feb049317bdb9e09d00bd6505f170 100644 (file)
@@ -9,6 +9,7 @@ DRIVER_SOURCES = \
        nv50_draw.c \
        nv50_miptree.c \
        nv50_query.c \
+       nv50_program.c \
        nv50_screen.c \
        nv50_state.c \
        nv50_state_validate.c \
index e68c702deaef39c7cddb884b90d5e9c84b47d6c3..d4d716b94bff6e40071d28aedbe8abf2f8e1afb5 100644 (file)
@@ -9,6 +9,7 @@
 
 #include "nouveau/nouveau_winsys.h"
 #include "nouveau/nouveau_gldefs.h"
+#include "nouveau/nouveau_stateobj.h"
 
 #define NOUVEAU_PUSH_CONTEXT(ctx)                                              \
        struct nv50_screen *ctx = nv50->screen
@@ -30,6 +31,9 @@
 #define NV50_NEW_VIEWPORT      (1 << 5)
 #define NV50_NEW_RASTERIZER    (1 << 6)
 #define NV50_NEW_FRAMEBUFFER   (1 << 7)
+#define NV50_NEW_VERTPROG      (1 << 8)
+#define NV50_NEW_FRAGPROG      (1 << 9)
+#define NV50_NEW_ARRAYS                (1 << 10)
 
 struct nv50_blend_stateobj {
        struct pipe_blend_state pipe;
@@ -63,6 +67,13 @@ struct nv50_context {
        struct pipe_scissor_state scissor;
        struct pipe_viewport_state viewport;
        struct pipe_framebuffer_state framebuffer;
+       struct nv50_program *vertprog;
+       struct nv50_program *fragprog;
+       struct pipe_buffer *constbuf[PIPE_SHADER_TYPES];
+       struct pipe_vertex_buffer vtxbuf[PIPE_MAX_ATTRIBS];
+       unsigned vtxbuf_nr;
+       struct pipe_vertex_element vtxelt[PIPE_MAX_ATTRIBS];
+       unsigned vtxelt_nr;
 };
 
 static INLINE struct nv50_context *
@@ -88,11 +99,18 @@ extern boolean nv50_draw_elements(struct pipe_context *pipe,
                                  unsigned indexSize,
                                  unsigned mode, unsigned start,
                                  unsigned count);
+extern void nv50_vbo_validate(struct nv50_context *nv50);
 
 /* nv50_clear.c */
 extern void nv50_clear(struct pipe_context *pipe, struct pipe_surface *ps,
                       unsigned clearValue);
 
+/* nv50_program.c */
+extern void nv50_vertprog_validate(struct nv50_context *nv50);
+extern void nv50_fragprog_validate(struct nv50_context *nv50);
+extern void nv50_program_destroy(struct nv50_context *nv50, struct nv50_program *p);
+
+/* nv50_state_validate.c */
 extern boolean nv50_state_validate(struct nv50_context *nv50);
 
 #endif
diff --git a/src/gallium/drivers/nv50/nv50_program.c b/src/gallium/drivers/nv50/nv50_program.c
new file mode 100644 (file)
index 0000000..30953b7
--- /dev/null
@@ -0,0 +1,742 @@
+#include "pipe/p_context.h"
+#include "pipe/p_defines.h"
+#include "pipe/p_state.h"
+#include "pipe/p_inlines.h"
+
+#include "pipe/p_shader_tokens.h"
+#include "tgsi/util/tgsi_parse.h"
+#include "tgsi/util/tgsi_util.h"
+
+#include "nv50_context.h"
+#include "nv50_state.h"
+
+#define OP_MOV 0x001
+#define OP_RCP 0x009
+#define OP_ADD 0x00b
+#define OP_MUL 0x00c
+#define NV50_SU_MAX_TEMP 64
+
+struct nv50_reg {
+       enum {
+               P_TEMP,
+               P_ATTR,
+               P_RESULT,
+               P_CONST,
+               P_IMMD
+       } type;
+       int index;
+
+       int hw;
+};
+
+struct nv50_pc {
+       struct nv50_program *p;
+
+       /* hw resources */
+       struct nv50_reg *r_temp[NV50_SU_MAX_TEMP];
+
+       /* tgsi resources */
+       struct nv50_reg *temp;
+       int temp_nr;
+       struct nv50_reg *attr;
+       int attr_nr;
+       struct nv50_reg *result;
+       int result_nr;
+       struct nv50_reg *param;
+       int param_nr;
+       struct nv50_reg *immd;
+       float *immd_buf;
+       int immd_nr;
+};
+
+static void
+alloc_reg(struct nv50_pc *pc, struct nv50_reg *reg)
+{
+       int i;
+
+       if (reg->type != P_TEMP || reg->hw >= 0)
+               return;
+
+       for (i = 0; i < NV50_SU_MAX_TEMP; i++) {
+               if (!(pc->r_temp[i])) {
+                       pc->r_temp[i] = reg;
+                       reg->hw = i;
+                       if (pc->p->cfg.vp.high_temp < (i + 1))
+                               pc->p->cfg.vp.high_temp = i + 1;
+                       return;
+               }
+       }
+
+       assert(0);
+}
+
+static struct nv50_reg *
+alloc_temp(struct nv50_pc *pc, struct nv50_reg *dst)
+{
+       struct nv50_reg *r;
+       int i;
+
+       if (dst && dst->type == P_TEMP && dst->hw == -1)
+               return dst;
+
+       for (i = 0; i < NV50_SU_MAX_TEMP; i++) {
+               if (!pc->r_temp[i]) {
+                       r = CALLOC_STRUCT(nv50_reg);
+                       r->type = P_TEMP;
+                       r->index = -1;
+                       r->hw = i;
+                       pc->r_temp[i] = r;
+                       return r;
+               }
+       }
+
+       assert(0);
+       return NULL;
+}
+
+static void
+free_temp(struct nv50_pc *pc, struct nv50_reg *r)
+{
+       if (r->index == -1) {
+               FREE(pc->r_temp[r->hw]);
+               pc->r_temp[r->hw] = NULL;
+       }
+}
+
+#if 0
+static struct nv50_reg *
+constant(struct nv50_pc *pc, int pipe, int c, float v)
+{
+       struct nv50_reg *r = CALLOC_STRUCT(nv50_reg);
+       struct nv50_program *p = pc->p;
+       struct nv50_program_data *pd;
+       int idx;
+
+       if (pipe >= 0) {
+               for (idx = 0; idx < p->nr_consts; idx++) {
+                       if (p->consts[idx].index == pipe)
+                               return nv40_sr(NV40SR_CONST, idx);
+               }
+       }
+
+       idx = p->nr_consts++;
+       p->consts = realloc(p->consts, sizeof(*pd) * p->nr_consts);
+       pd = &p->consts[idx];
+
+       pd->index = pipe;
+       pd->component = c;
+       pd->value = v;
+       return nv40_sr(NV40SR_CONST, idx);
+}
+#endif
+
+static void
+emit(struct nv50_pc *pc, unsigned op, struct nv50_reg *dst,
+     struct nv50_reg *src0, struct nv50_reg *src1, struct nv50_reg *src2)
+{
+       struct nv50_program *p = pc->p;
+       struct nv50_reg *tmp = NULL, *tmp2 = NULL;
+       unsigned inst[2] = { 0, 0 };
+
+       /* Grr.. Fun restrictions on where attribs can be sourced from.. */
+       if (src1 && src1->type == P_ATTR) {
+               tmp = alloc_temp(pc, dst);
+               emit(pc, 1, tmp, src1, NULL, NULL);
+               src1 = tmp;
+       }
+
+       if (src2 && src2->type == P_ATTR) {
+               tmp2 = alloc_temp(pc, dst);
+               emit(pc, 1, tmp2, src2, NULL, NULL);
+               src2 = tmp2;
+       }
+
+       /* Get this out of the way first.  What type of opcode do we
+        * want/need to build?
+        */
+       if ((op & 0x3f0) || dst->type == P_RESULT ||
+           (src0 && src0->type == P_ATTR) || src1 || src2)
+               inst[0] |= 0x00000001;
+
+       if (inst[0] & 0x00000001) {
+               inst[0] |= ((op & 0xf) << 28);
+               inst[1] |= ((op >> 4) << 26);
+
+               alloc_reg(pc, dst);
+               if (dst->type == P_RESULT)
+                       inst[1] |= 0x00000008;
+               inst[0] |= (dst->hw << 2);
+
+               if (src0) {
+                       if (src0->type == P_ATTR)
+                               inst[1] |= 0x00200000;
+                       else
+                       if (src0->type == P_CONST || src0->type == P_IMMD)
+                               assert(0);
+                       alloc_reg(pc, src0);
+                       inst[0] |= (src0->hw << 9);
+               }
+
+               if (src1) {
+                       if (src1->type == P_CONST || src1->type == P_IMMD) {
+                               inst[0] |= 0x00800000; /* src1 is const */
+                               /*XXX: does src1 come from "src2" now? */
+                               alloc_reg(pc, src1);
+                               inst[0] |= (src1->hw << 16);
+                       } else {
+                               alloc_reg(pc, src1);
+                               if (op == 0xc || op == 0xe)
+                                       inst[0] |= (src1->hw << 16);
+                               else
+                                       inst[1] |= (src1->hw << 14);
+                       }
+               } else {
+                       inst[1] |= 0x0003c000; /*XXX FIXME */
+               }
+
+               if (src2) {
+                       if (src2->type == P_CONST || src2->type == P_IMMD) {
+                               inst[0] |= 0x01000000; /* src2 is const */
+                               inst[1] |= (src2->hw << 14);
+                       } else {
+                               alloc_reg(pc, src2);
+                               if (inst[0] & 0x00800000 || op ==0xe)
+                                       inst[1] |= (src2->hw << 14);
+                               else
+                                       inst[0] |= (src2->hw << 16);
+                       }
+               }
+
+               /*XXX: FIXME */
+               if (op == 0xb || op == 0xc || op == 0x9 || op == 0xe) {
+                       /* 0x04000000 negates arg0 */
+                       /* 0x08000000 negates arg1 */
+                       /*XXX: true for !0xb also ? */
+                       inst[1] |= 0x00000780;
+               } else {
+                       /* 0x04000000 == arg0 32 bit, otherwise 16 bit */
+                       inst[1] |= 0x04000780;
+               }
+       } else {
+               inst[0] |= ((op & 0xf) << 28);
+
+               alloc_reg(pc, dst);
+               inst[0] |= (dst->hw << 2);
+
+               if (src0) {
+                       alloc_reg(pc, src0);
+                       inst[0] |= (src0->hw << 9);
+               }
+
+               /*XXX: NFI if this even works - probably not.. */
+               if (src1) {
+                       alloc_reg(pc, src1);
+                       inst[0] |= (src1->hw << 16);
+               }
+       }
+
+       if (tmp) free_temp(pc, tmp);
+       if (tmp2) free_temp(pc, tmp2);
+
+       if (inst[0] & 1) {
+               p->insns_nr += 2;
+               p->insns = realloc(p->insns, sizeof(unsigned) * p->insns_nr);
+               memcpy(p->insns + (p->insns_nr - 2), inst, sizeof(unsigned)*2);
+       } else {
+               p->insns_nr += 1;
+               p->insns = realloc(p->insns, sizeof(unsigned) * p->insns_nr);
+               memcpy(p->insns + (p->insns_nr - 1), inst, sizeof(unsigned));
+       }
+}
+
+static struct nv50_reg *
+tgsi_dst(struct nv50_pc *pc, int c, const struct tgsi_full_dst_register *dst)
+{
+       switch (dst->DstRegister.File) {
+       case TGSI_FILE_TEMPORARY:
+               return &pc->temp[dst->DstRegister.Index * 4 + c];
+       case TGSI_FILE_OUTPUT:
+               return &pc->result[dst->DstRegister.Index * 4 + c];
+       case TGSI_FILE_NULL:
+               return NULL;
+       default:
+               break;
+       }
+
+       return NULL;
+}
+
+static struct nv50_reg *
+tgsi_src(struct nv50_pc *pc, int c, const struct tgsi_full_src_register *src)
+{
+       /* Handle swizzling */
+       switch (c) {
+       case 0: c = src->SrcRegister.SwizzleX; break;
+       case 1: c = src->SrcRegister.SwizzleY; break;
+       case 2: c = src->SrcRegister.SwizzleZ; break;
+       case 3: c = src->SrcRegister.SwizzleW; break;
+       default:
+               assert(0);
+       }
+
+       switch (src->SrcRegister.File) {
+       case TGSI_FILE_INPUT:
+               return &pc->attr[src->SrcRegister.Index * 4 + c];
+       case TGSI_FILE_TEMPORARY:
+               return &pc->temp[src->SrcRegister.Index * 4 + c];
+       case TGSI_FILE_CONSTANT:
+               return &pc->param[src->SrcRegister.Index * 4 + c];
+       case TGSI_FILE_IMMEDIATE:
+               return &pc->immd[src->SrcRegister.Index * 4 + c];
+       default:
+               break;
+       }
+
+       return NULL;
+}
+
+static boolean
+nv50_program_tx_insn(struct nv50_pc *pc, const union tgsi_full_token *tok)
+{
+       const struct tgsi_full_instruction *inst = &tok->FullInstruction;
+       struct nv50_reg *dst[4], *src[3][4], *none = NULL, *tmp;
+       unsigned mask;
+       int i, c;
+
+       NOUVEAU_ERR("insn %p\n", tok);
+
+       mask = inst->FullDstRegisters[0].DstRegister.WriteMask;
+
+       for (c = 0; c < 4; c++) {
+               if (mask & (1 << c))
+                       dst[c] = tgsi_dst(pc, c, &inst->FullDstRegisters[0]);
+               else
+                       dst[c] = NULL;
+       }
+
+       for (i = 0; i < inst->Instruction.NumSrcRegs; i++) {
+               for (c = 0; c < 4; c++)
+                       src[i][c] = tgsi_src(pc, c, &inst->FullSrcRegisters[i]);
+       }
+
+       switch (inst->Instruction.Opcode) {
+       case TGSI_OPCODE_ADD:
+               for (c = 0; c < 4; c++) {
+                       if (mask & (1 << c)) {
+                               emit(pc, 0x0b, dst[c],
+                                    src[0][c], src[1][c], none);
+                       }
+               }
+               break;
+       case TGSI_OPCODE_DP3:
+               tmp = alloc_temp(pc, NULL);
+               emit(pc, 0x0c, tmp, src[0][0], src[1][0], NULL);
+               emit(pc, 0x0e, tmp, src[0][1], src[1][1], tmp);
+               emit(pc, 0x0e, tmp, src[0][2], src[1][2], tmp);
+               for (c = 0; c < 4; c++) {
+                       if (mask & (1 << c))
+                               emit(pc, 0x01, dst[c], tmp, none, none);
+               }
+               free_temp(pc, tmp);
+               break;
+       case TGSI_OPCODE_DP4:
+               tmp = alloc_temp(pc, NULL);
+               emit(pc, 0x0c, tmp, src[0][0], src[1][0], NULL);
+               emit(pc, 0x0e, tmp, src[0][1], src[1][1], tmp);
+               emit(pc, 0x0e, tmp, src[0][2], src[1][2], tmp);
+               emit(pc, 0x0e, tmp, src[0][3], src[1][3], tmp);
+               for (c = 0; c < 4; c++) {
+                       if (mask & (1 << c))
+                               emit(pc, 0x01, dst[c], tmp, none, none);
+               }
+               free_temp(pc, tmp);
+               break;
+       case TGSI_OPCODE_MAD:
+               for (c = 0; c < 4; c++) {
+                       if (mask & (1 << c))
+                               emit(pc, 0x0e, dst[c],
+                                    src[0][c], src[1][c], src[2][c]);
+               }
+               break;
+       case TGSI_OPCODE_MOV:
+               for (c = 0; c < 4; c++) {
+                       if (mask & (1 << c))
+                               emit(pc, 0x01, dst[c], src[0][c], none, none);
+               }
+               break;
+       case TGSI_OPCODE_MUL:
+               for (c = 0; c < 4; c++) {
+                       if (mask & (1 << c))
+                               emit(pc, 0x0c, dst[c],
+                                    src[0][c], src[1][c], none);
+               }
+               break;
+       case TGSI_OPCODE_RCP:
+               for (c = 0; c < 4; c++) {
+                       if (mask & (1 << c))
+                               emit(pc, 0x09, dst[c],
+                                    src[0][c], none, none);
+               }
+               break;
+       case TGSI_OPCODE_END:
+               break;
+       default:
+               NOUVEAU_ERR("invalid opcode %d\n", inst->Instruction.Opcode);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static boolean
+nv50_program_tx_prep(struct nv50_pc *pc)
+{
+       struct tgsi_parse_context p;
+       boolean ret = FALSE;
+       unsigned i, c;
+
+       tgsi_parse_init(&p, pc->p->pipe.tokens);
+       while (!tgsi_parse_end_of_tokens(&p)) {
+               const union tgsi_full_token *tok = &p.FullToken;
+
+               tgsi_parse_token(&p);
+               switch (tok->Token.Type) {
+               case TGSI_TOKEN_TYPE_IMMEDIATE:
+               {
+                       const struct tgsi_full_immediate *imm =
+                               &p.FullToken.FullImmediate;
+
+                       pc->immd_nr++;
+                       pc->immd_buf = realloc(pc->immd_buf, 4 * pc->immd_nr *
+                                                            sizeof(float));
+                       pc->immd_buf[4 * (pc->immd_nr - 1) + 0] =
+                               imm->u.ImmediateFloat32[0].Float;
+                       pc->immd_buf[4 * (pc->immd_nr - 1) + 1] =
+                               imm->u.ImmediateFloat32[1].Float;
+                       pc->immd_buf[4 * (pc->immd_nr - 1) + 2] =
+                               imm->u.ImmediateFloat32[2].Float;
+                       pc->immd_buf[4 * (pc->immd_nr - 1) + 3] =
+                               imm->u.ImmediateFloat32[3].Float;
+               }
+                       break;
+               case TGSI_TOKEN_TYPE_DECLARATION:
+               {
+                       const struct tgsi_full_declaration *d;
+                       unsigned last;
+
+                       d = &p.FullToken.FullDeclaration;
+                       last = d->u.DeclarationRange.Last;
+
+                       switch (d->Declaration.File) {
+                       case TGSI_FILE_TEMPORARY:
+                               if (pc->temp_nr < (last + 1))
+                                       pc->temp_nr = last + 1;
+                               break;
+                       case TGSI_FILE_OUTPUT:
+                               if (pc->result_nr < (last + 1))
+                                       pc->result_nr = last + 1;
+                               break;
+                       case TGSI_FILE_INPUT:
+                               if (pc->attr_nr < (last + 1))
+                                       pc->attr_nr = last + 1;
+                               break;
+                       case TGSI_FILE_CONSTANT:
+                               if (pc->param_nr < (last + 1))
+                                       pc->param_nr = last + 1;
+                               break;
+                       default:
+                               NOUVEAU_ERR("bad decl file %d\n",
+                                           d->Declaration.File);
+                               goto out_err;
+                       }
+               }
+                       break;
+               case TGSI_TOKEN_TYPE_INSTRUCTION:
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       NOUVEAU_ERR("%d temps\n", pc->temp_nr);
+       if (pc->temp_nr) {
+               pc->temp = calloc(pc->temp_nr * 4, sizeof(struct nv50_reg));
+               if (!pc->temp)
+                       goto out_err;
+
+               for (i = 0; i < pc->temp_nr; i++) {
+                       for (c = 0; c < 4; c++) {
+                               pc->temp[i*4+c].type = P_TEMP;
+                               pc->temp[i*4+c].hw = -1;
+                               pc->temp[i*4+c].index = i;
+                       }
+               }
+       }
+
+       NOUVEAU_ERR("%d attrib regs\n", pc->attr_nr);
+       if (pc->attr_nr) {
+               int aid = 0;
+
+               pc->attr = calloc(pc->attr_nr * 4, sizeof(struct nv50_reg));
+               if (!pc->attr)
+                       goto out_err;
+
+               for (i = 0; i < pc->attr_nr; i++) {
+                       for (c = 0; c < 4; c++) {
+                               pc->p->cfg.vp.attr[aid/32] |= (1 << (aid % 32));
+                               pc->attr[i*4+c].type = P_ATTR;
+                               pc->attr[i*4+c].hw = aid++;
+                               pc->attr[i*4+c].index = i;
+                       }
+               }
+       }
+
+       NOUVEAU_ERR("%d result regs\n", pc->result_nr);
+       if (pc->result_nr) {
+               int rid = 0;
+
+               pc->result = calloc(pc->result_nr * 4, sizeof(struct nv50_reg));
+               if (!pc->result)
+                       goto out_err;
+
+               for (i = 0; i < pc->result_nr; i++) {
+                       for (c = 0; c < 4; c++) {
+                               pc->result[i*4+c].type = P_RESULT;
+                               pc->result[i*4+c].hw = rid++;
+                               pc->result[i*4+c].index = i;
+                       }
+               }
+       }
+
+       NOUVEAU_ERR("%d param regs\n", pc->param_nr);
+       if (pc->param_nr) {
+               int rid = 0;
+
+               pc->param = calloc(pc->param_nr * 4, sizeof(struct nv50_reg));
+               if (!pc->param)
+                       goto out_err;
+
+               for (i = 0; i < pc->param_nr; i++) {
+                       for (c = 0; c < 4; c++) {
+                               pc->param[i*4+c].type = P_CONST;
+                               pc->param[i*4+c].hw = rid++;
+                               pc->param[i*4+c].index = i;
+                       }
+               }
+       }
+
+       if (pc->immd_nr) {
+               int rid = pc->param_nr * 4;
+
+               pc->immd = calloc(pc->immd_nr * 4, sizeof(struct nv50_reg));
+               if (!pc->immd)
+                       goto out_err;
+
+               for (i = 0; i < pc->immd_nr; i++) {
+                       for (c = 0; c < 4; c++) {
+                               pc->immd[i*4+c].type = P_IMMD;
+                               pc->immd[i*4+c].hw = rid++;
+                               pc->immd[i*4+c].index = i;
+                       }
+               }
+       }
+
+       ret = TRUE;
+out_err:
+       tgsi_parse_free(&p);
+       return ret;
+}
+
+static boolean
+nv50_program_tx(struct nv50_program *p)
+{
+       struct tgsi_parse_context parse;
+       struct nv50_pc *pc;
+       boolean ret;
+
+       pc = CALLOC_STRUCT(nv50_pc);
+       if (!pc)
+               return FALSE;
+       pc->p = p;
+       pc->p->cfg.vp.high_temp = 4;
+
+       ret = nv50_program_tx_prep(pc);
+       if (ret == FALSE)
+               goto out_cleanup;
+
+       tgsi_parse_init(&parse, pc->p->pipe.tokens);
+       while (!tgsi_parse_end_of_tokens(&parse)) {
+               const union tgsi_full_token *tok = &parse.FullToken;
+
+               tgsi_parse_token(&parse);
+
+               switch (tok->Token.Type) {
+               case TGSI_TOKEN_TYPE_INSTRUCTION:
+                       ret = nv50_program_tx_insn(pc, tok);
+                       if (ret == FALSE)
+                               goto out_err;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       p->param_nr = pc->param_nr * 4;
+       p->immd_nr = pc->immd_nr * 4;
+       p->immd = pc->immd_buf;
+
+out_err:
+       tgsi_parse_free(&parse);
+
+out_cleanup:
+       return ret;
+}
+
+static void
+nv50_program_validate(struct nv50_context *nv50, struct nv50_program *p)
+{
+       struct tgsi_parse_context pc;
+
+       tgsi_parse_init(&pc, p->pipe.tokens);
+
+       if (pc.FullHeader.Processor.Processor == TGSI_PROCESSOR_FRAGMENT) {
+               p->insns_nr = 8;
+               p->insns = malloc(p->insns_nr * sizeof(unsigned));
+               p->insns[0] = 0x80000000;
+               p->insns[1] = 0x9000000c;
+               p->insns[2] = 0x82010600;
+               p->insns[3] = 0x82020604;
+               p->insns[4] = 0x80030609;
+               p->insns[5] = 0x00020780;
+               p->insns[6] = 0x8004060d;
+               p->insns[7] = 0x00020781;
+       } else
+       if (pc.FullHeader.Processor.Processor == TGSI_PROCESSOR_VERTEX) {
+               int i;
+
+               if (nv50_program_tx(p) == FALSE)
+                       assert(0);
+               p->insns[p->insns_nr - 1] |= 0x00000001;
+
+               for (i = 0; i < p->insns_nr; i++)
+                       NOUVEAU_ERR("%d 0x%08x\n", i, p->insns[i]);
+       } else {
+               NOUVEAU_ERR("invalid TGSI processor\n");
+               tgsi_parse_free(&pc);
+               return;
+       }
+
+       tgsi_parse_free(&pc);
+
+       p->translated = TRUE;
+}
+
+void
+nv50_vertprog_validate(struct nv50_context *nv50)
+{
+       struct pipe_winsys *ws = nv50->pipe.winsys;
+       struct nouveau_winsys *nvws = nv50->screen->nvws;
+       struct nouveau_grobj *tesla = nv50->screen->tesla;
+       struct nv50_program *p = nv50->vertprog;
+       struct nouveau_stateobj *so;
+       void *map;
+       int i;
+
+       if (!p->translated) {
+               nv50_program_validate(nv50, p);
+               if (!p->translated)
+                       assert(0);
+       }
+
+       if (!p->buffer)
+               p->buffer = ws->buffer_create(ws, 0x100, 0, p->insns_nr * 4);
+       map = ws->buffer_map(ws, p->buffer, PIPE_BUFFER_USAGE_CPU_WRITE);
+       memcpy(map, p->insns, p->insns_nr * 4);
+       ws->buffer_unmap(ws, p->buffer);
+
+       if (p->param_nr) {
+               float *cb;
+
+               cb = ws->buffer_map(ws, nv50->constbuf[PIPE_SHADER_VERTEX],
+                                   PIPE_BUFFER_USAGE_CPU_READ);
+               for (i = 0; i < p->param_nr; i++) {
+                       BEGIN_RING(tesla, 0x0f00, 2);
+                       OUT_RING  (i << 8);
+                       OUT_RING  (fui(cb[i]));
+               }
+               ws->buffer_unmap(ws, nv50->constbuf[PIPE_SHADER_VERTEX]);
+       }
+
+
+       for (i = 0; i < p->immd_nr; i++) {
+               BEGIN_RING(tesla, 0x0f00, 2);
+               OUT_RING  ((p->param_nr + i) << 8);
+               OUT_RING  (fui(p->immd[i]));
+       }
+
+       so = so_new(11, 2);
+       so_method(so, tesla, NV50TCL_VP_ADDRESS_HIGH, 2);
+       so_reloc (so, p->buffer, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD |
+                 NOUVEAU_BO_HIGH, 0, 0);
+       so_reloc (so, p->buffer, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD |
+                 NOUVEAU_BO_LOW, 0, 0);
+       so_method(so, tesla, 0x1650, 2);
+       so_data  (so, p->cfg.vp.attr[0]);
+       so_data  (so, p->cfg.vp.attr[1]);
+       so_method(so, tesla, 0x16ac, 2);
+       so_data  (so, 8);
+       so_data  (so, p->cfg.vp.high_temp);
+       so_method(so, tesla, 0x140c, 1);
+       so_data  (so, 0); /* program start offset */
+       so_emit(nv50->screen->nvws, so);
+       so_ref(NULL, &so);
+}
+
+void
+nv50_fragprog_validate(struct nv50_context *nv50)
+{
+       struct pipe_winsys *ws = nv50->pipe.winsys;
+       struct nouveau_grobj *tesla = nv50->screen->tesla;
+       struct nv50_program *p = nv50->fragprog;
+       struct nouveau_stateobj *so;
+       void *map;
+
+       if (!p->translated) {
+               nv50_program_validate(nv50, p);
+               if (!p->translated)
+                       assert(0);
+       }
+
+       if (!p->buffer)
+               p->buffer = ws->buffer_create(ws, 0x100, 0, p->insns_nr * 4);
+       map = ws->buffer_map(ws, p->buffer, PIPE_BUFFER_USAGE_CPU_WRITE);
+       memcpy(map, p->insns, p->insns_nr * 4);
+       ws->buffer_unmap(ws, p->buffer);
+
+       so = so_new(3, 2);
+       so_method(so, tesla, NV50TCL_FP_ADDRESS_HIGH, 2);
+       so_reloc (so, p->buffer, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD |
+                 NOUVEAU_BO_HIGH, 0, 0);
+       so_reloc (so, p->buffer, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD |
+                 NOUVEAU_BO_LOW, 0, 0);
+       so_emit(nv50->screen->nvws, so);
+       so_ref(NULL, &so);
+}
+
+void
+nv50_program_destroy(struct nv50_context *nv50, struct nv50_program *p)
+{
+       struct pipe_winsys *ws = nv50->pipe.winsys;
+
+       if (p->insns_nr) {
+               if (p->insns)
+                       FREE(p->insns);
+               p->insns_nr = 0;
+       }
+
+       if (p->buffer)
+               pipe_buffer_reference(ws, &p->buffer, NULL);
+
+       p->translated = 0;
+}
+
index 29c057a14535be64bcf13a44dc755844487109c2..fc3eeed9133e762dfcea0c72f8403931abae3db5 100644 (file)
@@ -203,8 +203,19 @@ nv50_screen_create(struct pipe_winsys *ws, struct nouveau_winsys *nvws)
                return NULL;
        }
 
+       /* Static constant buffer */
+       screen->constbuf = ws->buffer_create(ws, 0, 0, 256 * 4 * 4);
+       if (nvws->res_init(&screen->vp_data_heap, 0, 256)) {
+               NOUVEAU_ERR("Error initialising constant buffer\n");
+               nv50_screen_destroy(&screen->pipe);
+               return NULL;
+       }
+
        /* Static tesla init */
-       so = so_new(128, 0);
+       so = so_new(256, 20);
+
+       so_method(so, screen->tesla, 0x1558, 1);
+       so_data  (so, 1);
        so_method(so, screen->tesla, NV50TCL_DMA_NOTIFY, 1);
        so_data  (so, screen->sync->handle);
        so_method(so, screen->tesla, NV50TCL_DMA_IN_MEMORY0(0),
@@ -215,6 +226,61 @@ nv50_screen_create(struct pipe_winsys *ws, struct nouveau_winsys *nvws)
                  NV50TCL_DMA_IN_MEMORY1__SIZE);
        for (i = 0; i < NV50TCL_DMA_IN_MEMORY1__SIZE; i++)
                so_data(so, nvws->channel->vram->handle);
+       so_method(so, screen->tesla, 0x121c, 1);
+       so_data  (so, 1);
+
+       so_method(so, screen->tesla, 0x13bc, 1);
+       so_data  (so, 0x54);
+       so_method(so, screen->tesla, 0x13ac, 1);
+       so_data  (so, 1);
+       so_method(so, screen->tesla, 0x16bc, 2);
+       so_data  (so, 0x03020100);
+       so_data  (so, 0x07060504);
+       so_method(so, screen->tesla, 0x1988, 2);
+       so_data  (so, 0x08040404);
+       so_data  (so, 0x00000004);
+       so_method(so, screen->tesla, 0x16ac, 2);
+       so_data  (so, 8);
+       so_data  (so, 4);
+       so_method(so, screen->tesla, 0x16b8, 1);
+       so_data  (so, 8);
+
+       so_method(so, screen->tesla, 0x1280, 3);
+       so_reloc (so, screen->constbuf, 0, NOUVEAU_BO_VRAM |
+                 NOUVEAU_BO_RD | NOUVEAU_BO_HIGH, 0, 0);
+       so_reloc (so, screen->constbuf, 0, NOUVEAU_BO_VRAM |
+                 NOUVEAU_BO_RD | NOUVEAU_BO_LOW, 0, 0);
+       so_data  (so, 0x00001000);
+       so_method(so, screen->tesla, 0x1280, 3);
+       so_reloc (so, screen->constbuf, 0, NOUVEAU_BO_VRAM |
+                 NOUVEAU_BO_RD | NOUVEAU_BO_HIGH, 0, 0);
+       so_reloc (so, screen->constbuf, 0, NOUVEAU_BO_VRAM |
+                 NOUVEAU_BO_RD | NOUVEAU_BO_LOW, 0, 0);
+       so_data  (so, 0x00014000);
+       so_method(so, screen->tesla, 0x1280, 3);
+       so_reloc (so, screen->constbuf, 0, NOUVEAU_BO_VRAM |
+                 NOUVEAU_BO_RD | NOUVEAU_BO_HIGH, 0, 0);
+       so_reloc (so, screen->constbuf, 0, NOUVEAU_BO_VRAM |
+                 NOUVEAU_BO_RD | NOUVEAU_BO_LOW, 0, 0);
+       so_data  (so, 0x00024000);
+       so_method(so, screen->tesla, 0x1280, 3);
+       so_reloc (so, screen->constbuf, 0, NOUVEAU_BO_VRAM |
+                 NOUVEAU_BO_RD | NOUVEAU_BO_HIGH, 0, 0);
+       so_reloc (so, screen->constbuf, 0, NOUVEAU_BO_VRAM |
+                 NOUVEAU_BO_RD | NOUVEAU_BO_LOW, 0, 0);
+       so_data  (so, 0x00034000);
+       so_method(so, screen->tesla, 0x1280, 3);
+       so_reloc (so, screen->constbuf, 0, NOUVEAU_BO_VRAM |
+                 NOUVEAU_BO_RD | NOUVEAU_BO_HIGH, 0, 0);
+       so_reloc (so, screen->constbuf, 0, NOUVEAU_BO_VRAM |
+                 NOUVEAU_BO_RD | NOUVEAU_BO_LOW, 0, 0);
+       so_data  (so, 0x00040100);
+
+       for (i = 0; i < 16; i++) {
+               so_method(so, screen->tesla, 0x1080 + (i * 8), 2);
+               so_data  (so, 0x000000ff);
+               so_data  (so, 0xffffffff);
+       }
 
        so_emit(nvws, so);
        so_ref(NULL, &so);
index 5a2732e37f455ea90f7d6b18f2907fcd265db9a3..d63dd485085dc303e350e22db2ecf0bc782f2e37 100644 (file)
@@ -12,6 +12,9 @@ struct nv50_screen {
 
        struct nouveau_grobj *tesla;
        struct nouveau_notifier *sync;
+
+       struct pipe_buffer *constbuf;
+       struct nouveau_resource *vp_data_heap;
 };
 
 static INLINE struct nv50_screen *
index f42bae0c28da1c2353aa8c510845f5ebf87f9d4b..fd10a38378209a42086b17c81de7e9f362f8140a 100644 (file)
@@ -248,8 +248,8 @@ nv50_depth_stencil_alpha_state_create(struct pipe_context *pipe,
        struct nouveau_stateobj *so = so_new(64, 0);
 
        so_method(so, tesla, NV50TCL_DEPTH_WRITE_ENABLE, 1);
-       so_data  (so, cso->depth.writemask ? 1 : 0);
-       if (cso->depth.enabled) {
+       so_data  (so, 0); //cso->depth.writemask ? 1 : 0);
+       if (0 && cso->depth.enabled) {
                so_method(so, tesla, NV50TCL_DEPTH_TEST_ENABLE, 1);
                so_data  (so, 1);
                so_method(so, tesla, NV50TCL_DEPTH_TEST_FUNC, 1);
@@ -328,34 +328,58 @@ static void *
 nv50_vp_state_create(struct pipe_context *pipe,
                     const struct pipe_shader_state *cso)
 {
-       return NULL;
+       struct nv50_program *p = CALLOC_STRUCT(nv50_program);
+
+       p->pipe = *cso;
+       tgsi_scan_shader(p->pipe.tokens, &p->info);
+       return (void *)p;
 }
 
 static void
 nv50_vp_state_bind(struct pipe_context *pipe, void *hwcso)
 {
+       struct nv50_context *nv50 = nv50_context(pipe);
+
+       nv50->vertprog = hwcso;
+       nv50->dirty |= NV50_NEW_VERTPROG;
 }
 
 static void
 nv50_vp_state_delete(struct pipe_context *pipe, void *hwcso)
 {
+       struct nv50_context *nv50 = nv50_context(pipe);
+
+       nv50_program_destroy(nv50, hwcso);
+       FREE(hwcso);
 }
 
 static void *
 nv50_fp_state_create(struct pipe_context *pipe,
                     const struct pipe_shader_state *cso)
 {
-       return NULL;
+       struct nv50_program *p = CALLOC_STRUCT(nv50_program);
+
+       p->pipe = *cso;
+       tgsi_scan_shader(p->pipe.tokens, &p->info);
+       return (void *)p;
 }
 
 static void
 nv50_fp_state_bind(struct pipe_context *pipe, void *hwcso)
 {
+       struct nv50_context *nv50 = nv50_context(pipe);
+
+       nv50->fragprog = hwcso;
+       nv50->dirty |= NV50_NEW_FRAGPROG;
 }
 
 static void
 nv50_fp_state_delete(struct pipe_context *pipe, void *hwcso)
 {
+       struct nv50_context *nv50 = nv50_context(pipe);
+
+       nv50_program_destroy(nv50, hwcso);
+       FREE(hwcso);
 }
 
 static void
@@ -378,6 +402,16 @@ static void
 nv50_set_constant_buffer(struct pipe_context *pipe, uint shader, uint index,
                         const struct pipe_constant_buffer *buf )
 {
+       struct nv50_context *nv50 = nv50_context(pipe);
+
+       if (shader == PIPE_SHADER_VERTEX) {
+               nv50->constbuf[PIPE_SHADER_VERTEX] = buf->buffer;
+               nv50->dirty |= NV50_NEW_VERTPROG;
+       } else
+       if (shader == PIPE_SHADER_FRAGMENT) {
+               nv50->constbuf[PIPE_SHADER_FRAGMENT] = buf->buffer;
+               nv50->dirty |= NV50_NEW_FRAGPROG;
+       }
 }
 
 static void
@@ -424,12 +458,24 @@ static void
 nv50_set_vertex_buffers(struct pipe_context *pipe, unsigned count,
                        const struct pipe_vertex_buffer *vb)
 {
+       struct nv50_context *nv50 = nv50_context(pipe);
+
+       memcpy(nv50->vtxbuf, vb, sizeof(*vb) * count);
+       nv50->vtxbuf_nr = count;
+
+       nv50->dirty |= NV50_NEW_ARRAYS;
 }
 
 static void
 nv50_set_vertex_elements(struct pipe_context *pipe, unsigned count,
                         const struct pipe_vertex_element *ve)
 {
+       struct nv50_context *nv50 = nv50_context(pipe);
+
+       memcpy(nv50->vtxelt, ve, sizeof(*ve) * count);
+       nv50->vtxelt_nr = count;
+
+       nv50->dirty |= NV50_NEW_ARRAYS;
 }
 
 void
index a3b781d4c619d8ca9ddf1351ee5ee6c2217ea57c..be0c75ad6ea80b6ef9f930434b899d256dda8615 100644 (file)
@@ -2,6 +2,36 @@
 #define __NV50_STATE_H__
 
 #include "pipe/p_state.h"
+#include "tgsi/util/tgsi_scan.h"
 
+struct nv50_program_data {
+       int index;
+       int component;
+       float value;
+};
+
+struct nv50_program {
+       struct pipe_shader_state pipe;
+       struct tgsi_shader_info info;
+       boolean translated;
+
+       unsigned *insns;
+       unsigned insns_nr;
+
+       struct pipe_buffer *buffer;
+
+       unsigned param_nr;
+       float *immd;
+       unsigned immd_nr;
+
+       struct nouveau_resource *data;
+
+       union {
+               struct {
+                       unsigned high_temp;
+                       unsigned attr[2];
+               } vp;
+       } cfg;
+};
 
 #endif
index 68d419f9fc85b8aff28d6ad17cab94dec4ec6309..4a548378b7148fcddf59d4b8d25604d67ac88687 100644 (file)
@@ -15,8 +15,8 @@ nv50_state_validate_fb(struct nv50_context *nv50)
                        h = fb->cbufs[i]->height;
                        gw = 1;
                } else {
-                       assert(w != fb->cbufs[i]->width);
-                       assert(h != fb->cbufs[i]->height);
+                       assert(w == fb->cbufs[i]->width);
+                       assert(h == fb->cbufs[i]->height);
                }
 
                so_method(so, tesla, NV50TCL_RT_HORIZ(i), 2);
@@ -46,6 +46,9 @@ nv50_state_validate_fb(struct nv50_context *nv50)
                }
                so_data(so, 0x00000040);
                so_data(so, 0x00000000);
+
+               so_method(so, tesla, 0x1224, 1);
+               so_data  (so, 1);
        }
 
        if (fb->zsbuf) {
@@ -54,11 +57,11 @@ nv50_state_validate_fb(struct nv50_context *nv50)
                        h = fb->zsbuf->height;
                        gw = 1;
                } else {
-                       assert(w != fb->zsbuf->width);
-                       assert(h != fb->zsbuf->height);
+                       assert(w == fb->zsbuf->width);
+                       assert(h == fb->zsbuf->height);
                }
 
-               so_method(so, tesla, NV50TCL_RT_ADDRESS_HIGH(i), 5);
+               so_method(so, tesla, NV50TCL_ZETA_ADDRESS_HIGH, 5);
                so_reloc (so, fb->zsbuf->buffer, fb->zsbuf->offset,
                          NOUVEAU_BO_VRAM | NOUVEAU_BO_HIGH, 0, 0);
                so_reloc (so, fb->zsbuf->buffer, fb->zsbuf->offset,
@@ -83,9 +86,12 @@ nv50_state_validate_fb(struct nv50_context *nv50)
        so_method(so, tesla, NV50TCL_VIEWPORT_HORIZ, 2);
        so_data  (so, w << 16);
        so_data  (so, h << 16);
-       so_method(so, tesla, 0xff0, 2);
+       so_method(so, tesla, 0xff4, 2);
        so_data  (so, w << 16);
        so_data  (so, h << 16);
+       so_method(so, tesla, 0xdf8, 2);
+       so_data  (so, 0);
+       so_data  (so, h);
 
        so_emit(nv50->screen->nvws, so);
        so_ref(NULL, &so);
@@ -108,6 +114,12 @@ nv50_state_validate(struct nv50_context *nv50)
        if (nv50->dirty & NV50_NEW_ZSA)
                so_emit(nvws, nv50->zsa->so);
 
+       if (nv50->dirty & NV50_NEW_VERTPROG)
+               nv50_vertprog_validate(nv50);
+
+       if (nv50->dirty & NV50_NEW_FRAGPROG)
+               nv50_fragprog_validate(nv50);
+
        if (nv50->dirty & NV50_NEW_RASTERIZER)
                so_emit(nvws, nv50->rasterizer->so);
 
@@ -150,12 +162,15 @@ nv50_state_validate(struct nv50_context *nv50)
                so_data  (so, fui(nv50->viewport.translate[2]));
                so_method(so, tesla, NV50TCL_VIEWPORT_UNK1(0), 3);
                so_data  (so, fui(nv50->viewport.scale[0]));
-               so_data  (so, fui(nv50->viewport.scale[1]));
+               so_data  (so, fui(-nv50->viewport.scale[1]));
                so_data  (so, fui(nv50->viewport.scale[2]));
                so_emit(nvws, so);
                so_ref(NULL, &so);
        }
 
+       if (nv50->dirty & NV50_NEW_ARRAYS)
+               nv50_vbo_validate(nv50);
+
        nv50->dirty = 0;
        return TRUE;
 }
index b01ce1d42c557cc8898ae3c017cd54072a0237d4..f086c7625822b4842c3b499f268a607e06612bf1 100644 (file)
@@ -5,6 +5,29 @@
 #include "nv50_context.h"
 #include "nv50_state.h"
 
+static INLINE unsigned
+nv50_prim(unsigned mode)
+{
+       switch (mode) {
+       case PIPE_PRIM_POINTS: return NV50TCL_VERTEX_BEGIN_POINTS;
+       case PIPE_PRIM_LINES: return NV50TCL_VERTEX_BEGIN_LINES;
+       case PIPE_PRIM_LINE_LOOP: return NV50TCL_VERTEX_BEGIN_LINE_LOOP;
+       case PIPE_PRIM_LINE_STRIP: return NV50TCL_VERTEX_BEGIN_LINE_STRIP;
+       case PIPE_PRIM_TRIANGLES: return NV50TCL_VERTEX_BEGIN_TRIANGLES;
+       case PIPE_PRIM_TRIANGLE_STRIP:
+               return NV50TCL_VERTEX_BEGIN_TRIANGLE_STRIP;
+       case PIPE_PRIM_TRIANGLE_FAN: return NV50TCL_VERTEX_BEGIN_TRIANGLE_FAN;
+       case PIPE_PRIM_QUADS: return NV50TCL_VERTEX_BEGIN_QUADS;
+       case PIPE_PRIM_QUAD_STRIP: return NV50TCL_VERTEX_BEGIN_QUAD_STRIP;
+       case PIPE_PRIM_POLYGON: return NV50TCL_VERTEX_BEGIN_POLYGON;
+       default:
+               break;
+       }
+
+       NOUVEAU_ERR("invalid primitive type %d\n", mode);
+       return NV50TCL_VERTEX_BEGIN_POINTS;
+}
+
 boolean
 nv50_draw_arrays(struct pipe_context *pipe, unsigned mode, unsigned start,
                 unsigned count)
@@ -12,8 +35,22 @@ nv50_draw_arrays(struct pipe_context *pipe, unsigned mode, unsigned start,
        struct nv50_context *nv50 = nv50_context(pipe);
 
        nv50_state_validate(nv50);
-
        NOUVEAU_ERR("unimplemented\n");
+
+       BEGIN_RING(tesla, 0x142c, 1);
+       OUT_RING  (0);
+       BEGIN_RING(tesla, 0x142c, 1);
+       OUT_RING  (0);
+
+       BEGIN_RING(tesla, NV50TCL_VERTEX_BEGIN, 1);
+       OUT_RING  (nv50_prim(mode));
+       BEGIN_RING(tesla, NV50TCL_VERTEX_BUFFER_FIRST, 2);
+       OUT_RING  (start);
+       OUT_RING  (count);
+       BEGIN_RING(tesla, NV50TCL_VERTEX_END, 1);
+       OUT_RING  (0);
+
+       pipe->flush(pipe, 0, NULL);
        return TRUE;
 }
 
@@ -30,3 +67,49 @@ nv50_draw_elements(struct pipe_context *pipe,
        return TRUE;
 }
 
+void
+nv50_vbo_validate(struct nv50_context *nv50)
+{
+       struct nouveau_grobj *tesla = nv50->screen->tesla;
+       struct nouveau_stateobj *vtxbuf, *vtxfmt;
+       int i, vpi = 0;
+
+       vtxbuf = so_new(nv50->vtxelt_nr * 4, nv50->vtxelt_nr * 2);
+       vtxfmt = so_new(nv50->vtxelt_nr + 1, 0);
+       so_method(vtxfmt, tesla, 0x1ac0, nv50->vtxelt_nr);
+
+       for (i = 0; i < nv50->vtxelt_nr; i++) {
+               struct pipe_vertex_element *ve = &nv50->vtxelt[i];
+               struct pipe_vertex_buffer *vb =
+                       &nv50->vtxbuf[ve->vertex_buffer_index];
+
+               switch (ve->src_format) {
+               case PIPE_FORMAT_R32G32B32_FLOAT:
+                       so_data(vtxfmt, 0x7e100000 | i);
+                       break;
+               default:
+               {
+                       char fmt[128];
+                       pf_sprint_name(fmt, ve->src_format);
+                       NOUVEAU_ERR("invalid vbo format %s\n", fmt);
+                       assert(0);
+                       return;
+               }
+               }
+
+               so_method(vtxbuf, tesla, 0x900 + (i * 16), 3);
+               so_data  (vtxbuf, 0x20000000 | vb->pitch);
+               so_reloc (vtxbuf, vb->buffer, vb->buffer_offset +
+                         ve->src_offset, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD |
+                         NOUVEAU_BO_HIGH, 0, 0);
+               so_reloc (vtxbuf, vb->buffer, vb->buffer_offset +
+                         ve->src_offset, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD |
+                         NOUVEAU_BO_LOW, 0, 0);
+       }
+
+       so_emit(nv50->screen->nvws, vtxfmt);
+       so_ref (NULL, &vtxfmt);
+       so_emit(nv50->screen->nvws, vtxbuf);
+       so_ref (NULL, &vtxbuf);
+}
+