gallium: add start_slot parameter to set_vertex_buffers
[mesa.git] / src / gallium / drivers / nv30 / nv30_draw.c
index 74fc138c051223b6ef32102d57a9f6439b901920..51fe4f86ed1126a895948f2538d32467a3729c99 100644 (file)
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ *
+ */
+
+#include "draw/draw_context.h"
+#include "draw/draw_vertex.h"
 #include "draw/draw_pipe.h"
+#include "draw/draw_vbuf.h"
+#include "draw/draw_private.h"
 
+#include "nouveau/nv_object.xml.h"
+#include "nv30-40_3d.xml.h"
 #include "nv30_context.h"
+#include "nv30_format.h"
+
+struct nv30_render {
+   struct vbuf_render base;
+   struct nv30_context *nv30;
+
+   struct pipe_transfer *transfer;
+   struct pipe_resource *buffer;
+   unsigned offset;
+   unsigned length;
 
-struct nv30_draw_stage {
-       struct draw_stage draw;
-       struct nv30_context *nv30;
+   struct vertex_info vertex_info;
+
+   struct nouveau_heap *vertprog;
+   uint32_t vtxprog[16][4];
+   uint32_t vtxfmt[16];
+   uint32_t vtxptr[16];
+   uint32_t prim;
 };
 
+static INLINE struct nv30_render *
+nv30_render(struct vbuf_render *render)
+{
+   return (struct nv30_render *)render;
+}
+
+static const struct vertex_info *
+nv30_render_get_vertex_info(struct vbuf_render *render)
+{
+   return &nv30_render(render)->vertex_info;
+}
+
+static boolean
+nv30_render_allocate_vertices(struct vbuf_render *render,
+                              ushort vertex_size, ushort nr_vertices)
+{
+   struct nv30_render *r = nv30_render(render);
+   struct nv30_context *nv30 = r->nv30;
+
+   r->length = vertex_size * nr_vertices;
+
+   if (r->offset + r->length >= render->max_vertex_buffer_bytes) {
+      pipe_resource_reference(&r->buffer, NULL);
+      r->buffer = pipe_buffer_create(&nv30->screen->base.base,
+                                     PIPE_BIND_VERTEX_BUFFER, 0,
+                                     render->max_vertex_buffer_bytes);
+      if (!r->buffer)
+         return FALSE;
+
+      r->offset = 0;
+   }
+
+   return TRUE;
+}
+
+static void *
+nv30_render_map_vertices(struct vbuf_render *render)
+{
+   struct nv30_render *r = nv30_render(render);
+   char *map = pipe_buffer_map(&r->nv30->base.pipe, r->buffer,
+                               PIPE_TRANSFER_WRITE |
+                               PIPE_TRANSFER_UNSYNCHRONIZED, &r->transfer);
+   return map + r->offset;
+}
+
 static void
-nv30_draw_point(struct draw_stage *draw, struct prim_header *prim)
+nv30_render_unmap_vertices(struct vbuf_render *render,
+                           ushort min_index, ushort max_index)
 {
-       NOUVEAU_ERR("\n");
+   struct nv30_render *r = nv30_render(render);
+   pipe_buffer_unmap(&r->nv30->base.pipe, r->transfer);
 }
 
 static void
-nv30_draw_line(struct draw_stage *draw, struct prim_header *prim)
+nv30_render_set_primitive(struct vbuf_render *render, unsigned prim)
 {
-       NOUVEAU_ERR("\n");
+   struct nv30_render *r = nv30_render(render);
+
+   r->prim = nv30_prim_gl(prim);
 }
 
 static void
-nv30_draw_tri(struct draw_stage *draw, struct prim_header *prim)
+nv30_render_draw_elements(struct vbuf_render *render,
+                          const ushort *indices, uint count)
 {
-       NOUVEAU_ERR("\n");
+   struct nv30_render *r = nv30_render(render);
+   struct nv30_context *nv30 = r->nv30;
+   struct nouveau_pushbuf *push = nv30->screen->base.pushbuf;
+   unsigned i;
+
+   BEGIN_NV04(push, NV30_3D(VTXBUF(0)), r->vertex_info.num_attribs);
+   for (i = 0; i < r->vertex_info.num_attribs; i++) {
+      PUSH_RESRC(push, NV30_3D(VTXBUF(i)), BUFCTX_VTXTMP,
+                       nv04_resource(r->buffer), r->offset + r->vtxptr[i],
+                       NOUVEAU_BO_LOW | NOUVEAU_BO_RD, 0, 0);
+   }
+
+   if (!nv30_state_validate(nv30, FALSE))
+      return;
+
+   BEGIN_NV04(push, NV30_3D(VERTEX_BEGIN_END), 1);
+   PUSH_DATA (push, r->prim);
+
+   if (count & 1) {
+      BEGIN_NV04(push, NV30_3D(VB_ELEMENT_U32), 1);
+      PUSH_DATA (push, *indices++);
+   }
+
+   count >>= 1;
+   while (count) {
+      unsigned npush = MIN2(count, NV04_PFIFO_MAX_PACKET_LEN);
+      count -= npush;
+
+      BEGIN_NI04(push, NV30_3D(VB_ELEMENT_U16), npush);
+      while (npush--) {
+         PUSH_DATA(push, (indices[1] << 16) | indices[0]);
+         indices += 2;
+      }
+   }
+
+   BEGIN_NV04(push, NV30_3D(VERTEX_BEGIN_END), 1);
+   PUSH_DATA (push, NV30_3D_VERTEX_BEGIN_END_STOP);
+   PUSH_RESET(push, BUFCTX_VTXTMP);
 }
 
 static void
-nv30_draw_flush(struct draw_stage *draw, unsigned flags)
+nv30_render_draw_arrays(struct vbuf_render *render, unsigned start, uint nr)
 {
+   struct nv30_render *r = nv30_render(render);
+   struct nv30_context *nv30 = r->nv30;
+   struct nouveau_pushbuf *push = nv30->base.pushbuf;
+   unsigned fn = nr >> 8, pn = nr & 0xff;
+   unsigned ps = fn + (pn ? 1 : 0);
+   unsigned i;
+
+   BEGIN_NV04(push, NV30_3D(VTXBUF(0)), r->vertex_info.num_attribs);
+   for (i = 0; i < r->vertex_info.num_attribs; i++) {
+      PUSH_RESRC(push, NV30_3D(VTXBUF(i)), BUFCTX_VTXTMP,
+                       nv04_resource(r->buffer), r->offset + r->vtxptr[i],
+                       NOUVEAU_BO_LOW | NOUVEAU_BO_RD, 0, 0);
+   }
+
+   if (!nv30_state_validate(nv30, FALSE))
+      return;
+
+   BEGIN_NV04(push, NV30_3D(VERTEX_BEGIN_END), 1);
+   PUSH_DATA (push, r->prim);
+
+   BEGIN_NI04(push, NV30_3D(VB_VERTEX_BATCH), ps);
+   while (fn--) {
+      PUSH_DATA (push, 0xff000000 | start);
+      start += 256;
+   }
+
+   if (pn)
+      PUSH_DATA (push, ((pn - 1) << 24) | start);
+
+   BEGIN_NV04(push, NV30_3D(VERTEX_BEGIN_END), 1);
+   PUSH_DATA (push, NV30_3D_VERTEX_BEGIN_END_STOP);
+   PUSH_RESET(push, BUFCTX_VTXTMP);
 }
 
 static void
-nv30_draw_reset_stipple_counter(struct draw_stage *draw)
+nv30_render_release_vertices(struct vbuf_render *render)
+{
+   struct nv30_render *r = nv30_render(render);
+   r->offset += r->length;
+}
+
+static const struct {
+   unsigned emit;
+   unsigned interp;
+   unsigned vp30;
+   unsigned vp40;
+   unsigned ow40;
+} vroute [] = {
+   [TGSI_SEMANTIC_POSITION] = { EMIT_4F, INTERP_PERSPECTIVE, 0, 0, 0x00000000 },
+   [TGSI_SEMANTIC_COLOR   ] = { EMIT_4F, INTERP_LINEAR     , 3, 1, 0x00000001 },
+   [TGSI_SEMANTIC_BCOLOR  ] = { EMIT_4F, INTERP_LINEAR     , 1, 3, 0x00000004 },
+   [TGSI_SEMANTIC_FOG     ] = { EMIT_4F, INTERP_PERSPECTIVE, 5, 5, 0x00000010 },
+   [TGSI_SEMANTIC_PSIZE   ] = { EMIT_1F_PSIZE, INTERP_POS  , 6, 6, 0x00000020 },
+   [TGSI_SEMANTIC_GENERIC ] = { EMIT_4F, INTERP_PERSPECTIVE, 8, 7, 0x00004000 }
+};
+
+static boolean
+vroute_add(struct nv30_render *r, uint attrib, uint sem, uint *idx)
+{
+   struct pipe_screen *pscreen = &r->nv30->screen->base.base;
+   struct nv30_fragprog *fp = r->nv30->fragprog.program;
+   struct vertex_info *vinfo = &r->vertex_info;
+   enum pipe_format format;
+   uint emit = EMIT_OMIT;
+   uint result = *idx;
+
+   if (sem == TGSI_SEMANTIC_GENERIC && result >= 8) {
+      for (result = 0; result < 8; result++) {
+         if (fp->texcoord[result] == *idx) {
+            emit = vroute[sem].emit;
+            break;
+         }
+      }
+   } else {
+      emit = vroute[sem].emit;
+   }
+
+   if (emit == EMIT_OMIT)
+      return FALSE;
+
+   draw_emit_vertex_attr(vinfo, emit, vroute[sem].interp, attrib);
+   format = draw_translate_vinfo_format(emit);
+
+   r->vtxfmt[attrib] = nv30_vtxfmt(pscreen, format)->hw;
+   r->vtxptr[attrib] = vinfo->size | NV30_3D_VTXBUF_DMA1;
+   vinfo->size += draw_translate_vinfo_size(emit);
+
+   if (nv30_screen(pscreen)->eng3d->oclass < NV40_3D_CLASS) {
+      r->vtxprog[attrib][0] = 0x001f38d8;
+      r->vtxprog[attrib][1] = 0x0080001b | (attrib << 9);
+      r->vtxprog[attrib][2] = 0x0836106c;
+      r->vtxprog[attrib][3] = 0x2000f800 | (result + vroute[sem].vp30) << 2;
+   } else {
+      r->vtxprog[attrib][0] = 0x401f9c6c;
+      r->vtxprog[attrib][1] = 0x0040000d | (attrib << 8);
+      r->vtxprog[attrib][2] = 0x8106c083;
+      r->vtxprog[attrib][3] = 0x6041ff80 | (result + vroute[sem].vp40) << 2;
+   }
+
+   *idx = vroute[sem].ow40 << result;
+   return TRUE;
+}
+
+static boolean
+nv30_render_validate(struct nv30_context *nv30)
+{
+   struct nv30_render *r = nv30_render(nv30->draw->render);
+   struct nv30_rasterizer_stateobj *rast = nv30->rast;
+   struct pipe_screen *pscreen = &nv30->screen->base.base;
+   struct nouveau_pushbuf *push = nv30->screen->base.pushbuf;
+   struct nouveau_object *eng3d = nv30->screen->eng3d;
+   struct nv30_vertprog *vp = nv30->vertprog.program;
+   struct vertex_info *vinfo = &r->vertex_info;
+   unsigned vp_attribs = 0;
+   unsigned vp_results = 0;
+   unsigned attrib = 0;
+   unsigned pntc;
+   int i;
+
+   if (!r->vertprog) {
+      struct nouveau_heap *heap = nv30_screen(pscreen)->vp_exec_heap;
+      if (nouveau_heap_alloc(heap, 16, &r->vertprog, &r->vertprog)) {
+         while (heap->next && heap->size < 16) {
+            struct nouveau_heap **evict = heap->next->priv;
+            nouveau_heap_free(evict);
+         }
+
+         if (nouveau_heap_alloc(heap, 16, &r->vertprog, &r->vertprog))
+            return FALSE;
+      }
+   }
+
+   vinfo->num_attribs = 0;
+   vinfo->size = 0;
+
+   /* setup routing for all necessary vp outputs */
+   for (i = 0; i < vp->info.num_outputs && attrib < 16; i++) {
+      uint semantic = vp->info.output_semantic_name[i];
+      uint index = vp->info.output_semantic_index[i];
+      if (vroute_add(r, attrib, semantic, &index)) {
+         vp_attribs |= (1 << attrib++);
+         vp_results |= index;
+      }
+   }
+
+   /* setup routing for replaced point coords not written by vp */
+   if (rast && rast->pipe.point_quad_rasterization)
+      pntc = rast->pipe.sprite_coord_enable & 0x000002ff;
+   else
+      pntc = 0;
+
+   while (pntc && attrib < 16) {
+      uint index = ffs(pntc) - 1; pntc &= ~(1 << index);
+      if (vroute_add(r, attrib, TGSI_SEMANTIC_GENERIC, &index)) {
+         vp_attribs |= (1 << attrib++);
+         vp_results |= index;
+      }
+   }
+
+   /* modify vertex format for correct stride, and stub out unused ones */
+   BEGIN_NV04(push, NV30_3D(VP_UPLOAD_FROM_ID), 1);
+   PUSH_DATA (push, r->vertprog->start);
+   r->vtxprog[attrib - 1][3] |= 1;
+   for (i = 0; i < attrib; i++) {
+      BEGIN_NV04(push, NV30_3D(VP_UPLOAD_INST(0)), 4);
+      PUSH_DATAp(push, r->vtxprog[i], 4);
+      r->vtxfmt[i] |= vinfo->size << 8;
+   }
+   for (; i < 16; i++)
+      r->vtxfmt[i]  = NV30_3D_VTXFMT_TYPE_V32_FLOAT;
+
+   BEGIN_NV04(push, NV30_3D(VIEWPORT_TRANSLATE_X), 8);
+   PUSH_DATAf(push, 0.0);
+   PUSH_DATAf(push, 0.0);
+   PUSH_DATAf(push, 0.0);
+   PUSH_DATAf(push, 0.0);
+   PUSH_DATAf(push, 1.0);
+   PUSH_DATAf(push, 1.0);
+   PUSH_DATAf(push, 1.0);
+   PUSH_DATAf(push, 1.0);
+   BEGIN_NV04(push, NV30_3D(DEPTH_RANGE_NEAR), 2);
+   PUSH_DATAf(push, 0.0);
+   PUSH_DATAf(push, 1.0);
+
+   BEGIN_NV04(push, NV30_3D(VTXFMT(0)), 16);
+   PUSH_DATAp(push, r->vtxfmt, 16);
+
+   BEGIN_NV04(push, NV30_3D(VP_START_FROM_ID), 1);
+   PUSH_DATA (push, r->vertprog->start);
+   BEGIN_NV04(push, NV30_3D(ENGINE), 1);
+   PUSH_DATA (push, 0x00000103);
+   if (eng3d->oclass >= NV40_3D_CLASS) {
+      BEGIN_NV04(push, NV40_3D(VP_ATTRIB_EN), 2);
+      PUSH_DATA (push, vp_attribs);
+      PUSH_DATA (push, vp_results);
+   }
+
+   vinfo->size /= 4;
+   return TRUE;
+}
+
+void
+nv30_render_vbo(struct pipe_context *pipe, const struct pipe_draw_info *info)
 {
-       NOUVEAU_ERR("\n");
+   struct nv30_context *nv30 = nv30_context(pipe);
+   struct draw_context *draw = nv30->draw;
+   struct pipe_transfer *transfer[PIPE_MAX_ATTRIBS] = {NULL};
+   struct pipe_transfer *transferi = NULL;
+   int i;
+
+   nv30_render_validate(nv30);
+
+   if (nv30->draw_dirty & NV30_NEW_VIEWPORT)
+      draw_set_viewport_state(draw, &nv30->viewport);
+   if (nv30->draw_dirty & NV30_NEW_RASTERIZER)
+      draw_set_rasterizer_state(draw, &nv30->rast->pipe, NULL);
+   if (nv30->draw_dirty & NV30_NEW_CLIP)
+      draw_set_clip_state(draw, &nv30->clip);
+   if (nv30->draw_dirty & NV30_NEW_ARRAYS) {
+      draw_set_vertex_buffers(draw, 0, nv30->num_vtxbufs, nv30->vtxbuf);
+      draw_set_vertex_elements(draw, nv30->vertex->num_elements, nv30->vertex->pipe);
+   }
+   if (nv30->draw_dirty & NV30_NEW_FRAGPROG) {
+      struct nv30_fragprog *fp = nv30->fragprog.program;
+      if (!fp->draw)
+         fp->draw = draw_create_fragment_shader(draw, &fp->pipe);
+      draw_bind_fragment_shader(draw, fp->draw);
+   }
+   if (nv30->draw_dirty & NV30_NEW_VERTPROG) {
+      struct nv30_vertprog *vp = nv30->vertprog.program;
+      if (!vp->draw)
+         vp->draw = draw_create_vertex_shader(draw, &vp->pipe);
+      draw_bind_vertex_shader(draw, vp->draw);
+   }
+   if (nv30->draw_dirty & NV30_NEW_VERTCONST) {
+      if (nv30->vertprog.constbuf) {
+         void *map = nv04_resource(nv30->vertprog.constbuf)->data;
+         draw_set_mapped_constant_buffer(draw, PIPE_SHADER_VERTEX, 0,
+                                         map, nv30->vertprog.constbuf_nr);
+      }
+   }
+
+   for (i = 0; i < nv30->num_vtxbufs; i++) {
+      const void *map = nv30->vtxbuf[i].user_buffer;
+      if (!map) {
+         if (!nv30->vtxbuf[i].buffer) {
+            continue;
+         }
+         map = pipe_buffer_map(pipe, nv30->vtxbuf[i].buffer,
+                                  PIPE_TRANSFER_UNSYNCHRONIZED |
+                                  PIPE_TRANSFER_READ, &transfer[i]);
+      }
+      draw_set_mapped_vertex_buffer(draw, i, map);
+   }
+
+   if (info->indexed) {
+      const void *map = nv30->idxbuf.user_buffer;
+      if (!map)
+         pipe_buffer_map(pipe, nv30->idxbuf.buffer,
+                                  PIPE_TRANSFER_UNSYNCHRONIZED |
+                                  PIPE_TRANSFER_READ, &transferi);
+      draw_set_indexes(draw,
+                       (ubyte *) map + nv30->idxbuf.offset,
+                       nv30->idxbuf.index_size);
+   } else {
+      draw_set_indexes(draw, NULL, 0);
+   }
+
+   draw_vbo(draw, info);
+   draw_flush(draw);
+
+   if (info->indexed && transferi)
+      pipe_buffer_unmap(pipe, transferi);
+   for (i = 0; i < nv30->num_vtxbufs; i++)
+      if (transfer[i])
+         pipe_buffer_unmap(pipe, transfer[i]);
+
+   nv30->draw_dirty = 0;
+   nv30_state_release(nv30);
 }
 
 static void
-nv30_draw_destroy(struct draw_stage *draw)
+nv30_render_destroy(struct vbuf_render *render)
 {
-       FREE(draw);
+   FREE(render);
 }
 
-struct draw_stage *
-nv30_draw_render_stage(struct nv30_context *nv30)
+static struct vbuf_render *
+nv30_render_create(struct nv30_context *nv30)
 {
-       struct nv30_draw_stage *nv30draw = CALLOC_STRUCT(nv30_draw_stage);
+   struct nv30_render *r = CALLOC_STRUCT(nv30_render);
+   if (!r)
+      return NULL;
+
+   r->nv30 = nv30;
+   r->offset = 1 * 1024 * 1024;
 
-       nv30draw->nv30 = nv30;
-       nv30draw->draw.draw = nv30->draw;
-       nv30draw->draw.point = nv30_draw_point;
-       nv30draw->draw.line = nv30_draw_line;
-       nv30draw->draw.tri = nv30_draw_tri;
-       nv30draw->draw.flush = nv30_draw_flush;
-       nv30draw->draw.reset_stipple_counter = nv30_draw_reset_stipple_counter;
-       nv30draw->draw.destroy = nv30_draw_destroy;
+   r->base.max_indices = 16 * 1024;
+   r->base.max_vertex_buffer_bytes = r->offset;
 
-       return &nv30draw->draw;
+   r->base.get_vertex_info = nv30_render_get_vertex_info;
+   r->base.allocate_vertices = nv30_render_allocate_vertices;
+   r->base.map_vertices = nv30_render_map_vertices;
+   r->base.unmap_vertices = nv30_render_unmap_vertices;
+   r->base.set_primitive = nv30_render_set_primitive;
+   r->base.draw_elements = nv30_render_draw_elements;
+   r->base.draw_arrays = nv30_render_draw_arrays;
+   r->base.release_vertices = nv30_render_release_vertices;
+   r->base.destroy = nv30_render_destroy;
+   return &r->base;
 }
 
+void
+nv30_draw_init(struct pipe_context *pipe)
+{
+   struct nv30_context *nv30 = nv30_context(pipe);
+   struct vbuf_render *render;
+   struct draw_context *draw;
+   struct draw_stage *stage;
+
+   draw = draw_create(pipe);
+   if (!draw)
+      return;
+
+   render = nv30_render_create(nv30);
+   if (!render) {
+      draw_destroy(draw);
+      return;
+   }
+
+   stage = draw_vbuf_stage(draw, render);
+   if (!stage) {
+      render->destroy(render);
+      draw_destroy(draw);
+      return;
+   }
+
+   draw_set_render(draw, render);
+   draw_set_rasterize_stage(draw, stage);
+   draw_wide_line_threshold(draw, 10000000.f);
+   draw_wide_point_threshold(draw, 10000000.f);
+   draw_wide_point_sprites(draw, TRUE);
+   nv30->draw = draw;
+}