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