pb_buffer_fenced.c revision b06cd4debfc4fb4162b4b45e61ea91948de0a279
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 struct fenced_buffer_list *fenced_list = fenced_buf->list; 138 139 assert(!fenced_buf->base.base.refcount); 140 assert(!fenced_buf->fence); 141 pb_reference(&fenced_buf->buffer, NULL); 142 FREE(fenced_buf); 143} 144 145 146static INLINE void 147_fenced_buffer_remove(struct fenced_buffer *fenced_buf) 148{ 149 struct fenced_buffer_list *fenced_list = fenced_buf->list; 150 struct pipe_winsys *winsys = fenced_list->winsys; 151 152 assert(fenced_buf->fence); 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 173/** 174 * Free as many fenced buffers from the list head as possible. 175 */ 176static void 177_fenced_buffer_list_check_free(struct fenced_buffer_list *fenced_list, 178 int wait) 179{ 180 struct pipe_winsys *winsys = fenced_list->winsys; 181 struct list_head *curr, *next; 182 struct fenced_buffer *fenced_buf; 183 struct pipe_fence_handle *prev_fence = NULL; 184 185 curr = fenced_list->delayed.next; 186 next = curr->next; 187 while(curr != &fenced_list->delayed) { 188 fenced_buf = LIST_ENTRY(struct fenced_buffer, curr, head); 189 190 if(fenced_buf->fence != prev_fence) { 191 int signaled; 192 if (wait) 193 signaled = winsys->fence_finish(winsys, fenced_buf->fence, 0); 194 else 195 signaled = winsys->fence_signalled(winsys, fenced_buf->fence, 0); 196 if (signaled != 0) 197 break; 198 prev_fence = fenced_buf->fence; 199 } 200 else { 201 assert(winsys->fence_signalled(winsys, fenced_buf->fence, 0) == 0); 202 } 203 204 _fenced_buffer_remove(fenced_buf); 205 206 curr = next; 207 next = curr->next; 208 } 209} 210 211 212/** 213 * Serialize writes, but allow concurrent reads. 214 */ 215static INLINE enum pipe_error 216fenced_buffer_serialize(struct fenced_buffer *fenced_buf, unsigned flags) 217{ 218 struct fenced_buffer_list *fenced_list = fenced_buf->list; 219 struct pipe_winsys *winsys = fenced_list->winsys; 220 221 /* Allow concurrent reads */ 222 if(((fenced_buf->flags | flags) & PIPE_BUFFER_USAGE_WRITE) == 0) 223 return PIPE_OK; 224 225 /* Wait for the CPU to finish */ 226 if(fenced_buf->mapcount) { 227 /* FIXME: Use thread conditions variables to signal when mapcount 228 * reaches zero */ 229 debug_warning("attemp to write concurrently to buffer"); 230 /* XXX: we must not fail here in order to support texture mipmap generation 231 return PIPE_ERROR_RETRY; 232 */ 233 } 234 235 /* Wait for the GPU to finish */ 236 if(fenced_buf->fence) { 237 if(winsys->fence_finish(winsys, fenced_buf->fence, 0) != 0) 238 return PIPE_ERROR_RETRY; 239 _fenced_buffer_remove(fenced_buf); 240 } 241 242 return PIPE_OK; 243} 244 245 246static void 247fenced_buffer_destroy(struct pb_buffer *buf) 248{ 249 struct fenced_buffer *fenced_buf = fenced_buffer(buf); 250 struct fenced_buffer_list *fenced_list = fenced_buf->list; 251 252 _glthread_LOCK_MUTEX(fenced_list->mutex); 253 assert(fenced_buf->base.base.refcount == 0); 254 if (fenced_buf->fence) { 255 struct pipe_winsys *winsys = fenced_list->winsys; 256 if(winsys->fence_signalled(winsys, fenced_buf->fence, 0) == 0) { 257 struct list_head *curr, *prev; 258 curr = &fenced_buf->head; 259 prev = curr->prev; 260 do { 261 fenced_buf = LIST_ENTRY(struct fenced_buffer, curr, head); 262 assert(winsys->fence_signalled(winsys, fenced_buf->fence, 0) == 0); 263 _fenced_buffer_remove(fenced_buf); 264 curr = prev; 265 prev = curr->prev; 266 } while (curr != &fenced_list->delayed); 267 } 268 else { 269 /* delay destruction */ 270 } 271 } 272 else { 273 _fenced_buffer_destroy(fenced_buf); 274 } 275 _glthread_UNLOCK_MUTEX(fenced_list->mutex); 276} 277 278 279static void * 280fenced_buffer_map(struct pb_buffer *buf, 281 unsigned flags) 282{ 283 struct fenced_buffer *fenced_buf = fenced_buffer(buf); 284 void *map; 285 assert((flags & ~PIPE_BUFFER_USAGE_CPU_READ_WRITE) == 0); 286 287 if(fenced_buffer_serialize(fenced_buf, flags) != PIPE_OK) 288 return NULL; 289 290 map = pb_map(fenced_buf->buffer, flags); 291 if(map) 292 ++fenced_buf->mapcount; 293 fenced_buf->flags |= flags & PIPE_BUFFER_USAGE_CPU_READ_WRITE; 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 pb_unmap(fenced_buf->buffer); 304 --fenced_buf->mapcount; 305 if(!fenced_buf->mapcount) 306 fenced_buf->flags &= ~PIPE_BUFFER_USAGE_CPU_READ_WRITE; 307} 308 309 310static void 311fenced_buffer_get_base_buffer(struct pb_buffer *buf, 312 struct pb_buffer **base_buf, 313 unsigned *offset) 314{ 315 struct fenced_buffer *fenced_buf = fenced_buffer(buf); 316 pb_get_base_buffer(fenced_buf->buffer, base_buf, offset); 317} 318 319 320const struct pb_vtbl 321fenced_buffer_vtbl = { 322 fenced_buffer_destroy, 323 fenced_buffer_map, 324 fenced_buffer_unmap, 325 fenced_buffer_get_base_buffer 326}; 327 328 329struct pb_buffer * 330fenced_buffer_create(struct fenced_buffer_list *fenced_list, 331 struct pb_buffer *buffer) 332{ 333 struct fenced_buffer *buf; 334 335 if(!buffer) 336 return NULL; 337 338 buf = CALLOC_STRUCT(fenced_buffer); 339 if(!buf) 340 return NULL; 341 342 buf->base.base.refcount = 1; 343 buf->base.base.alignment = buffer->base.alignment; 344 buf->base.base.usage = buffer->base.usage; 345 buf->base.base.size = buffer->base.size; 346 347 buf->base.vtbl = &fenced_buffer_vtbl; 348 buf->buffer = buffer; 349 buf->list = fenced_list; 350 351 return &buf->base; 352} 353 354 355void 356buffer_fence(struct pb_buffer *buf, 357 struct pipe_fence_handle *fence) 358{ 359 struct fenced_buffer *fenced_buf = fenced_buffer(buf); 360 struct fenced_buffer_list *fenced_list = fenced_buf->list; 361 struct pipe_winsys *winsys = fenced_list->winsys; 362 /* FIXME: receive this as a parameter */ 363 unsigned flags = fence ? PIPE_BUFFER_USAGE_GPU_READ_WRITE : 0; 364 365 if(fence == fenced_buf->fence) { 366 /* Handle the same fence case specially, not only because it is a fast 367 * path, but mostly to avoid serializing two writes with the same fence, 368 * as that would bring the hardware down to synchronous operation without 369 * any benefit. 370 */ 371 fenced_buf->flags |= flags & PIPE_BUFFER_USAGE_GPU_READ_WRITE; 372 return; 373 } 374 375 if(fenced_buffer_serialize(fenced_buf, flags) != PIPE_OK) { 376 /* FIXME: propagate error */ 377 (void)0; 378 } 379 380 _glthread_LOCK_MUTEX(fenced_list->mutex); 381 if (fenced_buf->fence) 382 _fenced_buffer_remove(fenced_buf); 383 if (fence) { 384 winsys->fence_reference(winsys, &fenced_buf->fence, fence); 385 fenced_buf->flags |= flags & PIPE_BUFFER_USAGE_GPU_READ_WRITE; 386 _fenced_buffer_add(fenced_buf); 387 } 388 _glthread_UNLOCK_MUTEX(fenced_list->mutex); 389} 390 391 392struct fenced_buffer_list * 393fenced_buffer_list_create(struct pipe_winsys *winsys) 394{ 395 struct fenced_buffer_list *fenced_list; 396 397 fenced_list = (struct fenced_buffer_list *)CALLOC(1, sizeof(*fenced_list)); 398 if (!fenced_list) 399 return NULL; 400 401 fenced_list->winsys = winsys; 402 403 LIST_INITHEAD(&fenced_list->delayed); 404 405 fenced_list->numDelayed = 0; 406 407 _glthread_INIT_MUTEX(fenced_list->mutex); 408 409 return fenced_list; 410} 411 412 413void 414fenced_buffer_list_check_free(struct fenced_buffer_list *fenced_list, 415 int wait) 416{ 417 _glthread_LOCK_MUTEX(fenced_list->mutex); 418 _fenced_buffer_list_check_free(fenced_list, wait); 419 _glthread_UNLOCK_MUTEX(fenced_list->mutex); 420} 421 422 423void 424fenced_buffer_list_destroy(struct fenced_buffer_list *fenced_list) 425{ 426 _glthread_LOCK_MUTEX(fenced_list->mutex); 427 428 /* Wait on outstanding fences */ 429 while (fenced_list->numDelayed) { 430 _glthread_UNLOCK_MUTEX(fenced_list->mutex); 431#if defined(PIPE_OS_LINUX) 432 sched_yield(); 433#endif 434 _fenced_buffer_list_check_free(fenced_list, 1); 435 _glthread_LOCK_MUTEX(fenced_list->mutex); 436 } 437 438 _glthread_UNLOCK_MUTEX(fenced_list->mutex); 439 440 FREE(fenced_list); 441} 442 443 444