Mesa (master): nvc0: update user vbufs on each draw call

Christoph Bumiller chrisbmr at kemper.freedesktop.org
Sat Jan 15 11:19:45 UTC 2011


Module: Mesa
Branch: master
Commit: 1ae982adfd8144d11334895c39aca65dd30ad946
URL:    http://cgit.freedesktop.org/mesa/mesa/commit/?id=1ae982adfd8144d11334895c39aca65dd30ad946

Author: Christoph Bumiller <e0425955 at student.tuwien.ac.at>
Date:   Sat Jan 15 12:18:52 2011 +0100

nvc0: update user vbufs on each draw call

This is required in case set_vertex_buffers is not called again.

---

 src/gallium/drivers/nvc0/nvc0_buffer.c   |   30 ++++++--
 src/gallium/drivers/nvc0/nvc0_context.h  |    3 +-
 src/gallium/drivers/nvc0/nvc0_resource.h |   11 +++-
 src/gallium/drivers/nvc0/nvc0_vbo.c      |  112 ++++++++++++++++++++++-------
 4 files changed, 119 insertions(+), 37 deletions(-)

diff --git a/src/gallium/drivers/nvc0/nvc0_buffer.c b/src/gallium/drivers/nvc0/nvc0_buffer.c
index dad69e1..ea3e642 100644
--- a/src/gallium/drivers/nvc0/nvc0_buffer.c
+++ b/src/gallium/drivers/nvc0/nvc0_buffer.c
@@ -59,6 +59,18 @@ release_allocation(struct nvc0_mm_allocation **mm, struct nvc0_fence *fence)
    (*mm) = NULL;
 }
 
+static INLINE boolean
+nvc0_buffer_reallocate(struct nvc0_screen *screen, struct nvc0_resource *buf,
+                       unsigned domain)
+{
+   nouveau_bo_ref(NULL, &buf->bo);
+
+   if (buf->mm)
+      release_allocation(&buf->mm, buf->fence);
+
+   return nvc0_buffer_allocate(screen, buf, domain);
+}
+
 static void
 nvc0_buffer_destroy(struct pipe_screen *pscreen,
                     struct pipe_resource *presource)
@@ -372,8 +384,9 @@ nvc0_user_buffer_create(struct pipe_screen *pscreen,
    return &buffer->base;
 }
 
+/* Like download, but for GART buffers. Merge ? */
 static INLINE boolean
-nvc0_buffer_fetch_data(struct nvc0_resource *buf,
+nvc0_buffer_data_fetch(struct nvc0_resource *buf,
                        struct nouveau_bo *bo, unsigned offset, unsigned size)
 {
    if (!buf->data) {
@@ -419,7 +432,7 @@ nvc0_buffer_migrate(struct nvc0_context *nvc0,
 
       if (new_domain == NOUVEAU_BO_VRAM) {
          /* keep a system memory copy of our data in case we hit a fallback */
-         if (!nvc0_buffer_fetch_data(buf, buf->bo, buf->offset, size))
+         if (!nvc0_buffer_data_fetch(buf, buf->bo, buf->offset, size))
             return FALSE;
          debug_printf("migrating %u KiB to VRAM\n", size / 1024);
       }
@@ -450,19 +463,22 @@ nvc0_buffer_migrate(struct nvc0_context *nvc0,
 }
 
 /* Migrate data from glVertexAttribPointer(non-VBO) user buffers to GART.
- * MUST NOT FLUSH THE PUSH BUFFER, we could be in the middle of a method.
+ * We'd like to only allocate @size bytes here, but then we'd have to rebase
+ * the vertex indices ...
  */
 boolean
-nvc0_migrate_vertices(struct nvc0_resource *buf, unsigned base, unsigned size)
+nvc0_user_buffer_upload(struct nvc0_resource *buf, unsigned base, unsigned size)
 {
    struct nvc0_screen *screen = nvc0_screen(buf->base.screen);
    int ret;
 
-   assert(buf->data && !buf->domain);
+   assert(buf->status & NVC0_BUFFER_STATUS_USER_MEMORY);
 
-   if (!nvc0_buffer_allocate(screen, buf, NOUVEAU_BO_GART))
+   buf->base.width0 = base + size;
+   if (!nvc0_buffer_reallocate(screen, buf, NOUVEAU_BO_GART))
       return FALSE;
-   ret = nouveau_bo_map_range(buf->bo, base + buf->offset, size,
+
+   ret = nouveau_bo_map_range(buf->bo, buf->offset + base, size,
                               NOUVEAU_BO_WR | NOUVEAU_BO_NOSYNC);
    if (ret)
       return FALSE;
diff --git a/src/gallium/drivers/nvc0/nvc0_context.h b/src/gallium/drivers/nvc0/nvc0_context.h
index 0f340be..eeb5bef 100644
--- a/src/gallium/drivers/nvc0/nvc0_context.h
+++ b/src/gallium/drivers/nvc0/nvc0_context.h
@@ -100,7 +100,8 @@ struct nvc0_context {
    struct pipe_vertex_buffer vtxbuf[PIPE_MAX_ATTRIBS];
    unsigned num_vtxbufs;
    struct pipe_index_buffer idxbuf;
-   uint32_t vbo_fifo;
+   uint32_t vbo_fifo; /* bitmask of vertex elements to be pushed to FIFO */
+   uint32_t vbo_user; /* bitmask of vertex buffers pointing to user memory */
    unsigned vbo_min_index; /* from pipe_draw_info, for vertex upload */
    unsigned vbo_max_index;
 
diff --git a/src/gallium/drivers/nvc0/nvc0_resource.h b/src/gallium/drivers/nvc0/nvc0_resource.h
index d33e2f0..17e7964 100644
--- a/src/gallium/drivers/nvc0/nvc0_resource.h
+++ b/src/gallium/drivers/nvc0/nvc0_resource.h
@@ -18,6 +18,12 @@ struct nvc0_context;
 #define NVC0_BUFFER_SCORE_MAX  25000
 #define NVC0_BUFFER_SCORE_VRAM_THRESHOLD 20000
 
+/* DIRTY: buffer was (or will be after the next flush) written to by GPU and
+ *  resource->data has not been updated to reflect modified VRAM contents
+ *
+ * USER_MEMORY: resource->data is a pointer to client memory and may change
+ *  between GL calls
+ */
 #define NVC0_BUFFER_STATUS_DIRTY       (1 << 0)
 #define NVC0_BUFFER_STATUS_USER_MEMORY (1 << 7)
 
@@ -84,7 +90,8 @@ nvc0_resource_map_offset(struct nvc0_context *nvc0,
        (res->status & NVC0_BUFFER_STATUS_DIRTY))
       nvc0_buffer_download(nvc0, res, 0, res->base.width0);
 
-   if (res->domain != NOUVEAU_BO_GART)
+   if ((res->domain != NOUVEAU_BO_GART) ||
+       (res->status & NVC0_BUFFER_STATUS_USER_MEMORY))
       return res->data + offset;
 
    if (res->mm)
@@ -189,6 +196,6 @@ void
 nvc0_miptree_surface_del(struct pipe_context *, struct pipe_surface *);
 
 boolean
-nvc0_migrate_vertices(struct nvc0_resource *buf, unsigned base, unsigned size);
+nvc0_user_buffer_upload(struct nvc0_resource *, unsigned base, unsigned size);
 
 #endif
diff --git a/src/gallium/drivers/nvc0/nvc0_vbo.c b/src/gallium/drivers/nvc0/nvc0_vbo.c
index fd7a794..a14e955 100644
--- a/src/gallium/drivers/nvc0/nvc0_vbo.c
+++ b/src/gallium/drivers/nvc0/nvc0_vbo.c
@@ -141,52 +141,107 @@ nvc0_emit_vtxattr(struct nvc0_context *nvc0, struct pipe_vertex_buffer *vb,
       OUT_RINGf(chan, v[i]);
 }
 
-void
-nvc0_vertex_arrays_validate(struct nvc0_context *nvc0)
+static void
+nvc0_prevalidate_vbufs(struct nvc0_context *nvc0)
 {
-   struct nouveau_channel *chan = nvc0->screen->base.channel;
-   struct nvc0_vertex_stateobj *vertex = nvc0->vertex;
    struct pipe_vertex_buffer *vb;
-   struct nvc0_vertex_element *ve;
-   unsigned i;
-   boolean push = FALSE;
+   struct nvc0_resource *buf;
+   int i;
+   uint32_t base, size;
 
-   nvc0->vbo_fifo = 0;
+   nvc0->vbo_fifo = nvc0->vbo_user = 0;
 
    for (i = 0; i < nvc0->num_vtxbufs; ++i) {
       vb = &nvc0->vtxbuf[i];
+      if (!vb->stride)
+         continue;
+      buf = nvc0_resource(vb->buffer);
 
       if (!nvc0_resource_mapped_by_gpu(vb->buffer)) {
-         if (vb->stride == 0)
+         if (nvc0->vbo_push_hint) {
+            nvc0->vbo_fifo = ~0;
             continue;
-         push = nvc0->vbo_push_hint;
-         if (!push) {
-            unsigned base, size;
-            base = vb->buffer_offset + nvc0->vbo_min_index * vb->stride;
-            size = (nvc0->vbo_max_index - nvc0->vbo_min_index + 1) * vb->stride;
-            nvc0_migrate_vertices(nvc0_resource(vb->buffer), base, size);
+         } else {
+            if (buf->status & NVC0_BUFFER_STATUS_USER_MEMORY) {
+               nvc0->vbo_user |= 1 << i;
+               assert(vb->stride > vb->buffer_offset);
+               size = vb->stride * (nvc0->vbo_max_index -
+                                    nvc0->vbo_min_index + 1);
+               base = vb->stride * nvc0->vbo_min_index;
+               nvc0_user_buffer_upload(buf, base, size);
+            } else {
+               nvc0_buffer_migrate(nvc0, buf, NOUVEAU_BO_GART);
+            }
             nvc0->vbo_dirty = TRUE;
-         } else
-            continue;
+         }
       }
-      nvc0_buffer_adjust_score(nvc0, nvc0_resource(vb->buffer), 1);
+      nvc0_bufctx_add_resident(nvc0, NVC0_BUFCTX_VERTEX, buf, NOUVEAU_BO_RD);
+      nvc0_buffer_adjust_score(nvc0, buf, 1);
+   }
+}
 
-      nvc0_bufctx_add_resident(nvc0, NVC0_BUFCTX_VERTEX,
-                               nvc0_resource(vb->buffer), NOUVEAU_BO_RD);
+static void
+nvc0_update_user_vbufs(struct nvc0_context *nvc0)
+{
+   struct nouveau_channel *chan = nvc0->screen->base.channel;
+   const uint32_t vertex_count = nvc0->vbo_max_index - nvc0->vbo_min_index + 1;
+   uint32_t base, offset, size;
+   int i;
+   uint32_t written = 0;
+
+   for (i = 0; i < nvc0->vertex->num_elements; ++i) {
+      struct pipe_vertex_element *ve = &nvc0->vertex->element[i].pipe;
+      const int b = ve->vertex_buffer_index;
+      struct pipe_vertex_buffer *vb = &nvc0->vtxbuf[b];
+      struct nvc0_resource *buf = nvc0_resource(vb->buffer);
+
+      if (!(nvc0->vbo_user & (1 << b)))
+         continue;
+
+      if (!vb->stride) {
+         nvc0_emit_vtxattr(nvc0, vb, ve, i);
+         continue;
+      }
+      size = vb->stride * vertex_count;
+      base = vb->stride * nvc0->vbo_min_index;
+
+      if (!(written & (1 << b))) {
+         written |= 1 << b;
+         nvc0_user_buffer_upload(buf, base, size);
+      }
+      offset = vb->buffer_offset + ve->src_offset;
+
+      BEGIN_RING_1I(chan, RING_3D(VERTEX_ARRAY_SELECT), 5);
+      OUT_RING  (chan, i);
+      OUT_RESRCh(chan, buf, size - 1, NOUVEAU_BO_RD);
+      OUT_RESRCl(chan, buf, size - 1, NOUVEAU_BO_RD);
+      OUT_RESRCh(chan, buf, offset, NOUVEAU_BO_RD);
+      OUT_RESRCl(chan, buf, offset, NOUVEAU_BO_RD);
    }
+   nvc0->vbo_dirty = TRUE;
+}
+
+void
+nvc0_vertex_arrays_validate(struct nvc0_context *nvc0)
+{
+   struct nouveau_channel *chan = nvc0->screen->base.channel;
+   struct nvc0_vertex_stateobj *vertex = nvc0->vertex;
+   struct pipe_vertex_buffer *vb;
+   struct nvc0_vertex_element *ve;
+   unsigned i;
+
+   nvc0_prevalidate_vbufs(nvc0);
 
    BEGIN_RING(chan, RING_3D(VERTEX_ATTRIB_FORMAT(0)), vertex->num_elements);
    for (i = 0; i < vertex->num_elements; ++i) {
       ve = &vertex->element[i];
       vb = &nvc0->vtxbuf[ve->pipe.vertex_buffer_index];
 
-      if (push)
-         nvc0->vbo_fifo |= 1 << i;
-
-      if (likely(vb->stride) || push) {
+      if (likely(vb->stride) || nvc0->vbo_fifo) {
          OUT_RING(chan, ve->state);
       } else {
          OUT_RING(chan, ve->state | NVC0_3D_VERTEX_ATTRIB_FORMAT_CONST);
+         nvc0->vbo_fifo &= ~(1 << i);
       }
    }
 
@@ -210,8 +265,8 @@ nvc0_vertex_arrays_validate(struct nvc0_context *nvc0)
 
       res = nvc0_resource(vb->buffer);
 
-      if (push || unlikely(vb->stride == 0)) {
-         if (!push)
+      if (nvc0->vbo_fifo || unlikely(vb->stride == 0)) {
+         if (!nvc0->vbo_fifo)
             nvc0_emit_vtxattr(nvc0, vb, &ve->pipe, i);
          BEGIN_RING(chan, RING_3D(VERTEX_ARRAY_FETCH(i)), 1);
          OUT_RING  (chan, 0);
@@ -540,6 +595,9 @@ nvc0_draw_vbo(struct pipe_context *pipe, const struct pipe_draw_info *info)
    nvc0->vbo_min_index = info->min_index;
    nvc0->vbo_max_index = info->max_index;
 
+   if (nvc0->vbo_user && !(nvc0->dirty & (NVC0_NEW_VERTEX | NVC0_NEW_ARRAYS)))
+      nvc0_update_user_vbufs(nvc0);
+
    nvc0_state_validate(nvc0);
 
    if (nvc0->state.instance_base != info->start_instance) {
@@ -554,7 +612,7 @@ nvc0_draw_vbo(struct pipe_context *pipe, const struct pipe_draw_info *info)
    }
 
    if (nvc0->vbo_dirty) {
-      BEGIN_RING(chan, RING_3D_(0x142c), 1);
+      BEGIN_RING(chan, RING_3D(VERTEX_ARRAY_FLUSH), 1);
       OUT_RING  (chan, 0);
       nvc0->vbo_dirty = FALSE;
    }




More information about the mesa-commit mailing list