pb_buffer_fenced.c revision a6d866f72c88d48d2bcfb3e3c882fdb639b5a8ce
1/************************************************************************** 2 * 3 * Copyright 2007 Tungsten Graphics, Inc., Cedar Park, Texas. 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 TUNGSTEN GRAPHICS 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/** 29 * \file 30 * Implementation of fenced buffers. 31 * 32 * \author Jose Fonseca <jrfonseca-at-tungstengraphics-dot-com> 33 * \author Thomas Hellström <thomas-at-tungstengraphics-dot-com> 34 */ 35 36 37#include "pipe/p_config.h" 38 39#if defined(PIPE_OS_LINUX) 40#include <unistd.h> 41#include <sched.h> 42#endif 43 44#include "pipe/p_compiler.h" 45#include "pipe/p_error.h" 46#include "pipe/p_debug.h" 47#include "pipe/p_winsys.h" 48#include "pipe/p_thread.h" 49#include "util/u_memory.h" 50#include "util/u_double_list.h" 51 52#include "pb_buffer.h" 53#include "pb_buffer_fenced.h" 54 55 56 57/** 58 * Convenience macro (type safe). 59 */ 60#define SUPER(__derived) (&(__derived)->base) 61 62 63struct fenced_buffer_list 64{ 65 pipe_mutex mutex; 66 67 struct pipe_winsys *winsys; 68 69 size_t numDelayed; 70 71 struct list_head delayed; 72}; 73 74 75/** 76 * Wrapper around a pipe buffer which adds fencing and reference counting. 77 */ 78struct fenced_buffer 79{ 80 struct pb_buffer base; 81 82 struct pb_buffer *buffer; 83 84 /* FIXME: protect access with mutex */ 85 86 /** 87 * A bitmask of PIPE_BUFFER_USAGE_CPU/GPU_READ/WRITE describing the current 88 * buffer usage. 89 */ 90 unsigned flags; 91 92 unsigned mapcount; 93 struct pb_validate *vl; 94 unsigned validation_flags; 95 struct pipe_fence_handle *fence; 96 97 struct list_head head; 98 struct fenced_buffer_list *list; 99}; 100 101 102static INLINE struct fenced_buffer * 103fenced_buffer(struct pb_buffer *buf) 104{ 105 assert(buf); 106 return (struct fenced_buffer *)buf; 107} 108 109 110static INLINE void 111_fenced_buffer_add(struct fenced_buffer *fenced_buf) 112{ 113 struct fenced_buffer_list *fenced_list = fenced_buf->list; 114 115 assert(fenced_buf->base.base.refcount); 116 assert(fenced_buf->flags & PIPE_BUFFER_USAGE_GPU_READ_WRITE); 117 assert(fenced_buf->fence); 118 119 assert(!fenced_buf->head.prev); 120 assert(!fenced_buf->head.next); 121 LIST_ADDTAIL(&fenced_buf->head, &fenced_list->delayed); 122 ++fenced_list->numDelayed; 123} 124 125 126/** 127 * Actually destroy the buffer. 128 */ 129static INLINE void 130_fenced_buffer_destroy(struct fenced_buffer *fenced_buf) 131{ 132 assert(!fenced_buf->base.base.refcount); 133 assert(!fenced_buf->fence); 134 pb_reference(&fenced_buf->buffer, NULL); 135 FREE(fenced_buf); 136} 137 138 139static INLINE void 140_fenced_buffer_remove(struct fenced_buffer_list *fenced_list, 141 struct fenced_buffer *fenced_buf) 142{ 143 struct pipe_winsys *winsys = fenced_list->winsys; 144 145 assert(fenced_buf->fence); 146 assert(fenced_buf->list == fenced_list); 147 148 winsys->fence_reference(winsys, &fenced_buf->fence, NULL); 149 fenced_buf->flags &= ~PIPE_BUFFER_USAGE_GPU_READ_WRITE; 150 151 assert(fenced_buf->head.prev); 152 assert(fenced_buf->head.next); 153 LIST_DEL(&fenced_buf->head); 154#ifdef DEBUG 155 fenced_buf->head.prev = NULL; 156 fenced_buf->head.next = NULL; 157#endif 158 159 assert(fenced_list->numDelayed); 160 --fenced_list->numDelayed; 161 162 if(!fenced_buf->base.base.refcount) 163 _fenced_buffer_destroy(fenced_buf); 164} 165 166 167static INLINE enum pipe_error 168_fenced_buffer_finish(struct fenced_buffer *fenced_buf) 169{ 170 struct fenced_buffer_list *fenced_list = fenced_buf->list; 171 struct pipe_winsys *winsys = fenced_list->winsys; 172 173#if 0 174 debug_warning("waiting for GPU"); 175#endif 176 177 assert(fenced_buf->fence); 178 if(fenced_buf->fence) { 179 if(winsys->fence_finish(winsys, fenced_buf->fence, 0) != 0) { 180 return PIPE_ERROR; 181 } 182 /* Remove from the fenced list */ 183 /* TODO: remove consequents */ 184 _fenced_buffer_remove(fenced_list, fenced_buf); 185 } 186 187 fenced_buf->flags &= ~PIPE_BUFFER_USAGE_GPU_READ_WRITE; 188 return PIPE_OK; 189} 190 191 192/** 193 * Free as many fenced buffers from the list head as possible. 194 */ 195static void 196_fenced_buffer_list_check_free(struct fenced_buffer_list *fenced_list, 197 int wait) 198{ 199 struct pipe_winsys *winsys = fenced_list->winsys; 200 struct list_head *curr, *next; 201 struct fenced_buffer *fenced_buf; 202 struct pipe_fence_handle *prev_fence = NULL; 203 204 curr = fenced_list->delayed.next; 205 next = curr->next; 206 while(curr != &fenced_list->delayed) { 207 fenced_buf = LIST_ENTRY(struct fenced_buffer, curr, head); 208 209 if(fenced_buf->fence != prev_fence) { 210 int signaled; 211 if (wait) 212 signaled = winsys->fence_finish(winsys, fenced_buf->fence, 0); 213 else 214 signaled = winsys->fence_signalled(winsys, fenced_buf->fence, 0); 215 if (signaled != 0) 216 break; 217 prev_fence = fenced_buf->fence; 218 } 219 else { 220 assert(winsys->fence_signalled(winsys, fenced_buf->fence, 0) == 0); 221 } 222 223 _fenced_buffer_remove(fenced_list, fenced_buf); 224 225 curr = next; 226 next = curr->next; 227 } 228} 229 230 231static void 232fenced_buffer_destroy(struct pb_buffer *buf) 233{ 234 struct fenced_buffer *fenced_buf = fenced_buffer(buf); 235 struct fenced_buffer_list *fenced_list = fenced_buf->list; 236 237 pipe_mutex_lock(fenced_list->mutex); 238 assert(fenced_buf->base.base.refcount == 0); 239 if (fenced_buf->fence) { 240 struct pipe_winsys *winsys = fenced_list->winsys; 241 if(winsys->fence_signalled(winsys, fenced_buf->fence, 0) == 0) { 242 struct list_head *curr, *prev; 243 curr = &fenced_buf->head; 244 prev = curr->prev; 245 do { 246 fenced_buf = LIST_ENTRY(struct fenced_buffer, curr, head); 247 assert(winsys->fence_signalled(winsys, fenced_buf->fence, 0) == 0); 248 _fenced_buffer_remove(fenced_list, fenced_buf); 249 curr = prev; 250 prev = curr->prev; 251 } while (curr != &fenced_list->delayed); 252 } 253 else { 254 /* delay destruction */ 255 } 256 } 257 else { 258 _fenced_buffer_destroy(fenced_buf); 259 } 260 pipe_mutex_unlock(fenced_list->mutex); 261} 262 263 264static void * 265fenced_buffer_map(struct pb_buffer *buf, 266 unsigned flags) 267{ 268 struct fenced_buffer *fenced_buf = fenced_buffer(buf); 269 void *map; 270 271 assert(flags & PIPE_BUFFER_USAGE_CPU_READ_WRITE); 272 assert(!(flags & ~PIPE_BUFFER_USAGE_CPU_READ_WRITE)); 273 flags &= PIPE_BUFFER_USAGE_CPU_READ_WRITE; 274 275 /* Check for GPU read/write access */ 276 if(fenced_buf->flags & PIPE_BUFFER_USAGE_GPU_WRITE) { 277 /* Wait for the GPU to finish writing */ 278 _fenced_buffer_finish(fenced_buf); 279 } 280 281#if 0 282 /* Check for CPU write access (read is OK) */ 283 if(fenced_buf->flags & PIPE_BUFFER_USAGE_CPU_READ_WRITE) { 284 /* this is legal -- just for debugging */ 285 debug_warning("concurrent CPU writes"); 286 } 287#endif 288 289 map = pb_map(fenced_buf->buffer, flags); 290 if(map) { 291 ++fenced_buf->mapcount; 292 fenced_buf->flags |= flags; 293 } 294 295 return map; 296} 297 298 299static void 300fenced_buffer_unmap(struct pb_buffer *buf) 301{ 302 struct fenced_buffer *fenced_buf = fenced_buffer(buf); 303 assert(fenced_buf->mapcount); 304 if(fenced_buf->mapcount) { 305 pb_unmap(fenced_buf->buffer); 306 --fenced_buf->mapcount; 307 if(!fenced_buf->mapcount) 308 fenced_buf->flags &= ~PIPE_BUFFER_USAGE_CPU_READ_WRITE; 309 } 310} 311 312 313static enum pipe_error 314fenced_buffer_validate(struct pb_buffer *buf, 315 struct pb_validate *vl, 316 unsigned flags) 317{ 318 struct fenced_buffer *fenced_buf = fenced_buffer(buf); 319 enum pipe_error ret; 320 321 if(!vl) { 322 /* invalidate */ 323 fenced_buf->vl = NULL; 324 fenced_buf->validation_flags = 0; 325 return PIPE_OK; 326 } 327 328 assert(flags & PIPE_BUFFER_USAGE_GPU_READ_WRITE); 329 assert(!(flags & ~PIPE_BUFFER_USAGE_GPU_READ_WRITE)); 330 flags &= PIPE_BUFFER_USAGE_GPU_READ_WRITE; 331 332 /* Buffer cannot be validated in two different lists */ 333 if(fenced_buf->vl && fenced_buf->vl != vl) 334 return PIPE_ERROR_RETRY; 335 336 /* Do not validate if buffer is still mapped */ 337 if(fenced_buf->flags & PIPE_BUFFER_USAGE_CPU_READ_WRITE) { 338 /* TODO: wait for the thread that mapped the buffer to unmap it */ 339 return PIPE_ERROR_RETRY; 340 } 341 342 /* Allow concurrent GPU reads, but serialize GPU writes */ 343 if(fenced_buf->flags & PIPE_BUFFER_USAGE_GPU_READ_WRITE) { 344 if((fenced_buf->flags | flags) & PIPE_BUFFER_USAGE_GPU_WRITE) { 345 _fenced_buffer_finish(fenced_buf); 346 } 347 } 348 349 if(fenced_buf->vl == vl && 350 (fenced_buf->validation_flags & flags) == flags) { 351 /* Nothing to do -- buffer already validated */ 352 return PIPE_OK; 353 } 354 355 /* Final sanity checking */ 356 assert(!(fenced_buf->flags & PIPE_BUFFER_USAGE_CPU_READ_WRITE)); 357 assert(!(fenced_buf->flags & PIPE_BUFFER_USAGE_GPU_WRITE)); 358 assert(!fenced_buf->mapcount); 359 360 ret = pb_validate(fenced_buf->buffer, vl, flags); 361 if (ret != PIPE_OK) 362 return ret; 363 364 fenced_buf->vl = vl; 365 fenced_buf->validation_flags |= flags; 366 367 return PIPE_OK; 368} 369 370 371static void 372fenced_buffer_fence(struct pb_buffer *buf, 373 struct pipe_fence_handle *fence) 374{ 375 struct fenced_buffer *fenced_buf; 376 struct fenced_buffer_list *fenced_list; 377 struct pipe_winsys *winsys; 378 379 fenced_buf = fenced_buffer(buf); 380 fenced_list = fenced_buf->list; 381 winsys = fenced_list->winsys; 382 383 if(fence == fenced_buf->fence) { 384 /* Nothing to do */ 385 return; 386 } 387 388 assert(fenced_buf->vl); 389 assert(fenced_buf->validation_flags); 390 391 pipe_mutex_lock(fenced_list->mutex); 392 if (fenced_buf->fence) 393 _fenced_buffer_remove(fenced_list, fenced_buf); 394 if (fence) { 395 winsys->fence_reference(winsys, &fenced_buf->fence, fence); 396 fenced_buf->flags |= fenced_buf->validation_flags; 397 _fenced_buffer_add(fenced_buf); 398 } 399 pipe_mutex_unlock(fenced_list->mutex); 400 401 pb_fence(fenced_buf->buffer, fence); 402 403 fenced_buf->vl = NULL; 404 fenced_buf->validation_flags = 0; 405} 406 407 408static void 409fenced_buffer_get_base_buffer(struct pb_buffer *buf, 410 struct pb_buffer **base_buf, 411 unsigned *offset) 412{ 413 struct fenced_buffer *fenced_buf = fenced_buffer(buf); 414 pb_get_base_buffer(fenced_buf->buffer, base_buf, offset); 415} 416 417 418static const struct pb_vtbl 419fenced_buffer_vtbl = { 420 fenced_buffer_destroy, 421 fenced_buffer_map, 422 fenced_buffer_unmap, 423 fenced_buffer_validate, 424 fenced_buffer_fence, 425 fenced_buffer_get_base_buffer 426}; 427 428 429struct pb_buffer * 430fenced_buffer_create(struct fenced_buffer_list *fenced_list, 431 struct pb_buffer *buffer) 432{ 433 struct fenced_buffer *buf; 434 435 if(!buffer) 436 return NULL; 437 438 buf = CALLOC_STRUCT(fenced_buffer); 439 if(!buf) { 440 pb_reference(&buffer, NULL); 441 return NULL; 442 } 443 444 buf->base.base.refcount = 1; 445 buf->base.base.alignment = buffer->base.alignment; 446 buf->base.base.usage = buffer->base.usage; 447 buf->base.base.size = buffer->base.size; 448 449 buf->base.vtbl = &fenced_buffer_vtbl; 450 buf->buffer = buffer; 451 buf->list = fenced_list; 452 453 return &buf->base; 454} 455 456 457struct fenced_buffer_list * 458fenced_buffer_list_create(struct pipe_winsys *winsys) 459{ 460 struct fenced_buffer_list *fenced_list; 461 462 fenced_list = CALLOC_STRUCT(fenced_buffer_list); 463 if (!fenced_list) 464 return NULL; 465 466 fenced_list->winsys = winsys; 467 468 LIST_INITHEAD(&fenced_list->delayed); 469 470 fenced_list->numDelayed = 0; 471 472 pipe_mutex_init(fenced_list->mutex); 473 474 return fenced_list; 475} 476 477 478void 479fenced_buffer_list_check_free(struct fenced_buffer_list *fenced_list, 480 int wait) 481{ 482 pipe_mutex_lock(fenced_list->mutex); 483 _fenced_buffer_list_check_free(fenced_list, wait); 484 pipe_mutex_unlock(fenced_list->mutex); 485} 486 487 488void 489fenced_buffer_list_destroy(struct fenced_buffer_list *fenced_list) 490{ 491 pipe_mutex_lock(fenced_list->mutex); 492 493 /* Wait on outstanding fences */ 494 while (fenced_list->numDelayed) { 495 pipe_mutex_unlock(fenced_list->mutex); 496#if defined(PIPE_OS_LINUX) 497 sched_yield(); 498#endif 499 _fenced_buffer_list_check_free(fenced_list, 1); 500 pipe_mutex_lock(fenced_list->mutex); 501 } 502 503 pipe_mutex_unlock(fenced_list->mutex); 504 505 FREE(fenced_list); 506} 507 508 509