vmw_context.c revision c9f98673c5b6830cd1f41c0c53a9e5e299d47464
1/********************************************************** 2 * Copyright 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 27#include "svga_cmd.h" 28 29#include "util/u_debug.h" 30#include "util/u_memory.h" 31#include "util/u_debug_stack.h" 32#include "pipebuffer/pb_buffer.h" 33#include "pipebuffer/pb_validate.h" 34 35#include "svga_winsys.h" 36#include "vmw_context.h" 37#include "vmw_screen.h" 38#include "vmw_buffer.h" 39#include "vmw_surface.h" 40#include "vmw_fence.h" 41 42#define VMW_COMMAND_SIZE (64*1024) 43#define VMW_SURFACE_RELOCS (1024) 44#define VMW_REGION_RELOCS (512) 45 46#define VMW_MUST_FLUSH_STACK 8 47 48struct vmw_region_relocation 49{ 50 struct SVGAGuestPtr *where; 51 struct pb_buffer *buffer; 52 /* TODO: put offset info inside where */ 53 uint32 offset; 54}; 55 56struct vmw_svga_winsys_context 57{ 58 struct svga_winsys_context base; 59 60 struct vmw_winsys_screen *vws; 61 62#ifdef DEBUG 63 boolean must_flush; 64 struct debug_stack_frame must_flush_stack[VMW_MUST_FLUSH_STACK]; 65#endif 66 67 struct { 68 uint8_t buffer[VMW_COMMAND_SIZE]; 69 uint32_t size; 70 uint32_t used; 71 uint32_t reserved; 72 } command; 73 74 struct { 75 struct vmw_svga_winsys_surface *handles[VMW_SURFACE_RELOCS]; 76 uint32_t size; 77 uint32_t used; 78 uint32_t staged; 79 uint32_t reserved; 80 } surface; 81 82 struct { 83 struct vmw_region_relocation relocs[VMW_REGION_RELOCS]; 84 uint32_t size; 85 uint32_t used; 86 uint32_t staged; 87 uint32_t reserved; 88 } region; 89 90 struct pb_validate *validate; 91 92 uint32_t last_fence; 93 94 /** 95 * The amount of GMR that is referred by the commands currently batched 96 * in the context. 97 */ 98 uint32_t seen_regions; 99 100 /** 101 * Whether this context should fail to reserve more commands, not because it 102 * ran out of command space, but because a substantial ammount of GMR was 103 * referred. 104 */ 105 boolean preemptive_flush; 106}; 107 108 109static INLINE struct vmw_svga_winsys_context * 110vmw_svga_winsys_context(struct svga_winsys_context *swc) 111{ 112 assert(swc); 113 return (struct vmw_svga_winsys_context *)swc; 114} 115 116 117static enum pipe_error 118vmw_swc_flush(struct svga_winsys_context *swc, 119 struct pipe_fence_handle **pfence) 120{ 121 struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc); 122 struct pipe_fence_handle *fence = NULL; 123 unsigned i; 124 enum pipe_error ret; 125 126 ret = pb_validate_validate(vswc->validate); 127 assert(ret == PIPE_OK); 128 if(ret == PIPE_OK) { 129 130 /* Apply relocations */ 131 for(i = 0; i < vswc->region.used; ++i) { 132 struct vmw_region_relocation *reloc = &vswc->region.relocs[i]; 133 struct SVGAGuestPtr ptr; 134 135 if(!vmw_gmr_bufmgr_region_ptr(reloc->buffer, &ptr)) 136 assert(0); 137 138 ptr.offset += reloc->offset; 139 140 *reloc->where = ptr; 141 } 142 143 if (vswc->command.used) 144 vmw_ioctl_command(vswc->vws, 145 vswc->command.buffer, 146 vswc->command.used, 147 &vswc->last_fence); 148 149 fence = vmw_pipe_fence(vswc->last_fence); 150 151 pb_validate_fence(vswc->validate, fence); 152 } 153 154 vswc->command.used = 0; 155 vswc->command.reserved = 0; 156 157 for(i = 0; i < vswc->surface.used + vswc->surface.staged; ++i) { 158 struct vmw_svga_winsys_surface *vsurf = 159 vswc->surface.handles[i]; 160 p_atomic_dec(&vsurf->validated); 161 vmw_svga_winsys_surface_reference(&vswc->surface.handles[i], NULL); 162 } 163 164 vswc->surface.used = 0; 165 vswc->surface.reserved = 0; 166 167 for(i = 0; i < vswc->region.used + vswc->region.staged; ++i) { 168 pb_reference(&vswc->region.relocs[i].buffer, NULL); 169 } 170 171 vswc->region.used = 0; 172 vswc->region.reserved = 0; 173 174#ifdef DEBUG 175 vswc->must_flush = FALSE; 176#endif 177 vswc->preemptive_flush = FALSE; 178 vswc->seen_regions = 0; 179 180 if(pfence) 181 *pfence = fence; 182 183 return ret; 184} 185 186 187static void * 188vmw_swc_reserve(struct svga_winsys_context *swc, 189 uint32_t nr_bytes, uint32_t nr_relocs ) 190{ 191 struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc); 192 193#ifdef DEBUG 194 /* Check if somebody forgot to check the previous failure */ 195 if(vswc->must_flush) { 196 debug_printf("Forgot to flush:\n"); 197 debug_backtrace_dump(vswc->must_flush_stack, VMW_MUST_FLUSH_STACK); 198 assert(!vswc->must_flush); 199 } 200#endif 201 202 assert(nr_bytes <= vswc->command.size); 203 if(nr_bytes > vswc->command.size) 204 return NULL; 205 206 if(vswc->preemptive_flush || 207 vswc->command.used + nr_bytes > vswc->command.size || 208 vswc->surface.used + nr_relocs > vswc->surface.size || 209 vswc->region.used + nr_relocs > vswc->region.size) { 210#ifdef DEBUG 211 vswc->must_flush = TRUE; 212 debug_backtrace_capture(vswc->must_flush_stack, 1, 213 VMW_MUST_FLUSH_STACK); 214#endif 215 return NULL; 216 } 217 218 assert(vswc->command.used + nr_bytes <= vswc->command.size); 219 assert(vswc->surface.used + nr_relocs <= vswc->surface.size); 220 assert(vswc->region.used + nr_relocs <= vswc->region.size); 221 222 vswc->command.reserved = nr_bytes; 223 vswc->surface.reserved = nr_relocs; 224 vswc->surface.staged = 0; 225 vswc->region.reserved = nr_relocs; 226 vswc->region.staged = 0; 227 228 return vswc->command.buffer + vswc->command.used; 229} 230 231 232static void 233vmw_swc_surface_relocation(struct svga_winsys_context *swc, 234 uint32 *where, 235 struct svga_winsys_surface *surface, 236 unsigned flags) 237{ 238 struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc); 239 struct vmw_svga_winsys_surface *vsurf; 240 241 if(!surface) { 242 *where = SVGA3D_INVALID_ID; 243 return; 244 } 245 246 assert(vswc->surface.staged < vswc->surface.reserved); 247 248 vsurf = vmw_svga_winsys_surface(surface); 249 250 *where = vsurf->sid; 251 252 vmw_svga_winsys_surface_reference(&vswc->surface.handles[vswc->surface.used + vswc->surface.staged], vsurf); 253 p_atomic_inc(&vsurf->validated); 254 ++vswc->surface.staged; 255} 256 257 258static void 259vmw_swc_region_relocation(struct svga_winsys_context *swc, 260 struct SVGAGuestPtr *where, 261 struct svga_winsys_buffer *buffer, 262 uint32 offset, 263 unsigned flags) 264{ 265 struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc); 266 struct vmw_region_relocation *reloc; 267 enum pipe_error ret; 268 269 assert(vswc->region.staged < vswc->region.reserved); 270 271 reloc = &vswc->region.relocs[vswc->region.used + vswc->region.staged]; 272 reloc->where = where; 273 pb_reference(&reloc->buffer, vmw_pb_buffer(buffer)); 274 reloc->offset = offset; 275 276 ++vswc->region.staged; 277 278 ret = pb_validate_add_buffer(vswc->validate, reloc->buffer, flags); 279 /* TODO: Update pipebuffer to reserve buffers and not fail here */ 280 assert(ret == PIPE_OK); 281 282 /* 283 * Flush preemptively the FIFO commands to keep the GMR working set within 284 * the GMR pool size. 285 * 286 * This is necessary for applications like SPECviewperf that generate huge 287 * amounts of immediate vertex data, so that we don't pile up too much of 288 * that vertex data neither in the guest nor in the host. 289 * 290 * Note that in the current implementation if a region is referred twice in 291 * a command stream, it will be accounted twice. We could detect repeated 292 * regions and count only once, but there is no incentive to do that, since 293 * regions are typically short-lived; always referred in a single command; 294 * and at the worst we just flush the commands a bit sooner, which for the 295 * SVGA virtual device it's not a performance issue since flushing commands 296 * to the FIFO won't cause flushing in the host. 297 */ 298 vswc->seen_regions += reloc->buffer->base.size; 299 if(vswc->seen_regions >= VMW_GMR_POOL_SIZE/2) 300 vswc->preemptive_flush = TRUE; 301} 302 303 304static void 305vmw_swc_commit(struct svga_winsys_context *swc) 306{ 307 struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc); 308 309 assert(vswc->command.reserved); 310 assert(vswc->command.used + vswc->command.reserved <= vswc->command.size); 311 vswc->command.used += vswc->command.reserved; 312 vswc->command.reserved = 0; 313 314 assert(vswc->surface.staged <= vswc->surface.reserved); 315 assert(vswc->surface.used + vswc->surface.staged <= vswc->surface.size); 316 vswc->surface.used += vswc->surface.staged; 317 vswc->surface.staged = 0; 318 vswc->surface.reserved = 0; 319 320 assert(vswc->region.staged <= vswc->region.reserved); 321 assert(vswc->region.used + vswc->region.staged <= vswc->region.size); 322 vswc->region.used += vswc->region.staged; 323 vswc->region.staged = 0; 324 vswc->region.reserved = 0; 325} 326 327 328static void 329vmw_swc_destroy(struct svga_winsys_context *swc) 330{ 331 struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc); 332 unsigned i; 333 334 for(i = 0; i < vswc->region.used; ++i) { 335 pb_reference(&vswc->region.relocs[i].buffer, NULL); 336 } 337 338 for(i = 0; i < vswc->surface.used; ++i) { 339 p_atomic_dec(&vswc->surface.handles[i]->validated); 340 vmw_svga_winsys_surface_reference(&vswc->surface.handles[i], NULL); 341 } 342 pb_validate_destroy(vswc->validate); 343 vmw_ioctl_context_destroy(vswc->vws, swc->cid); 344 FREE(vswc); 345} 346 347 348struct svga_winsys_context * 349vmw_svga_winsys_context_create(struct svga_winsys_screen *sws) 350{ 351 struct vmw_winsys_screen *vws = vmw_winsys_screen(sws); 352 struct vmw_svga_winsys_context *vswc; 353 354 vswc = CALLOC_STRUCT(vmw_svga_winsys_context); 355 if(!vswc) 356 return NULL; 357 358 vswc->base.destroy = vmw_swc_destroy; 359 vswc->base.reserve = vmw_swc_reserve; 360 vswc->base.surface_relocation = vmw_swc_surface_relocation; 361 vswc->base.region_relocation = vmw_swc_region_relocation; 362 vswc->base.commit = vmw_swc_commit; 363 vswc->base.flush = vmw_swc_flush; 364 365 vswc->base.cid = vmw_ioctl_context_create(vws); 366 367 vswc->vws = vws; 368 369 vswc->command.size = VMW_COMMAND_SIZE; 370 vswc->surface.size = VMW_SURFACE_RELOCS; 371 vswc->region.size = VMW_REGION_RELOCS; 372 373 vswc->validate = pb_validate_create(); 374 if(!vswc->validate) { 375 FREE(vswc); 376 return NULL; 377 } 378 379 return &vswc->base; 380} 381 382 383