1/************************************************************************** 2 * 3 * Copyright 2009 VMware, Inc. 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sub license, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * The above copyright notice and this permission notice (including the 15 * next paragraph) shall be included in all copies or substantial portions 16 * of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR 22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 * 26 **************************************************************************/ 27 28/* Helper utility for uploading user buffers & other data, and 29 * coalescing small buffers into larger ones. 30 */ 31 32#include "pipe/p_defines.h" 33#include "util/u_inlines.h" 34#include "pipe/p_context.h" 35#include "util/u_memory.h" 36#include "util/u_math.h" 37 38#include "u_upload_mgr.h" 39 40 41struct u_upload_mgr { 42 struct pipe_context *pipe; 43 44 unsigned default_size; /* Minimum size of the upload buffer, in bytes. */ 45 unsigned bind; /* Bitmask of PIPE_BIND_* flags. */ 46 enum pipe_resource_usage usage; 47 unsigned flags; 48 unsigned map_flags; /* Bitmask of PIPE_TRANSFER_* flags. */ 49 boolean map_persistent; /* If persistent mappings are supported. */ 50 51 struct pipe_resource *buffer; /* Upload buffer. */ 52 struct pipe_transfer *transfer; /* Transfer object for the upload buffer. */ 53 uint8_t *map; /* Pointer to the mapped upload buffer. */ 54 unsigned offset; /* Aligned offset to the upload buffer, pointing 55 * at the first unused byte. */ 56 unsigned flushed_size; /* Size we have flushed by transfer_flush_region. */ 57}; 58 59 60struct u_upload_mgr * 61u_upload_create(struct pipe_context *pipe, unsigned default_size, 62 unsigned bind, enum pipe_resource_usage usage, unsigned flags) 63{ 64 struct u_upload_mgr *upload = CALLOC_STRUCT(u_upload_mgr); 65 if (!upload) 66 return NULL; 67 68 upload->pipe = pipe; 69 upload->default_size = default_size; 70 upload->bind = bind; 71 upload->usage = usage; 72 upload->flags = flags; 73 74 upload->map_persistent = 75 pipe->screen->get_param(pipe->screen, 76 PIPE_CAP_BUFFER_MAP_PERSISTENT_COHERENT); 77 78 if (upload->map_persistent) { 79 upload->map_flags = PIPE_TRANSFER_WRITE | 80 PIPE_TRANSFER_UNSYNCHRONIZED | 81 PIPE_TRANSFER_PERSISTENT | 82 PIPE_TRANSFER_COHERENT; 83 } 84 else { 85 upload->map_flags = PIPE_TRANSFER_WRITE | 86 PIPE_TRANSFER_UNSYNCHRONIZED | 87 PIPE_TRANSFER_FLUSH_EXPLICIT; 88 } 89 90 return upload; 91} 92 93struct u_upload_mgr * 94u_upload_create_default(struct pipe_context *pipe) 95{ 96 return u_upload_create(pipe, 1024 * 1024, 97 PIPE_BIND_VERTEX_BUFFER | 98 PIPE_BIND_INDEX_BUFFER | 99 PIPE_BIND_CONSTANT_BUFFER, 100 PIPE_USAGE_STREAM, 0); 101} 102 103struct u_upload_mgr * 104u_upload_clone(struct pipe_context *pipe, struct u_upload_mgr *upload) 105{ 106 struct u_upload_mgr *result = u_upload_create(pipe, upload->default_size, 107 upload->bind, upload->usage, 108 upload->flags); 109 if (upload->map_persistent && 110 upload->map_flags & PIPE_TRANSFER_FLUSH_EXPLICIT) 111 u_upload_enable_flush_explicit(result); 112 113 return result; 114} 115 116void 117u_upload_enable_flush_explicit(struct u_upload_mgr *upload) 118{ 119 assert(upload->map_persistent); 120 upload->map_flags &= ~PIPE_TRANSFER_COHERENT; 121 upload->map_flags |= PIPE_TRANSFER_FLUSH_EXPLICIT; 122} 123 124static void 125upload_unmap_internal(struct u_upload_mgr *upload, boolean destroying) 126{ 127 if (!upload->transfer) 128 return; 129 130 if (upload->map_flags & PIPE_TRANSFER_FLUSH_EXPLICIT) { 131 struct pipe_box *box = &upload->transfer->box; 132 unsigned flush_offset = box->x + upload->flushed_size; 133 134 if (upload->offset > flush_offset) { 135 pipe_buffer_flush_mapped_range(upload->pipe, upload->transfer, 136 flush_offset, 137 upload->offset - flush_offset); 138 upload->flushed_size = upload->offset; 139 } 140 } 141 142 if (destroying || !upload->map_persistent) { 143 pipe_transfer_unmap(upload->pipe, upload->transfer); 144 upload->transfer = NULL; 145 upload->map = NULL; 146 upload->flushed_size = 0; 147 } 148} 149 150 151void 152u_upload_unmap(struct u_upload_mgr *upload) 153{ 154 upload_unmap_internal(upload, FALSE); 155} 156 157 158static void 159u_upload_release_buffer(struct u_upload_mgr *upload) 160{ 161 /* Unmap and unreference the upload buffer. */ 162 upload_unmap_internal(upload, TRUE); 163 pipe_resource_reference(&upload->buffer, NULL); 164} 165 166 167void 168u_upload_destroy(struct u_upload_mgr *upload) 169{ 170 u_upload_release_buffer(upload); 171 FREE(upload); 172} 173 174 175static void 176u_upload_alloc_buffer(struct u_upload_mgr *upload, unsigned min_size) 177{ 178 struct pipe_screen *screen = upload->pipe->screen; 179 struct pipe_resource buffer; 180 unsigned size; 181 182 /* Release the old buffer, if present: 183 */ 184 u_upload_release_buffer(upload); 185 186 /* Allocate a new one: 187 */ 188 size = align(MAX2(upload->default_size, min_size), 4096); 189 190 memset(&buffer, 0, sizeof buffer); 191 buffer.target = PIPE_BUFFER; 192 buffer.format = PIPE_FORMAT_R8_UNORM; /* want TYPELESS or similar */ 193 buffer.bind = upload->bind; 194 buffer.usage = upload->usage; 195 buffer.flags = upload->flags; 196 buffer.width0 = size; 197 buffer.height0 = 1; 198 buffer.depth0 = 1; 199 buffer.array_size = 1; 200 201 if (upload->map_persistent) { 202 buffer.flags |= PIPE_RESOURCE_FLAG_MAP_PERSISTENT | 203 PIPE_RESOURCE_FLAG_MAP_COHERENT; 204 } 205 206 upload->buffer = screen->resource_create(screen, &buffer); 207 if (upload->buffer == NULL) 208 return; 209 210 /* Map the new buffer. */ 211 upload->map = pipe_buffer_map_range(upload->pipe, upload->buffer, 212 0, size, upload->map_flags, 213 &upload->transfer); 214 if (upload->map == NULL) { 215 upload->transfer = NULL; 216 pipe_resource_reference(&upload->buffer, NULL); 217 return; 218 } 219 220 upload->offset = 0; 221} 222 223void 224u_upload_alloc(struct u_upload_mgr *upload, 225 unsigned min_out_offset, 226 unsigned size, 227 unsigned alignment, 228 unsigned *out_offset, 229 struct pipe_resource **outbuf, 230 void **ptr) 231{ 232 unsigned buffer_size = upload->buffer ? upload->buffer->width0 : 0; 233 unsigned offset; 234 235 min_out_offset = align(min_out_offset, alignment); 236 237 offset = align(upload->offset, alignment); 238 offset = MAX2(offset, min_out_offset); 239 240 /* Make sure we have enough space in the upload buffer 241 * for the sub-allocation. 242 */ 243 if (unlikely(!upload->buffer || offset + size > buffer_size)) { 244 u_upload_alloc_buffer(upload, min_out_offset + size); 245 246 if (unlikely(!upload->buffer)) { 247 *out_offset = ~0; 248 pipe_resource_reference(outbuf, NULL); 249 *ptr = NULL; 250 return; 251 } 252 253 offset = min_out_offset; 254 buffer_size = upload->buffer->width0; 255 } 256 257 if (unlikely(!upload->map)) { 258 upload->map = pipe_buffer_map_range(upload->pipe, upload->buffer, 259 offset, 260 buffer_size - offset, 261 upload->map_flags, 262 &upload->transfer); 263 if (unlikely(!upload->map)) { 264 upload->transfer = NULL; 265 *out_offset = ~0; 266 pipe_resource_reference(outbuf, NULL); 267 *ptr = NULL; 268 return; 269 } 270 271 upload->map -= offset; 272 } 273 274 assert(offset < buffer_size); 275 assert(offset + size <= buffer_size); 276 assert(size); 277 278 /* Emit the return values: */ 279 *ptr = upload->map + offset; 280 pipe_resource_reference(outbuf, upload->buffer); 281 *out_offset = offset; 282 283 upload->offset = offset + size; 284} 285 286void 287u_upload_data(struct u_upload_mgr *upload, 288 unsigned min_out_offset, 289 unsigned size, 290 unsigned alignment, 291 const void *data, 292 unsigned *out_offset, 293 struct pipe_resource **outbuf) 294{ 295 uint8_t *ptr; 296 297 u_upload_alloc(upload, min_out_offset, size, alignment, 298 out_offset, outbuf, 299 (void**)&ptr); 300 if (ptr) 301 memcpy(ptr, data, size); 302} 303