nv50: implement instanced drawing
authorChristoph Bumiller <e0425955@student.tuwien.ac.at>
Sun, 17 Jan 2010 16:37:55 +0000 (17:37 +0100)
committerChristoph Bumiller <e0425955@student.tuwien.ac.at>
Sun, 17 Jan 2010 16:48:01 +0000 (17:48 +0100)
Too bad we don't have hw array divisors or a method for
setting startInstance.

src/gallium/drivers/nv50/nv50_context.c
src/gallium/drivers/nv50/nv50_context.h
src/gallium/drivers/nv50/nv50_program.c
src/gallium/drivers/nv50/nv50_screen.c
src/gallium/drivers/nv50/nv50_screen.h
src/gallium/drivers/nv50/nv50_state_validate.c
src/gallium/drivers/nv50/nv50_vbo.c

index 22a8498..1e69746 100644 (file)
@@ -104,7 +104,9 @@ nv50_create(struct pipe_screen *pscreen, unsigned pctx_id)
        nv50->pipe.destroy = nv50_destroy;
 
        nv50->pipe.draw_arrays = nv50_draw_arrays;
+       nv50->pipe.draw_arrays_instanced = nv50_draw_arrays_instanced;
        nv50->pipe.draw_elements = nv50_draw_elements;
+       nv50->pipe.draw_elements_instanced = nv50_draw_elements_instanced;
        nv50->pipe.clear = nv50_clear;
 
        nv50->pipe.flush = nv50_flush;
index d024be6..bebcd95 100644 (file)
@@ -29,9 +29,7 @@
 #define NV50_CB_PVP            1
 #define NV50_CB_PFP            2
 #define NV50_CB_PGP            3
-#define NV50_CB_TIC            4
-#define NV50_CB_TSC            5
-#define NV50_CB_PUPLOAD         6
+#define NV50_CB_AUX            4
 
 #define NV50_NEW_BLEND         (1 << 0)
 #define NV50_NEW_ZSA           (1 << 1)
@@ -137,6 +135,7 @@ struct nv50_state {
        struct nouveau_stateobj *vtxfmt;
        struct nouveau_stateobj *vtxbuf;
        struct nouveau_stateobj *vtxattr;
+       struct nouveau_stateobj *instbuf;
        unsigned vtxelt_nr;
 };
 
@@ -198,11 +197,22 @@ extern struct draw_stage *nv50_draw_render_stage(struct nv50_context *nv50);
 /* nv50_vbo.c */
 extern void nv50_draw_arrays(struct pipe_context *, unsigned mode,
                                unsigned start, unsigned count);
+extern void nv50_draw_arrays_instanced(struct pipe_context *, unsigned mode,
+                                       unsigned start, unsigned count,
+                                       unsigned startInstance,
+                                       unsigned instanceCount);
 extern void nv50_draw_elements(struct pipe_context *pipe,
                                  struct pipe_buffer *indexBuffer,
                                  unsigned indexSize,
                                  unsigned mode, unsigned start,
                                  unsigned count);
+extern void nv50_draw_elements_instanced(struct pipe_context *pipe,
+                                        struct pipe_buffer *indexBuffer,
+                                        unsigned indexSize,
+                                        unsigned mode, unsigned start,
+                                        unsigned count,
+                                        unsigned startInstance,
+                                        unsigned instanceCount);
 extern void nv50_vbo_validate(struct nv50_context *nv50);
 
 /* nv50_clear.c */
index acd695b..4111d21 100644 (file)
@@ -95,6 +95,8 @@ struct nv50_reg {
 
        int vtx; /* vertex index, for GP inputs (TGSI Dimension.Index) */
        int indirect[2]; /* index into pc->addr, or -1 */
+
+       ubyte buf_index; /* c{0 .. 15}[] or g{0 .. 15}[] */
 };
 
 #define NV50_MOD_NEG 1
@@ -188,6 +190,7 @@ ctor_reg(struct nv50_reg *reg, unsigned type, int index, int hw)
        reg->vtx = -1;
        reg->acc = 0;
        reg->indirect[0] = reg->indirect[1] = -1;
+       reg->buf_index = (type == P_CONST) ? 1 : 0;
 }
 
 static INLINE unsigned
@@ -631,7 +634,7 @@ set_data(struct nv50_pc *pc, struct nv50_reg *src, unsigned m, unsigned s,
                set_addr(e, pc->addr[src->indirect[0]]);
        }
 
-       e->inst[1] |= (((src->type == P_IMMD) ? 0 : 1) << 22);
+       e->inst[1] |= (src->buf_index << 22);
 }
 
 /* Never apply nv50_reg::mod in emit_mov, or carefully check the code !!! */
@@ -3483,6 +3486,19 @@ load_frontfacing(struct nv50_pc *pc, struct nv50_reg *sv)
 }
 
 static void
+load_instance_id(struct nv50_pc *pc, unsigned index)
+{
+       struct nv50_reg reg, mem;
+
+       ctor_reg(&reg, P_TEMP, -1, -1);
+       ctor_reg(&mem, P_CONST, -1, 24); /* startInstance */
+       mem.buf_index = 2;
+
+       emit_add_b32(pc, &reg, &pc->sysval[index], &mem);
+       pc->sysval[index] = reg;
+}
+
+static void
 copy_semantic_info(struct nv50_program *p)
 {
        unsigned i, id;
@@ -3668,8 +3684,10 @@ nv50_program_tx_prep(struct nv50_pc *pc)
                        }
                        if (p->cfg.regs[0] & (1 << 0))
                                pc->sysval[vertex_id].hw = rid++;
-                       if (p->cfg.regs[0] & (1 << 4))
+                       if (p->cfg.regs[0] & (1 << 4)) {
                                pc->sysval[instance_id].hw = rid++;
+                               load_instance_id(pc, instance_id);
+                       }
                }
 
                for (i = 0, rid = 0; i < pc->result_nr; ++i) {
index 7320565..9d58f3c 100644 (file)
@@ -329,7 +329,7 @@ nv50_screen_create(struct pipe_winsys *ws, struct nouveau_device *dev)
        so_ref(NULL, &so);
 
        /* Static tesla init */
-       so = so_new(44, 90, 22);
+       so = so_new(47, 95, 24);
 
        so_method(so, screen->tesla, NV50TCL_COND_MODE, 1);
        so_data  (so, NV50TCL_COND_MODE_ALWAYS);
@@ -372,7 +372,7 @@ nv50_screen_create(struct pipe_winsys *ws, struct nouveau_device *dev)
        }
 
        for (i = 0; i < 3; i++) {
-               ret = nouveau_bo_new(dev, NOUVEAU_BO_VRAM, 0, (128 * 4) * 4,
+               ret = nouveau_bo_new(dev, NOUVEAU_BO_VRAM, 0, (256 * 4) * 4,
                                     &screen->constbuf_parm[i]);
                if (ret) {
                        nv50_screen_destroy(pscreen);
@@ -411,6 +411,18 @@ nv50_screen_create(struct pipe_winsys *ws, struct nouveau_device *dev)
        so_method(so, screen->tesla, NV50TCL_SET_PROGRAM_CB, 1);
        so_data  (so, 0x00000031 | (NV50_CB_PMISC << 12));
 
+       /* bind auxiliary constbuf to immediate data bo */
+       so_method(so, screen->tesla, NV50TCL_CB_DEF_ADDRESS_HIGH, 3);
+       so_reloc (so, screen->constbuf_misc[0], (128 * 4) * 4,
+                 NOUVEAU_BO_VRAM | NOUVEAU_BO_RD | NOUVEAU_BO_HIGH, 0, 0);
+       so_reloc (so, screen->constbuf_misc[0], (128 * 4) * 4,
+                 NOUVEAU_BO_VRAM | NOUVEAU_BO_RD | NOUVEAU_BO_LOW, 0, 0);
+       so_data  (so, (NV50_CB_AUX << 16) | 0x00000200);
+       so_method(so, screen->tesla, NV50TCL_SET_PROGRAM_CB, 1);
+       so_data  (so, 0x00000201 | (NV50_CB_AUX << 12));
+       so_method(so, screen->tesla, NV50TCL_SET_PROGRAM_CB, 1);
+       so_data  (so, 0x00000221 | (NV50_CB_AUX << 12));
+
        so_method(so, screen->tesla, NV50TCL_CB_DEF_ADDRESS_HIGH, 3);
        so_reloc (so, screen->constbuf_parm[PIPE_SHADER_VERTEX], 0,
                  NOUVEAU_BO_VRAM | NOUVEAU_BO_RD | NOUVEAU_BO_HIGH, 0, 0);
index 7a155ca..0d786b0 100644 (file)
@@ -23,6 +23,8 @@ struct nv50_screen {
        struct nouveau_resource *immd_heap[1];
        struct nouveau_resource *parm_heap[PIPE_SHADER_TYPES];
 
+       struct pipe_buffer *strm_vbuf[16];
+
        struct nouveau_bo *tic;
        struct nouveau_bo *tsc;
 
index fcd07b5..956da9b 100644 (file)
@@ -274,6 +274,9 @@ nv50_state_flush_notify(struct nouveau_channel *chan)
        so_emit_reloc_markers(chan, nv50->state.fragprog);
        so_emit_reloc_markers(chan, nv50->state.vtxbuf);
        so_emit_reloc_markers(chan, nv50->screen->static_init);
+
+       if (nv50->state.instbuf)
+               so_emit_reloc_markers(chan, nv50->state.instbuf);
 }
 
 boolean
index 89a94d2..3238629 100644 (file)
@@ -160,6 +160,188 @@ nv50_vbo_vtxelt_to_hw(struct pipe_vertex_element *ve)
        return (hw_type | hw_size);
 }
 
+/* For instanced drawing from user buffers, hitting the FIFO repeatedly
+ * with the same vertex data is probably worse than uploading all data.
+ */
+static boolean
+nv50_upload_vtxbuf(struct nv50_context *nv50, unsigned i)
+{
+       struct nv50_screen *nscreen = nv50->screen;
+       struct pipe_screen *pscreen = &nscreen->base.base;
+       struct pipe_buffer *buf = nscreen->strm_vbuf[i];
+       struct pipe_vertex_buffer *vb = &nv50->vtxbuf[i];
+       uint8_t *src;
+       unsigned size = MAX2(vb->buffer->size, 4096);
+
+       if (buf && buf->size < size)
+               pipe_buffer_reference(&nscreen->strm_vbuf[i], NULL);
+
+       if (!nscreen->strm_vbuf[i]) {
+               nscreen->strm_vbuf[i] = pipe_buffer_create(
+                       pscreen, 0, PIPE_BUFFER_USAGE_VERTEX, size);
+               buf = nscreen->strm_vbuf[i];
+       }
+
+       src = pipe_buffer_map(pscreen, vb->buffer, PIPE_BUFFER_USAGE_CPU_READ);
+       if (!src)
+               return FALSE;
+       src += vb->buffer_offset;
+
+       size = (vb->max_index + 1) * vb->stride + 16; /* + 16 is for stride 0 */
+       if (vb->buffer_offset + size > vb->buffer->size)
+               size = vb->buffer->size - vb->buffer_offset;
+
+       pipe_buffer_write(pscreen, buf, vb->buffer_offset, size, src);
+       pipe_buffer_unmap(pscreen, vb->buffer);
+
+       vb->buffer = buf; /* don't pipe_reference, this is a private copy */
+       return TRUE;
+}
+
+static void
+nv50_upload_user_vbufs(struct nv50_context *nv50)
+{
+       unsigned i;
+
+       if (nv50->vbo_fifo)
+               nv50->dirty |= NV50_NEW_ARRAYS;
+       if (!(nv50->dirty & NV50_NEW_ARRAYS))
+               return;
+
+       for (i = 0; i < nv50->vtxbuf_nr; ++i) {
+               if (nv50->vtxbuf[i].buffer->usage & PIPE_BUFFER_USAGE_VERTEX)
+                       continue;
+               nv50_upload_vtxbuf(nv50, i);
+       }
+}
+
+static unsigned
+init_per_instance_arrays(struct nv50_context *nv50,
+                        unsigned startInstance,
+                        unsigned pos[16], unsigned step[16])
+{
+       struct nouveau_grobj *tesla = nv50->screen->tesla;
+       struct nouveau_channel *chan = tesla->channel;
+       struct nouveau_bo *bo;
+       struct nouveau_stateobj *so;
+       unsigned i, b, count = 0;
+       const uint32_t rl = NOUVEAU_BO_VRAM | NOUVEAU_BO_GART | NOUVEAU_BO_RD;
+
+       so = so_new(nv50->vtxelt_nr, nv50->vtxelt_nr * 2, nv50->vtxelt_nr * 2);
+
+       for (i = 0; i < nv50->vtxelt_nr; ++i) {
+               if (!nv50->vtxelt[i].instance_divisor)
+                       continue;
+               ++count;
+               b = nv50->vtxelt[i].vertex_buffer_index;
+
+               pos[i] = nv50->vtxelt[i].src_offset +
+                       nv50->vtxbuf[b].buffer_offset +
+                       startInstance * nv50->vtxbuf[b].stride;
+
+               if (!startInstance) {
+                       step[i] = 0;
+                       continue;
+               }
+               step[i] = startInstance % nv50->vtxelt[i].instance_divisor;
+
+               bo = nouveau_bo(nv50->vtxbuf[b].buffer);
+
+               so_method(so, tesla, NV50TCL_VERTEX_ARRAY_START_HIGH(i), 2);
+               so_reloc (so, bo, pos[i], rl | NOUVEAU_BO_LOW, 0, 0);
+               so_reloc (so, bo, pos[i], rl | NOUVEAU_BO_HIGH, 0, 0);
+       }
+
+       if (count) {
+               so_ref (so, &nv50->state.instbuf); /* for flush notify */
+               so_emit(chan, nv50->state.instbuf);
+       }
+       so_ref (NULL, &so);
+
+       return count;
+}
+
+static void
+step_per_instance_arrays(struct nv50_context *nv50,
+                        unsigned pos[16], unsigned step[16])
+{
+       struct nouveau_grobj *tesla = nv50->screen->tesla;
+       struct nouveau_channel *chan = tesla->channel;
+       struct nouveau_bo *bo;
+       struct nouveau_stateobj *so;
+       unsigned i, b;
+       const uint32_t rl = NOUVEAU_BO_VRAM | NOUVEAU_BO_GART | NOUVEAU_BO_RD;
+
+       so = so_new(nv50->vtxelt_nr, nv50->vtxelt_nr * 2, nv50->vtxelt_nr * 2);
+
+       for (i = 0; i < nv50->vtxelt_nr; ++i) {
+               if (!nv50->vtxelt[i].instance_divisor)
+                       continue;
+               b = nv50->vtxelt[i].vertex_buffer_index;
+
+               if (++step[i] == nv50->vtxelt[i].instance_divisor) {
+                       step[i] = 0;
+                       pos[i] += nv50->vtxbuf[b].stride;
+               }
+
+               bo = nouveau_bo(nv50->vtxbuf[b].buffer);
+
+               so_method(so, tesla, NV50TCL_VERTEX_ARRAY_START_HIGH(i), 2);
+               so_reloc (so, bo, pos[i], rl | NOUVEAU_BO_LOW, 0, 0);
+               so_reloc (so, bo, pos[i], rl | NOUVEAU_BO_HIGH, 0, 0);
+       }
+
+       so_ref (so, &nv50->state.instbuf); /* for flush notify */
+       so_ref (NULL, &so);
+
+       so_emit(chan, nv50->state.instbuf);
+}
+
+void
+nv50_draw_arrays_instanced(struct pipe_context *pipe,
+                          unsigned mode, unsigned start, unsigned count,
+                          unsigned startInstance, unsigned instanceCount)
+{
+       struct nv50_context *nv50 = nv50_context(pipe);
+       struct nouveau_channel *chan = nv50->screen->tesla->channel;
+       struct nouveau_grobj *tesla = nv50->screen->tesla;
+       unsigned i, nz_divisors;
+       unsigned step[16], pos[16];
+
+       nv50_upload_user_vbufs(nv50);
+
+       nv50_state_validate(nv50);
+
+       nz_divisors = init_per_instance_arrays(nv50, startInstance, pos, step);
+
+       BEGIN_RING(chan, tesla, NV50TCL_CB_ADDR, 2);
+       OUT_RING  (chan, NV50_CB_AUX | (24 << 8));
+       OUT_RING  (chan, startInstance);
+
+       BEGIN_RING(chan, tesla, NV50TCL_VERTEX_BEGIN, 1);
+       OUT_RING  (chan, nv50_prim(mode));
+       BEGIN_RING(chan, tesla, NV50TCL_VERTEX_BUFFER_FIRST, 2);
+       OUT_RING  (chan, start);
+       OUT_RING  (chan, count);
+       BEGIN_RING(chan, tesla, NV50TCL_VERTEX_END, 1);
+       OUT_RING  (chan, 0);
+
+       for (i = 1; i < instanceCount; i++) {
+               if (nz_divisors) /* any non-zero array divisors ? */
+                       step_per_instance_arrays(nv50, pos, step);
+
+               BEGIN_RING(chan, tesla, NV50TCL_VERTEX_BEGIN, 1);
+               OUT_RING  (chan, nv50_prim(mode) | (1 << 28));
+               BEGIN_RING(chan, tesla, NV50TCL_VERTEX_BUFFER_FIRST, 2);
+               OUT_RING  (chan, start);
+               OUT_RING  (chan, count);
+               BEGIN_RING(chan, tesla, NV50TCL_VERTEX_END, 1);
+               OUT_RING  (chan, 0);
+       }
+
+       so_ref(NULL, &nv50->state.instbuf);
+}
+
 void
 nv50_draw_arrays(struct pipe_context *pipe, unsigned mode, unsigned start,
                 unsigned count)
@@ -285,6 +467,75 @@ nv50_draw_elements_inline_u32(struct nv50_context *nv50, uint32_t *map,
        return TRUE;
 }
 
+static INLINE void
+nv50_draw_elements_inline(struct nv50_context *nv50,
+                         void *map, unsigned indexSize,
+                         unsigned start, unsigned count)
+{
+       switch (indexSize) {
+       case 1:
+               nv50_draw_elements_inline_u08(nv50, map, start, count);
+               break;
+       case 2:
+               nv50_draw_elements_inline_u16(nv50, map, start, count);
+               break;
+       case 4:
+               nv50_draw_elements_inline_u32(nv50, map, start, count);
+               break;
+       }
+}
+
+void
+nv50_draw_elements_instanced(struct pipe_context *pipe,
+                            struct pipe_buffer *indexBuffer,
+                            unsigned indexSize,
+                            unsigned mode, unsigned start, unsigned count,
+                            unsigned startInstance, unsigned instanceCount)
+{
+       struct nv50_context *nv50 = nv50_context(pipe);
+       struct nouveau_grobj *tesla = nv50->screen->tesla;
+       struct nouveau_channel *chan = tesla->channel;
+       struct pipe_screen *pscreen = pipe->screen;
+       void *map;
+       unsigned i, nz_divisors;
+       unsigned step[16], pos[16];
+
+       map = pipe_buffer_map(pscreen, indexBuffer, PIPE_BUFFER_USAGE_CPU_READ);
+
+       nv50_upload_user_vbufs(nv50);
+
+       nv50_state_validate(nv50);
+
+       nz_divisors = init_per_instance_arrays(nv50, startInstance, pos, step);
+
+       BEGIN_RING(chan, tesla, NV50TCL_CB_ADDR, 2);
+       OUT_RING  (chan, NV50_CB_AUX | (24 << 8));
+       OUT_RING  (chan, startInstance);
+
+       BEGIN_RING(chan, tesla, NV50TCL_VERTEX_BEGIN, 1);
+       OUT_RING  (chan, nv50_prim(mode) | (1 << 28));
+
+       nv50_draw_elements_inline(nv50, map, indexSize, start, count);
+
+       BEGIN_RING(chan, tesla, NV50TCL_VERTEX_END, 1);
+       OUT_RING  (chan, 0);
+
+       for (i = 1; i < instanceCount; ++i) {
+               if (nz_divisors) /* any non-zero array divisors ? */
+                       step_per_instance_arrays(nv50, pos, step);
+
+               BEGIN_RING(chan, tesla, NV50TCL_VERTEX_BEGIN, 1);
+               OUT_RING  (chan, nv50_prim(mode) | (1 << 28));
+
+               nv50_draw_elements_inline(nv50, map, indexSize, start, count);
+
+               BEGIN_RING(chan, tesla, NV50TCL_VERTEX_END, 1);
+               OUT_RING  (chan, 0);
+       }
+
+       so_ref(NULL, &nv50->state.instbuf);
+}
+
 void
 nv50_draw_elements(struct pipe_context *pipe,
                   struct pipe_buffer *indexBuffer, unsigned indexSize,
@@ -295,7 +546,6 @@ nv50_draw_elements(struct pipe_context *pipe,
        struct nouveau_grobj *tesla = nv50->screen->tesla;
        struct pipe_screen *pscreen = pipe->screen;
        void *map;
-       boolean ret;
        
        map = pipe_buffer_map(pscreen, indexBuffer, PIPE_BUFFER_USAGE_CPU_READ);
 
@@ -308,29 +558,13 @@ nv50_draw_elements(struct pipe_context *pipe,
 
        BEGIN_RING(chan, tesla, NV50TCL_VERTEX_BEGIN, 1);
        OUT_RING  (chan, nv50_prim(mode));
-       switch (indexSize) {
-       case 1:
-               ret = nv50_draw_elements_inline_u08(nv50, map, start, count);
-               break;
-       case 2:
-               ret = nv50_draw_elements_inline_u16(nv50, map, start, count);
-               break;
-       case 4:
-               ret = nv50_draw_elements_inline_u32(nv50, map, start, count);
-               break;
-       default:
-               assert(0);
-               ret = FALSE;
-               break;
-       }
+
+       nv50_draw_elements_inline(nv50, map, indexSize, start, count);
+
        BEGIN_RING(chan, tesla, NV50TCL_VERTEX_END, 1);
        OUT_RING  (chan, 0);
 
        pipe_buffer_unmap(pscreen, indexBuffer);
-        
-        /* XXX: what to do if ret != TRUE?  Flush and retry?
-         */
-       assert(ret);
 }
 
 static INLINE boolean
@@ -343,23 +577,16 @@ nv50_vbo_static_attrib(struct nv50_context *nv50, unsigned attrib,
        struct nouveau_stateobj *so;
        struct nouveau_grobj *tesla = nv50->screen->tesla;
        struct nouveau_bo *bo = nouveau_bo(vb->buffer);
-       float *v;
+       float v[4];
        int ret;
-       enum pipe_format pf = ve->src_format;
-       const struct util_format_description *desc;
-
-       desc = util_format_description(pf);
-       assert(desc);
-
-       if ((desc->channel[0].type != UTIL_FORMAT_TYPE_FLOAT) ||
-           util_format_get_component_bits(pf, UTIL_FORMAT_COLORSPACE_RGB, 0) != 32)
-               return FALSE;
 
        ret = nouveau_bo_map(bo, NOUVEAU_BO_RD);
        if (ret)
                return FALSE;
-       v = (float *)(bo->map + (vb->buffer_offset + ve->src_offset));
 
+       util_format_read_4f(ve->src_format, v, 0, (uint8_t *)bo->map +
+                           (vb->buffer_offset + ve->src_offset), 0,
+                           0, 0, 1, 1);
        so = *pso;
        if (!so)
                *pso = so = so_new(nv50->vtxelt_nr, nv50->vtxelt_nr * 4, 0);
@@ -455,7 +682,8 @@ nv50_vbo_validate(struct nv50_context *nv50)
                }
 
                so_method(vtxbuf, tesla, NV50TCL_VERTEX_ARRAY_FORMAT(i), 3);
-               so_data  (vtxbuf, 0x20000000 | vb->stride);
+               so_data  (vtxbuf, 0x20000000 |
+                         (ve->instance_divisor ? 0 : vb->stride));
                so_reloc (vtxbuf, bo, vb->buffer_offset +
                          ve->src_offset, NOUVEAU_BO_VRAM | NOUVEAU_BO_GART |
                          NOUVEAU_BO_RD | NOUVEAU_BO_HIGH, 0, 0);