pb_buffer_fenced.c revision fe64aa0c8eb71e708a3530f8fec2c7df94d90e36
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 José 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#define PIPE_BUFFER_USAGE_CPU_READ_WRITE \ 63 ( PIPE_BUFFER_USAGE_CPU_READ | PIPE_BUFFER_USAGE_CPU_WRITE ) 64#define PIPE_BUFFER_USAGE_GPU_READ_WRITE \ 65 ( PIPE_BUFFER_USAGE_GPU_READ | PIPE_BUFFER_USAGE_GPU_WRITE ) 66#define PIPE_BUFFER_USAGE_WRITE \ 67 ( PIPE_BUFFER_USAGE_CPU_WRITE | PIPE_BUFFER_USAGE_GPU_WRITE ) 68 69 70struct fenced_buffer_list 71{ 72 pipe_mutex mutex; 73 74 struct pipe_winsys *winsys; 75 76 size_t numDelayed; 77 struct list_head delayed; 78 79#ifdef DEBUG 80 size_t numUnfenced; 81 struct list_head unfenced; 82#endif 83}; 84 85 86/** 87 * Wrapper around a pipe buffer which adds fencing and reference counting. 88 */ 89struct fenced_buffer 90{ 91 struct pb_buffer base; 92 93 struct pb_buffer *buffer; 94 95 /* FIXME: protect access with mutex */ 96 97 /** 98 * A bitmask of PIPE_BUFFER_USAGE_CPU/GPU_READ/WRITE describing the current 99 * buffer usage. 100 */ 101 unsigned flags; 102 103 unsigned mapcount; 104 struct pipe_fence_handle *fence; 105 106 struct list_head head; 107 struct fenced_buffer_list *list; 108}; 109 110 111static INLINE struct fenced_buffer * 112fenced_buffer(struct pb_buffer *buf) 113{ 114 assert(buf); 115 assert(buf->vtbl == &fenced_buffer_vtbl); 116 return (struct fenced_buffer *)buf; 117} 118 119 120static INLINE void 121_fenced_buffer_add(struct fenced_buffer *fenced_buf) 122{ 123 struct fenced_buffer_list *fenced_list = fenced_buf->list; 124 125 assert(fenced_buf->base.base.refcount); 126 assert(fenced_buf->flags & PIPE_BUFFER_USAGE_GPU_READ_WRITE); 127 assert(fenced_buf->fence); 128 129#ifdef DEBUG 130 LIST_DEL(&fenced_buf->head); 131 assert(fenced_list->numUnfenced); 132 --fenced_list->numUnfenced; 133#endif 134 LIST_ADDTAIL(&fenced_buf->head, &fenced_list->delayed); 135 ++fenced_list->numDelayed; 136} 137 138 139/** 140 * Actually destroy the buffer. 141 */ 142static INLINE void 143_fenced_buffer_destroy(struct fenced_buffer *fenced_buf) 144{ 145 struct fenced_buffer_list *fenced_list = fenced_buf->list; 146 147 assert(!fenced_buf->base.base.refcount); 148 assert(!fenced_buf->fence); 149#ifdef DEBUG 150 assert(fenced_buf->head.prev); 151 assert(fenced_buf->head.next); 152 LIST_DEL(&fenced_buf->head); 153 assert(fenced_list->numUnfenced); 154 --fenced_list->numUnfenced; 155#else 156 (void)fenced_list; 157#endif 158 pb_reference(&fenced_buf->buffer, NULL); 159 FREE(fenced_buf); 160} 161 162 163static INLINE void 164_fenced_buffer_remove(struct fenced_buffer_list *fenced_list, 165 struct fenced_buffer *fenced_buf) 166{ 167 struct pipe_winsys *winsys = fenced_list->winsys; 168 169 assert(fenced_buf->fence); 170 assert(fenced_buf->list == fenced_list); 171 172 winsys->fence_reference(winsys, &fenced_buf->fence, NULL); 173 fenced_buf->flags &= ~PIPE_BUFFER_USAGE_GPU_READ_WRITE; 174 175 assert(fenced_buf->head.prev); 176 assert(fenced_buf->head.next); 177 178 LIST_DEL(&fenced_buf->head); 179 assert(fenced_list->numDelayed); 180 --fenced_list->numDelayed; 181 182#ifdef DEBUG 183 LIST_ADDTAIL(&fenced_buf->head, &fenced_list->unfenced); 184 ++fenced_list->numUnfenced; 185#endif 186 187 if(!fenced_buf->base.base.refcount) 188 _fenced_buffer_destroy(fenced_buf); 189} 190 191 192static INLINE enum pipe_error 193_fenced_buffer_finish(struct fenced_buffer *fenced_buf) 194{ 195 struct fenced_buffer_list *fenced_list = fenced_buf->list; 196 struct pipe_winsys *winsys = fenced_list->winsys; 197 198#if 0 199 debug_warning("waiting for GPU"); 200#endif 201 202 assert(fenced_buf->fence); 203 if(fenced_buf->fence) { 204 if(winsys->fence_finish(winsys, fenced_buf->fence, 0) != 0) { 205 return PIPE_ERROR; 206 } 207 /* Remove from the fenced list */ 208 /* TODO: remove consequents */ 209 _fenced_buffer_remove(fenced_list, fenced_buf); 210 } 211 212 fenced_buf->flags &= ~PIPE_BUFFER_USAGE_GPU_READ_WRITE; 213 return PIPE_OK; 214} 215 216 217/** 218 * Free as many fenced buffers from the list head as possible. 219 */ 220static void 221_fenced_buffer_list_check_free(struct fenced_buffer_list *fenced_list, 222 int wait) 223{ 224 struct pipe_winsys *winsys = fenced_list->winsys; 225 struct list_head *curr, *next; 226 struct fenced_buffer *fenced_buf; 227 struct pipe_fence_handle *prev_fence = NULL; 228 229 curr = fenced_list->delayed.next; 230 next = curr->next; 231 while(curr != &fenced_list->delayed) { 232 fenced_buf = LIST_ENTRY(struct fenced_buffer, curr, head); 233 234 if(fenced_buf->fence != prev_fence) { 235 int signaled; 236 if (wait) 237 signaled = winsys->fence_finish(winsys, fenced_buf->fence, 0); 238 else 239 signaled = winsys->fence_signalled(winsys, fenced_buf->fence, 0); 240 if (signaled != 0) 241 break; 242 prev_fence = fenced_buf->fence; 243 } 244 else { 245 assert(winsys->fence_signalled(winsys, fenced_buf->fence, 0) == 0); 246 } 247 248 _fenced_buffer_remove(fenced_list, fenced_buf); 249 250 curr = next; 251 next = curr->next; 252 } 253} 254 255 256static void 257fenced_buffer_destroy(struct pb_buffer *buf) 258{ 259 struct fenced_buffer *fenced_buf = fenced_buffer(buf); 260 struct fenced_buffer_list *fenced_list = fenced_buf->list; 261 262 pipe_mutex_lock(fenced_list->mutex); 263 assert(fenced_buf->base.base.refcount == 0); 264 if (fenced_buf->fence) { 265 struct pipe_winsys *winsys = fenced_list->winsys; 266 if(winsys->fence_signalled(winsys, fenced_buf->fence, 0) == 0) { 267 struct list_head *curr, *prev; 268 curr = &fenced_buf->head; 269 prev = curr->prev; 270 do { 271 fenced_buf = LIST_ENTRY(struct fenced_buffer, curr, head); 272 assert(winsys->fence_signalled(winsys, fenced_buf->fence, 0) == 0); 273 _fenced_buffer_remove(fenced_list, fenced_buf); 274 curr = prev; 275 prev = curr->prev; 276 } while (curr != &fenced_list->delayed); 277 } 278 else { 279 /* delay destruction */ 280 } 281 } 282 else { 283 _fenced_buffer_destroy(fenced_buf); 284 } 285 pipe_mutex_unlock(fenced_list->mutex); 286} 287 288 289static void * 290fenced_buffer_map(struct pb_buffer *buf, 291 unsigned flags) 292{ 293 struct fenced_buffer *fenced_buf = fenced_buffer(buf); 294 struct fenced_buffer_list *fenced_list = fenced_buf->list; 295 struct pipe_winsys *winsys = fenced_list->winsys; 296 void *map; 297 298 assert(!(flags & PIPE_BUFFER_USAGE_GPU_READ_WRITE)); 299 300 /* Serialize writes */ 301 if((fenced_buf->flags & PIPE_BUFFER_USAGE_GPU_WRITE) || 302 ((fenced_buf->flags & PIPE_BUFFER_USAGE_GPU_READ) && (flags & PIPE_BUFFER_USAGE_CPU_WRITE))) { 303 if(flags & PIPE_BUFFER_USAGE_DONTBLOCK) { 304 /* Don't wait for the GPU to finish writing */ 305 if(winsys->fence_finish(winsys, fenced_buf->fence, 0) == 0) 306 _fenced_buffer_remove(fenced_list, fenced_buf); 307 else 308 return NULL; 309 } 310 else { 311 /* Wait for the GPU to finish writing */ 312 _fenced_buffer_finish(fenced_buf); 313 } 314 } 315 316#if 0 317 /* Check for CPU write access (read is OK) */ 318 if(fenced_buf->flags & PIPE_BUFFER_USAGE_CPU_READ_WRITE) { 319 /* this is legal -- just for debugging */ 320 debug_warning("concurrent CPU writes"); 321 } 322#endif 323 324 map = pb_map(fenced_buf->buffer, flags); 325 if(map) { 326 ++fenced_buf->mapcount; 327 fenced_buf->flags |= flags & PIPE_BUFFER_USAGE_CPU_READ_WRITE; 328 } 329 330 return map; 331} 332 333 334static void 335fenced_buffer_unmap(struct pb_buffer *buf) 336{ 337 struct fenced_buffer *fenced_buf = fenced_buffer(buf); 338 assert(fenced_buf->mapcount); 339 if(fenced_buf->mapcount) { 340 pb_unmap(fenced_buf->buffer); 341 --fenced_buf->mapcount; 342 if(!fenced_buf->mapcount) 343 fenced_buf->flags &= ~PIPE_BUFFER_USAGE_CPU_READ_WRITE; 344 } 345} 346 347 348static void 349fenced_buffer_get_base_buffer(struct pb_buffer *buf, 350 struct pb_buffer **base_buf, 351 unsigned *offset) 352{ 353 struct fenced_buffer *fenced_buf = fenced_buffer(buf); 354 pb_get_base_buffer(fenced_buf->buffer, base_buf, offset); 355} 356 357 358const struct pb_vtbl 359fenced_buffer_vtbl = { 360 fenced_buffer_destroy, 361 fenced_buffer_map, 362 fenced_buffer_unmap, 363 fenced_buffer_get_base_buffer 364}; 365 366 367struct pb_buffer * 368fenced_buffer_create(struct fenced_buffer_list *fenced_list, 369 struct pb_buffer *buffer) 370{ 371 struct fenced_buffer *buf; 372 373 if(!buffer) 374 return NULL; 375 376 buf = CALLOC_STRUCT(fenced_buffer); 377 if(!buf) { 378 pb_reference(&buffer, NULL); 379 return NULL; 380 } 381 382 buf->base.base.refcount = 1; 383 buf->base.base.alignment = buffer->base.alignment; 384 buf->base.base.usage = buffer->base.usage; 385 buf->base.base.size = buffer->base.size; 386 387 buf->base.vtbl = &fenced_buffer_vtbl; 388 buf->buffer = buffer; 389 buf->list = fenced_list; 390 391#ifdef DEBUG 392 pipe_mutex_lock(fenced_list->mutex); 393 LIST_ADDTAIL(&buf->head, &fenced_list->unfenced); 394 ++fenced_list->numUnfenced; 395 pipe_mutex_unlock(fenced_list->mutex); 396#endif 397 398 return &buf->base; 399} 400 401 402void 403buffer_fence(struct pb_buffer *buf, 404 struct pipe_fence_handle *fence) 405{ 406 struct fenced_buffer *fenced_buf; 407 struct fenced_buffer_list *fenced_list; 408 struct pipe_winsys *winsys; 409 /* FIXME: receive this as a parameter */ 410 unsigned flags = fence ? PIPE_BUFFER_USAGE_GPU_READ_WRITE : 0; 411 412 /* This is a public function, so be extra cautious with the buffer passed, 413 * as happens frequently to receive null buffers, or pointer to buffers 414 * other than fenced buffers. */ 415 assert(buf); 416 if(!buf) 417 return; 418 assert(buf->vtbl == &fenced_buffer_vtbl); 419 if(buf->vtbl != &fenced_buffer_vtbl) 420 return; 421 422 fenced_buf = fenced_buffer(buf); 423 fenced_list = fenced_buf->list; 424 winsys = fenced_list->winsys; 425 426 if(!fence || fence == fenced_buf->fence) { 427 /* Handle the same fence case specially, not only because it is a fast 428 * path, but mostly to avoid serializing two writes with the same fence, 429 * as that would bring the hardware down to synchronous operation without 430 * any benefit. 431 */ 432 fenced_buf->flags |= flags & PIPE_BUFFER_USAGE_GPU_READ_WRITE; 433 return; 434 } 435 436 pipe_mutex_lock(fenced_list->mutex); 437 if (fenced_buf->fence) 438 _fenced_buffer_remove(fenced_list, fenced_buf); 439 if (fence) { 440 winsys->fence_reference(winsys, &fenced_buf->fence, fence); 441 fenced_buf->flags |= flags & PIPE_BUFFER_USAGE_GPU_READ_WRITE; 442 _fenced_buffer_add(fenced_buf); 443 } 444 pipe_mutex_unlock(fenced_list->mutex); 445} 446 447 448struct fenced_buffer_list * 449fenced_buffer_list_create(struct pipe_winsys *winsys) 450{ 451 struct fenced_buffer_list *fenced_list; 452 453 fenced_list = CALLOC_STRUCT(fenced_buffer_list); 454 if (!fenced_list) 455 return NULL; 456 457 fenced_list->winsys = winsys; 458 459 LIST_INITHEAD(&fenced_list->delayed); 460 fenced_list->numDelayed = 0; 461 462#ifdef DEBUG 463 LIST_INITHEAD(&fenced_list->unfenced); 464 fenced_list->numUnfenced = 0; 465#endif 466 467 pipe_mutex_init(fenced_list->mutex); 468 469 return fenced_list; 470} 471 472 473void 474fenced_buffer_list_check_free(struct fenced_buffer_list *fenced_list, 475 int wait) 476{ 477 pipe_mutex_lock(fenced_list->mutex); 478 _fenced_buffer_list_check_free(fenced_list, wait); 479 pipe_mutex_unlock(fenced_list->mutex); 480} 481 482 483#ifdef DEBUG 484void 485fenced_buffer_list_dump(struct fenced_buffer_list *fenced_list) 486{ 487 struct pipe_winsys *winsys = fenced_list->winsys; 488 struct list_head *curr, *next; 489 struct fenced_buffer *fenced_buf; 490 491 pipe_mutex_lock(fenced_list->mutex); 492 493 debug_printf("%10s %7s %10s %s\n", 494 "buffer", "refcount", "fence", "signalled"); 495 496 curr = fenced_list->unfenced.next; 497 next = curr->next; 498 while(curr != &fenced_list->unfenced) { 499 fenced_buf = LIST_ENTRY(struct fenced_buffer, curr, head); 500 assert(!fenced_buf->fence); 501 debug_printf("%10p %7u\n", 502 fenced_buf, 503 fenced_buf->base.base.refcount); 504 curr = next; 505 next = curr->next; 506 } 507 508 curr = fenced_list->delayed.next; 509 next = curr->next; 510 while(curr != &fenced_list->delayed) { 511 int signaled; 512 fenced_buf = LIST_ENTRY(struct fenced_buffer, curr, head); 513 signaled = winsys->fence_signalled(winsys, fenced_buf->fence, 0); 514 debug_printf("%10p %7u %10p %s\n", 515 fenced_buf, 516 fenced_buf->base.base.refcount, 517 fenced_buf->fence, 518 signaled == 0 ? "y" : "n"); 519 curr = next; 520 next = curr->next; 521 } 522 523 pipe_mutex_unlock(fenced_list->mutex); 524} 525#endif 526 527 528void 529fenced_buffer_list_destroy(struct fenced_buffer_list *fenced_list) 530{ 531 pipe_mutex_lock(fenced_list->mutex); 532 533 /* Wait on outstanding fences */ 534 while (fenced_list->numDelayed) { 535 pipe_mutex_unlock(fenced_list->mutex); 536#if defined(PIPE_OS_LINUX) 537 sched_yield(); 538#endif 539 _fenced_buffer_list_check_free(fenced_list, 1); 540 pipe_mutex_lock(fenced_list->mutex); 541 } 542 543#ifdef DEBUG 544 //assert(!fenced_list->numUnfenced); 545#endif 546 547 pipe_mutex_unlock(fenced_list->mutex); 548 549 FREE(fenced_list); 550} 551 552 553