Esempio n. 1
0
static void
nvfx_ucp_validate(struct nvfx_context* nvfx)
{
	struct nouveau_channel* chan = nvfx->screen->base.channel;
	struct nouveau_grobj *eng3d = nvfx->screen->eng3d;
	unsigned enables[7] =
	{
			0,
			NV30_3D_VP_CLIP_PLANES_ENABLE_PLANE0,
			NV30_3D_VP_CLIP_PLANES_ENABLE_PLANE0 | NV30_3D_VP_CLIP_PLANES_ENABLE_PLANE1,
			NV30_3D_VP_CLIP_PLANES_ENABLE_PLANE0 | NV30_3D_VP_CLIP_PLANES_ENABLE_PLANE1 | NV30_3D_VP_CLIP_PLANES_ENABLE_PLANE2,
			NV30_3D_VP_CLIP_PLANES_ENABLE_PLANE0 | NV30_3D_VP_CLIP_PLANES_ENABLE_PLANE1 | NV30_3D_VP_CLIP_PLANES_ENABLE_PLANE2 | NV30_3D_VP_CLIP_PLANES_ENABLE_PLANE3,
			NV30_3D_VP_CLIP_PLANES_ENABLE_PLANE0 | NV30_3D_VP_CLIP_PLANES_ENABLE_PLANE1 | NV30_3D_VP_CLIP_PLANES_ENABLE_PLANE2 | NV30_3D_VP_CLIP_PLANES_ENABLE_PLANE3 | NV30_3D_VP_CLIP_PLANES_ENABLE_PLANE4,
			NV30_3D_VP_CLIP_PLANES_ENABLE_PLANE0 | NV30_3D_VP_CLIP_PLANES_ENABLE_PLANE1 | NV30_3D_VP_CLIP_PLANES_ENABLE_PLANE2 | NV30_3D_VP_CLIP_PLANES_ENABLE_PLANE3 | NV30_3D_VP_CLIP_PLANES_ENABLE_PLANE4 | NV30_3D_VP_CLIP_PLANES_ENABLE_PLANE5,
	};

	if(!nvfx->use_vp_clipping)
	{
		BEGIN_RING(chan, eng3d, NV30_3D_VP_CLIP_PLANES_ENABLE, 1);
		OUT_RING(chan, 0);

		BEGIN_RING(chan, eng3d, NV30_3D_VP_CLIP_PLANE(0, 0),
			   nvfx->clip.nr * 4);
		OUT_RINGp(chan, &nvfx->clip.ucp[0][0], nvfx->clip.nr * 4);
	}

	BEGIN_RING(chan, eng3d, NV30_3D_VP_CLIP_PLANES_ENABLE, 1);
	OUT_RING(chan, enables[nvfx->clip.nr]);
}
Esempio n. 2
0
static INLINE void
emit_elt32(void* priv, unsigned start, unsigned vc)
{
	struct push_context* ctx = priv;
	struct nouveau_channel *chan = ctx->chan;
	uint32_t *elts = (uint32_t *)ctx->idxbuf + start;
	int idxbias = ctx->idxbias;

	while (vc) {
		unsigned push = MIN2(vc, 2047);

		OUT_RING(chan, RING_3D_NI(NV30_3D_VB_ELEMENT_U32, push));
		assert(AVAIL_RING(chan) >= push);
		if(idxbias)
		{
			for(unsigned i = 0; i < push; ++i)
				OUT_RING(chan, elts[i] + idxbias);
		}
		else
			OUT_RINGp(chan, elts, push);

		vc -= push;
		elts += push;
	}
}
Esempio n. 3
0
void
nv10_emit_viewport(struct gl_context *ctx, int emit)
{
	struct nouveau_channel *chan = context_chan(ctx);
	struct nouveau_grobj *celsius = context_eng3d(ctx);
	struct gl_viewport_attrib *vp = &ctx->Viewport;
	struct gl_framebuffer *fb = ctx->DrawBuffer;
	float a[4] = {};

	get_viewport_translate(ctx, a);
	a[0] -= 2048;
	a[1] -= 2048;
	if (nv10_use_viewport_zclear(ctx))
		a[2] = nv10_transform_depth(ctx, (vp->Far + vp->Near) / 2);

	BEGIN_RING(chan, celsius, NV10_3D_VIEWPORT_TRANSLATE_X, 4);
	OUT_RINGp(chan, a, 4);

	BEGIN_RING(chan, celsius, NV10_3D_VIEWPORT_CLIP_HORIZ(0), 1);
	OUT_RING(chan, (fb->Width - 1) << 16 | 0x08000800);
	BEGIN_RING(chan, celsius, NV10_3D_VIEWPORT_CLIP_VERT(0), 1);
	OUT_RING(chan, (fb->Height - 1) << 16 | 0x08000800);

	context_dirty(ctx, PROJECTION);
}
Esempio n. 4
0
void
nv50_gp_linkage_validate(struct nv50_context *nv50)
{
   struct nouveau_channel *chan = nv50->screen->base.channel;
   struct nv50_program *vp = nv50->vertprog;
   struct nv50_program *gp = nv50->gmtyprog;
   int m = 0;
   int n;
   uint8_t map[64];

   if (!gp)
      return;
   memset(map, 0, sizeof(map));

   m = nv50_vp_gp_mapping(map, m, vp, gp);

   n = (m + 3) / 4;

   BEGIN_RING(chan, RING_3D(VP_GP_BUILTIN_ATTR_EN), 1);
   OUT_RING  (chan, vp->vp.attrs[2] | gp->vp.attrs[2]);

   BEGIN_RING(chan, RING_3D(VP_RESULT_MAP_SIZE), 1);
   OUT_RING  (chan, m);
   BEGIN_RING(chan, RING_3D(VP_RESULT_MAP(0)), n);
   OUT_RINGp (chan, map, n);
}
Esempio n. 5
0
static inline void nv10_render_line(GLcontext *ctx,GLuint v1,GLuint v2)
{
	struct nouveau_context *nmesa = NOUVEAU_CONTEXT(ctx);
	GLubyte *vertptr = (GLubyte *)nmesa->verts;
	GLuint vertsize = nmesa->vertex_size;
	GLuint size_dword = vertsize*(2)/4;

	/* OUT_RINGp wants size in DWORDS */
	vertsize >>= 2;

	nv10ExtendPrimitive(nmesa, size_dword);
	nv10StartPrimitive(nmesa,GL_LINES+1,size_dword);
	OUT_RINGp((nouveauVertex*)(vertptr+(v1*vertsize)),vertsize);
	OUT_RINGp((nouveauVertex*)(vertptr+(v2*vertsize)),vertsize);
	nv10FinishPrimitive(nmesa);
}
Esempio n. 6
0
void
nv20_emit_tex_gen(GLcontext *ctx, int emit)
{
	const int i = emit - NOUVEAU_STATE_TEX_GEN0;
	struct nouveau_context *nctx = to_nouveau_context(ctx);
	struct nouveau_channel *chan = context_chan(ctx);
	struct nouveau_grobj *kelvin = context_eng3d(ctx);
	struct gl_texture_unit *unit = &ctx->Texture.Unit[i];
	int j;

	for (j = 0; j < 4; j++) {
		if (nctx->fallback == HWTNL && (unit->TexGenEnabled & 1 << j)) {
			struct gl_texgen *coord = get_texgen_coord(unit, j);
			float *k = get_texgen_coeff(coord);

			if (k) {
				BEGIN_RING(chan, kelvin, TX_GEN_COEFF(i, j), 4);
				OUT_RINGp(chan, k, 4);
			}

			BEGIN_RING(chan, kelvin, TX_GEN_MODE(i, j), 1);
			OUT_RING(chan, nvgl_texgen_mode(coord->Mode));

		} else {
			BEGIN_RING(chan, kelvin, TX_GEN_MODE(i, j), 1);
			OUT_RING(chan, 0);
		}
	}
}
Esempio n. 7
0
void
nvfx_state_stipple_validate(struct nvfx_context *nvfx)
{
	struct nouveau_channel *chan = nvfx->screen->base.channel;
	struct nouveau_grobj *eng3d = nvfx->screen->eng3d;

	BEGIN_RING(chan, eng3d, NV30_3D_POLYGON_STIPPLE_PATTERN(0), 32);
	OUT_RINGp(chan, nvfx->stipple, 32);
}
Esempio n. 8
0
static void
nv50_sprite_coords_validate(struct nv50_context *nv50)
{
   struct nouveau_channel *chan = nv50->screen->base.channel;
   uint32_t pntc[8], mode;
   struct nv50_program *fp = nv50->fragprog;
   unsigned i, c;
   unsigned m = (nv50->state.interpolant_ctrl >> 8) & 0xff;

   if (!nv50->rast->pipe.point_quad_rasterization) {
      if (nv50->state.point_sprite) {
         BEGIN_RING(chan, RING_3D(POINT_COORD_REPLACE_MAP(0)), 8);
         for (i = 0; i < 8; ++i)
            OUT_RING(chan, 0);

         nv50->state.point_sprite = FALSE;
      }
      return;
   } else {
      nv50->state.point_sprite = TRUE;
   }

   memset(pntc, 0, sizeof(pntc));

   for (i = 0; i < fp->in_nr; i++) {
      unsigned n = util_bitcount(fp->in[i].mask);

      if (fp->in[i].sn != TGSI_SEMANTIC_GENERIC) {
         m += n;
         continue;
      }
      if (!(nv50->rast->pipe.sprite_coord_enable & (1 << fp->in[i].si))) {
         m += n;
         continue;
      }

      for (c = 0; c < 4; ++c) {
         if (fp->in[i].mask & (1 << c)) {
            pntc[m / 8] |= (c + 1) << ((m % 8) * 4);
            ++m;
         }
      }
   }

   if (nv50->rast->pipe.sprite_coord_mode == PIPE_SPRITE_COORD_LOWER_LEFT)
      mode = 0x00;
   else
      mode = 0x10;

   BEGIN_RING(chan, RING_3D(POINT_SPRITE_CTRL), 1);
   OUT_RING  (chan, mode);

   BEGIN_RING(chan, RING_3D(POINT_COORD_REPLACE_MAP(0)), 8);
   OUT_RINGp (chan, pntc, 8);
}
Esempio n. 9
0
static void
nvc0_m2mf_push_rect(struct pipe_screen *pscreen,
                    const struct nv50_m2mf_rect *dst,
                    const void *data,
                    unsigned nblocksx, unsigned nblocksy)
{
   struct nouveau_channel *chan;
   const uint8_t *src = (const uint8_t *)data;
   const int cpp = dst->cpp;
   const int line_len = nblocksx * cpp;
   int dy = dst->y;

   assert(nouveau_bo_tile_layout(dst->bo));

   BEGIN_RING(chan, RING_MF(TILING_MODE_OUT), 5);
   OUT_RING  (chan, dst->tile_mode);
   OUT_RING  (chan, dst->width * cpp);
   OUT_RING  (chan, dst->height);
   OUT_RING  (chan, dst->depth);
   OUT_RING  (chan, dst->z);

   while (nblocksy) {
      int line_count, words;
      int size = MIN2(AVAIL_RING(chan), NV04_PFIFO_MAX_PACKET_LEN);

      if (size < (12 + words)) {
         FIRE_RING(chan);
         continue;
      }
      line_count = (size * 4) / line_len;
      words = (line_count * line_len + 3) / 4;

      BEGIN_RING(chan, RING_MF(OFFSET_OUT_HIGH), 2);
      OUT_RELOCh(chan, dst->bo, dst->base, dst->domain | NOUVEAU_BO_WR);
      OUT_RELOCl(chan, dst->bo, dst->base, dst->domain | NOUVEAU_BO_WR);

      BEGIN_RING(chan, RING_MF(TILING_POSITION_OUT_X), 2);
      OUT_RING  (chan, dst->x * cpp);
      OUT_RING  (chan, dy);
      BEGIN_RING(chan, RING_MF(LINE_LENGTH_IN), 2);
      OUT_RING  (chan, line_len);
      OUT_RING  (chan, line_count);
      BEGIN_RING(chan, RING_MF(EXEC), 1);
      OUT_RING  (chan, (1 << NVC0_M2MF_EXEC_INC__SHIFT) |
                 NVC0_M2MF_EXEC_PUSH | NVC0_M2MF_EXEC_LINEAR_IN);

      BEGIN_RING_NI(chan, RING_MF(DATA), words);
      OUT_RINGp (chan, src, words);

      dy += line_count;
      src += line_len * line_count;
      nblocksy -= line_count;
   }
}
Esempio n. 10
0
static inline void nv10_render_generic_primitive_verts(GLcontext *ctx,GLuint start,GLuint count,GLuint flags,GLuint prim)
{
	struct nouveau_context *nmesa = NOUVEAU_CONTEXT(ctx);
	GLubyte *vertptr = (GLubyte *)nmesa->verts;
	GLuint vertsize = nmesa->vertex_size;
	GLuint size_dword = vertsize*(count-start)/4;

	nv10ExtendPrimitive(nmesa, size_dword);
	nv10StartPrimitive(nmesa,prim+1,size_dword);
	OUT_RINGp((nouveauVertex*)(vertptr+(start*vertsize)),size_dword);
	nv10FinishPrimitive(nmesa);
}
Esempio n. 11
0
static void
nvfx_vertprog_ucp_validate(struct nvfx_context* nvfx)
{
	struct nouveau_channel* chan = nvfx->screen->base.channel;
	struct nouveau_grobj *eng3d = nvfx->screen->eng3d;
	unsigned i;
	struct nvfx_vertex_program* vp = nvfx->hw_vertprog;
	if(nvfx->clip.nr != vp->clip_nr)
	{
		unsigned idx;

		/* remove last instruction bit */
		if(vp->clip_nr >= 0)
		{
			idx = vp->nr_insns - 7 + vp->clip_nr;
			BEGIN_RING(chan, eng3d, NV30_3D_VP_UPLOAD_FROM_ID, 1);
			OUT_RING(chan,  vp->exec->start + idx);
			BEGIN_RING(chan, eng3d, NV30_3D_VP_UPLOAD_INST(0), 4);
			OUT_RINGp (chan, vp->insns[idx].data, 4);
		}

		 /* set last instruction bit */
		idx = vp->nr_insns - 7 + nvfx->clip.nr;
		BEGIN_RING(chan, eng3d, NV30_3D_VP_UPLOAD_FROM_ID, 1);
		OUT_RING(chan,  vp->exec->start + idx);
		BEGIN_RING(chan, eng3d, NV30_3D_VP_UPLOAD_INST(0), 4);
		OUT_RINGp(chan, vp->insns[idx].data, 3);
		OUT_RING(chan, vp->insns[idx].data[3] | 1);
		vp->clip_nr = nvfx->clip.nr;
	}

	// TODO: only do this for the ones changed
	for(i = 0; i < nvfx->clip.nr; ++i)
	{
		BEGIN_RING(chan, eng3d, NV30_3D_VP_UPLOAD_CONST_ID, 5);
		OUT_RING(chan, vp->data->start + i);
		OUT_RINGp (chan, nvfx->clip.ucp[i], 4);
	}
}
Esempio n. 12
0
static inline void nv10_render_generic_primitive_elts(GLcontext *ctx,GLuint start,GLuint count,GLuint flags,GLuint prim)
{
	struct nouveau_context *nmesa = NOUVEAU_CONTEXT(ctx);
	GLubyte *vertptr = (GLubyte *)nmesa->verts;
	GLuint vertsize = nmesa->vertex_size;
	GLuint size_dword = vertsize*(count-start)/4;
	const GLuint * const elt = TNL_CONTEXT(ctx)->vb.Elts;
	GLuint j;

	nv10ExtendPrimitive(nmesa, size_dword);
	nv10StartPrimitive(nmesa,prim+1,size_dword);
	for (j=start; j<count; j++ ) {
		OUT_RINGp((nouveauVertex*)(vertptr+(elt[j]*vertsize)),vertsize/4);
	}
	nv10FinishPrimitive(nmesa);
}
Esempio n. 13
0
void
nv20_emit_viewport(GLcontext *ctx, int emit)
{
	struct nouveau_channel *chan = context_chan(ctx);
	struct nouveau_grobj *kelvin = context_eng3d(ctx);
	struct gl_framebuffer *fb = ctx->DrawBuffer;
	float a[4] = {};

	get_viewport_translate(ctx, a);

	BEGIN_RING(chan, kelvin, NV20TCL_VIEWPORT_TRANSLATE_X, 4);
	OUT_RINGp(chan, a, 4);

	BEGIN_RING(chan, kelvin, NV20TCL_VIEWPORT_CLIP_HORIZ(0), 1);
	OUT_RING(chan, (fb->Width - 1) << 16);
	BEGIN_RING(chan, kelvin, NV20TCL_VIEWPORT_CLIP_VERT(0), 1);
	OUT_RING(chan, (fb->Height - 1) << 16);

	context_dirty(ctx, PROJECTION);
}
Esempio n. 14
0
static INLINE void
nv40_draw_elements_u32(struct nv40_context *nv40, void *ib,
		       unsigned mode, unsigned start, unsigned count)
{
	struct nouveau_channel *chan = nv40->nvws->channel;

	while (count) {
		uint32_t *elts = (uint32_t *)ib + start;
		unsigned vc, push, restart;

		nv40_state_emit(nv40);

		vc = nouveau_vbuf_split(chan->pushbuf->remaining, 5, 1,
					mode, start, count, &restart);
		if (vc == 0) {
			FIRE_RING(NULL);
			continue;
		}
		count -= vc;

		BEGIN_RING(curie, NV40TCL_BEGIN_END, 1);
		OUT_RING  (nvgl_primitive(mode));

		while (vc) {
			push = MIN2(vc, 2047);

			BEGIN_RING_NI(curie, NV40TCL_VB_ELEMENT_U32, push);
			OUT_RINGp    (elts, push);

			vc -= push;
			elts += push;
		}

		BEGIN_RING(curie, NV40TCL_BEGIN_END, 1);
		OUT_RING  (0);

		start = restart;
	}
}
Esempio n. 15
0
void
nvc0_m2mf_push_linear(struct nouveau_context *nv,
                      struct nouveau_bo *dst, unsigned offset, unsigned domain,
                      unsigned size, void *data)
{
   struct nouveau_channel *chan = nv->screen->channel;
   uint32_t *src = (uint32_t *)data;
   unsigned count = (size + 3) / 4;

   MARK_RING (chan, 8, 2);

   BEGIN_RING(chan, RING_MF(OFFSET_OUT_HIGH), 2);
   OUT_RELOCh(chan, dst, offset, domain | NOUVEAU_BO_WR);
   OUT_RELOCl(chan, dst, offset, domain | NOUVEAU_BO_WR);
   BEGIN_RING(chan, RING_MF(LINE_LENGTH_IN), 2);
   OUT_RING  (chan, size);
   OUT_RING  (chan, 1);
   BEGIN_RING(chan, RING_MF(EXEC), 1);
   OUT_RING  (chan, 0x100111);

   while (count) {
      unsigned nr = AVAIL_RING(chan);

      if (nr < 9) {
         FIRE_RING(chan);
         nouveau_bo_validate(chan, dst, NOUVEAU_BO_WR);
         continue;
      }
      nr = MIN2(count, nr - 1);
      nr = MIN2(nr, NV04_PFIFO_MAX_PACKET_LEN);
   
      BEGIN_RING_NI(chan, RING_MF(DATA), nr);
      OUT_RINGp (chan, src, nr);

      src += nr;
      count -= nr;
   }
}
Esempio n. 16
0
void
nvc0_m2mf_push_linear(struct nouveau_context *nv,
                      struct nouveau_bo *dst, unsigned offset, unsigned domain,
                      unsigned size, const void *data)
{
   struct nouveau_channel *chan = nv->screen->channel;
   uint32_t *src = (uint32_t *)data;
   unsigned count = (size + 3) / 4;

   while (count) {
      unsigned nr;

      MARK_RING (chan, 16, 2);

      nr = AVAIL_RING(chan) - 9;
      nr = MIN2(count, nr);
      nr = MIN2(nr, NV04_PFIFO_MAX_PACKET_LEN);

      BEGIN_RING(chan, RING_MF(OFFSET_OUT_HIGH), 2);
      OUT_RELOCh(chan, dst, offset, domain | NOUVEAU_BO_WR);
      OUT_RELOCl(chan, dst, offset, domain | NOUVEAU_BO_WR);
      BEGIN_RING(chan, RING_MF(LINE_LENGTH_IN), 2);
      OUT_RING  (chan, nr * 4);
      OUT_RING  (chan, 1);
      BEGIN_RING(chan, RING_MF(EXEC), 1);
      OUT_RING  (chan, 0x100111);

      /* must not be interrupted (trap on QUERY fence, 0x50 works however) */
      BEGIN_RING_NI(chan, RING_MF(DATA), nr);
      OUT_RINGp (chan, src, nr);

      count -= nr;
      src += nr;
      offset += nr * 4;
   }
}
Esempio n. 17
0
void
nvc0_cb_push(struct nouveau_context *nv,
             struct nouveau_bo *bo, unsigned domain,
             unsigned base, unsigned size,
             unsigned offset, unsigned words, const uint32_t *data)
{
   struct nouveau_channel *chan = nv->screen->channel;

   assert(!(offset & 3));
   size = align(size, 0x100);

   MARK_RING (chan, 16, 2);
   BEGIN_RING(chan, RING_3D(CB_SIZE), 3);
   OUT_RING  (chan, size);
   OUT_RELOCh(chan, bo, base, domain | NOUVEAU_BO_WR);
   OUT_RELOCl(chan, bo, base, domain | NOUVEAU_BO_WR);

   while (words) {
      unsigned nr = AVAIL_RING(chan);
      nr = MIN2(nr, words);
      nr = MIN2(nr, NV04_PFIFO_MAX_PACKET_LEN - 1);

      BEGIN_RING_1I(chan, RING_3D(CB_POS), nr + 1);
      OUT_RING  (chan, offset);
      OUT_RINGp (chan, data, nr);

      words -= nr;
      data += nr;
      offset += nr * 4;

      if (words) {
         MARK_RING(chan, 6, 1);
         nouveau_bo_validate(chan, bo, domain | NOUVEAU_BO_WR);
      }
   }
}
Esempio n. 18
0
boolean
nvfx_vbo_validate(struct nvfx_context *nvfx)
{
	struct nouveau_channel* chan = nvfx->screen->base.channel;
	int i;
	int elements = MAX2(nvfx->vtxelt->num_elements, nvfx->hw_vtxelt_nr);
	unsigned vb_flags = nvfx->screen->vertex_buffer_reloc_flags | NOUVEAU_BO_RD;

	if (!elements)
		return TRUE;

	MARK_RING(chan, (5 + 2) * 16 + 2 + 11, 16 + 2);
	for(unsigned i = 0; i < nvfx->vtxelt->num_constant; ++i)
	{
		struct nvfx_low_frequency_element *ve = &nvfx->vtxelt->constant[i];
		struct pipe_vertex_buffer *vb = &nvfx->vtxbuf[ve->vertex_buffer_index];
		struct nvfx_buffer* buffer = nvfx_buffer(vb->buffer);
		float v[4];
		ve->fetch_rgba_float(v, buffer->data + vb->buffer_offset + ve->src_offset, 0, 0);
		nvfx_emit_vtx_attr(chan, ve->idx, v, ve->ncomp);
	}


	OUT_RING(chan, RING_3D(NV30_3D_VTXFMT(0), elements));
	if(nvfx->use_vertex_buffers)
	{
		unsigned idx = 0;
		for (i = 0; i < nvfx->vtxelt->num_per_vertex; i++) {
			struct nvfx_per_vertex_element *ve = &nvfx->vtxelt->per_vertex[i];
			struct pipe_vertex_buffer *vb = &nvfx->vtxbuf[ve->vertex_buffer_index];

			if(idx != ve->idx)
			{
				assert(idx < ve->idx);
				OUT_RINGp(chan, &nvfx->vtxelt->vtxfmt[idx], ve->idx - idx);
				idx = ve->idx;
			}

			OUT_RING(chan, nvfx->vtxelt->vtxfmt[idx] | (vb->stride << NV30_3D_VTXFMT_STRIDE__SHIFT));
			++idx;
		}
		if(idx != nvfx->vtxelt->num_elements)
			OUT_RINGp(chan, &nvfx->vtxelt->vtxfmt[idx], nvfx->vtxelt->num_elements - idx);
	}
	else
		OUT_RINGp(chan, nvfx->vtxelt->vtxfmt, nvfx->vtxelt->num_elements);

	for(i = nvfx->vtxelt->num_elements; i < elements; ++i)
		OUT_RING(chan, NV30_3D_VTXFMT_TYPE_V32_FLOAT);

	if(nvfx->is_nv4x) {
		unsigned i;
		/* seems to be some kind of cache flushing */
		for(i = 0; i < 3; ++i) {
			OUT_RING(chan, RING_3D(0x1718, 1));
			OUT_RING(chan, 0);
		}
	}

	OUT_RING(chan, RING_3D(NV30_3D_VTXBUF(0), elements));
	if(nvfx->use_vertex_buffers)
	{
		unsigned idx = 0;
		for (i = 0; i < nvfx->vtxelt->num_per_vertex; i++) {
			struct nvfx_per_vertex_element *ve = &nvfx->vtxelt->per_vertex[i];
			struct pipe_vertex_buffer *vb = &nvfx->vtxbuf[ve->vertex_buffer_index];
			struct nouveau_bo* bo = nvfx_resource(vb->buffer)->bo;

			for(; idx < ve->idx; ++idx)
				OUT_RING(chan, 0);

			OUT_RELOC(chan, bo,
					vb->buffer_offset + ve->src_offset + nvfx->base_vertex * vb->stride,
					vb_flags | NOUVEAU_BO_LOW | NOUVEAU_BO_OR,
					0, NV30_3D_VTXBUF_DMA1);
			++idx;
		}

		for(; idx < elements; ++idx)
			OUT_RING(chan, 0);
	}
	else
	{
		for (i = 0; i < elements; i++)
			OUT_RING(chan, 0);
	}

	OUT_RING(chan, RING_3D(0x1710, 1));
	OUT_RING(chan, 0);

	nvfx->hw_vtxelt_nr = nvfx->vtxelt->num_elements;
	nvfx->relocs_needed &=~ NVFX_RELOCATE_VTXBUF;
	return TRUE;
}
Esempio n. 19
0
void
nv50_fp_linkage_validate(struct nv50_context *nv50)
{
   struct nouveau_channel *chan = nv50->screen->base.channel;
   struct nv50_program *vp = nv50->gmtyprog ? nv50->gmtyprog : nv50->vertprog;
   struct nv50_program *fp = nv50->fragprog;
   struct nv50_varying dummy;
   int i, n, c, m;
   uint32_t primid = 0;
   uint32_t psiz = 0x000;
   uint32_t interp = fp->fp.interp;
   uint32_t colors = fp->fp.colors;
   uint32_t lin[4];
   uint8_t map[64];

   memset(lin, 0x00, sizeof(lin));

   /* XXX: in buggy-endian mode, is the first element of map (u32)0x000000xx
    *  or is it the first byte ?
    */
   memset(map, nv50->gmtyprog ? 0x80 : 0x40, sizeof(map));

   dummy.mask = 0xf; /* map all components of HPOS */
   dummy.linear = 0;
   m = nv50_vec4_map(map, 0, lin, &dummy, &vp->out[0]);

   for (c = 0; c < vp->vp.clpd_nr; ++c)
      map[m++] = vp->vp.clpd + c;

   colors |= m << 8; /* adjust BFC0 id */

   /* if light_twoside is active, FFC0_ID == BFC0_ID is invalid */
   if (nv50->rast->pipe.light_twoside) {
      for (i = 0; i < 2; ++i)
         m = nv50_vec4_map(map, m, lin,
                           &fp->in[fp->vp.bfc[i]], &vp->out[vp->vp.bfc[i]]);
   }
   colors += m - 4; /* adjust FFC0 id */
   interp |= m << 8; /* set map id where 'normal' FP inputs start */

   dummy.mask = 0x0;
   for (i = 0; i < fp->in_nr; ++i) {
      for (n = 0; n < vp->out_nr; ++n)
         if (vp->out[n].sn == fp->in[i].sn &&
             vp->out[n].si == fp->in[i].si)
            break;
      m = nv50_vec4_map(map, m, lin,
                        &fp->in[i], (n < vp->out_nr) ? &vp->out[n] : &dummy);
   }

   /* PrimitiveID either is replaced by the system value, or
    * written by the geometry shader into an output register
    */
   if (fp->gp.primid < 0x40) {
      primid = m;
      map[m++] = vp->gp.primid;
   }

   if (nv50->rast->pipe.point_size_per_vertex) {
      psiz = (m << 4) | 1;
      map[m++] = vp->vp.psiz;
   }

   if (nv50->rast->pipe.clamp_vertex_color)
      colors |= NV50_3D_MAP_SEMANTIC_0_CLMP_EN;

   n = (m + 3) / 4;
   assert(m <= 64);

   if (unlikely(nv50->gmtyprog)) {
      BEGIN_RING(chan, RING_3D(GP_RESULT_MAP_SIZE), 1);
      OUT_RING  (chan, m);
      BEGIN_RING(chan, RING_3D(GP_RESULT_MAP(0)), n);
      OUT_RINGp (chan, map, n);
   } else {
      BEGIN_RING(chan, RING_3D(VP_GP_BUILTIN_ATTR_EN), 1);
      OUT_RING  (chan, vp->vp.attrs[2]);

      BEGIN_RING(chan, RING_3D(MAP_SEMANTIC_4), 1);
      OUT_RING  (chan, primid);

      BEGIN_RING(chan, RING_3D(VP_RESULT_MAP_SIZE), 1);
      OUT_RING  (chan, m);
      BEGIN_RING(chan, RING_3D(VP_RESULT_MAP(0)), n);
      OUT_RINGp (chan, map, n);
   }

   BEGIN_RING(chan, RING_3D(MAP_SEMANTIC_0), 4);
   OUT_RING  (chan, colors);
   OUT_RING  (chan, (vp->vp.clpd_nr << 8) | 4);
   OUT_RING  (chan, 0);
   OUT_RING  (chan, psiz);

   BEGIN_RING(chan, RING_3D(FP_INTERPOLANT_CTRL), 1);
   OUT_RING  (chan, interp);

   nv50->state.interpolant_ctrl = interp;

   nv50->state.semantic_color = colors;
   nv50->state.semantic_psize = psiz;

   BEGIN_RING(chan, RING_3D(NOPERSPECTIVE_BITMAP(0)), 4);
   OUT_RINGp (chan, lin, 4);

   BEGIN_RING(chan, RING_3D(GP_ENABLE), 1);
   OUT_RING  (chan, nv50->gmtyprog ? 1 : 0);
}
Esempio n. 20
0
void
nv50_constbufs_validate(struct nv50_context *nv50)
{
   struct nouveau_channel *chan = nv50->screen->base.channel;
   unsigned s;

   for (s = 0; s < 3; ++s) {
      struct nv04_resource *res;
      int i;
      unsigned p, b;

      if (s == PIPE_SHADER_FRAGMENT)
         p = NV50_3D_SET_PROGRAM_CB_PROGRAM_FRAGMENT;
      else
      if (s == PIPE_SHADER_GEOMETRY)
         p = NV50_3D_SET_PROGRAM_CB_PROGRAM_GEOMETRY;
      else
         p = NV50_3D_SET_PROGRAM_CB_PROGRAM_VERTEX;

      while (nv50->constbuf_dirty[s]) {
         struct nouveau_bo *bo;
         unsigned start = 0;
         unsigned words = 0;

         i = ffs(nv50->constbuf_dirty[s]) - 1;
         nv50->constbuf_dirty[s] &= ~(1 << i);

         res = nv04_resource(nv50->constbuf[s][i]);
         if (!res) {
            if (i != 0) {
               BEGIN_RING(chan, RING_3D(SET_PROGRAM_CB), 1);
               OUT_RING  (chan, (i << 8) | p | 0);
            }
            continue;
         }

         if (i == 0) {
            b = NV50_CB_PVP + s;

            /* always upload GL uniforms through CB DATA */
            bo = nv50->screen->uniforms;
            words = res->base.width0 / 4;
         } else {
            b = s * 16 + i;

            assert(0);

            if (!nouveau_resource_mapped_by_gpu(&res->base)) {
               nouveau_buffer_migrate(&nv50->base, res, NOUVEAU_BO_VRAM);

               BEGIN_RING(chan, RING_3D(CODE_CB_FLUSH), 1);
               OUT_RING  (chan, 0);
            }
            MARK_RING (chan, 6, 2);
            BEGIN_RING(chan, RING_3D(CB_DEF_ADDRESS_HIGH), 3);
            OUT_RESRCh(chan, res, 0, NOUVEAU_BO_RD);
            OUT_RESRCl(chan, res, 0, NOUVEAU_BO_RD);
            OUT_RING  (chan, (b << 16) | (res->base.width0 & 0xffff));
            BEGIN_RING(chan, RING_3D(SET_PROGRAM_CB), 1);
            OUT_RING  (chan, (b << 12) | (i << 8) | p | 1);

            bo = res->bo;

            nv50_bufctx_add_resident(nv50, NV50_BUFCTX_CONSTANT, res,
                                     res->domain | NOUVEAU_BO_RD);
         }

         if (words) {
            MARK_RING(chan, 8, 1);

            nouveau_bo_validate(chan, bo, res->domain | NOUVEAU_BO_WR);
         }

         while (words) {
            unsigned nr = AVAIL_RING(chan);

            if (nr < 16) {
               FIRE_RING(chan);
               nouveau_bo_validate(chan, bo, res->domain | NOUVEAU_BO_WR);
               continue;
            }
            nr = MIN2(MIN2(nr - 3, words), NV04_PFIFO_MAX_PACKET_LEN);

            BEGIN_RING(chan, RING_3D(CB_ADDR), 1);
            OUT_RING  (chan, (start << 8) | b);
            BEGIN_RING_NI(chan, RING_3D(CB_DATA(0)), nr);
            OUT_RINGp (chan, &res->data[start * 4], nr);

            start += nr;
            words -= nr;
         }
      }
   }
}
Esempio n. 21
0
static void
nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
{
	struct nouveau_fbcon_par *par = info->par;
	struct drm_device *dev = par->dev;
	struct drm_nouveau_private *dev_priv = dev->dev_private;
	struct nouveau_channel *chan = dev_priv->channel;
	uint32_t fg;
	uint32_t bg;
	uint32_t dsize;
	uint32_t width;
	uint32_t *data = (uint32_t *)image->data;

	if (info->state != FBINFO_STATE_RUNNING)
		return;

	if (image->depth != 1) {
		cfb_imageblit(info, image);
		return;
	}

	if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 8)) {
		nouveau_fbcon_gpu_lockup(info);
	}

	if (info->flags & FBINFO_HWACCEL_DISABLED) {
		cfb_imageblit(info, image);
		return;
	}

	width = (image->width + 31) & ~31;
	dsize = (width * image->height) >> 5;

	if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
	    info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
		fg = ((uint32_t *) info->pseudo_palette)[image->fg_color];
		bg = ((uint32_t *) info->pseudo_palette)[image->bg_color];
	} else {
		fg = image->fg_color;
		bg = image->bg_color;
	}

	BEGIN_RING(chan, NvSubGdiRect, 0x0be4, 7);
	OUT_RING(chan, (image->dy << 16) | (image->dx & 0xffff));
	OUT_RING(chan, ((image->dy + image->height) << 16) |
			 ((image->dx + image->width) & 0xffff));
	OUT_RING(chan, bg);
	OUT_RING(chan, fg);
	OUT_RING(chan, (image->height << 16) | image->width);
	OUT_RING(chan, (image->height << 16) | width);
	OUT_RING(chan, (image->dy << 16) | (image->dx & 0xffff));

	while (dsize) {
		int iter_len = dsize > 128 ? 128 : dsize;

		if (RING_SPACE(chan, iter_len + 1)) {
			nouveau_fbcon_gpu_lockup(info);
			cfb_imageblit(info, image);
			return;
		}

		BEGIN_RING(chan, NvSubGdiRect, 0x0c00, iter_len);
		OUT_RINGp(chan, data, iter_len);
		data += iter_len;
		dsize -= iter_len;
	}

	FIRE_RING(chan);
}
Esempio n. 22
0
static boolean
nv30_vertprog_validate(struct nv30_context *nv30)
{ 
	struct pipe_screen *pscreen = nv30->pipe.screen;
	struct nouveau_grobj *rankine = nv30->screen->rankine;
	struct nv30_vertex_program *vp;
	struct pipe_buffer *constbuf;
	boolean upload_code = FALSE, upload_data = FALSE;
	int i;

	vp = nv30->vertprog;
	constbuf = nv30->constbuf[PIPE_SHADER_VERTEX];

	/* Translate TGSI shader into hw bytecode */
	if (!vp->translated) {
		nv30_vertprog_translate(nv30, vp);
		if (!vp->translated)
			return FALSE;
	}

	/* Allocate hw vtxprog exec slots */
	if (!vp->exec) {
		struct nouveau_resource *heap = nv30->screen->vp_exec_heap;
		struct nouveau_stateobj *so;
		uint vplen = vp->nr_insns;

		if (nouveau_resource_alloc(heap, vplen, vp, &vp->exec)) {
			while (heap->next && heap->size < vplen) {
				struct nv30_vertex_program *evict;
				
				evict = heap->next->priv;
				nouveau_resource_free(&evict->exec);
			}

			if (nouveau_resource_alloc(heap, vplen, vp, &vp->exec))
				assert(0);
		}

		so = so_new(2, 0);
		so_method(so, rankine, NV34TCL_VP_START_FROM_ID, 1);
		so_data  (so, vp->exec->start);
		so_ref(so, &vp->so);
		so_ref(NULL, &so);

		upload_code = TRUE;
	}

	/* Allocate hw vtxprog const slots */
	if (vp->nr_consts && !vp->data) {
		struct nouveau_resource *heap = nv30->screen->vp_data_heap;

		if (nouveau_resource_alloc(heap, vp->nr_consts, vp, &vp->data)) {
			while (heap->next && heap->size < vp->nr_consts) {
				struct nv30_vertex_program *evict;
				
				evict = heap->next->priv;
				nouveau_resource_free(&evict->data);
			}

			if (nouveau_resource_alloc(heap, vp->nr_consts, vp,
						   &vp->data))
				assert(0);
		}

		/*XXX: handle this some day */
		assert(vp->data->start >= vp->data_start_min);

		upload_data = TRUE;
		if (vp->data_start != vp->data->start)
			upload_code = TRUE;
	}

	/* If exec or data segments moved we need to patch the program to
	 * fixup offsets and register IDs.
	 */
	if (vp->exec_start != vp->exec->start) {
		for (i = 0; i < vp->nr_insns; i++) {
			struct nv30_vertex_program_exec *vpi = &vp->insns[i];

			if (vpi->has_branch_offset) {
				assert(0);
			}
		}

		vp->exec_start = vp->exec->start;
	}

	if (vp->nr_consts && vp->data_start != vp->data->start) {
		for (i = 0; i < vp->nr_insns; i++) {
			struct nv30_vertex_program_exec *vpi = &vp->insns[i];

			if (vpi->const_index >= 0) {
				vpi->data[1] &= ~NV30_VP_INST_CONST_SRC_MASK;
				vpi->data[1] |=
					(vpi->const_index + vp->data->start) <<
					NV30_VP_INST_CONST_SRC_SHIFT;

			}
		}

		vp->data_start = vp->data->start;
	}

	/* Update + Upload constant values */
	if (vp->nr_consts) {
		float *map = NULL;

		if (constbuf) {
			map = pipe_buffer_map(pscreen, constbuf,
					      PIPE_BUFFER_USAGE_CPU_READ);
		}

		for (i = 0; i < vp->nr_consts; i++) {
			struct nv30_vertex_program_data *vpd = &vp->consts[i];

			if (vpd->index >= 0) {
				if (!upload_data &&
				    !memcmp(vpd->value, &map[vpd->index * 4],
					    4 * sizeof(float)))
					continue;
				memcpy(vpd->value, &map[vpd->index * 4],
				       4 * sizeof(float));
			}

			BEGIN_RING(rankine, NV34TCL_VP_UPLOAD_CONST_ID, 5);
			OUT_RING  (i + vp->data->start);
			OUT_RINGp ((uint32_t *)vpd->value, 4);
		}

		if (constbuf)
			pipe_buffer_unmap(pscreen, constbuf);
	}

	/* Upload vtxprog */
	if (upload_code) {
#if 0
		for (i = 0; i < vp->nr_insns; i++) {
			NOUVEAU_MSG("VP inst %d: 0x%08x 0x%08x 0x%08x 0x%08x\n",
				i, vp->insns[i].data[0], vp->insns[i].data[1],
				vp->insns[i].data[2], vp->insns[i].data[3]);
		}
#endif
		BEGIN_RING(rankine, NV34TCL_VP_UPLOAD_FROM_ID, 1);
		OUT_RING  (vp->exec->start);
		for (i = 0; i < vp->nr_insns; i++) {
			BEGIN_RING(rankine, NV34TCL_VP_UPLOAD_INST(0), 4);
			OUT_RINGp (vp->insns[i].data, 4);
		}
	}

	if (vp->so != nv30->state.hw[NV30_STATE_VERTPROG]) {
		so_ref(vp->so, &nv30->state.hw[NV30_STATE_VERTPROG]);
		return TRUE;
	}

	return FALSE;
}
Esempio n. 23
0
void
nv50_upload_sifc(struct nv50_context *nv50,
		 struct nouveau_bo *bo, unsigned dst_offset, unsigned reloc,
		 unsigned dst_format, int dst_w, int dst_h, int dst_pitch,
		 void *src, unsigned src_format, int src_pitch,
		 int x, int y, int w, int h, int cpp)
{
	struct nouveau_channel *chan = nv50->screen->base.channel;
	struct nouveau_grobj *eng2d = nv50->screen->eng2d;
	unsigned line_dwords = (w * cpp + 3) / 4;

	reloc |= NOUVEAU_BO_WR;

	MARK_RING (chan, 32, 2); /* flush on lack of space or relocs */

	if (bo->tile_flags) {
		BEGIN_RING(chan, eng2d, NV50_2D_DST_FORMAT, 5);
		OUT_RING  (chan, dst_format);
		OUT_RING  (chan, 0);
		OUT_RING  (chan, bo->tile_mode << 4);
		OUT_RING  (chan, 1);
		OUT_RING  (chan, 0);
	} else {
		BEGIN_RING(chan, eng2d, NV50_2D_DST_FORMAT, 2);
		OUT_RING  (chan, dst_format);
		OUT_RING  (chan, 1);
		BEGIN_RING(chan, eng2d, NV50_2D_DST_PITCH, 1);
		OUT_RING  (chan, dst_pitch);
	}

	BEGIN_RING(chan, eng2d, NV50_2D_DST_WIDTH, 4);
	OUT_RING  (chan, dst_w);
	OUT_RING  (chan, dst_h);
	OUT_RELOCh(chan, bo, dst_offset, reloc);
	OUT_RELOCl(chan, bo, dst_offset, reloc);

	/* NV50_2D_OPERATION_SRCCOPY assumed already set */

	BEGIN_RING(chan, eng2d, NV50_2D_SIFC_BITMAP_ENABLE, 2);
	OUT_RING  (chan, 0);
	OUT_RING  (chan, src_format);
	BEGIN_RING(chan, eng2d, NV50_2D_SIFC_WIDTH, 10);
	OUT_RING  (chan, w);
	OUT_RING  (chan, h);
	OUT_RING  (chan, 0);
	OUT_RING  (chan, 1);
	OUT_RING  (chan, 0);
	OUT_RING  (chan, 1);
	OUT_RING  (chan, 0);
	OUT_RING  (chan, x);
	OUT_RING  (chan, 0);
	OUT_RING  (chan, y);

	while (h--) {
		const uint32_t *p = src;
		unsigned count = line_dwords;

		while (count) {
			unsigned nr = MIN2(count, 1792);

			if (AVAIL_RING(chan) <= nr) {
				FIRE_RING (chan);

				BEGIN_RING(chan, eng2d,
					   NV50_2D_DST_ADDRESS_HIGH, 2);
				OUT_RELOCh(chan, bo, dst_offset, reloc);
				OUT_RELOCl(chan, bo, dst_offset, reloc);
			}
			assert(AVAIL_RING(chan) > nr);

			BEGIN_RING(chan, eng2d,
				   NV50_2D_SIFC_DATA | (2 << 29), nr);
			OUT_RINGp (chan, p, nr);

			p += nr;
			count -= nr;
		}

		src = (uint8_t *) src + src_pitch;
	}
}
Esempio n. 24
0
static INLINE void
nvfx_render_prim(struct draw_stage *stage, struct prim_header *prim,
	       unsigned mode, unsigned count)
{
	struct nvfx_render_stage *rs = nvfx_render_stage(stage);
	struct nvfx_context *nvfx = rs->nvfx;

	struct nvfx_screen *screen = nvfx->screen;
	struct nouveau_channel *chan = screen->base.channel;
	boolean no_elements = nvfx->vertprog->draw_no_elements;
	unsigned num_attribs = nvfx->vertprog->draw_elements;

	/* we need to account the flush as well here even if it is done afterthis
	 * function
	 */
	if (AVAIL_RING(chan) < ((1 + count * num_attribs * 4) + 6 + 64)) {
		nvfx_render_flush(stage, 0);
		FIRE_RING(chan);
		nvfx_state_emit(nvfx);

		assert(AVAIL_RING(chan) >= ((1 + count * num_attribs * 4) + 6 + 64));
	}

	/* Switch primitive modes if necessary */
	if (rs->prim != mode) {
		if (rs->prim != NV30_3D_VERTEX_BEGIN_END_STOP) {
			OUT_RING(chan, RING_3D(NV30_3D_VERTEX_BEGIN_END, 1));
			OUT_RING(chan, NV30_3D_VERTEX_BEGIN_END_STOP);
		}

		/* XXX: any command a lot of times seems to (mostly) fix corruption that would otherwise happen */
		/* this seems to cause issues on nv3x, and also be unneeded there */
		if(nvfx->is_nv4x)
		{
			int i;
			for(i = 0; i < 32; ++i)
			{
				OUT_RING(chan, RING_3D(0x1dac, 1));
				OUT_RING(chan, 0);
			}
		}

		OUT_RING(chan, RING_3D(NV30_3D_VERTEX_BEGIN_END, 1));
		OUT_RING  (chan, mode);
		rs->prim = mode;
	}

	OUT_RING(chan, RING_3D_NI(NV30_3D_VERTEX_DATA, num_attribs * 4 * count));
	if(no_elements) {
		OUT_RING(chan, 0);
		OUT_RING(chan, 0);
		OUT_RING(chan, 0);
		OUT_RING(chan, 0);
	} else {
		for (unsigned i = 0; i < count; ++i)
		{
			struct vertex_header* v = prim->v[i];
			/* TODO: disable divide where it's causing the problem, and remove this hack */
			OUT_RING(chan, fui(v->data[0][0] / v->data[0][3]));
			OUT_RING(chan, fui(v->data[0][1] / v->data[0][3]));
			OUT_RING(chan, fui(v->data[0][2] / v->data[0][3]));
			OUT_RING(chan, fui(1.0f / v->data[0][3]));
			OUT_RINGp(chan, &v->data[1][0], 4 * (num_attribs - 1));
		}
	}
}
Esempio n. 25
0
void
nvc0_tfb_validate(struct nvc0_context *nvc0)
{
   struct nouveau_channel *chan = nvc0->screen->base.channel;
   struct nvc0_transform_feedback_state *tfb;
   unsigned b, n, i;

   if (nvc0->gmtyprog) tfb = nvc0->gmtyprog->tfb;
   else
   if (nvc0->tevlprog) tfb = nvc0->tevlprog->tfb;
   else
      tfb = nvc0->vertprog->tfb;

   IMMED_RING(chan, RING_3D(TFB_ENABLE), (tfb && nvc0->num_tfbbufs) ? 1 : 0);

   if (tfb && tfb != nvc0->state.tfb) {
      uint8_t var[128];

      for (n = 0, b = 0; b < 4; n += tfb->varying_count[b++]) {
         if (tfb->varying_count[b]) {
            BEGIN_RING(chan, RING_3D(TFB_STREAM(b)), 3);
            OUT_RING  (chan, 0);
            OUT_RING  (chan, tfb->varying_count[b]);
            OUT_RING  (chan, tfb->stride[b]);

            for (i = 0; i < tfb->varying_count[b]; ++i)
               var[i] = tfb->varying_index[n + i];
            for (; i & 3; ++i)
               var[i] = 0; /* zero rest of method word bits */

            BEGIN_RING(chan, RING_3D(TFB_VARYING_LOCS(b, 0)), i / 4);
            OUT_RINGp (chan, var, i / 4);

            if (nvc0->tfbbuf[b])
               nvc0_so_target(nvc0->tfbbuf[b])->stride = tfb->stride[b];
         } else {
            IMMED_RING(chan, RING_3D(TFB_VARYING_COUNT(b)), 0);
         }
      }
   }
   nvc0->state.tfb = tfb;

   if (!(nvc0->dirty & NVC0_NEW_TFB_TARGETS))
      return;
   nvc0_bufctx_reset(nvc0, NVC0_BUFCTX_TFB);

   for (b = 0; b < nvc0->num_tfbbufs; ++b) {
      struct nvc0_so_target *targ = nvc0_so_target(nvc0->tfbbuf[b]);
      struct nv04_resource *buf = nv04_resource(targ->pipe.buffer);

      if (tfb)
         targ->stride = tfb->stride[b];

      if (!(nvc0->tfbbuf_dirty & (1 << b)))
         continue;

      if (!targ->clean)
         nvc0_query_fifo_wait(chan, targ->pq);
      BEGIN_RING(chan, RING_3D(TFB_BUFFER_ENABLE(b)), 5);
      OUT_RING  (chan, 1);
      OUT_RESRCh(chan, buf, targ->pipe.buffer_offset, NOUVEAU_BO_WR);
      OUT_RESRCl(chan, buf, targ->pipe.buffer_offset, NOUVEAU_BO_WR);
      OUT_RING  (chan, targ->pipe.buffer_size);
      if (!targ->clean) {
         nvc0_query_pushbuf_submit(chan, targ->pq, 0x4);
      } else {
         OUT_RING(chan, 0); /* TFB_BUFFER_OFFSET */
         targ->clean = FALSE;
      }
      nvc0_bufctx_add_resident(nvc0, NVC0_BUFCTX_TFB, buf, NOUVEAU_BO_WR);
   }
   for (; b < 4; ++b)
      IMMED_RING(chan, RING_3D(TFB_BUFFER_ENABLE(b)), 0);
}