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  */
41 static EGLint
egl_g3d_wait_sync_condvar(struct egl_g3d_sync * gsync,EGLTimeKHR timeout)42 egl_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  */
64 static void
egl_g3d_signal_sync_condvar(struct egl_g3d_sync * gsync)65 egl_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  */
75 static EGLint
egl_g3d_insert_fence_sync(struct egl_g3d_sync * gsync)76 egl_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  */
95 static EGLint
egl_g3d_wait_fence_sync(struct egl_g3d_sync * gsync,EGLTimeKHR timeout)96 egl_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 
126 static INLINE void
egl_g3d_ref_sync(struct egl_g3d_sync * gsync)127 egl_g3d_ref_sync(struct egl_g3d_sync *gsync)
128 {
129    _eglGetSync(&gsync->base);
130 }
131 
132 static INLINE void
egl_g3d_unref_sync(struct egl_g3d_sync * gsync)133 egl_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 *
egl_g3d_create_sync(_EGLDriver * drv,_EGLDisplay * dpy,EGLenum type,const EGLint * attrib_list)152 egl_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 
199 EGLBoolean
egl_g3d_destroy_sync(_EGLDriver * drv,_EGLDisplay * dpy,_EGLSync * sync)200 egl_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 
221 EGLint
egl_g3d_client_wait_sync(_EGLDriver * drv,_EGLDisplay * dpy,_EGLSync * sync,EGLint flags,EGLTimeKHR timeout)222 egl_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 
262 EGLBoolean
egl_g3d_signal_sync(_EGLDriver * drv,_EGLDisplay * dpy,_EGLSync * sync,EGLenum mode)263 egl_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