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 (at) 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 } 280