2 #include "util/u_inlines.h"
3 #include "util/u_memory.h"
4 #include "util/u_math.h"
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"
13 struct nouveau_transfer
{
14 struct pipe_transfer base
;
17 static INLINE
struct nouveau_transfer
*
18 nouveau_transfer(struct pipe_transfer
*transfer
)
20 return (struct nouveau_transfer
*)transfer
;
24 nouveau_buffer_allocate(struct nouveau_screen
*screen
,
25 struct nv04_resource
*buf
, unsigned domain
)
27 uint32_t size
= buf
->base
.width0
;
29 if (buf
->base
.bind
& PIPE_BIND_CONSTANT_BUFFER
)
30 size
= align(size
, 0x100);
32 if (domain
== NOUVEAU_BO_VRAM
) {
33 buf
->mm
= nouveau_mm_allocate(screen
->mm_VRAM
, size
,
34 &buf
->bo
, &buf
->offset
);
36 return nouveau_buffer_allocate(screen
, buf
, NOUVEAU_BO_GART
);
38 if (domain
== NOUVEAU_BO_GART
) {
39 buf
->mm
= nouveau_mm_allocate(screen
->mm_GART
, size
,
40 &buf
->bo
, &buf
->offset
);
44 if (domain
!= NOUVEAU_BO_GART
) {
46 buf
->data
= MALLOC(buf
->base
.width0
);
53 buf
->address
= buf
->bo
->offset
+ buf
->offset
;
59 release_allocation(struct nouveau_mm_allocation
**mm
,
60 struct nouveau_fence
*fence
)
62 nouveau_fence_work(fence
, nouveau_mm_free_work
, *mm
);
67 nouveau_buffer_release_gpu_storage(struct nv04_resource
*buf
)
69 nouveau_bo_ref(NULL
, &buf
->bo
);
72 release_allocation(&buf
->mm
, buf
->fence
);
78 nouveau_buffer_reallocate(struct nouveau_screen
*screen
,
79 struct nv04_resource
*buf
, unsigned domain
)
81 nouveau_buffer_release_gpu_storage(buf
);
83 return nouveau_buffer_allocate(screen
, buf
, domain
);
87 nouveau_buffer_destroy(struct pipe_screen
*pscreen
,
88 struct pipe_resource
*presource
)
90 struct nv04_resource
*res
= nv04_resource(presource
);
92 nouveau_buffer_release_gpu_storage(res
);
94 if (res
->data
&& !(res
->status
& NOUVEAU_BUFFER_STATUS_USER_MEMORY
))
97 nouveau_fence_ref(NULL
, &res
->fence
);
98 nouveau_fence_ref(NULL
, &res
->fence_wr
);
103 /* Maybe just migrate to GART right away if we actually need to do this. */
105 nouveau_buffer_download(struct nouveau_context
*nv
, struct nv04_resource
*buf
,
106 unsigned start
, unsigned size
)
108 struct nouveau_mm_allocation
*mm
;
109 struct nouveau_bo
*bounce
= NULL
;
112 assert(buf
->domain
== NOUVEAU_BO_VRAM
);
114 mm
= nouveau_mm_allocate(nv
->screen
->mm_GART
, size
, &bounce
, &offset
);
118 nv
->copy_data(nv
, bounce
, offset
, NOUVEAU_BO_GART
,
119 buf
->bo
, buf
->offset
+ start
, NOUVEAU_BO_VRAM
, size
);
121 if (nouveau_bo_map(bounce
, NOUVEAU_BO_RD
, nv
->screen
->client
))
123 memcpy(buf
->data
+ start
, (uint8_t *)bounce
->map
+ offset
, size
);
125 buf
->status
&= ~NOUVEAU_BUFFER_STATUS_GPU_WRITING
;
127 nouveau_bo_ref(NULL
, &bounce
);
134 nouveau_buffer_upload(struct nouveau_context
*nv
, struct nv04_resource
*buf
,
135 unsigned start
, unsigned size
)
137 struct nouveau_mm_allocation
*mm
;
138 struct nouveau_bo
*bounce
= NULL
;
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
));
146 nv
->push_data(nv
, buf
->bo
, buf
->offset
+ start
, buf
->domain
,
147 size
, buf
->data
+ start
);
151 mm
= nouveau_mm_allocate(nv
->screen
->mm_GART
, size
, &bounce
, &offset
);
155 nouveau_bo_map(bounce
, 0, nv
->screen
->client
);
156 memcpy((uint8_t *)bounce
->map
+ offset
, buf
->data
+ start
, size
);
158 nv
->copy_data(nv
, buf
->bo
, buf
->offset
+ start
, NOUVEAU_BO_VRAM
,
159 bounce
, offset
, NOUVEAU_BO_GART
, size
);
161 nouveau_bo_ref(NULL
, &bounce
);
163 release_allocation(&mm
, nv
->screen
->fence
.current
);
165 if (start
== 0 && size
== buf
->base
.width0
)
166 buf
->status
&= ~NOUVEAU_BUFFER_STATUS_GPU_WRITING
;
170 static struct pipe_transfer
*
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
)
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
);
182 xfr
->base
.resource
= resource
;
183 xfr
->base
.box
.x
= box
->x
;
184 xfr
->base
.box
.width
= box
->width
;
185 xfr
->base
.usage
= usage
;
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
);
198 nouveau_buffer_transfer_destroy(struct pipe_context
*pipe
,
199 struct pipe_transfer
*transfer
)
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
);
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
);
210 if (buf
->domain
!= 0 && (buf
->base
.bind
& (PIPE_BIND_VERTEX_BUFFER
|
211 PIPE_BIND_INDEX_BUFFER
)))
212 nouveau_context(pipe
)->vbo_dirty
= TRUE
;
218 static INLINE boolean
219 nouveau_buffer_sync(struct nv04_resource
*buf
, unsigned rw
)
221 if (rw
== PIPE_TRANSFER_READ
) {
224 if (!nouveau_fence_wait(buf
->fence_wr
))
229 if (!nouveau_fence_wait(buf
->fence
))
232 nouveau_fence_ref(NULL
, &buf
->fence
);
234 nouveau_fence_ref(NULL
, &buf
->fence_wr
);
239 static INLINE boolean
240 nouveau_buffer_busy(struct nv04_resource
*buf
, unsigned rw
)
242 if (rw
== PIPE_TRANSFER_READ
)
243 return (buf
->fence_wr
&& !nouveau_fence_signalled(buf
->fence_wr
));
245 return (buf
->fence
&& !nouveau_fence_signalled(buf
->fence
));
249 nouveau_buffer_transfer_map(struct pipe_context
*pipe
,
250 struct pipe_transfer
*transfer
)
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
;
258 uint32_t offset
= xfr
->base
.box
.x
;
261 if (buf
->domain
!= NOUVEAU_BO_GART
)
262 return buf
->data
+ offset
;
265 flags
= nouveau_screen_transfer_flags(xfr
->base
.usage
);
267 offset
+= buf
->offset
;
269 ret
= nouveau_bo_map(buf
->bo
, flags
, nv
->screen
->client
);
272 map
= (uint8_t *)bo
->map
+ offset
;
275 if (xfr
->base
.usage
& PIPE_TRANSFER_DONTBLOCK
) {
276 if (nouveau_buffer_busy(buf
, xfr
->base
.usage
& PIPE_TRANSFER_READ_WRITE
))
279 if (!(xfr
->base
.usage
& PIPE_TRANSFER_UNSYNCHRONIZED
)) {
280 nouveau_buffer_sync(buf
, xfr
->base
.usage
& PIPE_TRANSFER_READ_WRITE
);
289 nouveau_buffer_transfer_flush_region(struct pipe_context
*pipe
,
290 struct pipe_transfer
*transfer
,
291 const struct pipe_box
*box
)
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
;
298 /* not using non-snoop system memory yet, no need for cflush */
302 /* XXX: maybe need to upload for VRAM buffers here */
307 nouveau_buffer_transfer_unmap(struct pipe_context
*pipe
,
308 struct pipe_transfer
*transfer
)
314 nouveau_resource_map_offset(struct nouveau_context
*nv
,
315 struct nv04_resource
*res
, uint32_t offset
,
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
);
322 if ((res
->domain
!= NOUVEAU_BO_GART
) ||
323 (res
->status
& NOUVEAU_BUFFER_STATUS_USER_MEMORY
))
324 return res
->data
+ offset
;
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
))
333 if (nouveau_bo_map(res
->bo
, flags
, nv
->screen
->client
))
336 return (uint8_t *)res
->bo
->map
+ res
->offset
+ offset
;
340 const struct u_resource_vtbl nouveau_buffer_vtbl
=
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 */
352 struct pipe_resource
*
353 nouveau_buffer_create(struct pipe_screen
*pscreen
,
354 const struct pipe_resource
*templ
)
356 struct nouveau_screen
*screen
= nouveau_screen(pscreen
);
357 struct nv04_resource
*buffer
;
360 buffer
= CALLOC_STRUCT(nv04_resource
);
364 buffer
->base
= *templ
;
365 buffer
->vtbl
= &nouveau_buffer_vtbl
;
366 pipe_reference_init(&buffer
->base
.reference
, 1);
367 buffer
->base
.screen
= pscreen
;
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
;
377 case PIPE_USAGE_DYNAMIC
:
378 case PIPE_USAGE_STAGING
:
379 case PIPE_USAGE_STREAM
:
380 buffer
->domain
= NOUVEAU_BO_GART
;
387 if (buffer
->base
.bind
& screen
->vidmem_bindings
)
388 buffer
->domain
= NOUVEAU_BO_VRAM
;
390 if (buffer
->base
.bind
& screen
->sysmem_bindings
)
391 buffer
->domain
= NOUVEAU_BO_GART
;
393 ret
= nouveau_buffer_allocate(screen
, buffer
, buffer
->domain
);
398 return &buffer
->base
;
406 struct pipe_resource
*
407 nouveau_user_buffer_create(struct pipe_screen
*pscreen
, void *ptr
,
408 unsigned bytes
, unsigned bind
)
410 struct nv04_resource
*buffer
;
412 buffer
= CALLOC_STRUCT(nv04_resource
);
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;
427 buffer
->status
= NOUVEAU_BUFFER_STATUS_USER_MEMORY
;
429 return &buffer
->base
;
432 /* Like download, but for GART buffers. Merge ? */
433 static INLINE boolean
434 nouveau_buffer_data_fetch(struct nouveau_context
*nv
, struct nv04_resource
*buf
,
435 struct nouveau_bo
*bo
, unsigned offset
, unsigned size
)
438 buf
->data
= MALLOC(size
);
442 if (nouveau_bo_map(bo
, NOUVEAU_BO_RD
, nv
->screen
->client
))
444 memcpy(buf
->data
, (uint8_t *)bo
->map
+ offset
, size
);
449 /* Migrate a linear buffer (vertex, index, constants) USER -> GART -> VRAM. */
451 nouveau_buffer_migrate(struct nouveau_context
*nv
,
452 struct nv04_resource
*buf
, const unsigned new_domain
)
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
;
461 assert(new_domain
!= old_domain
);
463 if (new_domain
== NOUVEAU_BO_GART
&& old_domain
== 0) {
464 if (!nouveau_buffer_allocate(screen
, buf
, new_domain
))
466 ret
= nouveau_bo_map(buf
->bo
, 0, nv
->screen
->client
);
469 memcpy((uint8_t *)buf
->bo
->map
+ buf
->offset
, buf
->data
, size
);
472 if (old_domain
!= 0 && new_domain
!= 0) {
473 struct nouveau_mm_allocation
*mm
= buf
->mm
;
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
))
479 if (nouveau_mesa_debug
)
480 debug_printf("migrating %u KiB to VRAM\n", size
/ 1024);
483 offset
= buf
->offset
;
487 nouveau_buffer_allocate(screen
, buf
, new_domain
);
489 nv
->copy_data(nv
, buf
->bo
, buf
->offset
, new_domain
,
490 bo
, offset
, old_domain
, buf
->base
.width0
);
492 nouveau_bo_ref(NULL
, &bo
);
494 release_allocation(&mm
, screen
->fence
.current
);
496 if (new_domain
== NOUVEAU_BO_VRAM
&& old_domain
== 0) {
497 if (!nouveau_buffer_allocate(screen
, buf
, NOUVEAU_BO_VRAM
))
499 if (!nouveau_buffer_upload(nv
, buf
, 0, buf
->base
.width0
))
504 assert(buf
->domain
== new_domain
);
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 ...
513 nouveau_user_buffer_upload(struct nouveau_context
*nv
,
514 struct nv04_resource
*buf
,
515 unsigned base
, unsigned size
)
517 struct nouveau_screen
*screen
= nouveau_screen(buf
->base
.screen
);
520 assert(buf
->status
& NOUVEAU_BUFFER_STATUS_USER_MEMORY
);
522 buf
->base
.width0
= base
+ size
;
523 if (!nouveau_buffer_reallocate(screen
, buf
, NOUVEAU_BO_GART
))
526 ret
= nouveau_bo_map(buf
->bo
, 0, nv
->screen
->client
);
529 memcpy((uint8_t *)buf
->bo
->map
+ buf
->offset
+ base
, buf
->data
+ base
, size
);
535 /* Scratch data allocation. */
538 nouveau_scratch_bo_alloc(struct nouveau_context
*nv
, struct nouveau_bo
**pbo
,
541 return nouveau_bo_new(nv
->screen
->device
, NOUVEAU_BO_GART
| NOUVEAU_BO_MAP
,
542 4096, size
, NULL
, pbo
);
546 nouveau_scratch_runout_release(struct nouveau_context
*nv
)
548 if (!nv
->scratch
.nr_runout
)
551 --nv
->scratch
.nr_runout
;
552 nouveau_bo_ref(NULL
, &nv
->scratch
.runout
[nv
->scratch
.nr_runout
]);
553 } while (nv
->scratch
.nr_runout
);
555 FREE(nv
->scratch
.runout
);
557 nv
->scratch
.runout
= NULL
;
560 /* Allocate an extra bo if we can't fit everything we need simultaneously.
561 * (Could happen for very large user arrays.)
563 static INLINE boolean
564 nouveau_scratch_runout(struct nouveau_context
*nv
, unsigned size
)
567 const unsigned n
= nv
->scratch
.nr_runout
++;
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
;
574 ret
= nouveau_scratch_bo_alloc(nv
, &nv
->scratch
.runout
[n
], size
);
576 ret
= nouveau_bo_map(nv
->scratch
.runout
[n
], 0, NULL
);
578 nouveau_bo_ref(NULL
, &nv
->scratch
.runout
[--nv
->scratch
.nr_runout
]);
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
;
589 /* Continue to next scratch buffer, if available (no wrapping, large enough).
590 * Allocate it if it has not yet been created.
592 static INLINE boolean
593 nouveau_scratch_next(struct nouveau_context
*nv
, unsigned size
)
595 struct nouveau_bo
*bo
;
597 const unsigned i
= (nv
->scratch
.id
+ 1) % NOUVEAU_MAX_SCRATCH_BUFS
;
599 if ((size
> nv
->scratch
.bo_size
) || (i
== nv
->scratch
.wrap
))
603 bo
= nv
->scratch
.bo
[i
];
605 ret
= nouveau_scratch_bo_alloc(nv
, &bo
, nv
->scratch
.bo_size
);
608 nv
->scratch
.bo
[i
] = bo
;
610 nv
->scratch
.current
= bo
;
611 nv
->scratch
.offset
= 0;
612 nv
->scratch
.end
= nv
->scratch
.bo_size
;
614 ret
= nouveau_bo_map(bo
, NOUVEAU_BO_WR
, nv
->screen
->client
);
616 nv
->scratch
.map
= bo
->map
;
621 nouveau_scratch_more(struct nouveau_context
*nv
, unsigned min_size
)
625 ret
= nouveau_scratch_next(nv
, min_size
);
627 ret
= nouveau_scratch_runout(nv
, min_size
);
632 /* Copy data to a scratch buffer and return address & bo the data resides in. */
634 nouveau_scratch_data(struct nouveau_context
*nv
,
635 const void *data
, unsigned base
, unsigned size
,
636 struct nouveau_bo
**bo
)
638 unsigned bgn
= MAX2(base
, nv
->scratch
.offset
);
639 unsigned end
= bgn
+ size
;
641 if (end
>= nv
->scratch
.end
) {
643 if (!nouveau_scratch_more(nv
, end
))
647 nv
->scratch
.offset
= align(end
, 4);
649 memcpy(nv
->scratch
.map
+ bgn
, (const uint8_t *)data
+ base
, size
);
651 *bo
= nv
->scratch
.current
;
652 return (*bo
)->offset
+ (bgn
- base
);
656 nouveau_scratch_get(struct nouveau_context
*nv
,
657 unsigned size
, uint64_t *gpu_addr
, struct nouveau_bo
**pbo
)
659 unsigned bgn
= nv
->scratch
.offset
;
660 unsigned end
= nv
->scratch
.offset
+ size
;
662 if (end
>= nv
->scratch
.end
) {
664 if (!nouveau_scratch_more(nv
, end
))
668 nv
->scratch
.offset
= align(end
, 4);
670 *pbo
= nv
->scratch
.current
;
671 *gpu_addr
= nv
->scratch
.current
->offset
+ bgn
;
672 return nv
->scratch
.map
+ bgn
;