u_upload_mgr.c revision 37246f854b49101b5a112cd5a0bd93bab1b78f7a
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 "pipe/p_inlines.h" 34#include "pipe/p_screen.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_screen *screen; 43 44 unsigned default_size; 45 unsigned alignment; 46 unsigned usage; 47 48 /* The active buffer: 49 */ 50 struct pipe_buffer *buffer; 51 unsigned size; 52 unsigned offset; 53}; 54 55 56struct u_upload_mgr *u_upload_create( struct pipe_screen *screen, 57 unsigned default_size, 58 unsigned alignment, 59 unsigned usage ) 60{ 61 struct u_upload_mgr *upload = CALLOC_STRUCT( u_upload_mgr ); 62 63 upload->default_size = default_size; 64 upload->screen = screen; 65 upload->alignment = alignment; 66 upload->usage = usage; 67 upload->buffer = NULL; 68 69 return upload; 70} 71 72 73static INLINE enum pipe_error 74my_buffer_write(struct pipe_screen *screen, 75 struct pipe_buffer *buf, 76 unsigned offset, unsigned size, unsigned dirty_size, 77 const void *data) 78{ 79 uint8_t *map; 80 81 assert(offset < buf->size); 82 assert(offset + size <= buf->size); 83 assert(dirty_size >= size); 84 assert(size); 85 86 map = pipe_buffer_map_range(screen, buf, offset, size, 87 PIPE_BUFFER_USAGE_CPU_WRITE | 88 PIPE_BUFFER_USAGE_FLUSH_EXPLICIT | 89 PIPE_BUFFER_USAGE_DISCARD | 90 PIPE_BUFFER_USAGE_UNSYNCHRONIZED); 91 if (map == NULL) 92 return PIPE_ERROR_OUT_OF_MEMORY; 93 94 memcpy(map + offset, data, size); 95 pipe_buffer_flush_mapped_range(screen, buf, offset, dirty_size); 96 pipe_buffer_unmap(screen, buf); 97 98 return PIPE_OK; 99} 100 101/* Release old buffer. 102 * 103 * This must usually be called prior to firing the command stream 104 * which references the upload buffer, as many memory managers will 105 * cause subsequent maps of a fired buffer to wait. 106 * 107 * Can improve this with a change to pipe_buffer_write to use the 108 * DONT_WAIT bit, but for now, it's easiest just to grab a new buffer. 109 */ 110void u_upload_flush( struct u_upload_mgr *upload ) 111{ 112 pipe_buffer_reference( &upload->buffer, NULL ); 113 upload->size = 0; 114} 115 116 117void u_upload_destroy( struct u_upload_mgr *upload ) 118{ 119 u_upload_flush( upload ); 120 FREE( upload ); 121} 122 123 124static enum pipe_error 125u_upload_alloc_buffer( struct u_upload_mgr *upload, 126 unsigned min_size ) 127{ 128 unsigned size; 129 130 /* Release old buffer, if present: 131 */ 132 u_upload_flush( upload ); 133 134 /* Allocate a new one: 135 */ 136 size = align(MAX2(upload->default_size, min_size), 4096); 137 138 upload->buffer = pipe_buffer_create( upload->screen, 139 upload->alignment, 140 upload->usage | PIPE_BUFFER_USAGE_CPU_WRITE, 141 size ); 142 if (upload->buffer == NULL) 143 goto fail; 144 145 upload->size = size; 146 147 upload->offset = 0; 148 return 0; 149 150fail: 151 if (upload->buffer) 152 pipe_buffer_reference( &upload->buffer, NULL ); 153 154 return PIPE_ERROR_OUT_OF_MEMORY; 155} 156 157 158enum pipe_error u_upload_data( struct u_upload_mgr *upload, 159 unsigned size, 160 const void *data, 161 unsigned *out_offset, 162 struct pipe_buffer **outbuf ) 163{ 164 unsigned alloc_size = align( size, upload->alignment ); 165 enum pipe_error ret = PIPE_OK; 166 167 if (upload->offset + alloc_size > upload->size) { 168 ret = u_upload_alloc_buffer( upload, alloc_size ); 169 if (ret) 170 return ret; 171 } 172 173 /* Copy the data, using map_range if available: 174 */ 175 ret = my_buffer_write( upload->screen, 176 upload->buffer, 177 upload->offset, 178 size, 179 alloc_size, 180 data ); 181 if (ret) 182 return ret; 183 184 /* Emit the return values: 185 */ 186 pipe_buffer_reference( outbuf, upload->buffer ); 187 *out_offset = upload->offset; 188 upload->offset += alloc_size; 189 return PIPE_OK; 190} 191 192 193/* As above, but upload the full contents of a buffer. Useful for 194 * uploading user buffers, avoids generating an explosion of GPU 195 * buffers if you have an app that does lots of small vertex buffer 196 * renders or DrawElements calls. 197 */ 198enum pipe_error u_upload_buffer( struct u_upload_mgr *upload, 199 unsigned offset, 200 unsigned size, 201 struct pipe_buffer *inbuf, 202 unsigned *out_offset, 203 struct pipe_buffer **outbuf ) 204{ 205 enum pipe_error ret = PIPE_OK; 206 const char *map = NULL; 207 208 map = (const char *)pipe_buffer_map( 209 upload->screen, inbuf, PIPE_BUFFER_USAGE_CPU_READ ); 210 211 if (map == NULL) { 212 ret = PIPE_ERROR_OUT_OF_MEMORY; 213 goto done; 214 } 215 216 if (0) 217 debug_printf("upload ptr %p ofs %d sz %d\n", map, offset, size); 218 219 ret = u_upload_data( upload, 220 size, 221 map + offset, 222 out_offset, 223 outbuf ); 224 if (ret) 225 goto done; 226 227done: 228 if (map) 229 pipe_buffer_unmap( upload->screen, inbuf ); 230 231 return ret; 232} 233