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 41 struct 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 }; 57 58 59 struct u_upload_mgr * 60 u_upload_create(struct pipe_context *pipe, unsigned default_size, 61 unsigned bind, enum pipe_resource_usage usage, unsigned flags) 62 { 63 struct u_upload_mgr *upload = CALLOC_STRUCT(u_upload_mgr); 64 if (!upload) 65 return NULL; 66 67 upload->pipe = pipe; 68 upload->default_size = default_size; 69 upload->bind = bind; 70 upload->usage = usage; 71 upload->flags = flags; 72 73 upload->map_persistent = 74 pipe->screen->get_param(pipe->screen, 75 PIPE_CAP_BUFFER_MAP_PERSISTENT_COHERENT); 76 77 if (upload->map_persistent) { 78 upload->map_flags = PIPE_TRANSFER_WRITE | 79 PIPE_TRANSFER_UNSYNCHRONIZED | 80 PIPE_TRANSFER_PERSISTENT | 81 PIPE_TRANSFER_COHERENT; 82 } 83 else { 84 upload->map_flags = PIPE_TRANSFER_WRITE | 85 PIPE_TRANSFER_UNSYNCHRONIZED | 86 PIPE_TRANSFER_FLUSH_EXPLICIT; 87 } 88 89 return upload; 90 } 91 92 struct u_upload_mgr * 93 u_upload_create_default(struct pipe_context *pipe) 94 { 95 return u_upload_create(pipe, 1024 * 1024, 96 PIPE_BIND_VERTEX_BUFFER | 97 PIPE_BIND_INDEX_BUFFER | 98 PIPE_BIND_CONSTANT_BUFFER, 99 PIPE_USAGE_STREAM, 0); 100 } 101 102 struct u_upload_mgr * 103 u_upload_clone(struct pipe_context *pipe, struct u_upload_mgr *upload) 104 { 105 return u_upload_create(pipe, upload->default_size, upload->bind, 106 upload->usage, upload->flags); 107 } 108 109 static void 110 upload_unmap_internal(struct u_upload_mgr *upload, boolean destroying) 111 { 112 if (!destroying && upload->map_persistent) 113 return; 114 115 if (upload->transfer) { 116 struct pipe_box *box = &upload->transfer->box; 117 118 if (!upload->map_persistent && (int) upload->offset > box->x) { 119 pipe_buffer_flush_mapped_range(upload->pipe, upload->transfer, 120 box->x, upload->offset - box->x); 121 } 122 123 pipe_transfer_unmap(upload->pipe, upload->transfer); 124 upload->transfer = NULL; 125 upload->map = NULL; 126 } 127 } 128 129 130 void 131 u_upload_unmap(struct u_upload_mgr *upload) 132 { 133 upload_unmap_internal(upload, FALSE); 134 } 135 136 137 static void 138 u_upload_release_buffer(struct u_upload_mgr *upload) 139 { 140 /* Unmap and unreference the upload buffer. */ 141 upload_unmap_internal(upload, TRUE); 142 pipe_resource_reference(&upload->buffer, NULL); 143 } 144 145 146 void 147 u_upload_destroy(struct u_upload_mgr *upload) 148 { 149 u_upload_release_buffer(upload); 150 FREE(upload); 151 } 152 153 154 static void 155 u_upload_alloc_buffer(struct u_upload_mgr *upload, unsigned min_size) 156 { 157 struct pipe_screen *screen = upload->pipe->screen; 158 struct pipe_resource buffer; 159 unsigned size; 160 161 /* Release the old buffer, if present: 162 */ 163 u_upload_release_buffer(upload); 164 165 /* Allocate a new one: 166 */ 167 size = align(MAX2(upload->default_size, min_size), 4096); 168 169 memset(&buffer, 0, sizeof buffer); 170 buffer.target = PIPE_BUFFER; 171 buffer.format = PIPE_FORMAT_R8_UNORM; /* want TYPELESS or similar */ 172 buffer.bind = upload->bind; 173 buffer.usage = upload->usage; 174 buffer.flags = upload->flags; 175 buffer.width0 = size; 176 buffer.height0 = 1; 177 buffer.depth0 = 1; 178 buffer.array_size = 1; 179 180 if (upload->map_persistent) { 181 buffer.flags |= PIPE_RESOURCE_FLAG_MAP_PERSISTENT | 182 PIPE_RESOURCE_FLAG_MAP_COHERENT; 183 } 184 185 upload->buffer = screen->resource_create(screen, &buffer); 186 if (upload->buffer == NULL) 187 return; 188 189 /* Map the new buffer. */ 190 upload->map = pipe_buffer_map_range(upload->pipe, upload->buffer, 191 0, size, upload->map_flags, 192 &upload->transfer); 193 if (upload->map == NULL) { 194 upload->transfer = NULL; 195 pipe_resource_reference(&upload->buffer, NULL); 196 return; 197 } 198 199 upload->offset = 0; 200 } 201 202 void 203 u_upload_alloc(struct u_upload_mgr *upload, 204 unsigned min_out_offset, 205 unsigned size, 206 unsigned alignment, 207 unsigned *out_offset, 208 struct pipe_resource **outbuf, 209 void **ptr) 210 { 211 unsigned buffer_size = upload->buffer ? upload->buffer->width0 : 0; 212 unsigned offset; 213 214 min_out_offset = align(min_out_offset, alignment); 215 216 offset = align(upload->offset, alignment); 217 offset = MAX2(offset, min_out_offset); 218 219 /* Make sure we have enough space in the upload buffer 220 * for the sub-allocation. 221 */ 222 if (unlikely(!upload->buffer || offset + size > buffer_size)) { 223 u_upload_alloc_buffer(upload, min_out_offset + size); 224 225 if (unlikely(!upload->buffer)) { 226 *out_offset = ~0; 227 pipe_resource_reference(outbuf, NULL); 228 *ptr = NULL; 229 return; 230 } 231 232 offset = min_out_offset; 233 buffer_size = upload->buffer->width0; 234 } 235 236 if (unlikely(!upload->map)) { 237 upload->map = pipe_buffer_map_range(upload->pipe, upload->buffer, 238 offset, 239 buffer_size - offset, 240 upload->map_flags, 241 &upload->transfer); 242 if (unlikely(!upload->map)) { 243 upload->transfer = NULL; 244 *out_offset = ~0; 245 pipe_resource_reference(outbuf, NULL); 246 *ptr = NULL; 247 return; 248 } 249 250 upload->map -= offset; 251 } 252 253 assert(offset < buffer_size); 254 assert(offset + size <= buffer_size); 255 assert(size); 256 257 /* Emit the return values: */ 258 *ptr = upload->map + offset; 259 pipe_resource_reference(outbuf, upload->buffer); 260 *out_offset = offset; 261 262 upload->offset = offset + size; 263 } 264 265 void 266 u_upload_data(struct u_upload_mgr *upload, 267 unsigned min_out_offset, 268 unsigned size, 269 unsigned alignment, 270 const void *data, 271 unsigned *out_offset, 272 struct pipe_resource **outbuf) 273 { 274 uint8_t *ptr; 275 276 u_upload_alloc(upload, min_out_offset, size, alignment, 277 out_offset, outbuf, 278 (void**)&ptr); 279 if (ptr) 280 memcpy(ptr, data, size); 281 } 282