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