summaryrefslogtreecommitdiffstats
path: root/src/gallium/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'src/gallium/drivers')
-rw-r--r--src/gallium/drivers/nv50/Makefile1
-rw-r--r--src/gallium/drivers/nv50/nv50_context.h18
-rw-r--r--src/gallium/drivers/nv50/nv50_program.c742
-rw-r--r--src/gallium/drivers/nv50/nv50_screen.c68
-rw-r--r--src/gallium/drivers/nv50/nv50_screen.h3
-rw-r--r--src/gallium/drivers/nv50/nv50_state.c54
-rw-r--r--src/gallium/drivers/nv50/nv50_state.h30
-rw-r--r--src/gallium/drivers/nv50/nv50_state_validate.c29
-rw-r--r--src/gallium/drivers/nv50/nv50_vbo.c85
9 files changed, 1017 insertions, 13 deletions
diff --git a/src/gallium/drivers/nv50/Makefile b/src/gallium/drivers/nv50/Makefile
index a62a4d961bc..2cc6e9de287 100644
--- a/src/gallium/drivers/nv50/Makefile
+++ b/src/gallium/drivers/nv50/Makefile
@@ -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 \
diff --git a/src/gallium/drivers/nv50/nv50_context.h b/src/gallium/drivers/nv50/nv50_context.h
index e68c702deae..d4d716b94bf 100644
--- a/src/gallium/drivers/nv50/nv50_context.h
+++ b/src/gallium/drivers/nv50/nv50_context.h
@@ -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
index 00000000000..30953b7d8af
--- /dev/null
+++ b/src/gallium/drivers/nv50/nv50_program.c
@@ -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;
+}
+
diff --git a/src/gallium/drivers/nv50/nv50_screen.c b/src/gallium/drivers/nv50/nv50_screen.c
index 29c057a1453..fc3eeed9133 100644
--- a/src/gallium/drivers/nv50/nv50_screen.c
+++ b/src/gallium/drivers/nv50/nv50_screen.c
@@ -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);
diff --git a/src/gallium/drivers/nv50/nv50_screen.h b/src/gallium/drivers/nv50/nv50_screen.h
index 5a2732e37f4..d63dd485085 100644
--- a/src/gallium/drivers/nv50/nv50_screen.h
+++ b/src/gallium/drivers/nv50/nv50_screen.h
@@ -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 *
diff --git a/src/gallium/drivers/nv50/nv50_state.c b/src/gallium/drivers/nv50/nv50_state.c
index f42bae0c28d..fd10a383782 100644
--- a/src/gallium/drivers/nv50/nv50_state.c
+++ b/src/gallium/drivers/nv50/nv50_state.c
@@ -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
diff --git a/src/gallium/drivers/nv50/nv50_state.h b/src/gallium/drivers/nv50/nv50_state.h
index a3b781d4c61..be0c75ad6ea 100644
--- a/src/gallium/drivers/nv50/nv50_state.h
+++ b/src/gallium/drivers/nv50/nv50_state.h
@@ -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
diff --git a/src/gallium/drivers/nv50/nv50_state_validate.c b/src/gallium/drivers/nv50/nv50_state_validate.c
index 68d419f9fc8..4a548378b71 100644
--- a/src/gallium/drivers/nv50/nv50_state_validate.c
+++ b/src/gallium/drivers/nv50/nv50_state_validate.c
@@ -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;
}
diff --git a/src/gallium/drivers/nv50/nv50_vbo.c b/src/gallium/drivers/nv50/nv50_vbo.c
index b01ce1d42c5..f086c762582 100644
--- a/src/gallium/drivers/nv50/nv50_vbo.c
+++ b/src/gallium/drivers/nv50/nv50_vbo.c
@@ -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);
+}
+