1/*
2 * Mesa 3-D graphics library
3 * Version:  7.9
4 *
5 * Copyright (C) 2010 LunarG Inc.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 *
25 * Authors:
26 *    Chia-I Wu <olv@lunarg.com>
27 */
28
29#include "util/u_memory.h"
30#include "util/u_atomic.h"
31#include "os/os_thread.h"
32#include "eglsync.h"
33#include "eglcurrent.h"
34
35#include "egl_g3d.h"
36#include "egl_g3d_sync.h"
37
38/**
39 * Wait for the conditional variable.
40 */
41static EGLint
42egl_g3d_wait_sync_condvar(struct egl_g3d_sync *gsync, EGLTimeKHR timeout)
43{
44   _EGLDisplay *dpy = gsync->base.Resource.Display;
45
46   pipe_mutex_lock(gsync->mutex);
47
48   /* unlock display lock just before waiting */
49   _eglUnlockMutex(&dpy->Mutex);
50
51   /* No timed wait.  Always treat timeout as EGL_FOREVER_KHR */
52   pipe_condvar_wait(gsync->condvar, gsync->mutex);
53
54   _eglLockMutex(&dpy->Mutex);
55
56   pipe_mutex_unlock(gsync->mutex);
57
58   return EGL_CONDITION_SATISFIED_KHR;
59}
60
61/**
62 * Signal the conditional variable.
63 */
64static void
65egl_g3d_signal_sync_condvar(struct egl_g3d_sync *gsync)
66{
67   pipe_mutex_lock(gsync->mutex);
68   pipe_condvar_broadcast(gsync->condvar);
69   pipe_mutex_unlock(gsync->mutex);
70}
71
72/**
73 * Insert a fence command to the command stream of the current context.
74 */
75static EGLint
76egl_g3d_insert_fence_sync(struct egl_g3d_sync *gsync)
77{
78   _EGLContext *ctx = _eglGetCurrentContext();
79   struct egl_g3d_context *gctx = egl_g3d_context(ctx);
80
81   /* already checked in egl_g3d_create_sync */
82   assert(gctx);
83
84   /* insert the fence command */
85   gctx->stctxi->flush(gctx->stctxi, 0x0, &gsync->fence);
86   if (!gsync->fence)
87      gsync->base.SyncStatus = EGL_SIGNALED_KHR;
88
89   return EGL_SUCCESS;
90}
91
92/**
93 * Wait for the fence sync to be signaled.
94 */
95static EGLint
96egl_g3d_wait_fence_sync(struct egl_g3d_sync *gsync, EGLTimeKHR timeout)
97{
98   EGLint ret;
99
100   if (gsync->fence) {
101      _EGLDisplay *dpy = gsync->base.Resource.Display;
102      struct egl_g3d_display *gdpy = egl_g3d_display(dpy);
103      struct pipe_screen *screen = gdpy->native->screen;
104      struct pipe_fence_handle *fence = gsync->fence;
105
106      gsync->fence = NULL;
107
108      _eglUnlockMutex(&dpy->Mutex);
109      /* no timed finish? */
110      screen->fence_finish(screen, fence, PIPE_TIMEOUT_INFINITE);
111      ret = EGL_CONDITION_SATISFIED_KHR;
112      _eglLockMutex(&dpy->Mutex);
113
114      gsync->base.SyncStatus = EGL_SIGNALED_KHR;
115
116      screen->fence_reference(screen, &fence, NULL);
117      egl_g3d_signal_sync_condvar(gsync);
118   }
119   else {
120      ret = egl_g3d_wait_sync_condvar(gsync, timeout);
121   }
122
123   return ret;
124}
125
126static INLINE void
127egl_g3d_ref_sync(struct egl_g3d_sync *gsync)
128{
129   _eglGetSync(&gsync->base);
130}
131
132static INLINE void
133egl_g3d_unref_sync(struct egl_g3d_sync *gsync)
134{
135   if (_eglPutSync(&gsync->base)) {
136      pipe_condvar_destroy(gsync->condvar);
137      pipe_mutex_destroy(gsync->mutex);
138
139      if (gsync->fence) {
140         struct egl_g3d_display *gdpy =
141            egl_g3d_display(gsync->base.Resource.Display);
142         struct pipe_screen *screen = gdpy->native->screen;
143
144         screen->fence_reference(screen, &gsync->fence, NULL);
145      }
146
147      FREE(gsync);
148   }
149}
150
151_EGLSync *
152egl_g3d_create_sync(_EGLDriver *drv, _EGLDisplay *dpy,
153                    EGLenum type, const EGLint *attrib_list)
154{
155   _EGLContext *ctx = _eglGetCurrentContext();
156   struct egl_g3d_sync *gsync;
157   EGLint err;
158
159   if (!ctx || ctx->Resource.Display != dpy) {
160      _eglError(EGL_BAD_MATCH, "eglCreateSyncKHR");
161      return NULL;
162   }
163
164   gsync = CALLOC_STRUCT(egl_g3d_sync);
165   if (!gsync) {
166      _eglError(EGL_BAD_ALLOC, "eglCreateSyncKHR");
167      return NULL;
168   }
169
170   if (!_eglInitSync(&gsync->base, dpy, type, attrib_list)) {
171      FREE(gsync);
172      return NULL;
173   }
174
175   switch (type) {
176   case EGL_SYNC_REUSABLE_KHR:
177      err = EGL_SUCCESS;
178      break;
179   case EGL_SYNC_FENCE_KHR:
180      err = egl_g3d_insert_fence_sync(gsync);
181      break;
182   default:
183      err = EGL_BAD_ATTRIBUTE;
184      break;
185   }
186
187   if (err != EGL_SUCCESS) {
188      _eglError(err, "eglCreateSyncKHR");
189      FREE(gsync);
190      return NULL;
191   }
192
193   pipe_mutex_init(gsync->mutex);
194   pipe_condvar_init(gsync->condvar);
195
196   return &gsync->base;
197}
198
199EGLBoolean
200egl_g3d_destroy_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync)
201{
202   struct egl_g3d_sync *gsync = egl_g3d_sync(sync);
203
204   switch (gsync->base.Type) {
205   case EGL_SYNC_REUSABLE_KHR:
206      /* signal the waiters */
207      if (gsync->base.SyncStatus != EGL_SIGNALED_KHR) {
208         gsync->base.SyncStatus = EGL_SIGNALED_KHR;
209         egl_g3d_signal_sync_condvar(gsync);
210      }
211      break;
212   default:
213      break;
214   }
215
216   egl_g3d_unref_sync(gsync);
217
218   return EGL_TRUE;
219}
220
221EGLint
222egl_g3d_client_wait_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync,
223                         EGLint flags, EGLTimeKHR timeout)
224{
225   struct egl_g3d_sync *gsync = egl_g3d_sync(sync);
226   EGLint ret = EGL_CONDITION_SATISFIED_KHR;
227
228   if (gsync->base.SyncStatus != EGL_SIGNALED_KHR) {
229      /* flush if there is a current context */
230      if (flags & EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) {
231         _EGLContext *ctx = _eglGetCurrentContext();
232         struct egl_g3d_context *gctx = egl_g3d_context(ctx);
233
234         if (gctx)
235            gctx->stctxi->flush(gctx->stctxi, ST_FLUSH_FRONT, NULL);
236      }
237
238      if (timeout) {
239         /* reference the sync object in case it is destroyed while waiting */
240         egl_g3d_ref_sync(gsync);
241
242         switch (gsync->base.Type) {
243         case EGL_SYNC_REUSABLE_KHR:
244            ret = egl_g3d_wait_sync_condvar(gsync, timeout);
245            break;
246         case EGL_SYNC_FENCE_KHR:
247            ret = egl_g3d_wait_fence_sync(gsync, timeout);
248         default:
249            break;
250         }
251
252         egl_g3d_unref_sync(gsync);
253      }
254      else {
255         ret = EGL_TIMEOUT_EXPIRED_KHR;
256      }
257   }
258
259   return ret;
260}
261
262EGLBoolean
263egl_g3d_signal_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync,
264                    EGLenum mode)
265{
266   struct egl_g3d_sync *gsync = egl_g3d_sync(sync);
267
268   /* only for reusable sync */
269   if (sync->Type != EGL_SYNC_REUSABLE_KHR)
270      return _eglError(EGL_BAD_MATCH, "eglSignalSyncKHR");
271
272   if (gsync->base.SyncStatus != mode) {
273      gsync->base.SyncStatus = mode;
274      if (mode == EGL_SIGNALED_KHR)
275         egl_g3d_signal_sync_condvar(gsync);
276   }
277
278   return EGL_TRUE;
279}
280