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