13464ebd5Sriastradh
23464ebd5Sriastradh#include "util/u_inlines.h"
33464ebd5Sriastradh#include "util/u_memory.h"
43464ebd5Sriastradh#include "util/u_math.h"
5af69d88dSmrg#include "util/u_surface.h"
63464ebd5Sriastradh
73464ebd5Sriastradh#include "nouveau_screen.h"
83464ebd5Sriastradh#include "nouveau_context.h"
93464ebd5Sriastradh#include "nouveau_winsys.h"
103464ebd5Sriastradh#include "nouveau_fence.h"
113464ebd5Sriastradh#include "nouveau_buffer.h"
123464ebd5Sriastradh#include "nouveau_mm.h"
133464ebd5Sriastradh
143464ebd5Sriastradhstruct nouveau_transfer {
153464ebd5Sriastradh   struct pipe_transfer base;
16af69d88dSmrg
17af69d88dSmrg   uint8_t *map;
18af69d88dSmrg   struct nouveau_bo *bo;
19af69d88dSmrg   struct nouveau_mm_allocation *mm;
20af69d88dSmrg   uint32_t offset;
213464ebd5Sriastradh};
223464ebd5Sriastradh
237ec681f3Smrgstatic void *
247ec681f3Smrgnouveau_user_ptr_transfer_map(struct pipe_context *pipe,
257ec681f3Smrg                              struct pipe_resource *resource,
267ec681f3Smrg                              unsigned level, unsigned usage,
277ec681f3Smrg                              const struct pipe_box *box,
287ec681f3Smrg                              struct pipe_transfer **ptransfer);
297ec681f3Smrg
307ec681f3Smrgstatic void
317ec681f3Smrgnouveau_user_ptr_transfer_unmap(struct pipe_context *pipe,
327ec681f3Smrg                                struct pipe_transfer *transfer);
337ec681f3Smrg
3401e04c3fSmrgstatic inline struct nouveau_transfer *
353464ebd5Sriastradhnouveau_transfer(struct pipe_transfer *transfer)
363464ebd5Sriastradh{
373464ebd5Sriastradh   return (struct nouveau_transfer *)transfer;
383464ebd5Sriastradh}
393464ebd5Sriastradh
4001e04c3fSmrgstatic inline bool
41af69d88dSmrgnouveau_buffer_malloc(struct nv04_resource *buf)
42af69d88dSmrg{
43af69d88dSmrg   if (!buf->data)
44af69d88dSmrg      buf->data = align_malloc(buf->base.width0, NOUVEAU_MIN_BUFFER_MAP_ALIGN);
45af69d88dSmrg   return !!buf->data;
46af69d88dSmrg}
47af69d88dSmrg
4801e04c3fSmrgstatic inline bool
493464ebd5Sriastradhnouveau_buffer_allocate(struct nouveau_screen *screen,
503464ebd5Sriastradh                        struct nv04_resource *buf, unsigned domain)
513464ebd5Sriastradh{
5201e04c3fSmrg   uint32_t size = align(buf->base.width0, 0x100);
53af69d88dSmrg
543464ebd5Sriastradh   if (domain == NOUVEAU_BO_VRAM) {
55af69d88dSmrg      buf->mm = nouveau_mm_allocate(screen->mm_VRAM, size,
563464ebd5Sriastradh                                    &buf->bo, &buf->offset);
573464ebd5Sriastradh      if (!buf->bo)
583464ebd5Sriastradh         return nouveau_buffer_allocate(screen, buf, NOUVEAU_BO_GART);
59af69d88dSmrg      NOUVEAU_DRV_STAT(screen, buf_obj_current_bytes_vid, buf->base.width0);
603464ebd5Sriastradh   } else
613464ebd5Sriastradh   if (domain == NOUVEAU_BO_GART) {
62af69d88dSmrg      buf->mm = nouveau_mm_allocate(screen->mm_GART, size,
633464ebd5Sriastradh                                    &buf->bo, &buf->offset);
643464ebd5Sriastradh      if (!buf->bo)
6501e04c3fSmrg         return false;
66af69d88dSmrg      NOUVEAU_DRV_STAT(screen, buf_obj_current_bytes_sys, buf->base.width0);
67af69d88dSmrg   } else {
68af69d88dSmrg      assert(domain == 0);
69af69d88dSmrg      if (!nouveau_buffer_malloc(buf))
7001e04c3fSmrg         return false;
713464ebd5Sriastradh   }
723464ebd5Sriastradh   buf->domain = domain;
73af69d88dSmrg   if (buf->bo)
74af69d88dSmrg      buf->address = buf->bo->offset + buf->offset;
75af69d88dSmrg
76af69d88dSmrg   util_range_set_empty(&buf->valid_buffer_range);
77af69d88dSmrg
7801e04c3fSmrg   return true;
793464ebd5Sriastradh}
803464ebd5Sriastradh
8101e04c3fSmrgstatic inline void
823464ebd5Sriastradhrelease_allocation(struct nouveau_mm_allocation **mm,
833464ebd5Sriastradh                   struct nouveau_fence *fence)
843464ebd5Sriastradh{
853464ebd5Sriastradh   nouveau_fence_work(fence, nouveau_mm_free_work, *mm);
863464ebd5Sriastradh   (*mm) = NULL;
873464ebd5Sriastradh}
883464ebd5Sriastradh
8901e04c3fSmrginline void
903464ebd5Sriastradhnouveau_buffer_release_gpu_storage(struct nv04_resource *buf)
913464ebd5Sriastradh{
927ec681f3Smrg   assert(!(buf->status & NOUVEAU_BUFFER_STATUS_USER_PTR));
937ec681f3Smrg
9401e04c3fSmrg   if (buf->fence && buf->fence->state < NOUVEAU_FENCE_STATE_FLUSHED) {
9501e04c3fSmrg      nouveau_fence_work(buf->fence, nouveau_fence_unref_bo, buf->bo);
9601e04c3fSmrg      buf->bo = NULL;
9701e04c3fSmrg   } else {
9801e04c3fSmrg      nouveau_bo_ref(NULL, &buf->bo);
9901e04c3fSmrg   }
1003464ebd5Sriastradh
1013464ebd5Sriastradh   if (buf->mm)
1023464ebd5Sriastradh      release_allocation(&buf->mm, buf->fence);
1033464ebd5Sriastradh
104af69d88dSmrg   if (buf->domain == NOUVEAU_BO_VRAM)
105af69d88dSmrg      NOUVEAU_DRV_STAT_RES(buf, buf_obj_current_bytes_vid, -(uint64_t)buf->base.width0);
106af69d88dSmrg   if (buf->domain == NOUVEAU_BO_GART)
107af69d88dSmrg      NOUVEAU_DRV_STAT_RES(buf, buf_obj_current_bytes_sys, -(uint64_t)buf->base.width0);
108af69d88dSmrg
1093464ebd5Sriastradh   buf->domain = 0;
1103464ebd5Sriastradh}
1113464ebd5Sriastradh
11201e04c3fSmrgstatic inline bool
1133464ebd5Sriastradhnouveau_buffer_reallocate(struct nouveau_screen *screen,
1143464ebd5Sriastradh                          struct nv04_resource *buf, unsigned domain)
1153464ebd5Sriastradh{
1163464ebd5Sriastradh   nouveau_buffer_release_gpu_storage(buf);
1173464ebd5Sriastradh
118af69d88dSmrg   nouveau_fence_ref(NULL, &buf->fence);
119af69d88dSmrg   nouveau_fence_ref(NULL, &buf->fence_wr);
120af69d88dSmrg
121af69d88dSmrg   buf->status &= NOUVEAU_BUFFER_STATUS_REALLOC_MASK;
122af69d88dSmrg
1233464ebd5Sriastradh   return nouveau_buffer_allocate(screen, buf, domain);
1243464ebd5Sriastradh}
1253464ebd5Sriastradh
1267ec681f3Smrgvoid
1273464ebd5Sriastradhnouveau_buffer_destroy(struct pipe_screen *pscreen,
1283464ebd5Sriastradh                       struct pipe_resource *presource)
1293464ebd5Sriastradh{
1303464ebd5Sriastradh   struct nv04_resource *res = nv04_resource(presource);
1313464ebd5Sriastradh
1327ec681f3Smrg   if (res->status & NOUVEAU_BUFFER_STATUS_USER_PTR) {
1337ec681f3Smrg      FREE(res);
1347ec681f3Smrg      return;
1357ec681f3Smrg   }
1367ec681f3Smrg
1373464ebd5Sriastradh   nouveau_buffer_release_gpu_storage(res);
1383464ebd5Sriastradh
1393464ebd5Sriastradh   if (res->data && !(res->status & NOUVEAU_BUFFER_STATUS_USER_MEMORY))
140af69d88dSmrg      align_free(res->data);
1413464ebd5Sriastradh
142af69d88dSmrg   nouveau_fence_ref(NULL, &res->fence);
143af69d88dSmrg   nouveau_fence_ref(NULL, &res->fence_wr);
1443464ebd5Sriastradh
145af69d88dSmrg   util_range_destroy(&res->valid_buffer_range);
1463464ebd5Sriastradh
147af69d88dSmrg   FREE(res);
1483464ebd5Sriastradh
149af69d88dSmrg   NOUVEAU_DRV_STAT(nouveau_screen(pscreen), buf_obj_current_count, -1);
150af69d88dSmrg}
1513464ebd5Sriastradh
152af69d88dSmrg/* Set up a staging area for the transfer. This is either done in "regular"
153af69d88dSmrg * system memory if the driver supports push_data (nv50+) and the data is
154af69d88dSmrg * small enough (and permit_pb == true), or in GART memory.
155af69d88dSmrg */
156af69d88dSmrgstatic uint8_t *
157af69d88dSmrgnouveau_transfer_staging(struct nouveau_context *nv,
15801e04c3fSmrg                         struct nouveau_transfer *tx, bool permit_pb)
159af69d88dSmrg{
160af69d88dSmrg   const unsigned adj = tx->base.box.x & NOUVEAU_MIN_BUFFER_MAP_ALIGN_MASK;
161af69d88dSmrg   const unsigned size = align(tx->base.box.width, 4) + adj;
1623464ebd5Sriastradh
163af69d88dSmrg   if (!nv->push_data)
16401e04c3fSmrg      permit_pb = false;
1653464ebd5Sriastradh
16601e04c3fSmrg   if ((size <= nv->screen->transfer_pushbuf_threshold) && permit_pb) {
167af69d88dSmrg      tx->map = align_malloc(size, NOUVEAU_MIN_BUFFER_MAP_ALIGN);
168af69d88dSmrg      if (tx->map)
169af69d88dSmrg         tx->map += adj;
170af69d88dSmrg   } else {
171af69d88dSmrg      tx->mm =
172af69d88dSmrg         nouveau_mm_allocate(nv->screen->mm_GART, size, &tx->bo, &tx->offset);
173af69d88dSmrg      if (tx->bo) {
174af69d88dSmrg         tx->offset += adj;
175af69d88dSmrg         if (!nouveau_bo_map(tx->bo, 0, NULL))
176af69d88dSmrg            tx->map = (uint8_t *)tx->bo->map + tx->offset;
177af69d88dSmrg      }
178af69d88dSmrg   }
179af69d88dSmrg   return tx->map;
1803464ebd5Sriastradh}
1813464ebd5Sriastradh
18201e04c3fSmrg/* Copies data from the resource into the transfer's temporary GART
183af69d88dSmrg * buffer. Also updates buf->data if present.
184af69d88dSmrg *
185af69d88dSmrg * Maybe just migrate to GART right away if we actually need to do this. */
18601e04c3fSmrgstatic bool
187af69d88dSmrgnouveau_transfer_read(struct nouveau_context *nv, struct nouveau_transfer *tx)
1883464ebd5Sriastradh{
189af69d88dSmrg   struct nv04_resource *buf = nv04_resource(tx->base.resource);
190af69d88dSmrg   const unsigned base = tx->base.box.x;
191af69d88dSmrg   const unsigned size = tx->base.box.width;
1923464ebd5Sriastradh
193af69d88dSmrg   NOUVEAU_DRV_STAT(nv->screen, buf_read_bytes_staging_vid, size);
1943464ebd5Sriastradh
195af69d88dSmrg   nv->copy_data(nv, tx->bo, tx->offset, NOUVEAU_BO_GART,
196af69d88dSmrg                 buf->bo, buf->offset + base, buf->domain, size);
1973464ebd5Sriastradh
198af69d88dSmrg   if (nouveau_bo_wait(tx->bo, NOUVEAU_BO_RD, nv->client))
19901e04c3fSmrg      return false;
2003464ebd5Sriastradh
201af69d88dSmrg   if (buf->data)
202af69d88dSmrg      memcpy(buf->data + base, tx->map, size);
2033464ebd5Sriastradh
20401e04c3fSmrg   return true;
2053464ebd5Sriastradh}
2063464ebd5Sriastradh
2073464ebd5Sriastradhstatic void
208af69d88dSmrgnouveau_transfer_write(struct nouveau_context *nv, struct nouveau_transfer *tx,
209af69d88dSmrg                       unsigned offset, unsigned size)
2103464ebd5Sriastradh{
211af69d88dSmrg   struct nv04_resource *buf = nv04_resource(tx->base.resource);
212af69d88dSmrg   uint8_t *data = tx->map + offset;
213af69d88dSmrg   const unsigned base = tx->base.box.x + offset;
21401e04c3fSmrg   const bool can_cb = !((base | size) & 3);
2153464ebd5Sriastradh
216af69d88dSmrg   if (buf->data)
217af69d88dSmrg      memcpy(data, buf->data + base, size);
218af69d88dSmrg   else
219af69d88dSmrg      buf->status |= NOUVEAU_BUFFER_STATUS_DIRTY;
2203464ebd5Sriastradh
221af69d88dSmrg   if (buf->domain == NOUVEAU_BO_VRAM)
222af69d88dSmrg      NOUVEAU_DRV_STAT(nv->screen, buf_write_bytes_staging_vid, size);
223af69d88dSmrg   if (buf->domain == NOUVEAU_BO_GART)
224af69d88dSmrg      NOUVEAU_DRV_STAT(nv->screen, buf_write_bytes_staging_sys, size);
2253464ebd5Sriastradh
226af69d88dSmrg   if (tx->bo)
227af69d88dSmrg      nv->copy_data(nv, buf->bo, buf->offset + base, buf->domain,
228af69d88dSmrg                    tx->bo, tx->offset + offset, NOUVEAU_BO_GART, size);
229af69d88dSmrg   else
23001e04c3fSmrg   if (nv->push_cb && can_cb)
23101e04c3fSmrg      nv->push_cb(nv, buf,
232af69d88dSmrg                  base, size / 4, (const uint32_t *)data);
233af69d88dSmrg   else
234af69d88dSmrg      nv->push_data(nv, buf->bo, buf->offset + base, buf->domain, size, data);
2353464ebd5Sriastradh
236af69d88dSmrg   nouveau_fence_ref(nv->screen->fence.current, &buf->fence);
237af69d88dSmrg   nouveau_fence_ref(nv->screen->fence.current, &buf->fence_wr);
2383464ebd5Sriastradh}
2393464ebd5Sriastradh
240af69d88dSmrg/* Does a CPU wait for the buffer's backing data to become reliably accessible
241af69d88dSmrg * for write/read by waiting on the buffer's relevant fences.
242af69d88dSmrg */
24301e04c3fSmrgstatic inline bool
24401e04c3fSmrgnouveau_buffer_sync(struct nouveau_context *nv,
24501e04c3fSmrg                    struct nv04_resource *buf, unsigned rw)
2463464ebd5Sriastradh{
2477ec681f3Smrg   if (rw == PIPE_MAP_READ) {
2483464ebd5Sriastradh      if (!buf->fence_wr)
24901e04c3fSmrg         return true;
250af69d88dSmrg      NOUVEAU_DRV_STAT_RES(buf, buf_non_kernel_fence_sync_count,
251af69d88dSmrg                           !nouveau_fence_signalled(buf->fence_wr));
25201e04c3fSmrg      if (!nouveau_fence_wait(buf->fence_wr, &nv->debug))
25301e04c3fSmrg         return false;
2543464ebd5Sriastradh   } else {
2553464ebd5Sriastradh      if (!buf->fence)
25601e04c3fSmrg         return true;
257af69d88dSmrg      NOUVEAU_DRV_STAT_RES(buf, buf_non_kernel_fence_sync_count,
258af69d88dSmrg                           !nouveau_fence_signalled(buf->fence));
25901e04c3fSmrg      if (!nouveau_fence_wait(buf->fence, &nv->debug))
26001e04c3fSmrg         return false;
2613464ebd5Sriastradh
2623464ebd5Sriastradh      nouveau_fence_ref(NULL, &buf->fence);
2633464ebd5Sriastradh   }
2643464ebd5Sriastradh   nouveau_fence_ref(NULL, &buf->fence_wr);
2653464ebd5Sriastradh
26601e04c3fSmrg   return true;
2673464ebd5Sriastradh}
2683464ebd5Sriastradh
26901e04c3fSmrgstatic inline bool
2703464ebd5Sriastradhnouveau_buffer_busy(struct nv04_resource *buf, unsigned rw)
2713464ebd5Sriastradh{
2727ec681f3Smrg   if (rw == PIPE_MAP_READ)
2733464ebd5Sriastradh      return (buf->fence_wr && !nouveau_fence_signalled(buf->fence_wr));
2743464ebd5Sriastradh   else
2753464ebd5Sriastradh      return (buf->fence && !nouveau_fence_signalled(buf->fence));
2763464ebd5Sriastradh}
2773464ebd5Sriastradh
27801e04c3fSmrgstatic inline void
279af69d88dSmrgnouveau_buffer_transfer_init(struct nouveau_transfer *tx,
280af69d88dSmrg                             struct pipe_resource *resource,
281af69d88dSmrg                             const struct pipe_box *box,
282af69d88dSmrg                             unsigned usage)
283af69d88dSmrg{
284af69d88dSmrg   tx->base.resource = resource;
285af69d88dSmrg   tx->base.level = 0;
286af69d88dSmrg   tx->base.usage = usage;
287af69d88dSmrg   tx->base.box.x = box->x;
288af69d88dSmrg   tx->base.box.y = 0;
289af69d88dSmrg   tx->base.box.z = 0;
290af69d88dSmrg   tx->base.box.width = box->width;
291af69d88dSmrg   tx->base.box.height = 1;
292af69d88dSmrg   tx->base.box.depth = 1;
293af69d88dSmrg   tx->base.stride = 0;
294af69d88dSmrg   tx->base.layer_stride = 0;
295af69d88dSmrg
296af69d88dSmrg   tx->bo = NULL;
297af69d88dSmrg   tx->map = NULL;
298af69d88dSmrg}
299af69d88dSmrg
30001e04c3fSmrgstatic inline void
301af69d88dSmrgnouveau_buffer_transfer_del(struct nouveau_context *nv,
302af69d88dSmrg                            struct nouveau_transfer *tx)
303af69d88dSmrg{
304af69d88dSmrg   if (tx->map) {
305af69d88dSmrg      if (likely(tx->bo)) {
30601e04c3fSmrg         nouveau_fence_work(nv->screen->fence.current,
30701e04c3fSmrg                            nouveau_fence_unref_bo, tx->bo);
308af69d88dSmrg         if (tx->mm)
309af69d88dSmrg            release_allocation(&tx->mm, nv->screen->fence.current);
310af69d88dSmrg      } else {
311af69d88dSmrg         align_free(tx->map -
312af69d88dSmrg                    (tx->base.box.x & NOUVEAU_MIN_BUFFER_MAP_ALIGN_MASK));
313af69d88dSmrg      }
314af69d88dSmrg   }
315af69d88dSmrg}
316af69d88dSmrg
317af69d88dSmrg/* Creates a cache in system memory of the buffer data. */
31801e04c3fSmrgstatic bool
319af69d88dSmrgnouveau_buffer_cache(struct nouveau_context *nv, struct nv04_resource *buf)
320af69d88dSmrg{
321af69d88dSmrg   struct nouveau_transfer tx;
32201e04c3fSmrg   bool ret;
323af69d88dSmrg   tx.base.resource = &buf->base;
324af69d88dSmrg   tx.base.box.x = 0;
325af69d88dSmrg   tx.base.box.width = buf->base.width0;
326af69d88dSmrg   tx.bo = NULL;
327af69d88dSmrg   tx.map = NULL;
328af69d88dSmrg
329af69d88dSmrg   if (!buf->data)
330af69d88dSmrg      if (!nouveau_buffer_malloc(buf))
33101e04c3fSmrg         return false;
332af69d88dSmrg   if (!(buf->status & NOUVEAU_BUFFER_STATUS_DIRTY))
33301e04c3fSmrg      return true;
334af69d88dSmrg   nv->stats.buf_cache_count++;
335af69d88dSmrg
33601e04c3fSmrg   if (!nouveau_transfer_staging(nv, &tx, false))
33701e04c3fSmrg      return false;
338af69d88dSmrg
339af69d88dSmrg   ret = nouveau_transfer_read(nv, &tx);
340af69d88dSmrg   if (ret) {
341af69d88dSmrg      buf->status &= ~NOUVEAU_BUFFER_STATUS_DIRTY;
342af69d88dSmrg      memcpy(buf->data, tx.map, buf->base.width0);
343af69d88dSmrg   }
344af69d88dSmrg   nouveau_buffer_transfer_del(nv, &tx);
345af69d88dSmrg   return ret;
346af69d88dSmrg}
347af69d88dSmrg
348af69d88dSmrg
349af69d88dSmrg#define NOUVEAU_TRANSFER_DISCARD \
3507ec681f3Smrg   (PIPE_MAP_DISCARD_RANGE | PIPE_MAP_DISCARD_WHOLE_RESOURCE)
351af69d88dSmrg
352af69d88dSmrg/* Checks whether it is possible to completely discard the memory backing this
353af69d88dSmrg * resource. This can be useful if we would otherwise have to wait for a read
354af69d88dSmrg * operation to complete on this data.
355af69d88dSmrg */
35601e04c3fSmrgstatic inline bool
357af69d88dSmrgnouveau_buffer_should_discard(struct nv04_resource *buf, unsigned usage)
358af69d88dSmrg{
3597ec681f3Smrg   if (!(usage & PIPE_MAP_DISCARD_WHOLE_RESOURCE))
36001e04c3fSmrg      return false;
361af69d88dSmrg   if (unlikely(buf->base.bind & PIPE_BIND_SHARED))
36201e04c3fSmrg      return false;
3637ec681f3Smrg   if (unlikely(usage & PIPE_MAP_PERSISTENT))
36401e04c3fSmrg      return false;
3657ec681f3Smrg   return buf->mm && nouveau_buffer_busy(buf, PIPE_MAP_WRITE);
366af69d88dSmrg}
367af69d88dSmrg
368af69d88dSmrg/* Returns a pointer to a memory area representing a window into the
369af69d88dSmrg * resource's data.
370af69d88dSmrg *
371af69d88dSmrg * This may or may not be the _actual_ memory area of the resource. However
372af69d88dSmrg * when calling nouveau_buffer_transfer_unmap, if it wasn't the actual memory
373af69d88dSmrg * area, the contents of the returned map are copied over to the resource.
374af69d88dSmrg *
375af69d88dSmrg * The usage indicates what the caller plans to do with the map:
376af69d88dSmrg *
377af69d88dSmrg *   WRITE means that the user plans to write to it
378af69d88dSmrg *
379af69d88dSmrg *   READ means that the user plans on reading from it
380af69d88dSmrg *
381af69d88dSmrg *   DISCARD_WHOLE_RESOURCE means that the whole resource is going to be
382af69d88dSmrg *   potentially overwritten, and even if it isn't, the bits that aren't don't
383af69d88dSmrg *   need to be maintained.
384af69d88dSmrg *
385af69d88dSmrg *   DISCARD_RANGE means that all the data in the specified range is going to
386af69d88dSmrg *   be overwritten.
387af69d88dSmrg *
388af69d88dSmrg * The strategy for determining what kind of memory area to return is complex,
389af69d88dSmrg * see comments inside of the function.
390af69d88dSmrg */
3917ec681f3Smrgvoid *
3923464ebd5Sriastradhnouveau_buffer_transfer_map(struct pipe_context *pipe,
393af69d88dSmrg                            struct pipe_resource *resource,
394af69d88dSmrg                            unsigned level, unsigned usage,
395af69d88dSmrg                            const struct pipe_box *box,
396af69d88dSmrg                            struct pipe_transfer **ptransfer)
3973464ebd5Sriastradh{
398af69d88dSmrg   struct nouveau_context *nv = nouveau_context(pipe);
399af69d88dSmrg   struct nv04_resource *buf = nv04_resource(resource);
4007ec681f3Smrg
4017ec681f3Smrg   if (buf->status & NOUVEAU_BUFFER_STATUS_USER_PTR)
4027ec681f3Smrg      return nouveau_user_ptr_transfer_map(pipe, resource, level, usage, box, ptransfer);
4037ec681f3Smrg
404af69d88dSmrg   struct nouveau_transfer *tx = MALLOC_STRUCT(nouveau_transfer);
4053464ebd5Sriastradh   uint8_t *map;
4063464ebd5Sriastradh   int ret;
4073464ebd5Sriastradh
408af69d88dSmrg   if (!tx)
409af69d88dSmrg      return NULL;
410af69d88dSmrg   nouveau_buffer_transfer_init(tx, resource, box, usage);
411af69d88dSmrg   *ptransfer = &tx->base;
412af69d88dSmrg
4137ec681f3Smrg   if (usage & PIPE_MAP_READ)
414af69d88dSmrg      NOUVEAU_DRV_STAT(nv->screen, buf_transfers_rd, 1);
4157ec681f3Smrg   if (usage & PIPE_MAP_WRITE)
416af69d88dSmrg      NOUVEAU_DRV_STAT(nv->screen, buf_transfers_wr, 1);
417af69d88dSmrg
418af69d88dSmrg   /* If we are trying to write to an uninitialized range, the user shouldn't
419af69d88dSmrg    * care what was there before. So we can treat the write as if the target
420af69d88dSmrg    * range were being discarded. Furthermore, since we know that even if this
421af69d88dSmrg    * buffer is busy due to GPU activity, because the contents were
422af69d88dSmrg    * uninitialized, the GPU can't care what was there, and so we can treat
423af69d88dSmrg    * the write as being unsynchronized.
424af69d88dSmrg    */
4257ec681f3Smrg   if ((usage & PIPE_MAP_WRITE) &&
426af69d88dSmrg       !util_ranges_intersect(&buf->valid_buffer_range, box->x, box->x + box->width))
4277ec681f3Smrg      usage |= PIPE_MAP_DISCARD_RANGE | PIPE_MAP_UNSYNCHRONIZED;
4283464ebd5Sriastradh
429af69d88dSmrg   if (buf->domain == NOUVEAU_BO_VRAM) {
430af69d88dSmrg      if (usage & NOUVEAU_TRANSFER_DISCARD) {
431af69d88dSmrg         /* Set up a staging area for the user to write to. It will be copied
432af69d88dSmrg          * back into VRAM on unmap. */
4337ec681f3Smrg         if (usage & PIPE_MAP_DISCARD_WHOLE_RESOURCE)
434af69d88dSmrg            buf->status &= NOUVEAU_BUFFER_STATUS_REALLOC_MASK;
43501e04c3fSmrg         nouveau_transfer_staging(nv, tx, true);
436af69d88dSmrg      } else {
437af69d88dSmrg         if (buf->status & NOUVEAU_BUFFER_STATUS_GPU_WRITING) {
438af69d88dSmrg            /* The GPU is currently writing to this buffer. Copy its current
439af69d88dSmrg             * contents to a staging area in the GART. This is necessary since
440af69d88dSmrg             * not the whole area being mapped is being discarded.
441af69d88dSmrg             */
442af69d88dSmrg            if (buf->data) {
443af69d88dSmrg               align_free(buf->data);
444af69d88dSmrg               buf->data = NULL;
445af69d88dSmrg            }
44601e04c3fSmrg            nouveau_transfer_staging(nv, tx, false);
447af69d88dSmrg            nouveau_transfer_read(nv, tx);
448af69d88dSmrg         } else {
449af69d88dSmrg            /* The buffer is currently idle. Create a staging area for writes,
450af69d88dSmrg             * and make sure that the cached data is up-to-date. */
4517ec681f3Smrg            if (usage & PIPE_MAP_WRITE)
45201e04c3fSmrg               nouveau_transfer_staging(nv, tx, true);
453af69d88dSmrg            if (!buf->data)
454af69d88dSmrg               nouveau_buffer_cache(nv, buf);
455af69d88dSmrg         }
456af69d88dSmrg      }
457af69d88dSmrg      return buf->data ? (buf->data + box->x) : tx->map;
458af69d88dSmrg   } else
459af69d88dSmrg   if (unlikely(buf->domain == 0)) {
460af69d88dSmrg      return buf->data + box->x;
461af69d88dSmrg   }
4623464ebd5Sriastradh
463af69d88dSmrg   /* At this point, buf->domain == GART */
4643464ebd5Sriastradh
465af69d88dSmrg   if (nouveau_buffer_should_discard(buf, usage)) {
466af69d88dSmrg      int ref = buf->base.reference.count - 1;
467af69d88dSmrg      nouveau_buffer_reallocate(nv->screen, buf, buf->domain);
468af69d88dSmrg      if (ref > 0) /* any references inside context possible ? */
469af69d88dSmrg         nv->invalidate_resource_storage(nv, &buf->base, ref);
470af69d88dSmrg   }
4713464ebd5Sriastradh
472af69d88dSmrg   /* Note that nouveau_bo_map ends up doing a nouveau_bo_wait with the
473af69d88dSmrg    * relevant flags. If buf->mm is set, that means this resource is part of a
474af69d88dSmrg    * larger slab bo that holds multiple resources. So in that case, don't
475af69d88dSmrg    * wait on the whole slab and instead use the logic below to return a
476af69d88dSmrg    * reasonable buffer for that case.
4773464ebd5Sriastradh    */
478af69d88dSmrg   ret = nouveau_bo_map(buf->bo,
479af69d88dSmrg                        buf->mm ? 0 : nouveau_screen_transfer_flags(usage),
480af69d88dSmrg                        nv->client);
481af69d88dSmrg   if (ret) {
482af69d88dSmrg      FREE(tx);
483af69d88dSmrg      return NULL;
484af69d88dSmrg   }
485af69d88dSmrg   map = (uint8_t *)buf->bo->map + buf->offset + box->x;
486af69d88dSmrg
487af69d88dSmrg   /* using kernel fences only if !buf->mm */
4887ec681f3Smrg   if ((usage & PIPE_MAP_UNSYNCHRONIZED) || !buf->mm)
489af69d88dSmrg      return map;
4903464ebd5Sriastradh
491af69d88dSmrg   /* If the GPU is currently reading/writing this buffer, we shouldn't
492af69d88dSmrg    * interfere with its progress. So instead we either wait for the GPU to
493af69d88dSmrg    * complete its operation, or set up a staging area to perform our work in.
494af69d88dSmrg    */
4957ec681f3Smrg   if (nouveau_buffer_busy(buf, usage & PIPE_MAP_READ_WRITE)) {
4967ec681f3Smrg      if (unlikely(usage & (PIPE_MAP_DISCARD_WHOLE_RESOURCE |
4977ec681f3Smrg                            PIPE_MAP_PERSISTENT))) {
498af69d88dSmrg         /* Discarding was not possible, must sync because
499af69d88dSmrg          * subsequent transfers might use UNSYNCHRONIZED. */
5007ec681f3Smrg         nouveau_buffer_sync(nv, buf, usage & PIPE_MAP_READ_WRITE);
501af69d88dSmrg      } else
5027ec681f3Smrg      if (usage & PIPE_MAP_DISCARD_RANGE) {
503af69d88dSmrg         /* The whole range is being discarded, so it doesn't matter what was
504af69d88dSmrg          * there before. No need to copy anything over. */
50501e04c3fSmrg         nouveau_transfer_staging(nv, tx, true);
506af69d88dSmrg         map = tx->map;
5073464ebd5Sriastradh      } else
5087ec681f3Smrg      if (nouveau_buffer_busy(buf, PIPE_MAP_READ)) {
5097ec681f3Smrg         if (usage & PIPE_MAP_DONTBLOCK)
510af69d88dSmrg            map = NULL;
511af69d88dSmrg         else
5127ec681f3Smrg            nouveau_buffer_sync(nv, buf, usage & PIPE_MAP_READ_WRITE);
513af69d88dSmrg      } else {
514af69d88dSmrg         /* It is expected that the returned buffer be a representation of the
515af69d88dSmrg          * data in question, so we must copy it over from the buffer. */
51601e04c3fSmrg         nouveau_transfer_staging(nv, tx, true);
517af69d88dSmrg         if (tx->map)
518af69d88dSmrg            memcpy(tx->map, map, box->width);
519af69d88dSmrg         map = tx->map;
5203464ebd5Sriastradh      }
5213464ebd5Sriastradh   }
522af69d88dSmrg   if (!map)
523af69d88dSmrg      FREE(tx);
5243464ebd5Sriastradh   return map;
5253464ebd5Sriastradh}
5263464ebd5Sriastradh
5273464ebd5Sriastradh
5283464ebd5Sriastradh
5297ec681f3Smrgvoid
5303464ebd5Sriastradhnouveau_buffer_transfer_flush_region(struct pipe_context *pipe,
5313464ebd5Sriastradh                                     struct pipe_transfer *transfer,
5323464ebd5Sriastradh                                     const struct pipe_box *box)
5333464ebd5Sriastradh{
534af69d88dSmrg   struct nouveau_transfer *tx = nouveau_transfer(transfer);
535af69d88dSmrg   struct nv04_resource *buf = nv04_resource(transfer->resource);
5363464ebd5Sriastradh
537af69d88dSmrg   if (tx->map)
538af69d88dSmrg      nouveau_transfer_write(nouveau_context(pipe), tx, box->x, box->width);
5393464ebd5Sriastradh
5407ec681f3Smrg   util_range_add(&buf->base, &buf->valid_buffer_range,
541af69d88dSmrg                  tx->base.box.x + box->x,
542af69d88dSmrg                  tx->base.box.x + box->x + box->width);
5433464ebd5Sriastradh}
5443464ebd5Sriastradh
545af69d88dSmrg/* Unmap stage of the transfer. If it was a WRITE transfer and the map that
546af69d88dSmrg * was returned was not the real resource's data, this needs to transfer the
547af69d88dSmrg * data back to the resource.
548af69d88dSmrg *
549af69d88dSmrg * Also marks vbo dirty based on the buffer's binding
550af69d88dSmrg */
5517ec681f3Smrgvoid
5523464ebd5Sriastradhnouveau_buffer_transfer_unmap(struct pipe_context *pipe,
5533464ebd5Sriastradh                              struct pipe_transfer *transfer)
5543464ebd5Sriastradh{
555af69d88dSmrg   struct nouveau_context *nv = nouveau_context(pipe);
556af69d88dSmrg   struct nv04_resource *buf = nv04_resource(transfer->resource);
557af69d88dSmrg
5587ec681f3Smrg   if (buf->status & NOUVEAU_BUFFER_STATUS_USER_PTR)
5597ec681f3Smrg      return nouveau_user_ptr_transfer_unmap(pipe, transfer);
5607ec681f3Smrg
5617ec681f3Smrg   struct nouveau_transfer *tx = nouveau_transfer(transfer);
5627ec681f3Smrg
5637ec681f3Smrg   if (tx->base.usage & PIPE_MAP_WRITE) {
5647ec681f3Smrg      if (!(tx->base.usage & PIPE_MAP_FLUSH_EXPLICIT)) {
56501e04c3fSmrg         if (tx->map)
56601e04c3fSmrg            nouveau_transfer_write(nv, tx, 0, tx->base.box.width);
56701e04c3fSmrg
5687ec681f3Smrg         util_range_add(&buf->base, &buf->valid_buffer_range,
56901e04c3fSmrg                        tx->base.box.x, tx->base.box.x + tx->base.box.width);
57001e04c3fSmrg      }
571af69d88dSmrg
572af69d88dSmrg      if (likely(buf->domain)) {
573af69d88dSmrg         const uint8_t bind = buf->base.bind;
574af69d88dSmrg         /* make sure we invalidate dedicated caches */
575af69d88dSmrg         if (bind & (PIPE_BIND_VERTEX_BUFFER | PIPE_BIND_INDEX_BUFFER))
57601e04c3fSmrg            nv->vbo_dirty = true;
577af69d88dSmrg      }
578af69d88dSmrg   }
579af69d88dSmrg
5807ec681f3Smrg   if (!tx->bo && (tx->base.usage & PIPE_MAP_WRITE))
581af69d88dSmrg      NOUVEAU_DRV_STAT(nv->screen, buf_write_bytes_direct, tx->base.box.width);
582af69d88dSmrg
583af69d88dSmrg   nouveau_buffer_transfer_del(nv, tx);
584af69d88dSmrg   FREE(tx);
585af69d88dSmrg}
586af69d88dSmrg
587af69d88dSmrg
588af69d88dSmrgvoid
589af69d88dSmrgnouveau_copy_buffer(struct nouveau_context *nv,
590af69d88dSmrg                    struct nv04_resource *dst, unsigned dstx,
591af69d88dSmrg                    struct nv04_resource *src, unsigned srcx, unsigned size)
592af69d88dSmrg{
593af69d88dSmrg   assert(dst->base.target == PIPE_BUFFER && src->base.target == PIPE_BUFFER);
594af69d88dSmrg
5957ec681f3Smrg   assert(!(dst->status & NOUVEAU_BUFFER_STATUS_USER_PTR));
5967ec681f3Smrg   assert(!(src->status & NOUVEAU_BUFFER_STATUS_USER_PTR));
5977ec681f3Smrg
598af69d88dSmrg   if (likely(dst->domain) && likely(src->domain)) {
599af69d88dSmrg      nv->copy_data(nv,
600af69d88dSmrg                    dst->bo, dst->offset + dstx, dst->domain,
601af69d88dSmrg                    src->bo, src->offset + srcx, src->domain, size);
602af69d88dSmrg
603af69d88dSmrg      dst->status |= NOUVEAU_BUFFER_STATUS_GPU_WRITING;
604af69d88dSmrg      nouveau_fence_ref(nv->screen->fence.current, &dst->fence);
605af69d88dSmrg      nouveau_fence_ref(nv->screen->fence.current, &dst->fence_wr);
606af69d88dSmrg
607af69d88dSmrg      src->status |= NOUVEAU_BUFFER_STATUS_GPU_READING;
608af69d88dSmrg      nouveau_fence_ref(nv->screen->fence.current, &src->fence);
609af69d88dSmrg   } else {
610af69d88dSmrg      struct pipe_box src_box;
611af69d88dSmrg      src_box.x = srcx;
612af69d88dSmrg      src_box.y = 0;
613af69d88dSmrg      src_box.z = 0;
614af69d88dSmrg      src_box.width = size;
615af69d88dSmrg      src_box.height = 1;
616af69d88dSmrg      src_box.depth = 1;
617af69d88dSmrg      util_resource_copy_region(&nv->pipe,
618af69d88dSmrg                                &dst->base, 0, dstx, 0, 0,
619af69d88dSmrg                                &src->base, 0, &src_box);
620af69d88dSmrg   }
621af69d88dSmrg
6227ec681f3Smrg   util_range_add(&dst->base, &dst->valid_buffer_range, dstx, dstx + size);
6233464ebd5Sriastradh}
6243464ebd5Sriastradh
625af69d88dSmrg
626af69d88dSmrgvoid *
627af69d88dSmrgnouveau_resource_map_offset(struct nouveau_context *nv,
628af69d88dSmrg                            struct nv04_resource *res, uint32_t offset,
629af69d88dSmrg                            uint32_t flags)
630af69d88dSmrg{
6317ec681f3Smrg   if (unlikely(res->status & NOUVEAU_BUFFER_STATUS_USER_MEMORY) ||
6327ec681f3Smrg       unlikely(res->status & NOUVEAU_BUFFER_STATUS_USER_PTR))
633af69d88dSmrg      return res->data + offset;
634af69d88dSmrg
635af69d88dSmrg   if (res->domain == NOUVEAU_BO_VRAM) {
636af69d88dSmrg      if (!res->data || (res->status & NOUVEAU_BUFFER_STATUS_GPU_WRITING))
637af69d88dSmrg         nouveau_buffer_cache(nv, res);
638af69d88dSmrg   }
639af69d88dSmrg   if (res->domain != NOUVEAU_BO_GART)
640af69d88dSmrg      return res->data + offset;
641af69d88dSmrg
642af69d88dSmrg   if (res->mm) {
643af69d88dSmrg      unsigned rw;
6447ec681f3Smrg      rw = (flags & NOUVEAU_BO_WR) ? PIPE_MAP_WRITE : PIPE_MAP_READ;
64501e04c3fSmrg      nouveau_buffer_sync(nv, res, rw);
646af69d88dSmrg      if (nouveau_bo_map(res->bo, 0, NULL))
647af69d88dSmrg         return NULL;
648af69d88dSmrg   } else {
649af69d88dSmrg      if (nouveau_bo_map(res->bo, flags, nv->client))
650af69d88dSmrg         return NULL;
651af69d88dSmrg   }
652af69d88dSmrg   return (uint8_t *)res->bo->map + res->offset + offset;
653af69d88dSmrg}
654af69d88dSmrg
6557ec681f3Smrgstatic void *
6567ec681f3Smrgnouveau_user_ptr_transfer_map(struct pipe_context *pipe,
6577ec681f3Smrg                              struct pipe_resource *resource,
6587ec681f3Smrg                              unsigned level, unsigned usage,
6597ec681f3Smrg                              const struct pipe_box *box,
6607ec681f3Smrg                              struct pipe_transfer **ptransfer)
6617ec681f3Smrg{
6627ec681f3Smrg   struct nouveau_transfer *tx = MALLOC_STRUCT(nouveau_transfer);
6637ec681f3Smrg   if (!tx)
6647ec681f3Smrg      return NULL;
6657ec681f3Smrg   nouveau_buffer_transfer_init(tx, resource, box, usage);
6667ec681f3Smrg   *ptransfer = &tx->base;
6677ec681f3Smrg   return nv04_resource(resource)->data;
6687ec681f3Smrg}
669af69d88dSmrg
6707ec681f3Smrgstatic void
6717ec681f3Smrgnouveau_user_ptr_transfer_unmap(struct pipe_context *pipe,
6727ec681f3Smrg                                struct pipe_transfer *transfer)
6733464ebd5Sriastradh{
6747ec681f3Smrg   struct nouveau_transfer *tx = nouveau_transfer(transfer);
6757ec681f3Smrg   FREE(tx);
6767ec681f3Smrg}
6773464ebd5Sriastradh
6783464ebd5Sriastradhstruct pipe_resource *
6793464ebd5Sriastradhnouveau_buffer_create(struct pipe_screen *pscreen,
6803464ebd5Sriastradh                      const struct pipe_resource *templ)
6813464ebd5Sriastradh{
6823464ebd5Sriastradh   struct nouveau_screen *screen = nouveau_screen(pscreen);
6833464ebd5Sriastradh   struct nv04_resource *buffer;
68401e04c3fSmrg   bool ret;
6853464ebd5Sriastradh
6863464ebd5Sriastradh   buffer = CALLOC_STRUCT(nv04_resource);
6873464ebd5Sriastradh   if (!buffer)
6883464ebd5Sriastradh      return NULL;
6893464ebd5Sriastradh
6903464ebd5Sriastradh   buffer->base = *templ;
6913464ebd5Sriastradh   pipe_reference_init(&buffer->base.reference, 1);
6923464ebd5Sriastradh   buffer->base.screen = pscreen;
6933464ebd5Sriastradh
694af69d88dSmrg   if (buffer->base.flags & (PIPE_RESOURCE_FLAG_MAP_PERSISTENT |
695af69d88dSmrg                             PIPE_RESOURCE_FLAG_MAP_COHERENT)) {
696af69d88dSmrg      buffer->domain = NOUVEAU_BO_GART;
69701e04c3fSmrg   } else if (buffer->base.bind == 0 || (buffer->base.bind &
69801e04c3fSmrg              (screen->vidmem_bindings & screen->sysmem_bindings))) {
699af69d88dSmrg      switch (buffer->base.usage) {
700af69d88dSmrg      case PIPE_USAGE_DEFAULT:
701af69d88dSmrg      case PIPE_USAGE_IMMUTABLE:
70201e04c3fSmrg         buffer->domain = NV_VRAM_DOMAIN(screen);
703af69d88dSmrg         break;
704af69d88dSmrg      case PIPE_USAGE_DYNAMIC:
705af69d88dSmrg         /* For most apps, we'd have to do staging transfers to avoid sync
706af69d88dSmrg          * with this usage, and GART -> GART copies would be suboptimal.
707af69d88dSmrg          */
70801e04c3fSmrg         buffer->domain = NV_VRAM_DOMAIN(screen);
709af69d88dSmrg         break;
710af69d88dSmrg      case PIPE_USAGE_STAGING:
711af69d88dSmrg      case PIPE_USAGE_STREAM:
712af69d88dSmrg         buffer->domain = NOUVEAU_BO_GART;
713af69d88dSmrg         break;
714af69d88dSmrg      default:
715af69d88dSmrg         assert(0);
716af69d88dSmrg         break;
717af69d88dSmrg      }
718af69d88dSmrg   } else {
719af69d88dSmrg      if (buffer->base.bind & screen->vidmem_bindings)
72001e04c3fSmrg         buffer->domain = NV_VRAM_DOMAIN(screen);
721af69d88dSmrg      else
722af69d88dSmrg      if (buffer->base.bind & screen->sysmem_bindings)
723af69d88dSmrg         buffer->domain = NOUVEAU_BO_GART;
724af69d88dSmrg   }
72501e04c3fSmrg
726af69d88dSmrg   ret = nouveau_buffer_allocate(screen, buffer, buffer->domain);
7273464ebd5Sriastradh
72801e04c3fSmrg   if (ret == false)
7293464ebd5Sriastradh      goto fail;
7303464ebd5Sriastradh
731af69d88dSmrg   if (buffer->domain == NOUVEAU_BO_VRAM && screen->hint_buf_keep_sysmem_copy)
732af69d88dSmrg      nouveau_buffer_cache(NULL, buffer);
733af69d88dSmrg
734af69d88dSmrg   NOUVEAU_DRV_STAT(screen, buf_obj_current_count, 1);
735af69d88dSmrg
736af69d88dSmrg   util_range_init(&buffer->valid_buffer_range);
737af69d88dSmrg
7383464ebd5Sriastradh   return &buffer->base;
7393464ebd5Sriastradh
7403464ebd5Sriastradhfail:
7413464ebd5Sriastradh   FREE(buffer);
7423464ebd5Sriastradh   return NULL;
7433464ebd5Sriastradh}
7443464ebd5Sriastradh
7457ec681f3Smrgstruct pipe_resource *
7467ec681f3Smrgnouveau_buffer_create_from_user(struct pipe_screen *pscreen,
7477ec681f3Smrg                                const struct pipe_resource *templ,
7487ec681f3Smrg                                void *user_ptr)
7497ec681f3Smrg{
7507ec681f3Smrg   struct nv04_resource *buffer;
7517ec681f3Smrg
7527ec681f3Smrg   buffer = CALLOC_STRUCT(nv04_resource);
7537ec681f3Smrg   if (!buffer)
7547ec681f3Smrg      return NULL;
7557ec681f3Smrg
7567ec681f3Smrg   buffer->base = *templ;
7577ec681f3Smrg   /* set address and data to the same thing for higher compatibility with
7587ec681f3Smrg    * existing code. It's correct nonetheless as the same pointer is equally
7597ec681f3Smrg    * valid on the CPU and the GPU.
7607ec681f3Smrg    */
7617ec681f3Smrg   buffer->address = (uintptr_t)user_ptr;
7627ec681f3Smrg   buffer->data = user_ptr;
7637ec681f3Smrg   buffer->status = NOUVEAU_BUFFER_STATUS_USER_PTR;
7647ec681f3Smrg   buffer->base.screen = pscreen;
7657ec681f3Smrg
7667ec681f3Smrg   pipe_reference_init(&buffer->base.reference, 1);
7677ec681f3Smrg
7687ec681f3Smrg   return &buffer->base;
7697ec681f3Smrg}
7703464ebd5Sriastradh
7713464ebd5Sriastradhstruct pipe_resource *
7723464ebd5Sriastradhnouveau_user_buffer_create(struct pipe_screen *pscreen, void *ptr,
7733464ebd5Sriastradh                           unsigned bytes, unsigned bind)
7743464ebd5Sriastradh{
7753464ebd5Sriastradh   struct nv04_resource *buffer;
7763464ebd5Sriastradh
7773464ebd5Sriastradh   buffer = CALLOC_STRUCT(nv04_resource);
7783464ebd5Sriastradh   if (!buffer)
7793464ebd5Sriastradh      return NULL;
7803464ebd5Sriastradh
7813464ebd5Sriastradh   pipe_reference_init(&buffer->base.reference, 1);
7823464ebd5Sriastradh   buffer->base.screen = pscreen;
7833464ebd5Sriastradh   buffer->base.format = PIPE_FORMAT_R8_UNORM;
7843464ebd5Sriastradh   buffer->base.usage = PIPE_USAGE_IMMUTABLE;
7853464ebd5Sriastradh   buffer->base.bind = bind;
7863464ebd5Sriastradh   buffer->base.width0 = bytes;
7873464ebd5Sriastradh   buffer->base.height0 = 1;
7883464ebd5Sriastradh   buffer->base.depth0 = 1;
7893464ebd5Sriastradh
7903464ebd5Sriastradh   buffer->data = ptr;
7913464ebd5Sriastradh   buffer->status = NOUVEAU_BUFFER_STATUS_USER_MEMORY;
7923464ebd5Sriastradh
793af69d88dSmrg   util_range_init(&buffer->valid_buffer_range);
7947ec681f3Smrg   util_range_add(&buffer->base, &buffer->valid_buffer_range, 0, bytes);
795af69d88dSmrg
7963464ebd5Sriastradh   return &buffer->base;
7973464ebd5Sriastradh}
7983464ebd5Sriastradh
79901e04c3fSmrgstatic inline bool
800af69d88dSmrgnouveau_buffer_data_fetch(struct nouveau_context *nv, struct nv04_resource *buf,
801af69d88dSmrg                          struct nouveau_bo *bo, unsigned offset, unsigned size)
8023464ebd5Sriastradh{
803af69d88dSmrg   if (!nouveau_buffer_malloc(buf))
80401e04c3fSmrg      return false;
805af69d88dSmrg   if (nouveau_bo_map(bo, NOUVEAU_BO_RD, nv->client))
80601e04c3fSmrg      return false;
807af69d88dSmrg   memcpy(buf->data, (uint8_t *)bo->map + offset, size);
80801e04c3fSmrg   return true;
8093464ebd5Sriastradh}
8103464ebd5Sriastradh
8113464ebd5Sriastradh/* Migrate a linear buffer (vertex, index, constants) USER -> GART -> VRAM. */
81201e04c3fSmrgbool
8133464ebd5Sriastradhnouveau_buffer_migrate(struct nouveau_context *nv,
8143464ebd5Sriastradh                       struct nv04_resource *buf, const unsigned new_domain)
8153464ebd5Sriastradh{
8167ec681f3Smrg   assert(!(buf->status & NOUVEAU_BUFFER_STATUS_USER_PTR));
8177ec681f3Smrg
8183464ebd5Sriastradh   struct nouveau_screen *screen = nv->screen;
8193464ebd5Sriastradh   struct nouveau_bo *bo;
8203464ebd5Sriastradh   const unsigned old_domain = buf->domain;
8213464ebd5Sriastradh   unsigned size = buf->base.width0;
8223464ebd5Sriastradh   unsigned offset;
8233464ebd5Sriastradh   int ret;
8243464ebd5Sriastradh
8253464ebd5Sriastradh   assert(new_domain != old_domain);
8263464ebd5Sriastradh
8273464ebd5Sriastradh   if (new_domain == NOUVEAU_BO_GART && old_domain == 0) {
8283464ebd5Sriastradh      if (!nouveau_buffer_allocate(screen, buf, new_domain))
82901e04c3fSmrg         return false;
830af69d88dSmrg      ret = nouveau_bo_map(buf->bo, 0, nv->client);
8313464ebd5Sriastradh      if (ret)
8323464ebd5Sriastradh         return ret;
833af69d88dSmrg      memcpy((uint8_t *)buf->bo->map + buf->offset, buf->data, size);
834af69d88dSmrg      align_free(buf->data);
8353464ebd5Sriastradh   } else
8363464ebd5Sriastradh   if (old_domain != 0 && new_domain != 0) {
8373464ebd5Sriastradh      struct nouveau_mm_allocation *mm = buf->mm;
8383464ebd5Sriastradh
8393464ebd5Sriastradh      if (new_domain == NOUVEAU_BO_VRAM) {
8403464ebd5Sriastradh         /* keep a system memory copy of our data in case we hit a fallback */
841af69d88dSmrg         if (!nouveau_buffer_data_fetch(nv, buf, buf->bo, buf->offset, size))
84201e04c3fSmrg            return false;
843af69d88dSmrg         if (nouveau_mesa_debug)
844af69d88dSmrg            debug_printf("migrating %u KiB to VRAM\n", size / 1024);
8453464ebd5Sriastradh      }
8463464ebd5Sriastradh
8473464ebd5Sriastradh      offset = buf->offset;
8483464ebd5Sriastradh      bo = buf->bo;
8493464ebd5Sriastradh      buf->bo = NULL;
8503464ebd5Sriastradh      buf->mm = NULL;
8513464ebd5Sriastradh      nouveau_buffer_allocate(screen, buf, new_domain);
8523464ebd5Sriastradh
8533464ebd5Sriastradh      nv->copy_data(nv, buf->bo, buf->offset, new_domain,
8543464ebd5Sriastradh                    bo, offset, old_domain, buf->base.width0);
8553464ebd5Sriastradh
85601e04c3fSmrg      nouveau_fence_work(screen->fence.current, nouveau_fence_unref_bo, bo);
8573464ebd5Sriastradh      if (mm)
8583464ebd5Sriastradh         release_allocation(&mm, screen->fence.current);
8593464ebd5Sriastradh   } else
8603464ebd5Sriastradh   if (new_domain == NOUVEAU_BO_VRAM && old_domain == 0) {
861af69d88dSmrg      struct nouveau_transfer tx;
8623464ebd5Sriastradh      if (!nouveau_buffer_allocate(screen, buf, NOUVEAU_BO_VRAM))
86301e04c3fSmrg         return false;
864af69d88dSmrg      tx.base.resource = &buf->base;
865af69d88dSmrg      tx.base.box.x = 0;
866af69d88dSmrg      tx.base.box.width = buf->base.width0;
867af69d88dSmrg      tx.bo = NULL;
868af69d88dSmrg      tx.map = NULL;
86901e04c3fSmrg      if (!nouveau_transfer_staging(nv, &tx, false))
87001e04c3fSmrg         return false;
871af69d88dSmrg      nouveau_transfer_write(nv, &tx, 0, tx.base.box.width);
872af69d88dSmrg      nouveau_buffer_transfer_del(nv, &tx);
8733464ebd5Sriastradh   } else
87401e04c3fSmrg      return false;
8753464ebd5Sriastradh
8763464ebd5Sriastradh   assert(buf->domain == new_domain);
87701e04c3fSmrg   return true;
8783464ebd5Sriastradh}
8793464ebd5Sriastradh
8803464ebd5Sriastradh/* Migrate data from glVertexAttribPointer(non-VBO) user buffers to GART.
8813464ebd5Sriastradh * We'd like to only allocate @size bytes here, but then we'd have to rebase
8823464ebd5Sriastradh * the vertex indices ...
8833464ebd5Sriastradh */
88401e04c3fSmrgbool
885af69d88dSmrgnouveau_user_buffer_upload(struct nouveau_context *nv,
886af69d88dSmrg                           struct nv04_resource *buf,
8873464ebd5Sriastradh                           unsigned base, unsigned size)
8883464ebd5Sriastradh{
8897ec681f3Smrg   assert(!(buf->status & NOUVEAU_BUFFER_STATUS_USER_PTR));
8907ec681f3Smrg
8913464ebd5Sriastradh   struct nouveau_screen *screen = nouveau_screen(buf->base.screen);
8923464ebd5Sriastradh   int ret;
8933464ebd5Sriastradh
8943464ebd5Sriastradh   assert(buf->status & NOUVEAU_BUFFER_STATUS_USER_MEMORY);
8953464ebd5Sriastradh
8963464ebd5Sriastradh   buf->base.width0 = base + size;
8973464ebd5Sriastradh   if (!nouveau_buffer_reallocate(screen, buf, NOUVEAU_BO_GART))
89801e04c3fSmrg      return false;
8993464ebd5Sriastradh
900af69d88dSmrg   ret = nouveau_bo_map(buf->bo, 0, nv->client);
9013464ebd5Sriastradh   if (ret)
90201e04c3fSmrg      return false;
903af69d88dSmrg   memcpy((uint8_t *)buf->bo->map + buf->offset + base, buf->data + base, size);
9043464ebd5Sriastradh
90501e04c3fSmrg   return true;
90601e04c3fSmrg}
90701e04c3fSmrg
90801e04c3fSmrg/* Invalidate underlying buffer storage, reset fences, reallocate to non-busy
90901e04c3fSmrg * buffer.
91001e04c3fSmrg */
91101e04c3fSmrgvoid
91201e04c3fSmrgnouveau_buffer_invalidate(struct pipe_context *pipe,
91301e04c3fSmrg                          struct pipe_resource *resource)
91401e04c3fSmrg{
91501e04c3fSmrg   struct nouveau_context *nv = nouveau_context(pipe);
91601e04c3fSmrg   struct nv04_resource *buf = nv04_resource(resource);
91701e04c3fSmrg   int ref = buf->base.reference.count - 1;
91801e04c3fSmrg
9197ec681f3Smrg   assert(!(buf->status & NOUVEAU_BUFFER_STATUS_USER_PTR));
9207ec681f3Smrg
92101e04c3fSmrg   /* Shared buffers shouldn't get reallocated */
92201e04c3fSmrg   if (unlikely(buf->base.bind & PIPE_BIND_SHARED))
92301e04c3fSmrg      return;
92401e04c3fSmrg
92501e04c3fSmrg   /* If the buffer is sub-allocated and not currently being written, just
92601e04c3fSmrg    * wipe the valid buffer range. Otherwise we have to create fresh
92701e04c3fSmrg    * storage. (We don't keep track of fences for non-sub-allocated BO's.)
92801e04c3fSmrg    */
9297ec681f3Smrg   if (buf->mm && !nouveau_buffer_busy(buf, PIPE_MAP_WRITE)) {
93001e04c3fSmrg      util_range_set_empty(&buf->valid_buffer_range);
93101e04c3fSmrg   } else {
93201e04c3fSmrg      nouveau_buffer_reallocate(nv->screen, buf, buf->domain);
93301e04c3fSmrg      if (ref > 0) /* any references inside context possible ? */
93401e04c3fSmrg         nv->invalidate_resource_storage(nv, &buf->base, ref);
93501e04c3fSmrg   }
9363464ebd5Sriastradh}
937af69d88dSmrg
938af69d88dSmrg
939af69d88dSmrg/* Scratch data allocation. */
940af69d88dSmrg
94101e04c3fSmrgstatic inline int
942af69d88dSmrgnouveau_scratch_bo_alloc(struct nouveau_context *nv, struct nouveau_bo **pbo,
943af69d88dSmrg                         unsigned size)
944af69d88dSmrg{
945af69d88dSmrg   return nouveau_bo_new(nv->screen->device, NOUVEAU_BO_GART | NOUVEAU_BO_MAP,
946af69d88dSmrg                         4096, size, NULL, pbo);
947af69d88dSmrg}
948af69d88dSmrg
94901e04c3fSmrgstatic void
95001e04c3fSmrgnouveau_scratch_unref_bos(void *d)
95101e04c3fSmrg{
95201e04c3fSmrg   struct runout *b = d;
95301e04c3fSmrg   int i;
95401e04c3fSmrg
95501e04c3fSmrg   for (i = 0; i < b->nr; ++i)
95601e04c3fSmrg      nouveau_bo_ref(NULL, &b->bo[i]);
95701e04c3fSmrg
95801e04c3fSmrg   FREE(b);
95901e04c3fSmrg}
96001e04c3fSmrg
961af69d88dSmrgvoid
962af69d88dSmrgnouveau_scratch_runout_release(struct nouveau_context *nv)
963af69d88dSmrg{
96401e04c3fSmrg   if (!nv->scratch.runout)
96501e04c3fSmrg      return;
96601e04c3fSmrg
96701e04c3fSmrg   if (!nouveau_fence_work(nv->screen->fence.current, nouveau_scratch_unref_bos,
96801e04c3fSmrg         nv->scratch.runout))
969af69d88dSmrg      return;
970af69d88dSmrg
971af69d88dSmrg   nv->scratch.end = 0;
972af69d88dSmrg   nv->scratch.runout = NULL;
973af69d88dSmrg}
974af69d88dSmrg
975af69d88dSmrg/* Allocate an extra bo if we can't fit everything we need simultaneously.
976af69d88dSmrg * (Could happen for very large user arrays.)
977af69d88dSmrg */
97801e04c3fSmrgstatic inline bool
979af69d88dSmrgnouveau_scratch_runout(struct nouveau_context *nv, unsigned size)
980af69d88dSmrg{
981af69d88dSmrg   int ret;
98201e04c3fSmrg   unsigned n;
983af69d88dSmrg
98401e04c3fSmrg   if (nv->scratch.runout)
98501e04c3fSmrg      n = nv->scratch.runout->nr;
98601e04c3fSmrg   else
98701e04c3fSmrg      n = 0;
98801e04c3fSmrg   nv->scratch.runout = REALLOC(nv->scratch.runout, n == 0 ? 0 :
98901e04c3fSmrg                                (sizeof(*nv->scratch.runout) + (n + 0) * sizeof(void *)),
99001e04c3fSmrg                                 sizeof(*nv->scratch.runout) + (n + 1) * sizeof(void *));
99101e04c3fSmrg   nv->scratch.runout->nr = n + 1;
99201e04c3fSmrg   nv->scratch.runout->bo[n] = NULL;
99301e04c3fSmrg
99401e04c3fSmrg   ret = nouveau_scratch_bo_alloc(nv, &nv->scratch.runout->bo[n], size);
995af69d88dSmrg   if (!ret) {
99601e04c3fSmrg      ret = nouveau_bo_map(nv->scratch.runout->bo[n], 0, NULL);
997af69d88dSmrg      if (ret)
99801e04c3fSmrg         nouveau_bo_ref(NULL, &nv->scratch.runout->bo[--nv->scratch.runout->nr]);
999af69d88dSmrg   }
1000af69d88dSmrg   if (!ret) {
100101e04c3fSmrg      nv->scratch.current = nv->scratch.runout->bo[n];
1002af69d88dSmrg      nv->scratch.offset = 0;
1003af69d88dSmrg      nv->scratch.end = size;
1004af69d88dSmrg      nv->scratch.map = nv->scratch.current->map;
1005af69d88dSmrg   }
1006af69d88dSmrg   return !ret;
1007af69d88dSmrg}
1008af69d88dSmrg
1009af69d88dSmrg/* Continue to next scratch buffer, if available (no wrapping, large enough).
1010af69d88dSmrg * Allocate it if it has not yet been created.
1011af69d88dSmrg */
101201e04c3fSmrgstatic inline bool
1013af69d88dSmrgnouveau_scratch_next(struct nouveau_context *nv, unsigned size)
1014af69d88dSmrg{
1015af69d88dSmrg   struct nouveau_bo *bo;
1016af69d88dSmrg   int ret;
1017af69d88dSmrg   const unsigned i = (nv->scratch.id + 1) % NOUVEAU_MAX_SCRATCH_BUFS;
1018af69d88dSmrg
1019af69d88dSmrg   if ((size > nv->scratch.bo_size) || (i == nv->scratch.wrap))
102001e04c3fSmrg      return false;
1021af69d88dSmrg   nv->scratch.id = i;
1022af69d88dSmrg
1023af69d88dSmrg   bo = nv->scratch.bo[i];
1024af69d88dSmrg   if (!bo) {
1025af69d88dSmrg      ret = nouveau_scratch_bo_alloc(nv, &bo, nv->scratch.bo_size);
1026af69d88dSmrg      if (ret)
102701e04c3fSmrg         return false;
1028af69d88dSmrg      nv->scratch.bo[i] = bo;
1029af69d88dSmrg   }
1030af69d88dSmrg   nv->scratch.current = bo;
1031af69d88dSmrg   nv->scratch.offset = 0;
1032af69d88dSmrg   nv->scratch.end = nv->scratch.bo_size;
1033af69d88dSmrg
1034af69d88dSmrg   ret = nouveau_bo_map(bo, NOUVEAU_BO_WR, nv->client);
1035af69d88dSmrg   if (!ret)
1036af69d88dSmrg      nv->scratch.map = bo->map;
1037af69d88dSmrg   return !ret;
1038af69d88dSmrg}
1039af69d88dSmrg
104001e04c3fSmrgstatic bool
1041af69d88dSmrgnouveau_scratch_more(struct nouveau_context *nv, unsigned min_size)
1042af69d88dSmrg{
104301e04c3fSmrg   bool ret;
1044af69d88dSmrg
1045af69d88dSmrg   ret = nouveau_scratch_next(nv, min_size);
1046af69d88dSmrg   if (!ret)
1047af69d88dSmrg      ret = nouveau_scratch_runout(nv, min_size);
1048af69d88dSmrg   return ret;
1049af69d88dSmrg}
1050af69d88dSmrg
1051af69d88dSmrg
1052af69d88dSmrg/* Copy data to a scratch buffer and return address & bo the data resides in. */
1053af69d88dSmrguint64_t
1054af69d88dSmrgnouveau_scratch_data(struct nouveau_context *nv,
1055af69d88dSmrg                     const void *data, unsigned base, unsigned size,
1056af69d88dSmrg                     struct nouveau_bo **bo)
1057af69d88dSmrg{
1058af69d88dSmrg   unsigned bgn = MAX2(base, nv->scratch.offset);
1059af69d88dSmrg   unsigned end = bgn + size;
1060af69d88dSmrg
1061af69d88dSmrg   if (end >= nv->scratch.end) {
1062af69d88dSmrg      end = base + size;
1063af69d88dSmrg      if (!nouveau_scratch_more(nv, end))
1064af69d88dSmrg         return 0;
1065af69d88dSmrg      bgn = base;
1066af69d88dSmrg   }
1067af69d88dSmrg   nv->scratch.offset = align(end, 4);
1068af69d88dSmrg
1069af69d88dSmrg   memcpy(nv->scratch.map + bgn, (const uint8_t *)data + base, size);
1070af69d88dSmrg
1071af69d88dSmrg   *bo = nv->scratch.current;
1072af69d88dSmrg   return (*bo)->offset + (bgn - base);
1073af69d88dSmrg}
1074af69d88dSmrg
1075af69d88dSmrgvoid *
1076af69d88dSmrgnouveau_scratch_get(struct nouveau_context *nv,
1077af69d88dSmrg                    unsigned size, uint64_t *gpu_addr, struct nouveau_bo **pbo)
1078af69d88dSmrg{
1079af69d88dSmrg   unsigned bgn = nv->scratch.offset;
1080af69d88dSmrg   unsigned end = nv->scratch.offset + size;
1081af69d88dSmrg
1082af69d88dSmrg   if (end >= nv->scratch.end) {
1083af69d88dSmrg      end = size;
1084af69d88dSmrg      if (!nouveau_scratch_more(nv, end))
1085af69d88dSmrg         return NULL;
1086af69d88dSmrg      bgn = 0;
1087af69d88dSmrg   }
1088af69d88dSmrg   nv->scratch.offset = align(end, 4);
1089af69d88dSmrg
1090af69d88dSmrg   *pbo = nv->scratch.current;
1091af69d88dSmrg   *gpu_addr = nv->scratch.current->offset + bgn;
1092af69d88dSmrg   return nv->scratch.map + bgn;
1093af69d88dSmrg}
1094