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