9a1002b29a77c22bf9249c37ec35072384b9426f
[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 /**
39 * Wait for the conditional variable.
40 */
41 static EGLint
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
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
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
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
127 egl_g3d_ref_sync(struct egl_g3d_sync *gsync)
128 {
129 _eglGetSync(&gsync->base);
130 }
131
132 static INLINE void
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 *
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
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
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
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 }