Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4358 | Serge | 1 | |
2 | #include "util/u_memory.h" |
||
3 | #include "util/u_math.h" |
||
4 | #include "util/u_surface.h" |
||
5 | |||
6 | |||
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 | |||
14 | |||
15 | |||
16 | struct pipe_transfer base; |
||
17 | |||
18 | |||
19 | struct nouveau_bo *bo; |
||
20 | struct nouveau_mm_allocation *mm; |
||
21 | uint32_t offset; |
||
22 | }; |
||
23 | |||
24 | |||
25 | nouveau_transfer(struct pipe_transfer *transfer) |
||
26 | { |
||
27 | return (struct nouveau_transfer *)transfer; |
||
28 | } |
||
29 | |||
30 | |||
31 | nouveau_buffer_malloc(struct nv04_resource *buf) |
||
32 | { |
||
33 | if (!buf->data) |
||
34 | buf->data = align_malloc(buf->base.width0, NOUVEAU_MIN_BUFFER_MAP_ALIGN); |
||
35 | return !!buf->data; |
||
36 | } |
||
37 | |||
38 | |||
39 | nouveau_buffer_allocate(struct nouveau_screen *screen, |
||
40 | struct nv04_resource *buf, unsigned domain) |
||
41 | { |
||
42 | uint32_t size = buf->base.width0; |
||
43 | |||
44 | |||
45 | PIPE_BIND_COMPUTE_RESOURCE | |
||
46 | PIPE_BIND_SHADER_RESOURCE)) |
||
47 | size = align(size, 0x100); |
||
48 | |||
49 | |||
50 | buf->mm = nouveau_mm_allocate(screen->mm_VRAM, size, |
||
51 | &buf->bo, &buf->offset); |
||
52 | if (!buf->bo) |
||
53 | return nouveau_buffer_allocate(screen, buf, NOUVEAU_BO_GART); |
||
54 | NOUVEAU_DRV_STAT(screen, buf_obj_current_bytes_vid, buf->base.width0); |
||
55 | } else |
||
56 | if (domain == NOUVEAU_BO_GART) { |
||
57 | buf->mm = nouveau_mm_allocate(screen->mm_GART, size, |
||
58 | &buf->bo, &buf->offset); |
||
59 | if (!buf->bo) |
||
60 | return FALSE; |
||
61 | NOUVEAU_DRV_STAT(screen, buf_obj_current_bytes_sys, buf->base.width0); |
||
62 | } else { |
||
63 | assert(domain == 0); |
||
64 | if (!nouveau_buffer_malloc(buf)) |
||
65 | return FALSE; |
||
66 | } |
||
67 | buf->domain = domain; |
||
68 | if (buf->bo) |
||
69 | buf->address = buf->bo->offset + buf->offset; |
||
70 | |||
71 | |||
72 | } |
||
73 | |||
74 | |||
75 | release_allocation(struct nouveau_mm_allocation **mm, |
||
76 | struct nouveau_fence *fence) |
||
77 | { |
||
78 | nouveau_fence_work(fence, nouveau_mm_free_work, *mm); |
||
79 | (*mm) = NULL; |
||
80 | } |
||
81 | |||
82 | |||
83 | nouveau_buffer_release_gpu_storage(struct nv04_resource *buf) |
||
84 | { |
||
85 | nouveau_bo_ref(NULL, &buf->bo); |
||
86 | |||
87 | |||
88 | release_allocation(&buf->mm, buf->fence); |
||
89 | |||
90 | |||
91 | NOUVEAU_DRV_STAT_RES(buf, buf_obj_current_bytes_vid, -(uint64_t)buf->base.width0); |
||
92 | if (buf->domain == NOUVEAU_BO_GART) |
||
93 | NOUVEAU_DRV_STAT_RES(buf, buf_obj_current_bytes_sys, -(uint64_t)buf->base.width0); |
||
94 | |||
95 | |||
96 | } |
||
97 | |||
98 | |||
99 | nouveau_buffer_reallocate(struct nouveau_screen *screen, |
||
100 | struct nv04_resource *buf, unsigned domain) |
||
101 | { |
||
102 | nouveau_buffer_release_gpu_storage(buf); |
||
103 | |||
104 | |||
105 | nouveau_fence_ref(NULL, &buf->fence_wr); |
||
106 | |||
107 | |||
108 | |||
109 | |||
110 | } |
||
111 | |||
112 | |||
113 | nouveau_buffer_destroy(struct pipe_screen *pscreen, |
||
114 | struct pipe_resource *presource) |
||
115 | { |
||
116 | struct nv04_resource *res = nv04_resource(presource); |
||
117 | |||
118 | |||
119 | |||
120 | |||
121 | align_free(res->data); |
||
122 | |||
123 | |||
124 | nouveau_fence_ref(NULL, &res->fence_wr); |
||
125 | |||
126 | |||
127 | |||
128 | |||
129 | } |
||
130 | |||
131 | |||
132 | nouveau_transfer_staging(struct nouveau_context *nv, |
||
133 | struct nouveau_transfer *tx, boolean permit_pb) |
||
134 | { |
||
135 | const unsigned adj = tx->base.box.x & NOUVEAU_MIN_BUFFER_MAP_ALIGN_MASK; |
||
136 | const unsigned size = align(tx->base.box.width, 4) + adj; |
||
137 | |||
138 | |||
139 | permit_pb = FALSE; |
||
140 | |||
141 | |||
142 | tx->map = align_malloc(size, NOUVEAU_MIN_BUFFER_MAP_ALIGN); |
||
143 | if (tx->map) |
||
144 | tx->map += adj; |
||
145 | } else { |
||
146 | tx->mm = |
||
147 | nouveau_mm_allocate(nv->screen->mm_GART, size, &tx->bo, &tx->offset); |
||
148 | if (tx->bo) { |
||
149 | tx->offset += adj; |
||
150 | if (!nouveau_bo_map(tx->bo, 0, NULL)) |
||
151 | tx->map = (uint8_t *)tx->bo->map + tx->offset; |
||
152 | } |
||
153 | } |
||
154 | return tx->map; |
||
155 | } |
||
156 | |||
157 | |||
158 | static boolean |
||
159 | nouveau_transfer_read(struct nouveau_context *nv, struct nouveau_transfer *tx) |
||
160 | { |
||
161 | struct nv04_resource *buf = nv04_resource(tx->base.resource); |
||
162 | const unsigned base = tx->base.box.x; |
||
163 | const unsigned size = tx->base.box.width; |
||
164 | |||
165 | |||
166 | |||
167 | |||
168 | buf->bo, buf->offset + base, buf->domain, size); |
||
169 | |||
170 | |||
171 | return FALSE; |
||
172 | |||
173 | |||
174 | memcpy(buf->data + base, tx->map, size); |
||
175 | |||
176 | |||
177 | } |
||
178 | |||
179 | |||
180 | nouveau_transfer_write(struct nouveau_context *nv, struct nouveau_transfer *tx, |
||
181 | unsigned offset, unsigned size) |
||
182 | { |
||
183 | struct nv04_resource *buf = nv04_resource(tx->base.resource); |
||
184 | uint8_t *data = tx->map + offset; |
||
185 | const unsigned base = tx->base.box.x + offset; |
||
186 | const boolean can_cb = !((base | size) & 3); |
||
187 | |||
188 | |||
189 | memcpy(data, buf->data + base, size); |
||
190 | else |
||
191 | buf->status |= NOUVEAU_BUFFER_STATUS_DIRTY; |
||
192 | |||
193 | |||
194 | NOUVEAU_DRV_STAT(nv->screen, buf_write_bytes_staging_vid, size); |
||
195 | if (buf->domain == NOUVEAU_BO_GART) |
||
196 | NOUVEAU_DRV_STAT(nv->screen, buf_write_bytes_staging_sys, size); |
||
197 | |||
198 | |||
199 | nv->copy_data(nv, buf->bo, buf->offset + base, buf->domain, |
||
200 | tx->bo, tx->offset + offset, NOUVEAU_BO_GART, size); |
||
201 | else |
||
202 | if ((buf->base.bind & PIPE_BIND_CONSTANT_BUFFER) && nv->push_cb && can_cb) |
||
203 | nv->push_cb(nv, buf->bo, buf->domain, buf->offset, buf->base.width0, |
||
204 | base, size / 4, (const uint32_t *)data); |
||
205 | else |
||
206 | nv->push_data(nv, buf->bo, buf->offset + base, buf->domain, size, data); |
||
207 | } |
||
208 | |||
209 | |||
210 | |||
211 | nouveau_buffer_sync(struct nv04_resource *buf, unsigned rw) |
||
212 | { |
||
213 | if (rw == PIPE_TRANSFER_READ) { |
||
214 | if (!buf->fence_wr) |
||
215 | return TRUE; |
||
216 | NOUVEAU_DRV_STAT_RES(buf, buf_non_kernel_fence_sync_count, |
||
217 | !nouveau_fence_signalled(buf->fence_wr)); |
||
218 | if (!nouveau_fence_wait(buf->fence_wr)) |
||
219 | return FALSE; |
||
220 | } else { |
||
221 | if (!buf->fence) |
||
222 | return TRUE; |
||
223 | NOUVEAU_DRV_STAT_RES(buf, buf_non_kernel_fence_sync_count, |
||
224 | !nouveau_fence_signalled(buf->fence)); |
||
225 | if (!nouveau_fence_wait(buf->fence)) |
||
226 | return FALSE; |
||
227 | |||
228 | |||
229 | } |
||
230 | nouveau_fence_ref(NULL, &buf->fence_wr); |
||
231 | |||
232 | |||
233 | } |
||
234 | |||
235 | |||
236 | nouveau_buffer_busy(struct nv04_resource *buf, unsigned rw) |
||
237 | { |
||
238 | if (rw == PIPE_TRANSFER_READ) |
||
239 | return (buf->fence_wr && !nouveau_fence_signalled(buf->fence_wr)); |
||
240 | else |
||
241 | return (buf->fence && !nouveau_fence_signalled(buf->fence)); |
||
242 | } |
||
243 | |||
244 | |||
245 | nouveau_buffer_transfer_init(struct nouveau_transfer *tx, |
||
246 | struct pipe_resource *resource, |
||
247 | const struct pipe_box *box, |
||
248 | unsigned usage) |
||
249 | { |
||
250 | tx->base.resource = resource; |
||
251 | tx->base.level = 0; |
||
252 | tx->base.usage = usage; |
||
253 | tx->base.box.x = box->x; |
||
254 | tx->base.box.y = 0; |
||
255 | tx->base.box.z = 0; |
||
256 | tx->base.box.width = box->width; |
||
257 | tx->base.box.height = 1; |
||
258 | tx->base.box.depth = 1; |
||
259 | tx->base.stride = 0; |
||
260 | tx->base.layer_stride = 0; |
||
261 | |||
262 | |||
263 | tx->map = NULL; |
||
264 | } |
||
265 | |||
266 | |||
267 | nouveau_buffer_transfer_del(struct nouveau_context *nv, |
||
268 | struct nouveau_transfer *tx) |
||
269 | { |
||
270 | if (tx->map) { |
||
271 | if (likely(tx->bo)) { |
||
272 | nouveau_bo_ref(NULL, &tx->bo); |
||
273 | if (tx->mm) |
||
274 | release_allocation(&tx->mm, nv->screen->fence.current); |
||
275 | } else { |
||
276 | align_free(tx->map - |
||
277 | (tx->base.box.x & NOUVEAU_MIN_BUFFER_MAP_ALIGN_MASK)); |
||
278 | } |
||
279 | } |
||
280 | } |
||
281 | |||
282 | |||
283 | nouveau_buffer_cache(struct nouveau_context *nv, struct nv04_resource *buf) |
||
284 | { |
||
285 | struct nouveau_transfer tx; |
||
286 | boolean ret; |
||
287 | tx.base.resource = &buf->base; |
||
288 | tx.base.box.x = 0; |
||
289 | tx.base.box.width = buf->base.width0; |
||
290 | tx.bo = NULL; |
||
291 | tx.map = NULL; |
||
292 | |||
293 | |||
294 | if (!nouveau_buffer_malloc(buf)) |
||
295 | return FALSE; |
||
296 | if (!(buf->status & NOUVEAU_BUFFER_STATUS_DIRTY)) |
||
297 | return TRUE; |
||
298 | nv->stats.buf_cache_count++; |
||
299 | |||
300 | |||
301 | return FALSE; |
||
302 | |||
303 | |||
304 | if (ret) { |
||
305 | buf->status &= ~NOUVEAU_BUFFER_STATUS_DIRTY; |
||
306 | memcpy(buf->data, tx.map, buf->base.width0); |
||
307 | } |
||
308 | nouveau_buffer_transfer_del(nv, &tx); |
||
309 | return ret; |
||
310 | } |
||
311 | |||
312 | |||
313 | |||
314 | (PIPE_TRANSFER_DISCARD_RANGE | PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE) |
||
315 | |||
316 | |||
317 | nouveau_buffer_should_discard(struct nv04_resource *buf, unsigned usage) |
||
318 | { |
||
319 | if (!(usage & PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE)) |
||
320 | return FALSE; |
||
321 | if (unlikely(buf->base.bind & PIPE_BIND_SHARED)) |
||
322 | return FALSE; |
||
323 | return buf->mm && nouveau_buffer_busy(buf, PIPE_TRANSFER_WRITE); |
||
324 | } |
||
325 | |||
326 | |||
327 | nouveau_buffer_transfer_map(struct pipe_context *pipe, |
||
328 | struct pipe_resource *resource, |
||
329 | unsigned level, unsigned usage, |
||
330 | const struct pipe_box *box, |
||
331 | struct pipe_transfer **ptransfer) |
||
332 | { |
||
333 | struct nouveau_context *nv = nouveau_context(pipe); |
||
334 | struct nv04_resource *buf = nv04_resource(resource); |
||
335 | struct nouveau_transfer *tx = MALLOC_STRUCT(nouveau_transfer); |
||
336 | uint8_t *map; |
||
337 | int ret; |
||
338 | |||
339 | |||
340 | return NULL; |
||
341 | nouveau_buffer_transfer_init(tx, resource, box, usage); |
||
342 | *ptransfer = &tx->base; |
||
343 | |||
344 | |||
345 | NOUVEAU_DRV_STAT(nv->screen, buf_transfers_rd, 1); |
||
346 | if (usage & PIPE_TRANSFER_WRITE) |
||
347 | NOUVEAU_DRV_STAT(nv->screen, buf_transfers_wr, 1); |
||
348 | |||
349 | |||
350 | if (usage & NOUVEAU_TRANSFER_DISCARD) { |
||
351 | if (usage & PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE) |
||
352 | buf->status &= NOUVEAU_BUFFER_STATUS_REALLOC_MASK; |
||
353 | nouveau_transfer_staging(nv, tx, TRUE); |
||
354 | } else { |
||
355 | if (buf->status & NOUVEAU_BUFFER_STATUS_GPU_WRITING) { |
||
356 | if (buf->data) { |
||
357 | align_free(buf->data); |
||
358 | buf->data = NULL; |
||
359 | } |
||
360 | nouveau_transfer_staging(nv, tx, FALSE); |
||
361 | nouveau_transfer_read(nv, tx); |
||
362 | } else { |
||
363 | if (usage & PIPE_TRANSFER_WRITE) |
||
364 | nouveau_transfer_staging(nv, tx, TRUE); |
||
365 | if (!buf->data) |
||
366 | nouveau_buffer_cache(nv, buf); |
||
367 | } |
||
368 | } |
||
369 | return buf->data ? (buf->data + box->x) : tx->map; |
||
370 | } else |
||
371 | if (unlikely(buf->domain == 0)) { |
||
372 | return buf->data + box->x; |
||
373 | } |
||
374 | |||
375 | |||
376 | int ref = buf->base.reference.count - 1; |
||
377 | nouveau_buffer_reallocate(nv->screen, buf, buf->domain); |
||
378 | if (ref > 0) /* any references inside context possible ? */ |
||
379 | nv->invalidate_resource_storage(nv, &buf->base, ref); |
||
380 | } |
||
381 | |||
382 | |||
383 | buf->mm ? 0 : nouveau_screen_transfer_flags(usage), |
||
384 | nv->client); |
||
385 | if (ret) { |
||
386 | FREE(tx); |
||
387 | return NULL; |
||
388 | } |
||
389 | map = (uint8_t *)buf->bo->map + buf->offset + box->x; |
||
390 | |||
391 | |||
392 | if ((usage & PIPE_TRANSFER_UNSYNCHRONIZED) || !buf->mm) |
||
393 | return map; |
||
394 | |||
395 | |||
396 | if (unlikely(usage & PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE)) { |
||
397 | /* Discarding was not possible, must sync because |
||
398 | * subsequent transfers might use UNSYNCHRONIZED. */ |
||
399 | nouveau_buffer_sync(buf, usage & PIPE_TRANSFER_READ_WRITE); |
||
400 | } else |
||
401 | if (usage & PIPE_TRANSFER_DISCARD_RANGE) { |
||
402 | nouveau_transfer_staging(nv, tx, TRUE); |
||
403 | map = tx->map; |
||
404 | } else |
||
405 | if (nouveau_buffer_busy(buf, PIPE_TRANSFER_READ)) { |
||
406 | if (usage & PIPE_TRANSFER_DONTBLOCK) |
||
407 | map = NULL; |
||
408 | else |
||
409 | nouveau_buffer_sync(buf, usage & PIPE_TRANSFER_READ_WRITE); |
||
410 | } else { |
||
411 | nouveau_transfer_staging(nv, tx, TRUE); |
||
412 | if (tx->map) |
||
413 | memcpy(tx->map, map, box->width); |
||
414 | map = tx->map; |
||
415 | } |
||
416 | } |
||
417 | if (!map) |
||
418 | FREE(tx); |
||
419 | return map; |
||
420 | } |
||
421 | |||
422 | |||
423 | |||
424 | |||
425 | nouveau_buffer_transfer_flush_region(struct pipe_context *pipe, |
||
426 | struct pipe_transfer *transfer, |
||
427 | const struct pipe_box *box) |
||
428 | { |
||
429 | struct nouveau_transfer *tx = nouveau_transfer(transfer); |
||
430 | if (tx->map) |
||
431 | nouveau_transfer_write(nouveau_context(pipe), tx, box->x, box->width); |
||
432 | } |
||
433 | |||
434 | |||
435 | nouveau_buffer_transfer_unmap(struct pipe_context *pipe, |
||
436 | struct pipe_transfer *transfer) |
||
437 | { |
||
438 | struct nouveau_context *nv = nouveau_context(pipe); |
||
439 | struct nouveau_transfer *tx = nouveau_transfer(transfer); |
||
440 | struct nv04_resource *buf = nv04_resource(transfer->resource); |
||
441 | |||
442 | |||
443 | if (!(tx->base.usage & PIPE_TRANSFER_FLUSH_EXPLICIT) && tx->map) |
||
444 | nouveau_transfer_write(nv, tx, 0, tx->base.box.width); |
||
445 | |||
446 | |||
447 | const uint8_t bind = buf->base.bind; |
||
448 | /* make sure we invalidate dedicated caches */ |
||
449 | if (bind & (PIPE_BIND_VERTEX_BUFFER | PIPE_BIND_INDEX_BUFFER)) |
||
450 | nv->vbo_dirty = TRUE; |
||
451 | if (bind & (PIPE_BIND_CONSTANT_BUFFER)) |
||
452 | nv->cb_dirty = TRUE; |
||
453 | } |
||
454 | } |
||
455 | |||
456 | |||
457 | NOUVEAU_DRV_STAT(nv->screen, buf_write_bytes_direct, tx->base.box.width); |
||
458 | |||
459 | |||
460 | FREE(tx); |
||
461 | } |
||
462 | |||
463 | |||
464 | |||
465 | nouveau_copy_buffer(struct nouveau_context *nv, |
||
466 | struct nv04_resource *dst, unsigned dstx, |
||
467 | struct nv04_resource *src, unsigned srcx, unsigned size) |
||
468 | { |
||
469 | assert(dst->base.target == PIPE_BUFFER && src->base.target == PIPE_BUFFER); |
||
470 | |||
471 | |||
472 | nv->copy_data(nv, |
||
473 | dst->bo, dst->offset + dstx, dst->domain, |
||
474 | src->bo, src->offset + srcx, src->domain, size); |
||
475 | |||
476 | |||
477 | nouveau_fence_ref(nv->screen->fence.current, &dst->fence); |
||
478 | nouveau_fence_ref(nv->screen->fence.current, &dst->fence_wr); |
||
479 | |||
480 | |||
481 | nouveau_fence_ref(nv->screen->fence.current, &src->fence); |
||
482 | } else { |
||
483 | struct pipe_box src_box; |
||
484 | src_box.x = srcx; |
||
485 | src_box.y = 0; |
||
486 | src_box.z = 0; |
||
487 | src_box.width = size; |
||
488 | src_box.height = 1; |
||
489 | src_box.depth = 1; |
||
490 | util_resource_copy_region(&nv->pipe, |
||
491 | &dst->base, 0, dstx, 0, 0, |
||
492 | &src->base, 0, &src_box); |
||
493 | } |
||
494 | } |
||
495 | |||
496 | |||
497 | |||
498 | nouveau_resource_map_offset(struct nouveau_context *nv, |
||
499 | struct nv04_resource *res, uint32_t offset, |
||
500 | uint32_t flags) |
||
501 | { |
||
502 | if (unlikely(res->status & NOUVEAU_BUFFER_STATUS_USER_MEMORY)) |
||
503 | return res->data + offset; |
||
504 | |||
505 | |||
506 | if (!res->data || (res->status & NOUVEAU_BUFFER_STATUS_GPU_WRITING)) |
||
507 | nouveau_buffer_cache(nv, res); |
||
508 | } |
||
509 | if (res->domain != NOUVEAU_BO_GART) |
||
510 | return res->data + offset; |
||
511 | |||
512 | |||
513 | unsigned rw; |
||
514 | rw = (flags & NOUVEAU_BO_WR) ? PIPE_TRANSFER_WRITE : PIPE_TRANSFER_READ; |
||
515 | nouveau_buffer_sync(res, rw); |
||
516 | if (nouveau_bo_map(res->bo, 0, NULL)) |
||
517 | return NULL; |
||
518 | } else { |
||
519 | if (nouveau_bo_map(res->bo, flags, nv->client)) |
||
520 | return NULL; |
||
521 | } |
||
522 | return (uint8_t *)res->bo->map + res->offset + offset; |
||
523 | } |
||
524 | |||
525 | |||
526 | |||
527 | { |
||
528 | u_default_resource_get_handle, /* get_handle */ |
||
529 | nouveau_buffer_destroy, /* resource_destroy */ |
||
530 | nouveau_buffer_transfer_map, /* transfer_map */ |
||
531 | nouveau_buffer_transfer_flush_region, /* transfer_flush_region */ |
||
532 | nouveau_buffer_transfer_unmap, /* transfer_unmap */ |
||
533 | u_default_transfer_inline_write /* transfer_inline_write */ |
||
534 | }; |
||
535 | |||
536 | |||
537 | nouveau_buffer_create(struct pipe_screen *pscreen, |
||
538 | const struct pipe_resource *templ) |
||
539 | { |
||
540 | struct nouveau_screen *screen = nouveau_screen(pscreen); |
||
541 | struct nv04_resource *buffer; |
||
542 | boolean ret; |
||
543 | |||
544 | |||
545 | if (!buffer) |
||
546 | return NULL; |
||
547 | |||
548 | |||
549 | buffer->vtbl = &nouveau_buffer_vtbl; |
||
550 | pipe_reference_init(&buffer->base.reference, 1); |
||
551 | buffer->base.screen = pscreen; |
||
552 | |||
553 | |||
554 | (screen->vidmem_bindings & screen->sysmem_bindings)) { |
||
555 | switch (buffer->base.usage) { |
||
556 | case PIPE_USAGE_DEFAULT: |
||
557 | case PIPE_USAGE_IMMUTABLE: |
||
558 | case PIPE_USAGE_STATIC: |
||
559 | buffer->domain = NOUVEAU_BO_VRAM; |
||
560 | break; |
||
561 | case PIPE_USAGE_DYNAMIC: |
||
562 | /* For most apps, we'd have to do staging transfers to avoid sync |
||
563 | * with this usage, and GART -> GART copies would be suboptimal. |
||
564 | */ |
||
565 | buffer->domain = NOUVEAU_BO_VRAM; |
||
566 | break; |
||
567 | case PIPE_USAGE_STAGING: |
||
568 | case PIPE_USAGE_STREAM: |
||
569 | buffer->domain = NOUVEAU_BO_GART; |
||
570 | break; |
||
571 | default: |
||
572 | assert(0); |
||
573 | break; |
||
574 | } |
||
575 | } else { |
||
576 | if (buffer->base.bind & screen->vidmem_bindings) |
||
577 | buffer->domain = NOUVEAU_BO_VRAM; |
||
578 | else |
||
579 | if (buffer->base.bind & screen->sysmem_bindings) |
||
580 | buffer->domain = NOUVEAU_BO_GART; |
||
581 | } |
||
582 | ret = nouveau_buffer_allocate(screen, buffer, buffer->domain); |
||
583 | |||
584 | |||
585 | goto fail; |
||
586 | |||
587 | |||
588 | nouveau_buffer_cache(NULL, buffer); |
||
589 | |||
590 | |||
591 | |||
592 | |||
593 | |||
594 | |||
595 | FREE(buffer); |
||
596 | return NULL; |
||
597 | } |
||
598 | |||
599 | |||
600 | |||
601 | nouveau_user_buffer_create(struct pipe_screen *pscreen, void *ptr, |
||
602 | unsigned bytes, unsigned bind) |
||
603 | { |
||
604 | struct nv04_resource *buffer; |
||
605 | |||
606 | |||
607 | if (!buffer) |
||
608 | return NULL; |
||
609 | |||
610 | |||
611 | buffer->vtbl = &nouveau_buffer_vtbl; |
||
612 | buffer->base.screen = pscreen; |
||
613 | buffer->base.format = PIPE_FORMAT_R8_UNORM; |
||
614 | buffer->base.usage = PIPE_USAGE_IMMUTABLE; |
||
615 | buffer->base.bind = bind; |
||
616 | buffer->base.width0 = bytes; |
||
617 | buffer->base.height0 = 1; |
||
618 | buffer->base.depth0 = 1; |
||
619 | |||
620 | |||
621 | buffer->status = NOUVEAU_BUFFER_STATUS_USER_MEMORY; |
||
622 | |||
623 | |||
624 | } |
||
625 | |||
626 | |||
627 | nouveau_buffer_data_fetch(struct nouveau_context *nv, struct nv04_resource *buf, |
||
628 | struct nouveau_bo *bo, unsigned offset, unsigned size) |
||
629 | { |
||
630 | if (!nouveau_buffer_malloc(buf)) |
||
631 | return FALSE; |
||
632 | if (nouveau_bo_map(bo, NOUVEAU_BO_RD, nv->client)) |
||
633 | return FALSE; |
||
634 | memcpy(buf->data, (uint8_t *)bo->map + offset, size); |
||
635 | return TRUE; |
||
636 | } |
||
637 | |||
638 | |||
639 | boolean |
||
640 | nouveau_buffer_migrate(struct nouveau_context *nv, |
||
641 | struct nv04_resource *buf, const unsigned new_domain) |
||
642 | { |
||
643 | struct nouveau_screen *screen = nv->screen; |
||
644 | struct nouveau_bo *bo; |
||
645 | const unsigned old_domain = buf->domain; |
||
646 | unsigned size = buf->base.width0; |
||
647 | unsigned offset; |
||
648 | int ret; |
||
649 | |||
650 | |||
651 | |||
652 | |||
653 | if (!nouveau_buffer_allocate(screen, buf, new_domain)) |
||
654 | return FALSE; |
||
655 | ret = nouveau_bo_map(buf->bo, 0, nv->client); |
||
656 | if (ret) |
||
657 | return ret; |
||
658 | memcpy((uint8_t *)buf->bo->map + buf->offset, buf->data, size); |
||
659 | align_free(buf->data); |
||
660 | } else |
||
661 | if (old_domain != 0 && new_domain != 0) { |
||
662 | struct nouveau_mm_allocation *mm = buf->mm; |
||
663 | |||
664 | |||
665 | /* keep a system memory copy of our data in case we hit a fallback */ |
||
666 | if (!nouveau_buffer_data_fetch(nv, buf, buf->bo, buf->offset, size)) |
||
667 | return FALSE; |
||
668 | if (nouveau_mesa_debug) |
||
669 | debug_printf("migrating %u KiB to VRAM\n", size / 1024); |
||
670 | } |
||
671 | |||
672 | |||
673 | bo = buf->bo; |
||
674 | buf->bo = NULL; |
||
675 | buf->mm = NULL; |
||
676 | nouveau_buffer_allocate(screen, buf, new_domain); |
||
677 | |||
678 | |||
679 | bo, offset, old_domain, buf->base.width0); |
||
680 | |||
681 | |||
682 | if (mm) |
||
683 | release_allocation(&mm, screen->fence.current); |
||
684 | } else |
||
685 | if (new_domain == NOUVEAU_BO_VRAM && old_domain == 0) { |
||
686 | struct nouveau_transfer tx; |
||
687 | if (!nouveau_buffer_allocate(screen, buf, NOUVEAU_BO_VRAM)) |
||
688 | return FALSE; |
||
689 | tx.base.resource = &buf->base; |
||
690 | tx.base.box.x = 0; |
||
691 | tx.base.box.width = buf->base.width0; |
||
692 | tx.bo = NULL; |
||
693 | tx.map = NULL; |
||
694 | if (!nouveau_transfer_staging(nv, &tx, FALSE)) |
||
695 | return FALSE; |
||
696 | nouveau_transfer_write(nv, &tx, 0, tx.base.box.width); |
||
697 | nouveau_buffer_transfer_del(nv, &tx); |
||
698 | } else |
||
699 | return FALSE; |
||
700 | |||
701 | |||
702 | return TRUE; |
||
703 | } |
||
704 | |||
705 | |||
706 | * We'd like to only allocate @size bytes here, but then we'd have to rebase |
||
707 | * the vertex indices ... |
||
708 | */ |
||
709 | boolean |
||
710 | nouveau_user_buffer_upload(struct nouveau_context *nv, |
||
711 | struct nv04_resource *buf, |
||
712 | unsigned base, unsigned size) |
||
713 | { |
||
714 | struct nouveau_screen *screen = nouveau_screen(buf->base.screen); |
||
715 | int ret; |
||
716 | |||
717 | |||
718 | |||
719 | |||
720 | if (!nouveau_buffer_reallocate(screen, buf, NOUVEAU_BO_GART)) |
||
721 | return FALSE; |
||
722 | |||
723 | |||
724 | if (ret) |
||
725 | return FALSE; |
||
726 | memcpy((uint8_t *)buf->bo->map + buf->offset + base, buf->data + base, size); |
||
727 | |||
728 | |||
729 | } |
||
730 | |||
731 | |||
732 | |||
733 | |||
734 | |||
735 | nouveau_scratch_bo_alloc(struct nouveau_context *nv, struct nouveau_bo **pbo, |
||
736 | unsigned size) |
||
737 | { |
||
738 | return nouveau_bo_new(nv->screen->device, NOUVEAU_BO_GART | NOUVEAU_BO_MAP, |
||
739 | 4096, size, NULL, pbo); |
||
740 | } |
||
741 | |||
742 | |||
743 | nouveau_scratch_runout_release(struct nouveau_context *nv) |
||
744 | { |
||
745 | if (!nv->scratch.nr_runout) |
||
746 | return; |
||
747 | do { |
||
748 | --nv->scratch.nr_runout; |
||
749 | nouveau_bo_ref(NULL, &nv->scratch.runout[nv->scratch.nr_runout]); |
||
750 | } while (nv->scratch.nr_runout); |
||
751 | |||
752 | |||
753 | nv->scratch.end = 0; |
||
754 | nv->scratch.runout = NULL; |
||
755 | } |
||
756 | |||
757 | |||
758 | * (Could happen for very large user arrays.) |
||
759 | */ |
||
760 | static INLINE boolean |
||
761 | nouveau_scratch_runout(struct nouveau_context *nv, unsigned size) |
||
762 | { |
||
763 | int ret; |
||
764 | const unsigned n = nv->scratch.nr_runout++; |
||
765 | |||
766 | |||
767 | (n + 0) * sizeof(*nv->scratch.runout), |
||
768 | (n + 1) * sizeof(*nv->scratch.runout)); |
||
769 | nv->scratch.runout[n] = NULL; |
||
770 | |||
771 | |||
772 | if (!ret) { |
||
773 | ret = nouveau_bo_map(nv->scratch.runout[n], 0, NULL); |
||
774 | if (ret) |
||
775 | nouveau_bo_ref(NULL, &nv->scratch.runout[--nv->scratch.nr_runout]); |
||
776 | } |
||
777 | if (!ret) { |
||
778 | nv->scratch.current = nv->scratch.runout[n]; |
||
779 | nv->scratch.offset = 0; |
||
780 | nv->scratch.end = size; |
||
781 | nv->scratch.map = nv->scratch.current->map; |
||
782 | } |
||
783 | return !ret; |
||
784 | } |
||
785 | |||
786 | |||
787 | * Allocate it if it has not yet been created. |
||
788 | */ |
||
789 | static INLINE boolean |
||
790 | nouveau_scratch_next(struct nouveau_context *nv, unsigned size) |
||
791 | { |
||
792 | struct nouveau_bo *bo; |
||
793 | int ret; |
||
794 | const unsigned i = (nv->scratch.id + 1) % NOUVEAU_MAX_SCRATCH_BUFS; |
||
795 | |||
796 | |||
797 | return FALSE; |
||
798 | nv->scratch.id = i; |
||
799 | |||
800 | |||
801 | if (!bo) { |
||
802 | ret = nouveau_scratch_bo_alloc(nv, &bo, nv->scratch.bo_size); |
||
803 | if (ret) |
||
804 | return FALSE; |
||
805 | nv->scratch.bo[i] = bo; |
||
806 | } |
||
807 | nv->scratch.current = bo; |
||
808 | nv->scratch.offset = 0; |
||
809 | nv->scratch.end = nv->scratch.bo_size; |
||
810 | |||
811 | |||
812 | if (!ret) |
||
813 | nv->scratch.map = bo->map; |
||
814 | return !ret; |
||
815 | } |
||
816 | |||
817 | |||
818 | nouveau_scratch_more(struct nouveau_context *nv, unsigned min_size) |
||
819 | { |
||
820 | boolean ret; |
||
821 | |||
822 | |||
823 | if (!ret) |
||
824 | ret = nouveau_scratch_runout(nv, min_size); |
||
825 | return ret; |
||
826 | } |
||
827 | |||
828 | |||
829 | |||
830 | uint64_t |
||
831 | nouveau_scratch_data(struct nouveau_context *nv, |
||
832 | const void *data, unsigned base, unsigned size, |
||
833 | struct nouveau_bo **bo) |
||
834 | { |
||
835 | unsigned bgn = MAX2(base, nv->scratch.offset); |
||
836 | unsigned end = bgn + size; |
||
837 | |||
838 | |||
839 | end = base + size; |
||
840 | if (!nouveau_scratch_more(nv, end)) |
||
841 | return 0; |
||
842 | bgn = base; |
||
843 | } |
||
844 | nv->scratch.offset = align(end, 4); |
||
845 | |||
846 | |||
847 | |||
848 | |||
849 | return (*bo)->offset + (bgn - base); |
||
850 | } |
||
851 | |||
852 | |||
853 | nouveau_scratch_get(struct nouveau_context *nv, |
||
854 | unsigned size, uint64_t *gpu_addr, struct nouveau_bo **pbo) |
||
855 | { |
||
856 | unsigned bgn = nv->scratch.offset; |
||
857 | unsigned end = nv->scratch.offset + size; |
||
858 | |||
859 | |||
860 | end = size; |
||
861 | if (!nouveau_scratch_more(nv, end)) |
||
862 | return NULL; |
||
863 | bgn = 0; |
||
864 | } |
||
865 | nv->scratch.offset = align(end, 4); |
||
866 | |||
867 | |||
868 | *gpu_addr = nv->scratch.current->offset + bgn; |
||
869 | return nv->scratch.map + bgn; |
||
870 | }=> |
||
871 |