1/********************************************************** 2 * Copyright 2008-2009 VMware, Inc. All rights reserved. 3 * 4 * Permission is hereby granted, free of charge, to any person 5 * obtaining a copy of this software and associated documentation 6 * files (the "Software"), to deal in the Software without 7 * restriction, including without limitation the rights to use, copy, 8 * modify, merge, publish, distribute, sublicense, and/or sell copies 9 * of the Software, and to permit persons to whom the Software is 10 * furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be 13 * included in all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 * 24 **********************************************************/ 25 26#include "svga_cmd.h" 27 28#include "pipe/p_state.h" 29#include "pipe/p_defines.h" 30#include "util/u_inlines.h" 31#include "os/os_thread.h" 32#include "util/u_math.h" 33#include "util/u_memory.h" 34 35#include "svga_context.h" 36#include "svga_screen.h" 37#include "svga_resource_buffer.h" 38#include "svga_resource_buffer_upload.h" 39#include "svga_winsys.h" 40#include "svga_debug.h" 41 42 43/** 44 * Vertex and index buffers need hardware backing. Constant buffers 45 * do not. No other types of buffers currently supported. 46 */ 47static INLINE boolean 48svga_buffer_needs_hw_storage(unsigned usage) 49{ 50 return usage & (PIPE_BIND_VERTEX_BUFFER | PIPE_BIND_INDEX_BUFFER); 51} 52 53 54/** 55 * Create a buffer transfer. 56 * 57 * Unlike texture DMAs (which are written immediately to the command buffer and 58 * therefore inherently serialized with other context operations), for buffers 59 * we try to coalesce multiple range mappings (i.e, multiple calls to this 60 * function) into a single DMA command, for better efficiency in command 61 * processing. This means we need to exercise extra care here to ensure that 62 * the end result is exactly the same as if one DMA was used for every mapped 63 * range. 64 */ 65static struct pipe_transfer * 66svga_buffer_get_transfer(struct pipe_context *pipe, 67 struct pipe_resource *resource, 68 unsigned level, 69 unsigned usage, 70 const struct pipe_box *box) 71{ 72 struct svga_context *svga = svga_context(pipe); 73 struct svga_screen *ss = svga_screen(pipe->screen); 74 struct svga_buffer *sbuf = svga_buffer(resource); 75 struct pipe_transfer *transfer; 76 77 transfer = CALLOC_STRUCT(pipe_transfer); 78 if (transfer == NULL) { 79 return NULL; 80 } 81 82 transfer->resource = resource; 83 transfer->level = level; 84 transfer->usage = usage; 85 transfer->box = *box; 86 87 if (usage & PIPE_TRANSFER_WRITE) { 88 if (usage & PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE) { 89 /* 90 * Flush any pending primitives, finish writing any pending DMA 91 * commands, and tell the host to discard the buffer contents on 92 * the next DMA operation. 93 */ 94 95 svga_hwtnl_flush_buffer(svga, resource); 96 97 if (sbuf->dma.pending) { 98 svga_buffer_upload_flush(svga, sbuf); 99 100 /* 101 * Instead of flushing the context command buffer, simply discard 102 * the current hwbuf, and start a new one. 103 */ 104 105 svga_buffer_destroy_hw_storage(ss, sbuf); 106 } 107 108 sbuf->map.num_ranges = 0; 109 sbuf->dma.flags.discard = TRUE; 110 } 111 112 if (usage & PIPE_TRANSFER_UNSYNCHRONIZED) { 113 if (!sbuf->map.num_ranges) { 114 /* 115 * No pending ranges to upload so far, so we can tell the host to 116 * not synchronize on the next DMA command. 117 */ 118 119 sbuf->dma.flags.unsynchronized = TRUE; 120 } 121 } else { 122 /* 123 * Synchronizing, so flush any pending primitives, finish writing any 124 * pending DMA command, and ensure the next DMA will be done in order. 125 */ 126 127 svga_hwtnl_flush_buffer(svga, resource); 128 129 if (sbuf->dma.pending) { 130 svga_buffer_upload_flush(svga, sbuf); 131 132 if (sbuf->hwbuf) { 133 /* 134 * We have a pending DMA upload from a hardware buffer, therefore 135 * we need to ensure that the host finishes processing that DMA 136 * command before the state tracker can start overwriting the 137 * hardware buffer. 138 * 139 * XXX: This could be avoided by tying the hardware buffer to 140 * the transfer (just as done with textures), which would allow 141 * overlapping DMAs commands to be queued on the same context 142 * buffer. However, due to the likelihood of software vertex 143 * processing, it is more convenient to hold on to the hardware 144 * buffer, allowing to quickly access the contents from the CPU 145 * without having to do a DMA download from the host. 146 */ 147 148 if (usage & PIPE_TRANSFER_DONTBLOCK) { 149 /* 150 * Flushing the command buffer here will most likely cause 151 * the map of the hwbuf below to block, so preemptively 152 * return NULL here if DONTBLOCK is set to prevent unnecessary 153 * command buffer flushes. 154 */ 155 156 FREE(transfer); 157 return NULL; 158 } 159 160 svga_context_flush(svga, NULL); 161 } 162 } 163 164 sbuf->dma.flags.unsynchronized = FALSE; 165 } 166 } 167 168 if (!sbuf->swbuf && !sbuf->hwbuf) { 169 if (svga_buffer_create_hw_storage(ss, sbuf) != PIPE_OK) { 170 /* 171 * We can't create a hardware buffer big enough, so create a malloc 172 * buffer instead. 173 */ 174 if (0) { 175 debug_printf("%s: failed to allocate %u KB of DMA, " 176 "splitting DMA transfers\n", 177 __FUNCTION__, 178 (sbuf->b.b.width0 + 1023)/1024); 179 } 180 181 sbuf->swbuf = align_malloc(sbuf->b.b.width0, 16); 182 if (!sbuf->swbuf) { 183 FREE(transfer); 184 return NULL; 185 } 186 } 187 } 188 189 return transfer; 190} 191 192 193/** 194 * Map a range of a buffer. 195 */ 196static void * 197svga_buffer_transfer_map( struct pipe_context *pipe, 198 struct pipe_transfer *transfer ) 199{ 200 struct svga_buffer *sbuf = svga_buffer(transfer->resource); 201 202 uint8_t *map; 203 204 if (sbuf->swbuf) { 205 /* User/malloc buffer */ 206 map = sbuf->swbuf; 207 } 208 else if (sbuf->hwbuf) { 209 struct svga_screen *ss = svga_screen(pipe->screen); 210 struct svga_winsys_screen *sws = ss->sws; 211 212 map = sws->buffer_map(sws, sbuf->hwbuf, transfer->usage); 213 } 214 else { 215 map = NULL; 216 } 217 218 if (map) { 219 ++sbuf->map.count; 220 map += transfer->box.x; 221 } 222 223 return map; 224} 225 226 227static void 228svga_buffer_transfer_flush_region( struct pipe_context *pipe, 229 struct pipe_transfer *transfer, 230 const struct pipe_box *box) 231{ 232 struct svga_screen *ss = svga_screen(pipe->screen); 233 struct svga_buffer *sbuf = svga_buffer(transfer->resource); 234 235 unsigned offset = transfer->box.x + box->x; 236 unsigned length = box->width; 237 238 assert(transfer->usage & PIPE_TRANSFER_WRITE); 239 assert(transfer->usage & PIPE_TRANSFER_FLUSH_EXPLICIT); 240 241 pipe_mutex_lock(ss->swc_mutex); 242 svga_buffer_add_range(sbuf, offset, offset + length); 243 pipe_mutex_unlock(ss->swc_mutex); 244} 245 246 247static void 248svga_buffer_transfer_unmap( struct pipe_context *pipe, 249 struct pipe_transfer *transfer ) 250{ 251 struct svga_screen *ss = svga_screen(pipe->screen); 252 struct svga_winsys_screen *sws = ss->sws; 253 struct svga_buffer *sbuf = svga_buffer(transfer->resource); 254 255 pipe_mutex_lock(ss->swc_mutex); 256 257 assert(sbuf->map.count); 258 if (sbuf->map.count) { 259 --sbuf->map.count; 260 } 261 262 if (sbuf->hwbuf) { 263 sws->buffer_unmap(sws, sbuf->hwbuf); 264 } 265 266 if (transfer->usage & PIPE_TRANSFER_WRITE) { 267 if (!(transfer->usage & PIPE_TRANSFER_FLUSH_EXPLICIT)) { 268 /* 269 * Mapped range not flushed explicitly, so flush the whole buffer, 270 * and tell the host to discard the contents when processing the DMA 271 * command. 272 */ 273 274 SVGA_DBG(DEBUG_DMA, "flushing the whole buffer\n"); 275 276 sbuf->dma.flags.discard = TRUE; 277 278 svga_buffer_add_range(sbuf, 0, sbuf->b.b.width0); 279 } 280 } 281 282 pipe_mutex_unlock(ss->swc_mutex); 283} 284 285 286/** 287 * Destroy transfer 288 */ 289static void 290svga_buffer_transfer_destroy(struct pipe_context *pipe, 291 struct pipe_transfer *transfer) 292{ 293 FREE(transfer); 294} 295 296 297static void 298svga_buffer_destroy( struct pipe_screen *screen, 299 struct pipe_resource *buf ) 300{ 301 struct svga_screen *ss = svga_screen(screen); 302 struct svga_buffer *sbuf = svga_buffer( buf ); 303 304 assert(!p_atomic_read(&buf->reference.count)); 305 306 assert(!sbuf->dma.pending); 307 308 if(sbuf->handle) 309 svga_buffer_destroy_host_surface(ss, sbuf); 310 311 if(sbuf->uploaded.buffer) 312 pipe_resource_reference(&sbuf->uploaded.buffer, NULL); 313 314 if(sbuf->hwbuf) 315 svga_buffer_destroy_hw_storage(ss, sbuf); 316 317 if(sbuf->swbuf && !sbuf->user) 318 align_free(sbuf->swbuf); 319 320 FREE(sbuf); 321} 322 323 324struct u_resource_vtbl svga_buffer_vtbl = 325{ 326 u_default_resource_get_handle, /* get_handle */ 327 svga_buffer_destroy, /* resource_destroy */ 328 svga_buffer_get_transfer, /* get_transfer */ 329 svga_buffer_transfer_destroy, /* transfer_destroy */ 330 svga_buffer_transfer_map, /* transfer_map */ 331 svga_buffer_transfer_flush_region, /* transfer_flush_region */ 332 svga_buffer_transfer_unmap, /* transfer_unmap */ 333 u_default_transfer_inline_write /* transfer_inline_write */ 334}; 335 336 337 338struct pipe_resource * 339svga_buffer_create(struct pipe_screen *screen, 340 const struct pipe_resource *template) 341{ 342 struct svga_screen *ss = svga_screen(screen); 343 struct svga_buffer *sbuf; 344 345 sbuf = CALLOC_STRUCT(svga_buffer); 346 if(!sbuf) 347 goto error1; 348 349 sbuf->b.b = *template; 350 sbuf->b.vtbl = &svga_buffer_vtbl; 351 pipe_reference_init(&sbuf->b.b.reference, 1); 352 sbuf->b.b.screen = screen; 353 354 if(svga_buffer_needs_hw_storage(template->bind)) { 355 if(svga_buffer_create_host_surface(ss, sbuf) != PIPE_OK) 356 goto error2; 357 } 358 else { 359 sbuf->swbuf = align_malloc(template->width0, 64); 360 if(!sbuf->swbuf) 361 goto error2; 362 } 363 364 debug_reference(&sbuf->b.b.reference, 365 (debug_reference_descriptor)debug_describe_resource, 0); 366 367 return &sbuf->b.b; 368 369error2: 370 FREE(sbuf); 371error1: 372 return NULL; 373} 374 375struct pipe_resource * 376svga_user_buffer_create(struct pipe_screen *screen, 377 void *ptr, 378 unsigned bytes, 379 unsigned bind) 380{ 381 struct svga_buffer *sbuf; 382 383 sbuf = CALLOC_STRUCT(svga_buffer); 384 if(!sbuf) 385 goto no_sbuf; 386 387 pipe_reference_init(&sbuf->b.b.reference, 1); 388 sbuf->b.vtbl = &svga_buffer_vtbl; 389 sbuf->b.b.screen = screen; 390 sbuf->b.b.format = PIPE_FORMAT_R8_UNORM; /* ?? */ 391 sbuf->b.b.usage = PIPE_USAGE_IMMUTABLE; 392 sbuf->b.b.bind = bind; 393 sbuf->b.b.width0 = bytes; 394 sbuf->b.b.height0 = 1; 395 sbuf->b.b.depth0 = 1; 396 sbuf->b.b.array_size = 1; 397 398 sbuf->swbuf = ptr; 399 sbuf->user = TRUE; 400 401 debug_reference(&sbuf->b.b.reference, 402 (debug_reference_descriptor)debug_describe_resource, 0); 403 404 return &sbuf->b.b; 405 406no_sbuf: 407 return NULL; 408} 409 410 411 412