st/egl: Add support for EGL_KHR_fence_sync.
[mesa.git] / src / gallium / state_trackers / egl / common / egl_g3d_sync.c
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 #ifdef EGL_KHR_reusable_sync
39
40 /**
41 * Wait for the conditional variable.
42 */
43 static EGLint
44 egl_g3d_wait_sync_condvar(struct egl_g3d_sync *gsync, EGLTimeKHR timeout)
45 {
46 _EGLDisplay *dpy = gsync->base.Resource.Display;
47
48 pipe_mutex_lock(gsync->mutex);
49
50 /* unlock display lock just before waiting */
51 _eglUnlockMutex(&dpy->Mutex);
52
53 /* No timed wait. Always treat timeout as EGL_FOREVER_KHR */
54 pipe_condvar_wait(gsync->condvar, gsync->mutex);
55
56 _eglLockMutex(&dpy->Mutex);
57
58 pipe_mutex_unlock(gsync->mutex);
59
60 return EGL_CONDITION_SATISFIED_KHR;
61 }
62
63 /**
64 * Signal the conditional variable.
65 */
66 static void
67 egl_g3d_signal_sync_condvar(struct egl_g3d_sync *gsync)
68 {
69 pipe_mutex_lock(gsync->mutex);
70 pipe_condvar_broadcast(gsync->condvar);
71 pipe_mutex_unlock(gsync->mutex);
72 }
73
74 /**
75 * Insert a fence command to the command stream of the current context.
76 */
77 static EGLint
78 egl_g3d_insert_fence_sync(struct egl_g3d_sync *gsync)
79 {
80 _EGLContext *ctx = _eglGetCurrentContext();
81 struct egl_g3d_context *gctx = egl_g3d_context(ctx);
82
83 /* already checked in egl_g3d_create_sync */
84 assert(gctx);
85
86 /* insert the fence command */
87 gctx->stctxi->flush(gctx->stctxi, 0x0, &gsync->fence);
88 if (!gsync->fence)
89 gsync->base.SyncStatus = EGL_SIGNALED_KHR;
90
91 return EGL_SUCCESS;
92 }
93
94 /**
95 * Wait for the fence sync to be signaled.
96 */
97 static EGLint
98 egl_g3d_wait_fence_sync(struct egl_g3d_sync *gsync, EGLTimeKHR timeout)
99 {
100 EGLint ret;
101
102 if (gsync->fence) {
103 _EGLDisplay *dpy = gsync->base.Resource.Display;
104 struct egl_g3d_display *gdpy = egl_g3d_display(dpy);
105 struct pipe_screen *screen = gdpy->native->screen;
106 struct pipe_fence_handle *fence = gsync->fence;
107
108 gsync->fence = NULL;
109
110 _eglUnlockMutex(&dpy->Mutex);
111 /* no timed finish? */
112 screen->fence_finish(screen, fence, 0x0);
113 ret = EGL_CONDITION_SATISFIED_KHR;
114 _eglLockMutex(&dpy->Mutex);
115
116 gsync->base.SyncStatus = EGL_SIGNALED_KHR;
117
118 screen->fence_reference(screen, &fence, NULL);
119 egl_g3d_signal_sync_condvar(gsync);
120 }
121 else {
122 ret = egl_g3d_wait_sync_condvar(gsync, timeout);
123 }
124
125 return ret;
126 }
127
128 static INLINE void
129 egl_g3d_ref_sync(struct egl_g3d_sync *gsync)
130 {
131 p_atomic_inc(&gsync->refs);
132 }
133
134 static INLINE void
135 egl_g3d_unref_sync(struct egl_g3d_sync *gsync)
136 {
137 if (p_atomic_dec_zero(&gsync->refs)) {
138 pipe_condvar_destroy(gsync->condvar);
139 pipe_mutex_destroy(gsync->mutex);
140
141 if (gsync->fence) {
142 struct egl_g3d_display *gdpy =
143 egl_g3d_display(gsync->base.Resource.Display);
144 struct pipe_screen *screen = gdpy->native->screen;
145
146 screen->fence_reference(screen, &gsync->fence, NULL);
147 }
148
149 FREE(gsync);
150 }
151 }
152
153 _EGLSync *
154 egl_g3d_create_sync(_EGLDriver *drv, _EGLDisplay *dpy,
155 EGLenum type, const EGLint *attrib_list)
156 {
157 _EGLContext *ctx = _eglGetCurrentContext();
158 struct egl_g3d_sync *gsync;
159 EGLint err;
160
161 if (!ctx || ctx->Resource.Display != dpy) {
162 _eglError(EGL_BAD_MATCH, "eglCreateSyncKHR");
163 return NULL;
164 }
165
166 gsync = CALLOC_STRUCT(egl_g3d_sync);
167 if (!gsync) {
168 _eglError(EGL_BAD_ALLOC, "eglCreateSyncKHR");
169 return NULL;
170 }
171
172 if (!_eglInitSync(&gsync->base, dpy, type, attrib_list)) {
173 FREE(gsync);
174 return NULL;
175 }
176
177 switch (type) {
178 case EGL_SYNC_REUSABLE_KHR:
179 err = EGL_SUCCESS;
180 break;
181 case EGL_SYNC_FENCE_KHR:
182 err = egl_g3d_insert_fence_sync(gsync);
183 break;
184 default:
185 err = EGL_BAD_ATTRIBUTE;
186 break;
187 }
188
189 if (err != EGL_SUCCESS) {
190 _eglError(err, "eglCreateSyncKHR");
191 FREE(gsync);
192 return NULL;
193 }
194
195 pipe_mutex_init(gsync->mutex);
196 pipe_condvar_init(gsync->condvar);
197 p_atomic_set(&gsync->refs, 1);
198
199 return &gsync->base;
200 }
201
202 EGLBoolean
203 egl_g3d_destroy_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync)
204 {
205 struct egl_g3d_sync *gsync = egl_g3d_sync(sync);
206
207 switch (gsync->base.Type) {
208 case EGL_SYNC_REUSABLE_KHR:
209 /* signal the waiters */
210 if (gsync->base.SyncStatus != EGL_SIGNALED_KHR) {
211 gsync->base.SyncStatus = EGL_SIGNALED_KHR;
212 egl_g3d_signal_sync_condvar(gsync);
213 }
214 break;
215 default:
216 break;
217 }
218
219 egl_g3d_unref_sync(gsync);
220
221 return EGL_TRUE;
222 }
223
224 EGLint
225 egl_g3d_client_wait_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync,
226 EGLint flags, EGLTimeKHR timeout)
227 {
228 struct egl_g3d_sync *gsync = egl_g3d_sync(sync);
229 EGLint ret = EGL_CONDITION_SATISFIED_KHR;
230
231 if (gsync->base.SyncStatus != EGL_SIGNALED_KHR) {
232 /* flush if there is a current context */
233 if (flags & EGL_SYNC_FLUSH_COMMANDS_BIT_KHR) {
234 _EGLContext *ctx = _eglGetCurrentContext();
235 struct egl_g3d_context *gctx = egl_g3d_context(ctx);
236
237 if (gctx)
238 gctx->stctxi->flush(gctx->stctxi, PIPE_FLUSH_RENDER_CACHE , NULL);
239 }
240
241 if (timeout) {
242 /* reference the sync object in case it is destroyed while waiting */
243 egl_g3d_ref_sync(gsync);
244
245 switch (gsync->base.Type) {
246 case EGL_SYNC_REUSABLE_KHR:
247 ret = egl_g3d_wait_sync_condvar(gsync, timeout);
248 break;
249 case EGL_SYNC_FENCE_KHR:
250 ret = egl_g3d_wait_fence_sync(gsync, timeout);
251 default:
252 break;
253 }
254
255 egl_g3d_unref_sync(gsync);
256 }
257 else {
258 ret = EGL_TIMEOUT_EXPIRED_KHR;
259 }
260 }
261
262 return ret;
263 }
264
265 EGLBoolean
266 egl_g3d_signal_sync(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSync *sync,
267 EGLenum mode)
268 {
269 struct egl_g3d_sync *gsync = egl_g3d_sync(sync);
270
271 /* only for reusable sync */
272 if (sync->Type != EGL_SYNC_REUSABLE_KHR)
273 return _eglError(EGL_BAD_MATCH, "eglSignalSyncKHR");
274
275 if (gsync->base.SyncStatus != mode) {
276 gsync->base.SyncStatus = mode;
277 if (mode == EGL_SIGNALED_KHR)
278 egl_g3d_signal_sync_condvar(gsync);
279 }
280
281 return EGL_TRUE;
282 }
283
284 #endif /* EGL_KHR_reusable_sync */