1 
2 #include "util/u_inlines.h"
3 #include "util/u_memory.h"
4 #include "util/u_math.h"
5 
6 #include "nouveau_screen.h"
7 #include "nouveau_context.h"
8 #include "nouveau_winsys.h"
9 #include "nouveau_fence.h"
10 #include "nouveau_buffer.h"
11 #include "nouveau_mm.h"
12 
13 struct nouveau_transfer {
14    struct pipe_transfer base;
15 };
16 
17 static INLINE struct nouveau_transfer *
nouveau_transfer(struct pipe_transfer * transfer)18 nouveau_transfer(struct pipe_transfer *transfer)
19 {
20    return (struct nouveau_transfer *)transfer;
21 }
22 
23 static INLINE boolean
nouveau_buffer_allocate(struct nouveau_screen * screen,struct nv04_resource * buf,unsigned domain)24 nouveau_buffer_allocate(struct nouveau_screen *screen,
25                         struct nv04_resource *buf, unsigned domain)
26 {
27    uint32_t size = buf->base.width0;
28 
29    if (buf->base.bind & PIPE_BIND_CONSTANT_BUFFER)
30       size = align(size, 0x100);
31 
32    if (domain == NOUVEAU_BO_VRAM) {
33       buf->mm = nouveau_mm_allocate(screen->mm_VRAM, size,
34                                     &buf->bo, &buf->offset);
35       if (!buf->bo)
36          return nouveau_buffer_allocate(screen, buf, NOUVEAU_BO_GART);
37    } else
38    if (domain == NOUVEAU_BO_GART) {
39       buf->mm = nouveau_mm_allocate(screen->mm_GART, size,
40                                     &buf->bo, &buf->offset);
41       if (!buf->bo)
42          return FALSE;
43    }
44    if (domain != NOUVEAU_BO_GART) {
45       if (!buf->data) {
46          buf->data = MALLOC(buf->base.width0);
47          if (!buf->data)
48             return FALSE;
49       }
50    }
51    buf->domain = domain;
52    if (buf->bo)
53       buf->address = buf->bo->offset + buf->offset;
54 
55    return TRUE;
56 }
57 
58 static INLINE void
release_allocation(struct nouveau_mm_allocation ** mm,struct nouveau_fence * fence)59 release_allocation(struct nouveau_mm_allocation **mm,
60                    struct nouveau_fence *fence)
61 {
62    nouveau_fence_work(fence, nouveau_mm_free_work, *mm);
63    (*mm) = NULL;
64 }
65 
66 INLINE void
nouveau_buffer_release_gpu_storage(struct nv04_resource * buf)67 nouveau_buffer_release_gpu_storage(struct nv04_resource *buf)
68 {
69    nouveau_bo_ref(NULL, &buf->bo);
70 
71    if (buf->mm)
72       release_allocation(&buf->mm, buf->fence);
73 
74    buf->domain = 0;
75 }
76 
77 static INLINE boolean
nouveau_buffer_reallocate(struct nouveau_screen * screen,struct nv04_resource * buf,unsigned domain)78 nouveau_buffer_reallocate(struct nouveau_screen *screen,
79                           struct nv04_resource *buf, unsigned domain)
80 {
81    nouveau_buffer_release_gpu_storage(buf);
82 
83    return nouveau_buffer_allocate(screen, buf, domain);
84 }
85 
86 static void
nouveau_buffer_destroy(struct pipe_screen * pscreen,struct pipe_resource * presource)87 nouveau_buffer_destroy(struct pipe_screen *pscreen,
88                        struct pipe_resource *presource)
89 {
90    struct nv04_resource *res = nv04_resource(presource);
91 
92    nouveau_buffer_release_gpu_storage(res);
93 
94    if (res->data && !(res->status & NOUVEAU_BUFFER_STATUS_USER_MEMORY))
95       FREE(res->data);
96 
97    nouveau_fence_ref(NULL, &res->fence);
98    nouveau_fence_ref(NULL, &res->fence_wr);
99 
100    FREE(res);
101 }
102 
103 /* Maybe just migrate to GART right away if we actually need to do this. */
104 boolean
nouveau_buffer_download(struct nouveau_context * nv,struct nv04_resource * buf,unsigned start,unsigned size)105 nouveau_buffer_download(struct nouveau_context *nv, struct nv04_resource *buf,
106                         unsigned start, unsigned size)
107 {
108    struct nouveau_mm_allocation *mm;
109    struct nouveau_bo *bounce = NULL;
110    uint32_t offset;
111 
112    assert(buf->domain == NOUVEAU_BO_VRAM);
113 
114    mm = nouveau_mm_allocate(nv->screen->mm_GART, size, &bounce, &offset);
115    if (!bounce)
116       return FALSE;
117 
118    nv->copy_data(nv, bounce, offset, NOUVEAU_BO_GART,
119                  buf->bo, buf->offset + start, NOUVEAU_BO_VRAM, size);
120 
121    if (nouveau_bo_map(bounce, NOUVEAU_BO_RD, nv->screen->client))
122       return FALSE;
123    memcpy(buf->data + start, (uint8_t *)bounce->map + offset, size);
124 
125    buf->status &= ~NOUVEAU_BUFFER_STATUS_GPU_WRITING;
126 
127    nouveau_bo_ref(NULL, &bounce);
128    if (mm)
129       nouveau_mm_free(mm);
130    return TRUE;
131 }
132 
133 static boolean
nouveau_buffer_upload(struct nouveau_context * nv,struct nv04_resource * buf,unsigned start,unsigned size)134 nouveau_buffer_upload(struct nouveau_context *nv, struct nv04_resource *buf,
135                       unsigned start, unsigned size)
136 {
137    struct nouveau_mm_allocation *mm;
138    struct nouveau_bo *bounce = NULL;
139    uint32_t offset;
140 
141    if (size <= 192 && (nv->push_data || nv->push_cb)) {
142       if (buf->base.bind & PIPE_BIND_CONSTANT_BUFFER)
143          nv->push_cb(nv, buf->bo, buf->domain, buf->offset, buf->base.width0,
144                      start, size / 4, (const uint32_t *)(buf->data + start));
145       else
146          nv->push_data(nv, buf->bo, buf->offset + start, buf->domain,
147                        size, buf->data + start);
148       return TRUE;
149    }
150 
151    mm = nouveau_mm_allocate(nv->screen->mm_GART, size, &bounce, &offset);
152    if (!bounce)
153       return FALSE;
154 
155    nouveau_bo_map(bounce, 0, nv->screen->client);
156    memcpy((uint8_t *)bounce->map + offset, buf->data + start, size);
157 
158    nv->copy_data(nv, buf->bo, buf->offset + start, NOUVEAU_BO_VRAM,
159                  bounce, offset, NOUVEAU_BO_GART, size);
160 
161    nouveau_bo_ref(NULL, &bounce);
162    if (mm)
163       release_allocation(&mm, nv->screen->fence.current);
164 
165    if (start == 0 && size == buf->base.width0)
166       buf->status &= ~NOUVEAU_BUFFER_STATUS_GPU_WRITING;
167    return TRUE;
168 }
169 
170 static struct pipe_transfer *
nouveau_buffer_transfer_get(struct pipe_context * pipe,struct pipe_resource * resource,unsigned level,unsigned usage,const struct pipe_box * box)171 nouveau_buffer_transfer_get(struct pipe_context *pipe,
172                             struct pipe_resource *resource,
173                             unsigned level, unsigned usage,
174                             const struct pipe_box *box)
175 {
176    struct nv04_resource *buf = nv04_resource(resource);
177    struct nouveau_context *nv = nouveau_context(pipe);
178    struct nouveau_transfer *xfr = CALLOC_STRUCT(nouveau_transfer);
179    if (!xfr)
180       return NULL;
181 
182    xfr->base.resource = resource;
183    xfr->base.box.x = box->x;
184    xfr->base.box.width = box->width;
185    xfr->base.usage = usage;
186 
187    if (buf->domain == NOUVEAU_BO_VRAM) {
188       if (usage & PIPE_TRANSFER_READ) {
189          if (buf->status & NOUVEAU_BUFFER_STATUS_GPU_WRITING)
190             nouveau_buffer_download(nv, buf, 0, buf->base.width0);
191       }
192    }
193 
194    return &xfr->base;
195 }
196 
197 static void
nouveau_buffer_transfer_destroy(struct pipe_context * pipe,struct pipe_transfer * transfer)198 nouveau_buffer_transfer_destroy(struct pipe_context *pipe,
199                                 struct pipe_transfer *transfer)
200 {
201    struct nv04_resource *buf = nv04_resource(transfer->resource);
202    struct nouveau_transfer *xfr = nouveau_transfer(transfer);
203    struct nouveau_context *nv = nouveau_context(pipe);
204 
205    if (xfr->base.usage & PIPE_TRANSFER_WRITE) {
206       if (buf->domain == NOUVEAU_BO_VRAM) {
207          nouveau_buffer_upload(nv, buf, transfer->box.x, transfer->box.width);
208       }
209 
210       if (buf->domain != 0 && (buf->base.bind & (PIPE_BIND_VERTEX_BUFFER |
211                                                  PIPE_BIND_INDEX_BUFFER)))
212          nouveau_context(pipe)->vbo_dirty = TRUE;
213    }
214 
215    FREE(xfr);
216 }
217 
218 static INLINE boolean
nouveau_buffer_sync(struct nv04_resource * buf,unsigned rw)219 nouveau_buffer_sync(struct nv04_resource *buf, unsigned rw)
220 {
221    if (rw == PIPE_TRANSFER_READ) {
222       if (!buf->fence_wr)
223          return TRUE;
224       if (!nouveau_fence_wait(buf->fence_wr))
225          return FALSE;
226    } else {
227       if (!buf->fence)
228          return TRUE;
229       if (!nouveau_fence_wait(buf->fence))
230          return FALSE;
231 
232       nouveau_fence_ref(NULL, &buf->fence);
233    }
234    nouveau_fence_ref(NULL, &buf->fence_wr);
235 
236    return TRUE;
237 }
238 
239 static INLINE boolean
nouveau_buffer_busy(struct nv04_resource * buf,unsigned rw)240 nouveau_buffer_busy(struct nv04_resource *buf, unsigned rw)
241 {
242    if (rw == PIPE_TRANSFER_READ)
243       return (buf->fence_wr && !nouveau_fence_signalled(buf->fence_wr));
244    else
245       return (buf->fence && !nouveau_fence_signalled(buf->fence));
246 }
247 
248 static void *
nouveau_buffer_transfer_map(struct pipe_context * pipe,struct pipe_transfer * transfer)249 nouveau_buffer_transfer_map(struct pipe_context *pipe,
250                             struct pipe_transfer *transfer)
251 {
252    struct nouveau_context *nv = nouveau_context(pipe);
253    struct nouveau_transfer *xfr = nouveau_transfer(transfer);
254    struct nv04_resource *buf = nv04_resource(transfer->resource);
255    struct nouveau_bo *bo = buf->bo;
256    uint8_t *map;
257    int ret;
258    uint32_t offset = xfr->base.box.x;
259    uint32_t flags = 0;
260 
261    if (buf->domain != NOUVEAU_BO_GART)
262       return buf->data + offset;
263 
264    if (!buf->mm)
265       flags = nouveau_screen_transfer_flags(xfr->base.usage);
266 
267    offset += buf->offset;
268 
269    ret = nouveau_bo_map(buf->bo, flags, nv->screen->client);
270    if (ret)
271       return NULL;
272    map = (uint8_t *)bo->map + offset;
273 
274    if (buf->mm) {
275       if (xfr->base.usage & PIPE_TRANSFER_DONTBLOCK) {
276          if (nouveau_buffer_busy(buf, xfr->base.usage & PIPE_TRANSFER_READ_WRITE))
277             return NULL;
278       } else
279       if (!(xfr->base.usage & PIPE_TRANSFER_UNSYNCHRONIZED)) {
280          nouveau_buffer_sync(buf, xfr->base.usage & PIPE_TRANSFER_READ_WRITE);
281       }
282    }
283    return map;
284 }
285 
286 
287 
288 static void
nouveau_buffer_transfer_flush_region(struct pipe_context * pipe,struct pipe_transfer * transfer,const struct pipe_box * box)289 nouveau_buffer_transfer_flush_region(struct pipe_context *pipe,
290                                      struct pipe_transfer *transfer,
291                                      const struct pipe_box *box)
292 {
293 #if 0
294    struct nv04_resource *res = nv04_resource(transfer->resource);
295    struct nouveau_bo *bo = res->bo;
296    unsigned offset = res->offset + transfer->box.x + box->x;
297 
298    /* not using non-snoop system memory yet, no need for cflush */
299    if (1)
300       return;
301 
302    /* XXX: maybe need to upload for VRAM buffers here */
303 #endif
304 }
305 
306 static void
nouveau_buffer_transfer_unmap(struct pipe_context * pipe,struct pipe_transfer * transfer)307 nouveau_buffer_transfer_unmap(struct pipe_context *pipe,
308                               struct pipe_transfer *transfer)
309 {
310 }
311 
312 
313 void *
nouveau_resource_map_offset(struct nouveau_context * nv,struct nv04_resource * res,uint32_t offset,uint32_t flags)314 nouveau_resource_map_offset(struct nouveau_context *nv,
315                             struct nv04_resource *res, uint32_t offset,
316                             uint32_t flags)
317 {
318    if ((res->domain == NOUVEAU_BO_VRAM) &&
319        (res->status & NOUVEAU_BUFFER_STATUS_GPU_WRITING))
320       nouveau_buffer_download(nv, res, 0, res->base.width0);
321 
322    if ((res->domain != NOUVEAU_BO_GART) ||
323        (res->status & NOUVEAU_BUFFER_STATUS_USER_MEMORY))
324       return res->data + offset;
325 
326    if (res->mm) {
327       unsigned rw;
328       rw = (flags & NOUVEAU_BO_WR) ? PIPE_TRANSFER_WRITE : PIPE_TRANSFER_READ;
329       nouveau_buffer_sync(res, rw);
330       if (nouveau_bo_map(res->bo, 0, NULL))
331          return NULL;
332    } else {
333       if (nouveau_bo_map(res->bo, flags, nv->screen->client))
334          return NULL;
335    }
336    return (uint8_t *)res->bo->map + res->offset + offset;
337 }
338 
339 
340 const struct u_resource_vtbl nouveau_buffer_vtbl =
341 {
342    u_default_resource_get_handle,     /* get_handle */
343    nouveau_buffer_destroy,               /* resource_destroy */
344    nouveau_buffer_transfer_get,          /* get_transfer */
345    nouveau_buffer_transfer_destroy,      /* transfer_destroy */
346    nouveau_buffer_transfer_map,          /* transfer_map */
347    nouveau_buffer_transfer_flush_region, /* transfer_flush_region */
348    nouveau_buffer_transfer_unmap,        /* transfer_unmap */
349    u_default_transfer_inline_write    /* transfer_inline_write */
350 };
351 
352 struct pipe_resource *
nouveau_buffer_create(struct pipe_screen * pscreen,const struct pipe_resource * templ)353 nouveau_buffer_create(struct pipe_screen *pscreen,
354                       const struct pipe_resource *templ)
355 {
356    struct nouveau_screen *screen = nouveau_screen(pscreen);
357    struct nv04_resource *buffer;
358    boolean ret;
359 
360    buffer = CALLOC_STRUCT(nv04_resource);
361    if (!buffer)
362       return NULL;
363 
364    buffer->base = *templ;
365    buffer->vtbl = &nouveau_buffer_vtbl;
366    pipe_reference_init(&buffer->base.reference, 1);
367    buffer->base.screen = pscreen;
368 
369    if (buffer->base.bind &
370        (screen->vidmem_bindings & screen->sysmem_bindings)) {
371       switch (buffer->base.usage) {
372       case PIPE_USAGE_DEFAULT:
373       case PIPE_USAGE_IMMUTABLE:
374       case PIPE_USAGE_STATIC:
375          buffer->domain = NOUVEAU_BO_VRAM;
376          break;
377       case PIPE_USAGE_DYNAMIC:
378       case PIPE_USAGE_STAGING:
379       case PIPE_USAGE_STREAM:
380          buffer->domain = NOUVEAU_BO_GART;
381          break;
382       default:
383          assert(0);
384          break;
385       }
386    } else {
387       if (buffer->base.bind & screen->vidmem_bindings)
388          buffer->domain = NOUVEAU_BO_VRAM;
389       else
390       if (buffer->base.bind & screen->sysmem_bindings)
391          buffer->domain = NOUVEAU_BO_GART;
392    }
393    ret = nouveau_buffer_allocate(screen, buffer, buffer->domain);
394 
395    if (ret == FALSE)
396       goto fail;
397 
398    return &buffer->base;
399 
400 fail:
401    FREE(buffer);
402    return NULL;
403 }
404 
405 
406 struct pipe_resource *
nouveau_user_buffer_create(struct pipe_screen * pscreen,void * ptr,unsigned bytes,unsigned bind)407 nouveau_user_buffer_create(struct pipe_screen *pscreen, void *ptr,
408                            unsigned bytes, unsigned bind)
409 {
410    struct nv04_resource *buffer;
411 
412    buffer = CALLOC_STRUCT(nv04_resource);
413    if (!buffer)
414       return NULL;
415 
416    pipe_reference_init(&buffer->base.reference, 1);
417    buffer->vtbl = &nouveau_buffer_vtbl;
418    buffer->base.screen = pscreen;
419    buffer->base.format = PIPE_FORMAT_R8_UNORM;
420    buffer->base.usage = PIPE_USAGE_IMMUTABLE;
421    buffer->base.bind = bind;
422    buffer->base.width0 = bytes;
423    buffer->base.height0 = 1;
424    buffer->base.depth0 = 1;
425 
426    buffer->data = ptr;
427    buffer->status = NOUVEAU_BUFFER_STATUS_USER_MEMORY;
428 
429    return &buffer->base;
430 }
431 
432 /* Like download, but for GART buffers. Merge ? */
433 static INLINE boolean
nouveau_buffer_data_fetch(struct nouveau_context * nv,struct nv04_resource * buf,struct nouveau_bo * bo,unsigned offset,unsigned size)434 nouveau_buffer_data_fetch(struct nouveau_context *nv, struct nv04_resource *buf,
435                           struct nouveau_bo *bo, unsigned offset, unsigned size)
436 {
437    if (!buf->data) {
438       buf->data = MALLOC(size);
439       if (!buf->data)
440          return FALSE;
441    }
442    if (nouveau_bo_map(bo, NOUVEAU_BO_RD, nv->screen->client))
443       return FALSE;
444    memcpy(buf->data, (uint8_t *)bo->map + offset, size);
445 
446    return TRUE;
447 }
448 
449 /* Migrate a linear buffer (vertex, index, constants) USER -> GART -> VRAM. */
450 boolean
nouveau_buffer_migrate(struct nouveau_context * nv,struct nv04_resource * buf,const unsigned new_domain)451 nouveau_buffer_migrate(struct nouveau_context *nv,
452                        struct nv04_resource *buf, const unsigned new_domain)
453 {
454    struct nouveau_screen *screen = nv->screen;
455    struct nouveau_bo *bo;
456    const unsigned old_domain = buf->domain;
457    unsigned size = buf->base.width0;
458    unsigned offset;
459    int ret;
460 
461    assert(new_domain != old_domain);
462 
463    if (new_domain == NOUVEAU_BO_GART && old_domain == 0) {
464       if (!nouveau_buffer_allocate(screen, buf, new_domain))
465          return FALSE;
466       ret = nouveau_bo_map(buf->bo, 0, nv->screen->client);
467       if (ret)
468          return ret;
469       memcpy((uint8_t *)buf->bo->map + buf->offset, buf->data, size);
470       FREE(buf->data);
471    } else
472    if (old_domain != 0 && new_domain != 0) {
473       struct nouveau_mm_allocation *mm = buf->mm;
474 
475       if (new_domain == NOUVEAU_BO_VRAM) {
476          /* keep a system memory copy of our data in case we hit a fallback */
477          if (!nouveau_buffer_data_fetch(nv, buf, buf->bo, buf->offset, size))
478             return FALSE;
479          if (nouveau_mesa_debug)
480             debug_printf("migrating %u KiB to VRAM\n", size / 1024);
481       }
482 
483       offset = buf->offset;
484       bo = buf->bo;
485       buf->bo = NULL;
486       buf->mm = NULL;
487       nouveau_buffer_allocate(screen, buf, new_domain);
488 
489       nv->copy_data(nv, buf->bo, buf->offset, new_domain,
490                     bo, offset, old_domain, buf->base.width0);
491 
492       nouveau_bo_ref(NULL, &bo);
493       if (mm)
494          release_allocation(&mm, screen->fence.current);
495    } else
496    if (new_domain == NOUVEAU_BO_VRAM && old_domain == 0) {
497       if (!nouveau_buffer_allocate(screen, buf, NOUVEAU_BO_VRAM))
498          return FALSE;
499       if (!nouveau_buffer_upload(nv, buf, 0, buf->base.width0))
500          return FALSE;
501    } else
502       return FALSE;
503 
504    assert(buf->domain == new_domain);
505    return TRUE;
506 }
507 
508 /* Migrate data from glVertexAttribPointer(non-VBO) user buffers to GART.
509  * We'd like to only allocate @size bytes here, but then we'd have to rebase
510  * the vertex indices ...
511  */
512 boolean
nouveau_user_buffer_upload(struct nouveau_context * nv,struct nv04_resource * buf,unsigned base,unsigned size)513 nouveau_user_buffer_upload(struct nouveau_context *nv,
514                            struct nv04_resource *buf,
515                            unsigned base, unsigned size)
516 {
517    struct nouveau_screen *screen = nouveau_screen(buf->base.screen);
518    int ret;
519 
520    assert(buf->status & NOUVEAU_BUFFER_STATUS_USER_MEMORY);
521 
522    buf->base.width0 = base + size;
523    if (!nouveau_buffer_reallocate(screen, buf, NOUVEAU_BO_GART))
524       return FALSE;
525 
526    ret = nouveau_bo_map(buf->bo, 0, nv->screen->client);
527    if (ret)
528       return FALSE;
529    memcpy((uint8_t *)buf->bo->map + buf->offset + base, buf->data + base, size);
530 
531    return TRUE;
532 }
533 
534 
535 /* Scratch data allocation. */
536 
537 static INLINE int
nouveau_scratch_bo_alloc(struct nouveau_context * nv,struct nouveau_bo ** pbo,unsigned size)538 nouveau_scratch_bo_alloc(struct nouveau_context *nv, struct nouveau_bo **pbo,
539                          unsigned size)
540 {
541    return nouveau_bo_new(nv->screen->device, NOUVEAU_BO_GART | NOUVEAU_BO_MAP,
542                          4096, size, NULL, pbo);
543 }
544 
545 void
nouveau_scratch_runout_release(struct nouveau_context * nv)546 nouveau_scratch_runout_release(struct nouveau_context *nv)
547 {
548    if (!nv->scratch.nr_runout)
549       return;
550    do {
551       --nv->scratch.nr_runout;
552       nouveau_bo_ref(NULL, &nv->scratch.runout[nv->scratch.nr_runout]);
553    } while (nv->scratch.nr_runout);
554 
555    FREE(nv->scratch.runout);
556    nv->scratch.end = 0;
557    nv->scratch.runout = NULL;
558 }
559 
560 /* Allocate an extra bo if we can't fit everything we need simultaneously.
561  * (Could happen for very large user arrays.)
562  */
563 static INLINE boolean
nouveau_scratch_runout(struct nouveau_context * nv,unsigned size)564 nouveau_scratch_runout(struct nouveau_context *nv, unsigned size)
565 {
566    int ret;
567    const unsigned n = nv->scratch.nr_runout++;
568 
569    nv->scratch.runout = REALLOC(nv->scratch.runout,
570                                 (n + 0) * sizeof(*nv->scratch.runout),
571                                 (n + 1) * sizeof(*nv->scratch.runout));
572    nv->scratch.runout[n] = NULL;
573 
574    ret = nouveau_scratch_bo_alloc(nv, &nv->scratch.runout[n], size);
575    if (!ret) {
576       ret = nouveau_bo_map(nv->scratch.runout[n], 0, NULL);
577       if (ret)
578          nouveau_bo_ref(NULL, &nv->scratch.runout[--nv->scratch.nr_runout]);
579    }
580    if (!ret) {
581       nv->scratch.current = nv->scratch.runout[n];
582       nv->scratch.offset = 0;
583       nv->scratch.end = size;
584       nv->scratch.map = nv->scratch.current->map;
585    }
586    return !ret;
587 }
588 
589 /* Continue to next scratch buffer, if available (no wrapping, large enough).
590  * Allocate it if it has not yet been created.
591  */
592 static INLINE boolean
nouveau_scratch_next(struct nouveau_context * nv,unsigned size)593 nouveau_scratch_next(struct nouveau_context *nv, unsigned size)
594 {
595    struct nouveau_bo *bo;
596    int ret;
597    const unsigned i = (nv->scratch.id + 1) % NOUVEAU_MAX_SCRATCH_BUFS;
598 
599    if ((size > nv->scratch.bo_size) || (i == nv->scratch.wrap))
600       return FALSE;
601    nv->scratch.id = i;
602 
603    bo = nv->scratch.bo[i];
604    if (!bo) {
605       ret = nouveau_scratch_bo_alloc(nv, &bo, nv->scratch.bo_size);
606       if (ret)
607          return FALSE;
608       nv->scratch.bo[i] = bo;
609    }
610    nv->scratch.current = bo;
611    nv->scratch.offset = 0;
612    nv->scratch.end = nv->scratch.bo_size;
613 
614    ret = nouveau_bo_map(bo, NOUVEAU_BO_WR, nv->screen->client);
615    if (!ret)
616       nv->scratch.map = bo->map;
617    return !ret;
618 }
619 
620 static boolean
nouveau_scratch_more(struct nouveau_context * nv,unsigned min_size)621 nouveau_scratch_more(struct nouveau_context *nv, unsigned min_size)
622 {
623    boolean ret;
624 
625    ret = nouveau_scratch_next(nv, min_size);
626    if (!ret)
627       ret = nouveau_scratch_runout(nv, min_size);
628    return ret;
629 }
630 
631 
632 /* Copy data to a scratch buffer and return address & bo the data resides in. */
633 uint64_t
nouveau_scratch_data(struct nouveau_context * nv,const void * data,unsigned base,unsigned size,struct nouveau_bo ** bo)634 nouveau_scratch_data(struct nouveau_context *nv,
635                      const void *data, unsigned base, unsigned size,
636                      struct nouveau_bo **bo)
637 {
638    unsigned bgn = MAX2(base, nv->scratch.offset);
639    unsigned end = bgn + size;
640 
641    if (end >= nv->scratch.end) {
642       end = base + size;
643       if (!nouveau_scratch_more(nv, end))
644          return 0;
645       bgn = base;
646    }
647    nv->scratch.offset = align(end, 4);
648 
649    memcpy(nv->scratch.map + bgn, (const uint8_t *)data + base, size);
650 
651    *bo = nv->scratch.current;
652    return (*bo)->offset + (bgn - base);
653 }
654 
655 void *
nouveau_scratch_get(struct nouveau_context * nv,unsigned size,uint64_t * gpu_addr,struct nouveau_bo ** pbo)656 nouveau_scratch_get(struct nouveau_context *nv,
657                     unsigned size, uint64_t *gpu_addr, struct nouveau_bo **pbo)
658 {
659    unsigned bgn = nv->scratch.offset;
660    unsigned end = nv->scratch.offset + size;
661 
662    if (end >= nv->scratch.end) {
663       end = size;
664       if (!nouveau_scratch_more(nv, end))
665          return NULL;
666       bgn = 0;
667    }
668    nv->scratch.offset = align(end, 4);
669 
670    *pbo = nv->scratch.current;
671    *gpu_addr = nv->scratch.current->offset + bgn;
672    return nv->scratch.map + bgn;
673 }
674