summaryrefslogtreecommitdiffstats
path: root/src/gallium/drivers/nv50/nv50_program.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gallium/drivers/nv50/nv50_program.c')
-rw-r--r--src/gallium/drivers/nv50/nv50_program.c742
1 files changed, 742 insertions, 0 deletions
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;
+}
+