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