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