nouveau: add new shared scratch buffers
authorChristoph Bumiller <e0425955@student.tuwien.ac.at>
Sat, 14 Apr 2012 03:38:16 +0000 (05:38 +0200)
committerChristoph Bumiller <e0425955@student.tuwien.ac.at>
Sat, 14 Apr 2012 04:14:21 +0000 (06:14 +0200)
src/gallium/drivers/nouveau/nouveau_buffer.c
src/gallium/drivers/nouveau/nouveau_buffer.h
src/gallium/drivers/nouveau/nouveau_context.h

index d2d011346e91876b46ea7457291b87b60e1ac480..ff199debd7445238efb8ae863acf3b884ba2f8b8 100644 (file)
@@ -506,3 +506,144 @@ nouveau_user_buffer_upload(struct nouveau_context *nv,
 
    return TRUE;
 }
+
+
+/* Scratch data allocation. */
+
+static INLINE int
+nouveau_scratch_bo_alloc(struct nouveau_context *nv, struct nouveau_bo **pbo,
+                         unsigned size)
+{
+   return nouveau_bo_new(nv->screen->device, NOUVEAU_BO_GART | NOUVEAU_BO_MAP,
+                         4096, size, NULL, pbo);
+}
+
+void
+nouveau_scratch_runout_release(struct nouveau_context *nv)
+{
+   if (!nv->scratch.nr_runout)
+      return;
+   while (nv->scratch.nr_runout--)
+      nouveau_bo_ref(NULL, &nv->scratch.runout[nv->scratch.nr_runout]);
+
+   FREE(nv->scratch.runout);
+   nv->scratch.end = 0;
+   nv->scratch.runout = NULL;
+}
+
+/* Allocate an extra bo if we can't fit everything we need simultaneously.
+ * (Could happen for very large user arrays.)
+ */
+static INLINE boolean
+nouveau_scratch_runout(struct nouveau_context *nv, unsigned size)
+{
+   int ret;
+   const unsigned n = nv->scratch.nr_runout++;
+
+   nv->scratch.runout = REALLOC(nv->scratch.runout,
+                                (n + 0) * sizeof(*nv->scratch.runout),
+                                (n + 1) * sizeof(*nv->scratch.runout));
+   nv->scratch.runout[n] = NULL;
+
+   ret = nouveau_scratch_bo_alloc(nv, &nv->scratch.runout[n], size);
+   if (!ret) {
+      ret = nouveau_bo_map(nv->scratch.runout[n], 0, NULL);
+      if (ret)
+         nouveau_bo_ref(NULL, &nv->scratch.runout[--nv->scratch.nr_runout]);
+   }
+   if (!ret) {
+      nv->scratch.current = nv->scratch.runout[n];
+      nv->scratch.offset = 0;
+      nv->scratch.end = size;
+      nv->scratch.map = nv->scratch.current->map;
+   }
+   return !ret;
+}
+
+/* Continue to next scratch buffer, if available (no wrapping, large enough).
+ * Allocate it if it has not yet been created.
+ */
+static INLINE boolean
+nouveau_scratch_next(struct nouveau_context *nv, unsigned size)
+{
+   struct nouveau_bo *bo;
+   int ret;
+   const unsigned i = (nv->scratch.id + 1) % NOUVEAU_MAX_SCRATCH_BUFS;
+
+   if ((size > nv->scratch.bo_size) || (i == nv->scratch.wrap))
+      return FALSE;
+   nv->scratch.id = i;
+
+   bo = nv->scratch.bo[i];
+   if (!bo) {
+      ret = nouveau_scratch_bo_alloc(nv, &bo, nv->scratch.bo_size);
+      if (ret)
+         return FALSE;
+      nv->scratch.bo[i] = bo;
+   }
+   nv->scratch.current = bo;
+   nv->scratch.offset = 0;
+   nv->scratch.end = nv->scratch.bo_size;
+
+   ret = nouveau_bo_map(bo, NOUVEAU_BO_WR, nv->screen->client);
+   if (!ret)
+      nv->scratch.map = bo->map;
+   return !ret;
+}
+
+static boolean
+nouveau_scratch_more(struct nouveau_context *nv, unsigned min_size)
+{
+   boolean ret;
+
+   ret = nouveau_scratch_next(nv, min_size);
+   if (!ret)
+      ret = nouveau_scratch_runout(nv, min_size);
+   return ret;
+}
+
+/* Upload data to scratch memory and update buffer address.
+ * Returns the bo the data resides in, if successful.
+ */
+struct nouveau_bo *
+nouveau_scratch_data(struct nouveau_context *nv,
+                     struct nv04_resource *buf, unsigned base, unsigned size)
+{
+   struct nouveau_bo *bo;
+   unsigned bgn = MAX2(base, nv->scratch.offset);
+   unsigned end = bgn + size;
+
+   if (end >= nv->scratch.end) {
+      end = base + size;
+      if (!nouveau_scratch_more(nv, end))
+         return NULL;
+      bgn = base;
+   }
+   nv->scratch.offset = align(end, 4);
+
+   memcpy(nv->scratch.map + bgn, buf->data + base, size);
+
+   bo = nv->scratch.current;
+   buf->address = bo->offset + (bgn - base);
+   return bo;
+}
+
+void *
+nouveau_scratch_get(struct nouveau_context *nv,
+                    unsigned size, uint64_t *gpu_addr, struct nouveau_bo **pbo)
+{
+   unsigned bgn = nv->scratch.offset;
+   unsigned end = nv->scratch.offset + size;
+
+   if (end >= nv->scratch.end) {
+      end = size;
+      if (!nouveau_scratch_more(nv, end))
+         return NULL;
+      bgn = 0;
+   }
+   nv->scratch.offset = align(end, 4);
+
+   *pbo = nv->scratch.current;
+   *gpu_addr = nv->scratch.current->offset + bgn;
+   return nv->scratch.map + bgn;
+}
index 3d97ac33b7d133ff55e303799f284de7030ccebe..19255a3c0fd20b0b86b847d635d749ac1111e522 100644 (file)
@@ -89,4 +89,11 @@ boolean
 nouveau_user_buffer_upload(struct nouveau_context *, struct nv04_resource *,
                            unsigned base, unsigned size);
 
+/* Copy data to a scratch buffer, update buffer address.
+ * Returns the bo the data resides in, or NULL on failure.
+ */
+struct nouveau_bo *
+nouveau_scratch_data(struct nouveau_context *,
+                     struct nv04_resource *, unsigned base, unsigned size);
+
 #endif
index 79efd08fa91c9d4d6de2caa1d65eb811af939d7b..531c6238b9f639fe2d2be4e8c684c7cc5e2d595e 100644 (file)
@@ -3,6 +3,8 @@
 
 #include "pipe/p_context.h"
 
+#define NOUVEAU_MAX_SCRATCH_BUFS 4
+
 struct nouveau_context {
    struct pipe_context pipe;
    struct nouveau_screen *screen;
@@ -24,6 +26,19 @@ struct nouveau_context {
                    struct nouveau_bo *, unsigned domain,
                    unsigned base, unsigned size,
                    unsigned offset, unsigned words, const uint32_t *);
+
+   struct {
+      uint8_t *map;
+      unsigned id;
+      unsigned wrap;
+      unsigned offset;
+      unsigned end;
+      struct nouveau_bo *bo[NOUVEAU_MAX_SCRATCH_BUFS];
+      struct nouveau_bo *current;
+      struct nouveau_bo **runout;
+      unsigned nr_runout;
+      unsigned bo_size;
+   } scratch;
 };
 
 static INLINE struct nouveau_context *
@@ -35,4 +50,26 @@ nouveau_context(struct pipe_context *pipe)
 void
 nouveau_context_init_vdec(struct nouveau_context *);
 
+void
+nouveau_scratch_runout_release(struct nouveau_context *);
+
+/* This is needed because we don't hold references outside of context::scratch,
+ * because we don't want to un-bo_ref each allocation every time. This is less
+ * work, and we need the wrap index anyway for extreme situations.
+ */
+static INLINE void
+nouveau_scratch_done(struct nouveau_context *nv)
+{
+   nv->scratch.wrap = nv->scratch.id;
+   if (unlikely(nv->scratch.nr_runout))
+      nouveau_scratch_runout_release(nv);
+}
+
+/* Get pointer to scratch buffer.
+ * The returned nouveau_bo is only referenced by the context, don't un-ref it !
+ */
+void *
+nouveau_scratch_get(struct nouveau_context *, unsigned size, uint64_t *gpu_addr,
+                    struct nouveau_bo **);
+
 #endif