u_upload_mgr.c revision 18a6f0f1a7fd509cebdc364d67b9476df1d33917
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_error.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 if (map == NULL) 90 return PIPE_ERROR_OUT_OF_MEMORY; 91 92 memcpy(map + offset, data, size); 93 pipe_buffer_flush_mapped_range(screen, buf, offset, dirty_size); 94 pipe_buffer_unmap(screen, buf); 95 96 return PIPE_OK; 97} 98 99/* Release old buffer. 100 * 101 * This must usually be called prior to firing the command stream 102 * which references the upload buffer, as many memory managers will 103 * cause subsequent maps of a fired buffer to wait. 104 * 105 * Can improve this with a change to pipe_buffer_write to use the 106 * DONT_WAIT bit, but for now, it's easiest just to grab a new buffer. 107 */ 108void u_upload_flush( struct u_upload_mgr *upload ) 109{ 110 pipe_buffer_reference( &upload->buffer, NULL ); 111 upload->size = 0; 112} 113 114 115void u_upload_destroy( struct u_upload_mgr *upload ) 116{ 117 u_upload_flush( upload ); 118 FREE( upload ); 119} 120 121 122static enum pipe_error 123u_upload_alloc_buffer( struct u_upload_mgr *upload, 124 unsigned min_size ) 125{ 126 /* Release old buffer, if present: 127 */ 128 u_upload_flush( upload ); 129 130 /* Allocate a new one: 131 */ 132 upload->size = align(MAX2(upload->default_size, min_size), 4096); 133 134 upload->buffer = pipe_buffer_create( upload->screen, 135 upload->alignment, 136 upload->usage | PIPE_BUFFER_USAGE_CPU_WRITE, 137 upload->size ); 138 if (upload->buffer == NULL) 139 goto fail; 140 141 upload->offset = 0; 142 return 0; 143 144fail: 145 if (upload->buffer) 146 pipe_buffer_reference( &upload->buffer, NULL ); 147 148 return PIPE_ERROR_OUT_OF_MEMORY; 149} 150 151 152enum pipe_error u_upload_data( struct u_upload_mgr *upload, 153 unsigned size, 154 const void *data, 155 unsigned *out_offset, 156 struct pipe_buffer **outbuf ) 157{ 158 unsigned alloc_size = align( size, upload->alignment ); 159 enum pipe_error ret = PIPE_OK; 160 161 if (upload->offset + alloc_size > upload->size) { 162 ret = u_upload_alloc_buffer( upload, alloc_size ); 163 if (ret) 164 return ret; 165 } 166 167 /* Copy the data, using map_range if available: 168 */ 169 ret = my_buffer_write( upload->screen, 170 upload->buffer, 171 upload->offset, 172 size, 173 alloc_size, 174 data ); 175 if (ret) 176 return ret; 177 178 /* Emit the return values: 179 */ 180 pipe_buffer_reference( outbuf, upload->buffer ); 181 *out_offset = upload->offset; 182 upload->offset += alloc_size; 183 return PIPE_OK; 184} 185 186 187/* As above, but upload the full contents of a buffer. Useful for 188 * uploading user buffers, avoids generating an explosion of GPU 189 * buffers if you have an app that does lots of small vertex buffer 190 * renders or DrawElements calls. 191 */ 192enum pipe_error u_upload_buffer( struct u_upload_mgr *upload, 193 unsigned offset, 194 unsigned size, 195 struct pipe_buffer *inbuf, 196 unsigned *out_offset, 197 struct pipe_buffer **outbuf ) 198{ 199 enum pipe_error ret = PIPE_OK; 200 const char *map = NULL; 201 202 map = (const char *)pipe_buffer_map( 203 upload->screen, inbuf, PIPE_BUFFER_USAGE_CPU_READ ); 204 205 if (map == NULL) { 206 ret = PIPE_ERROR_OUT_OF_MEMORY; 207 goto done; 208 } 209 210 if (0) 211 debug_printf("upload ptr %p ofs %d sz %d\n", map, offset, size); 212 213 ret = u_upload_data( upload, 214 size, 215 map + offset, 216 out_offset, 217 outbuf ); 218 if (ret) 219 goto done; 220 221done: 222 if (map) 223 pipe_buffer_unmap( upload->screen, inbuf ); 224 225 return ret; 226} 227