1bf215546Sopenharmony_ci/************************************************************************** 2bf215546Sopenharmony_ci * 3bf215546Sopenharmony_ci * Copyright 2009 VMware, Inc. 4bf215546Sopenharmony_ci * All Rights Reserved. 5bf215546Sopenharmony_ci * 6bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 7bf215546Sopenharmony_ci * copy of this software and associated documentation files (the 8bf215546Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 9bf215546Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 10bf215546Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to 11bf215546Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 12bf215546Sopenharmony_ci * the following conditions: 13bf215546Sopenharmony_ci * 14bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the 15bf215546Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 16bf215546Sopenharmony_ci * of the Software. 17bf215546Sopenharmony_ci * 18bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19bf215546Sopenharmony_ci * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20bf215546Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 21bf215546Sopenharmony_ci * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR 22bf215546Sopenharmony_ci * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23bf215546Sopenharmony_ci * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24bf215546Sopenharmony_ci * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25bf215546Sopenharmony_ci * 26bf215546Sopenharmony_ci **************************************************************************/ 27bf215546Sopenharmony_ci 28bf215546Sopenharmony_ci/* Helper utility for uploading user buffers & other data, and 29bf215546Sopenharmony_ci * coalescing small buffers into larger ones. 30bf215546Sopenharmony_ci */ 31bf215546Sopenharmony_ci 32bf215546Sopenharmony_ci#include "pipe/p_defines.h" 33bf215546Sopenharmony_ci#include "util/u_inlines.h" 34bf215546Sopenharmony_ci#include "pipe/p_context.h" 35bf215546Sopenharmony_ci#include "util/u_memory.h" 36bf215546Sopenharmony_ci#include "util/u_math.h" 37bf215546Sopenharmony_ci 38bf215546Sopenharmony_ci#include "u_upload_mgr.h" 39bf215546Sopenharmony_ci 40bf215546Sopenharmony_ci 41bf215546Sopenharmony_cistruct u_upload_mgr { 42bf215546Sopenharmony_ci struct pipe_context *pipe; 43bf215546Sopenharmony_ci 44bf215546Sopenharmony_ci unsigned default_size; /* Minimum size of the upload buffer, in bytes. */ 45bf215546Sopenharmony_ci unsigned bind; /* Bitmask of PIPE_BIND_* flags. */ 46bf215546Sopenharmony_ci enum pipe_resource_usage usage; 47bf215546Sopenharmony_ci unsigned flags; 48bf215546Sopenharmony_ci unsigned map_flags; /* Bitmask of PIPE_MAP_* flags. */ 49bf215546Sopenharmony_ci boolean map_persistent; /* If persistent mappings are supported. */ 50bf215546Sopenharmony_ci 51bf215546Sopenharmony_ci struct pipe_resource *buffer; /* Upload buffer. */ 52bf215546Sopenharmony_ci struct pipe_transfer *transfer; /* Transfer object for the upload buffer. */ 53bf215546Sopenharmony_ci uint8_t *map; /* Pointer to the mapped upload buffer. */ 54bf215546Sopenharmony_ci unsigned buffer_size; /* Same as buffer->width0. */ 55bf215546Sopenharmony_ci unsigned offset; /* Aligned offset to the upload buffer, pointing 56bf215546Sopenharmony_ci * at the first unused byte. */ 57bf215546Sopenharmony_ci int buffer_private_refcount; 58bf215546Sopenharmony_ci}; 59bf215546Sopenharmony_ci 60bf215546Sopenharmony_ci 61bf215546Sopenharmony_cistruct u_upload_mgr * 62bf215546Sopenharmony_ciu_upload_create(struct pipe_context *pipe, unsigned default_size, 63bf215546Sopenharmony_ci unsigned bind, enum pipe_resource_usage usage, unsigned flags) 64bf215546Sopenharmony_ci{ 65bf215546Sopenharmony_ci struct u_upload_mgr *upload = CALLOC_STRUCT(u_upload_mgr); 66bf215546Sopenharmony_ci if (!upload) 67bf215546Sopenharmony_ci return NULL; 68bf215546Sopenharmony_ci 69bf215546Sopenharmony_ci upload->pipe = pipe; 70bf215546Sopenharmony_ci upload->default_size = default_size; 71bf215546Sopenharmony_ci upload->bind = bind; 72bf215546Sopenharmony_ci upload->usage = usage; 73bf215546Sopenharmony_ci upload->flags = flags; 74bf215546Sopenharmony_ci 75bf215546Sopenharmony_ci upload->map_persistent = 76bf215546Sopenharmony_ci pipe->screen->get_param(pipe->screen, 77bf215546Sopenharmony_ci PIPE_CAP_BUFFER_MAP_PERSISTENT_COHERENT); 78bf215546Sopenharmony_ci 79bf215546Sopenharmony_ci if (upload->map_persistent) { 80bf215546Sopenharmony_ci upload->map_flags = PIPE_MAP_WRITE | 81bf215546Sopenharmony_ci PIPE_MAP_UNSYNCHRONIZED | 82bf215546Sopenharmony_ci PIPE_MAP_PERSISTENT | 83bf215546Sopenharmony_ci PIPE_MAP_COHERENT; 84bf215546Sopenharmony_ci } 85bf215546Sopenharmony_ci else { 86bf215546Sopenharmony_ci upload->map_flags = PIPE_MAP_WRITE | 87bf215546Sopenharmony_ci PIPE_MAP_UNSYNCHRONIZED | 88bf215546Sopenharmony_ci PIPE_MAP_FLUSH_EXPLICIT; 89bf215546Sopenharmony_ci } 90bf215546Sopenharmony_ci 91bf215546Sopenharmony_ci return upload; 92bf215546Sopenharmony_ci} 93bf215546Sopenharmony_ci 94bf215546Sopenharmony_cistruct u_upload_mgr * 95bf215546Sopenharmony_ciu_upload_create_default(struct pipe_context *pipe) 96bf215546Sopenharmony_ci{ 97bf215546Sopenharmony_ci return u_upload_create(pipe, 1024 * 1024, 98bf215546Sopenharmony_ci PIPE_BIND_VERTEX_BUFFER | 99bf215546Sopenharmony_ci PIPE_BIND_INDEX_BUFFER | 100bf215546Sopenharmony_ci PIPE_BIND_CONSTANT_BUFFER, 101bf215546Sopenharmony_ci PIPE_USAGE_STREAM, 0); 102bf215546Sopenharmony_ci} 103bf215546Sopenharmony_ci 104bf215546Sopenharmony_cistruct u_upload_mgr * 105bf215546Sopenharmony_ciu_upload_clone(struct pipe_context *pipe, struct u_upload_mgr *upload) 106bf215546Sopenharmony_ci{ 107bf215546Sopenharmony_ci struct u_upload_mgr *result = u_upload_create(pipe, upload->default_size, 108bf215546Sopenharmony_ci upload->bind, upload->usage, 109bf215546Sopenharmony_ci upload->flags); 110bf215546Sopenharmony_ci if (!upload->map_persistent && result->map_persistent) 111bf215546Sopenharmony_ci u_upload_disable_persistent(result); 112bf215546Sopenharmony_ci 113bf215546Sopenharmony_ci return result; 114bf215546Sopenharmony_ci} 115bf215546Sopenharmony_ci 116bf215546Sopenharmony_civoid 117bf215546Sopenharmony_ciu_upload_disable_persistent(struct u_upload_mgr *upload) 118bf215546Sopenharmony_ci{ 119bf215546Sopenharmony_ci upload->map_persistent = FALSE; 120bf215546Sopenharmony_ci upload->map_flags &= ~(PIPE_MAP_COHERENT | PIPE_MAP_PERSISTENT); 121bf215546Sopenharmony_ci upload->map_flags |= PIPE_MAP_FLUSH_EXPLICIT; 122bf215546Sopenharmony_ci} 123bf215546Sopenharmony_ci 124bf215546Sopenharmony_cistatic void 125bf215546Sopenharmony_ciupload_unmap_internal(struct u_upload_mgr *upload, boolean destroying) 126bf215546Sopenharmony_ci{ 127bf215546Sopenharmony_ci if ((!destroying && upload->map_persistent) || !upload->transfer) 128bf215546Sopenharmony_ci return; 129bf215546Sopenharmony_ci 130bf215546Sopenharmony_ci struct pipe_box *box = &upload->transfer->box; 131bf215546Sopenharmony_ci 132bf215546Sopenharmony_ci if (!upload->map_persistent && (int) upload->offset > box->x) { 133bf215546Sopenharmony_ci pipe_buffer_flush_mapped_range(upload->pipe, upload->transfer, 134bf215546Sopenharmony_ci box->x, upload->offset - box->x); 135bf215546Sopenharmony_ci } 136bf215546Sopenharmony_ci 137bf215546Sopenharmony_ci pipe_buffer_unmap(upload->pipe, upload->transfer); 138bf215546Sopenharmony_ci upload->transfer = NULL; 139bf215546Sopenharmony_ci upload->map = NULL; 140bf215546Sopenharmony_ci} 141bf215546Sopenharmony_ci 142bf215546Sopenharmony_ci 143bf215546Sopenharmony_civoid 144bf215546Sopenharmony_ciu_upload_unmap(struct u_upload_mgr *upload) 145bf215546Sopenharmony_ci{ 146bf215546Sopenharmony_ci upload_unmap_internal(upload, FALSE); 147bf215546Sopenharmony_ci} 148bf215546Sopenharmony_ci 149bf215546Sopenharmony_ci 150bf215546Sopenharmony_cistatic void 151bf215546Sopenharmony_ciu_upload_release_buffer(struct u_upload_mgr *upload) 152bf215546Sopenharmony_ci{ 153bf215546Sopenharmony_ci /* Unmap and unreference the upload buffer. */ 154bf215546Sopenharmony_ci upload_unmap_internal(upload, TRUE); 155bf215546Sopenharmony_ci if (upload->buffer_private_refcount) { 156bf215546Sopenharmony_ci /* Subtract the remaining private references before unreferencing 157bf215546Sopenharmony_ci * the buffer. The mega comment below explains it. 158bf215546Sopenharmony_ci */ 159bf215546Sopenharmony_ci assert(upload->buffer_private_refcount > 0); 160bf215546Sopenharmony_ci p_atomic_add(&upload->buffer->reference.count, 161bf215546Sopenharmony_ci -upload->buffer_private_refcount); 162bf215546Sopenharmony_ci upload->buffer_private_refcount = 0; 163bf215546Sopenharmony_ci } 164bf215546Sopenharmony_ci pipe_resource_reference(&upload->buffer, NULL); 165bf215546Sopenharmony_ci upload->buffer_size = 0; 166bf215546Sopenharmony_ci} 167bf215546Sopenharmony_ci 168bf215546Sopenharmony_ci 169bf215546Sopenharmony_civoid 170bf215546Sopenharmony_ciu_upload_destroy(struct u_upload_mgr *upload) 171bf215546Sopenharmony_ci{ 172bf215546Sopenharmony_ci u_upload_release_buffer(upload); 173bf215546Sopenharmony_ci FREE(upload); 174bf215546Sopenharmony_ci} 175bf215546Sopenharmony_ci 176bf215546Sopenharmony_ci/* Return the allocated buffer size or 0 if it failed. */ 177bf215546Sopenharmony_cistatic unsigned 178bf215546Sopenharmony_ciu_upload_alloc_buffer(struct u_upload_mgr *upload, unsigned min_size) 179bf215546Sopenharmony_ci{ 180bf215546Sopenharmony_ci struct pipe_screen *screen = upload->pipe->screen; 181bf215546Sopenharmony_ci struct pipe_resource buffer; 182bf215546Sopenharmony_ci unsigned size; 183bf215546Sopenharmony_ci 184bf215546Sopenharmony_ci /* Release the old buffer, if present: 185bf215546Sopenharmony_ci */ 186bf215546Sopenharmony_ci u_upload_release_buffer(upload); 187bf215546Sopenharmony_ci 188bf215546Sopenharmony_ci /* Allocate a new one: 189bf215546Sopenharmony_ci */ 190bf215546Sopenharmony_ci size = align(MAX2(upload->default_size, min_size), 4096); 191bf215546Sopenharmony_ci 192bf215546Sopenharmony_ci memset(&buffer, 0, sizeof buffer); 193bf215546Sopenharmony_ci buffer.target = PIPE_BUFFER; 194bf215546Sopenharmony_ci buffer.format = PIPE_FORMAT_R8_UNORM; /* want TYPELESS or similar */ 195bf215546Sopenharmony_ci buffer.bind = upload->bind; 196bf215546Sopenharmony_ci buffer.usage = upload->usage; 197bf215546Sopenharmony_ci buffer.flags = upload->flags | PIPE_RESOURCE_FLAG_SINGLE_THREAD_USE; 198bf215546Sopenharmony_ci buffer.width0 = size; 199bf215546Sopenharmony_ci buffer.height0 = 1; 200bf215546Sopenharmony_ci buffer.depth0 = 1; 201bf215546Sopenharmony_ci buffer.array_size = 1; 202bf215546Sopenharmony_ci 203bf215546Sopenharmony_ci if (upload->map_persistent) { 204bf215546Sopenharmony_ci buffer.flags |= PIPE_RESOURCE_FLAG_MAP_PERSISTENT | 205bf215546Sopenharmony_ci PIPE_RESOURCE_FLAG_MAP_COHERENT; 206bf215546Sopenharmony_ci } 207bf215546Sopenharmony_ci 208bf215546Sopenharmony_ci upload->buffer = screen->resource_create(screen, &buffer); 209bf215546Sopenharmony_ci if (upload->buffer == NULL) 210bf215546Sopenharmony_ci return 0; 211bf215546Sopenharmony_ci 212bf215546Sopenharmony_ci /* Since atomic operations are very very slow when 2 threads are not 213bf215546Sopenharmony_ci * sharing the same L3 cache (which happens on AMD Zen), eliminate all 214bf215546Sopenharmony_ci * atomics in u_upload_alloc as follows: 215bf215546Sopenharmony_ci * 216bf215546Sopenharmony_ci * u_upload_alloc has to return a buffer reference to the caller. 217bf215546Sopenharmony_ci * Instead of atomic_inc for every call, it does all possible future 218bf215546Sopenharmony_ci * increments in advance here. The maximum number of times u_upload_alloc 219bf215546Sopenharmony_ci * can be called per upload buffer is "size", because the minimum 220bf215546Sopenharmony_ci * allocation size is 1, thus u_upload_alloc can only return "size" number 221bf215546Sopenharmony_ci * of suballocations at most, so we will never need more. This is 222bf215546Sopenharmony_ci * the number that is added to reference.count here. 223bf215546Sopenharmony_ci * 224bf215546Sopenharmony_ci * buffer_private_refcount tracks how many buffer references we can return 225bf215546Sopenharmony_ci * without using atomics. If the buffer is full and there are still 226bf215546Sopenharmony_ci * references left, they are atomically subtracted from reference.count 227bf215546Sopenharmony_ci * before the buffer is unreferenced. 228bf215546Sopenharmony_ci * 229bf215546Sopenharmony_ci * This technique can increase CPU performance by 10%. 230bf215546Sopenharmony_ci * 231bf215546Sopenharmony_ci * The caller of u_upload_alloc_buffer will consume min_size bytes, 232bf215546Sopenharmony_ci * so init the buffer_private_refcount to 1 + size - min_size, instead 233bf215546Sopenharmony_ci * of size to avoid overflowing reference.count when size is huge. 234bf215546Sopenharmony_ci */ 235bf215546Sopenharmony_ci upload->buffer_private_refcount = 1 + (size - min_size); 236bf215546Sopenharmony_ci assert(upload->buffer_private_refcount < INT32_MAX / 2); 237bf215546Sopenharmony_ci p_atomic_add(&upload->buffer->reference.count, upload->buffer_private_refcount); 238bf215546Sopenharmony_ci 239bf215546Sopenharmony_ci /* Map the new buffer. */ 240bf215546Sopenharmony_ci upload->map = pipe_buffer_map_range(upload->pipe, upload->buffer, 241bf215546Sopenharmony_ci 0, size, upload->map_flags, 242bf215546Sopenharmony_ci &upload->transfer); 243bf215546Sopenharmony_ci if (upload->map == NULL) { 244bf215546Sopenharmony_ci u_upload_release_buffer(upload); 245bf215546Sopenharmony_ci return 0; 246bf215546Sopenharmony_ci } 247bf215546Sopenharmony_ci 248bf215546Sopenharmony_ci upload->buffer_size = size; 249bf215546Sopenharmony_ci upload->offset = 0; 250bf215546Sopenharmony_ci return size; 251bf215546Sopenharmony_ci} 252bf215546Sopenharmony_ci 253bf215546Sopenharmony_civoid 254bf215546Sopenharmony_ciu_upload_alloc(struct u_upload_mgr *upload, 255bf215546Sopenharmony_ci unsigned min_out_offset, 256bf215546Sopenharmony_ci unsigned size, 257bf215546Sopenharmony_ci unsigned alignment, 258bf215546Sopenharmony_ci unsigned *out_offset, 259bf215546Sopenharmony_ci struct pipe_resource **outbuf, 260bf215546Sopenharmony_ci void **ptr) 261bf215546Sopenharmony_ci{ 262bf215546Sopenharmony_ci unsigned buffer_size = upload->buffer_size; 263bf215546Sopenharmony_ci unsigned offset = MAX2(min_out_offset, upload->offset); 264bf215546Sopenharmony_ci 265bf215546Sopenharmony_ci offset = align(offset, alignment); 266bf215546Sopenharmony_ci 267bf215546Sopenharmony_ci /* Make sure we have enough space in the upload buffer 268bf215546Sopenharmony_ci * for the sub-allocation. 269bf215546Sopenharmony_ci */ 270bf215546Sopenharmony_ci if (unlikely(offset + size > buffer_size)) { 271bf215546Sopenharmony_ci /* Allocate a new buffer and set the offset to the smallest one. */ 272bf215546Sopenharmony_ci offset = align(min_out_offset, alignment); 273bf215546Sopenharmony_ci buffer_size = u_upload_alloc_buffer(upload, offset + size); 274bf215546Sopenharmony_ci 275bf215546Sopenharmony_ci if (unlikely(!buffer_size)) { 276bf215546Sopenharmony_ci *out_offset = ~0; 277bf215546Sopenharmony_ci pipe_resource_reference(outbuf, NULL); 278bf215546Sopenharmony_ci *ptr = NULL; 279bf215546Sopenharmony_ci return; 280bf215546Sopenharmony_ci } 281bf215546Sopenharmony_ci } 282bf215546Sopenharmony_ci 283bf215546Sopenharmony_ci if (unlikely(!upload->map)) { 284bf215546Sopenharmony_ci upload->map = pipe_buffer_map_range(upload->pipe, upload->buffer, 285bf215546Sopenharmony_ci offset, 286bf215546Sopenharmony_ci buffer_size - offset, 287bf215546Sopenharmony_ci upload->map_flags, 288bf215546Sopenharmony_ci &upload->transfer); 289bf215546Sopenharmony_ci if (unlikely(!upload->map)) { 290bf215546Sopenharmony_ci upload->transfer = NULL; 291bf215546Sopenharmony_ci *out_offset = ~0; 292bf215546Sopenharmony_ci pipe_resource_reference(outbuf, NULL); 293bf215546Sopenharmony_ci *ptr = NULL; 294bf215546Sopenharmony_ci return; 295bf215546Sopenharmony_ci } 296bf215546Sopenharmony_ci 297bf215546Sopenharmony_ci upload->map -= offset; 298bf215546Sopenharmony_ci } 299bf215546Sopenharmony_ci 300bf215546Sopenharmony_ci assert(offset < buffer_size); 301bf215546Sopenharmony_ci assert(offset + size <= buffer_size); 302bf215546Sopenharmony_ci assert(size); 303bf215546Sopenharmony_ci 304bf215546Sopenharmony_ci /* Emit the return values: */ 305bf215546Sopenharmony_ci *ptr = upload->map + offset; 306bf215546Sopenharmony_ci *out_offset = offset; 307bf215546Sopenharmony_ci 308bf215546Sopenharmony_ci if (*outbuf != upload->buffer) { 309bf215546Sopenharmony_ci pipe_resource_reference(outbuf, NULL); 310bf215546Sopenharmony_ci *outbuf = upload->buffer; 311bf215546Sopenharmony_ci assert (upload->buffer_private_refcount > 0); 312bf215546Sopenharmony_ci upload->buffer_private_refcount--; 313bf215546Sopenharmony_ci } 314bf215546Sopenharmony_ci 315bf215546Sopenharmony_ci upload->offset = offset + size; 316bf215546Sopenharmony_ci} 317bf215546Sopenharmony_ci 318bf215546Sopenharmony_civoid 319bf215546Sopenharmony_ciu_upload_data(struct u_upload_mgr *upload, 320bf215546Sopenharmony_ci unsigned min_out_offset, 321bf215546Sopenharmony_ci unsigned size, 322bf215546Sopenharmony_ci unsigned alignment, 323bf215546Sopenharmony_ci const void *data, 324bf215546Sopenharmony_ci unsigned *out_offset, 325bf215546Sopenharmony_ci struct pipe_resource **outbuf) 326bf215546Sopenharmony_ci{ 327bf215546Sopenharmony_ci uint8_t *ptr; 328bf215546Sopenharmony_ci 329bf215546Sopenharmony_ci u_upload_alloc(upload, min_out_offset, size, alignment, 330bf215546Sopenharmony_ci out_offset, outbuf, 331bf215546Sopenharmony_ci (void**)&ptr); 332bf215546Sopenharmony_ci if (ptr) 333bf215546Sopenharmony_ci memcpy(ptr, data, size); 334bf215546Sopenharmony_ci} 335