diff --git a/meson.build b/meson.build index 1109916..dd2bfbf 100644 --- a/meson.build +++ b/meson.build @@ -21,6 +21,7 @@ project('egl-x11', 'c', dep_libdrm = dependency('libdrm') dep_threads = dependency('threads') dep_eglexternal = dependency('eglexternalplatform', version : ['>=1.2', '<2']) +dep_egl = dependency('egl', version : ['>=1.5', '<2']).partial_dependency(includes : true, compile_args : true) inc_base = include_directories('src/base') cc = meson.get_compiler('c') diff --git a/src/base/meson.build b/src/base/meson.build index a909998..0c863e1 100644 --- a/src/base/meson.build +++ b/src/base/meson.build @@ -24,6 +24,7 @@ platform_base = static_library('platform-base', dep_libdrm.partial_dependency(compile_args : true, includes : true), dep_threads, dep_eglexternal, + dep_egl, ], gnu_symbol_visibility: 'hidden', install: false) diff --git a/src/base/platform-base.c b/src/base/platform-base.c index 8710d10..2560f14 100644 --- a/src/base/platform-base.c +++ b/src/base/platform-base.c @@ -26,8 +26,6 @@ #include "platform-utils.h" #include "platform-impl.h" -#define USE_ERRORCHECK_MUTEX 1 - static void *eplGetHookAddressExport(void *platformData, const char *name); static EGLBoolean eplIsValidNativeDisplayExport(void *platformData, void *nativeDisplay); static EGLDisplay eplGetPlatformDisplayExport(void *platformData, EGLenum platform, void *nativeDisplay, const EGLAttrib* attribs); @@ -69,6 +67,7 @@ static __attribute__((destructor)) void LibraryFini(void) EPL_REFCOUNT_DEFINE_TYPE_FUNCS(EplPlatformData, eplPlatformData, refcount, free); EPL_REFCOUNT_DEFINE_TYPE_FUNCS(EplInternalDisplay, eplInternalDisplay, refcount, free); +EPL_REFCOUNT_DEFINE_TYPE_FUNCS(EplDisplay, eplDisplay, refcount, DestroyDisplay); EplPlatformData *eplPlatformBaseAllocate(int major, int minor, const EGLExtDriver *driver, EGLExtPlatform *extplatform, @@ -85,7 +84,6 @@ EplPlatformData *eplPlatformBaseAllocate(int major, int minor, assert(impl->InitializeDisplay != NULL); assert(impl->TerminateDisplay != NULL); assert(impl->DestroySurface != NULL); - assert(impl->FreeSurface != NULL); // SwapBuffers is only required if the platform supports windows. assert(impl->CreateWindowSurface == NULL || impl->SwapBuffers != NULL); @@ -132,6 +130,7 @@ EplPlatformData *eplPlatformBaseAllocate(int major, int minor, platform->egl.MakeCurrent = driver->getProcAddress("eglMakeCurrent"); platform->egl.WaitGL = driver->getProcAddress("eglWaitGL"); platform->egl.WaitNative = driver->getProcAddress("eglWaitNative"); + platform->egl.SwapInterval = driver->getProcAddress("eglSwapInterval"); platform->egl.WaitClient = driver->getProcAddress("eglWaitClient"); platform->egl.ChooseConfig = driver->getProcAddress("eglChooseConfig"); platform->egl.GetConfigAttrib = driver->getProcAddress("eglGetConfigAttrib"); @@ -232,21 +231,19 @@ static EGLBoolean eplUnloadExternalPlatformExport(void *platformData) continue; } - pthread_mutex_lock(&pdpy->mutex); - // Remove the display from the list and decrement its refcount. glvnd_list_del(&pdpy->entry); - pthread_mutex_unlock(&pdpy->mutex); + + pthread_rwlock_wrlock(&pdpy->init_lock); + TerminateDisplay(pdpy); + pthread_rwlock_unlock(&pdpy->init_lock); // Note that if some other thread is still holding a reference to this // display, then it might get leaked. // TODO: Should we just unconditionally free the display here? If // another thread is in the middle of a function call, then it's going // to crash anyway. - if (eplRefCountUnref(&pdpy->refcount)) - { - DestroyDisplay(pdpy); - } + eplDisplayUnref(pdpy); } pthread_mutex_unlock(&display_list_mutex); @@ -272,7 +269,7 @@ static EGLBoolean eplUnloadExternalPlatformExport(void *platformData) * This looks up and locks an EGLDisplay, but it does not check whether the * display is initialized. */ -static EplDisplay *eplLockDisplayInternal(EGLDisplay edpy) +static EplDisplay *eplLookupDisplay(EGLDisplay edpy) { EplDisplay *pdpy = NULL; EplDisplay *node = NULL; @@ -298,9 +295,7 @@ static EplDisplay *eplLockDisplayInternal(EGLDisplay edpy) return NULL; } - pthread_mutex_lock(&pdpy->mutex); - eplRefCountRef(&pdpy->refcount); - pdpy->use_count++; + eplDisplayRef(pdpy); pthread_mutex_unlock(&display_list_mutex); @@ -309,27 +304,31 @@ static EplDisplay *eplLockDisplayInternal(EGLDisplay edpy) EplDisplay *eplDisplayAcquire(EGLDisplay edpy) { - EplDisplay *pdpy = eplLockDisplayInternal(edpy); + EplDisplay *pdpy = eplLookupDisplay(edpy); if (pdpy == NULL) { return NULL; } + pthread_rwlock_rdlock(&pdpy->init_lock); + if (!pdpy->initialized) { eplSetError(pdpy->platform, EGL_NOT_INITIALIZED, "EGLDisplay %p is not initialized", edpy); - eplDisplayRelease(pdpy); + pthread_rwlock_unlock(&pdpy->init_lock); + eplDisplayUnref(pdpy); return NULL; } return pdpy; } -EGLDisplay eplGetCurrentDisplay(void) +void eplGetCurrentSurface(EGLDisplay *ret_edpy, EGLSurface *ret_esurf) { EplPlatformData *plat; EGLDisplay edpy = EGL_NO_DISPLAY; + EGLSurface esurf = EGL_NO_SURFACE; /* * In practice, loadEGLExternalPlatform is only ever going to get called @@ -346,27 +345,34 @@ EGLDisplay eplGetCurrentDisplay(void) edpy = plat->egl.GetCurrentDisplay(); if (edpy != EGL_NO_DISPLAY) { + esurf = plat->egl.GetCurrentSurface(EGL_DRAW); break; } } pthread_mutex_unlock(&platform_data_list_mutex); - return edpy; + if (ret_edpy != NULL) + { + *ret_edpy = edpy; + } + if (ret_esurf != NULL) + { + *ret_esurf = esurf; + } } static void DestroyAllSurfaces(EplDisplay *pdpy) { + pthread_rwlock_wrlock(&pdpy->surface_list_lock); + while (!glvnd_list_is_empty(&pdpy->surface_list)) { EplSurface *psurf = glvnd_list_first_entry(&pdpy->surface_list, EplSurface, entry); - // Bump the refcount, as if we'd called eglSurfaceAcquire, so that - // eplSurfaceRelease works below. - eplRefCountRef(&psurf->refcount); - DeleteSurfaceCommon(pdpy, psurf); - eplSurfaceRelease(pdpy, psurf); } + + pthread_rwlock_unlock(&pdpy->surface_list_lock); } static void DestroyDisplay(EplDisplay *pdpy) @@ -377,7 +383,8 @@ static void DestroyDisplay(EplDisplay *pdpy) DestroyAllSurfaces(pdpy); pdpy->platform->impl->CleanupDisplay(pdpy); - pthread_mutex_destroy(&pdpy->mutex); + pthread_rwlock_destroy(&pdpy->init_lock); + pthread_rwlock_destroy(&pdpy->surface_list_lock); eplPlatformDataUnref(pdpy->platform); free(pdpy); @@ -390,24 +397,9 @@ void eplDisplayRelease(EplDisplay *pdpy) return; } - pdpy->use_count--; - CheckTerminateDisplay(pdpy); - pthread_mutex_unlock(&pdpy->mutex); + pthread_rwlock_unlock(&pdpy->init_lock); - if (eplRefCountUnref(&pdpy->refcount)) - { - DestroyDisplay(pdpy); - } -} - -void eplDisplayUnlock(EplDisplay *pdpy) -{ - pthread_mutex_unlock(&pdpy->mutex); -} - -void eplDisplayLock(EplDisplay *pdpy) -{ - pthread_mutex_lock(&pdpy->mutex); + eplDisplayUnref(pdpy); } EplInternalDisplay *eplLookupInternalDisplay(EplPlatformData *platform, EGLDisplay handle) @@ -518,47 +510,78 @@ EGLBoolean eplTerminateInternalDisplay(EplPlatformData *platform, EplInternalDis return EGL_TRUE; } -EplSurface *eplSurfaceAcquire(EplDisplay *pdpy, EGLSurface esurf) +const struct glvnd_list *eplDisplayLockSurfaceList(EplDisplay *pdpy) +{ + pthread_rwlock_rdlock(&pdpy->surface_list_lock); + + return &pdpy->surface_list; +} + +void eplDisplayUnlockSurfaceList(EplDisplay *pdpy) +{ + pthread_rwlock_unlock(&pdpy->surface_list_lock); +} + +EplSurface *eplSurfaceListLookup(const struct glvnd_list *surface_list, EGLSurface esurf) { EplSurface *psurf; - EplSurface *found = NULL; - if (pdpy == NULL || esurf == EGL_NO_SURFACE) + if (esurf == EGL_NO_SURFACE) { return NULL; } - glvnd_list_for_each_entry(psurf, &pdpy->surface_list, entry) + glvnd_list_for_each_entry(psurf, surface_list, entry) { if (psurf->external_surface == esurf) { - found = psurf; - break; + return psurf; } } - if (found != NULL) + return NULL; +} + +EGLBoolean eplHookDisplaySurface(EGLDisplay edpy, EGLSurface esurf, + EplDisplay **ret_pdpy, EplSurface **ret_psurf) +{ + EplDisplay *pdpy = eplDisplayAcquire(edpy); + const struct glvnd_list *surface_list = NULL; + EplSurface *psurf = NULL; + + *ret_pdpy = NULL; + *ret_psurf = NULL; + + if (pdpy == NULL) { - eplRefCountRef(&found->refcount); + return EGL_FALSE; } - return found; + if (esurf == EGL_NO_SURFACE) + { + eplSetError(pdpy->platform, EGL_BAD_SURFACE, "EGLSurface handle is EGL_NO_SURFACE"); + eplDisplayRelease(pdpy); + return EGL_FALSE; + } + + surface_list = eplDisplayLockSurfaceList(pdpy); + psurf = eplSurfaceListLookup(surface_list, esurf); + if (psurf == NULL) + { + eplDisplayUnlockSurfaceList(pdpy); + } + *ret_pdpy = pdpy; + *ret_psurf = psurf; + return EGL_TRUE; } -void eplSurfaceRelease(EplDisplay *pdpy, EplSurface *psurf) +void eplHookDisplaySurfaceEnd(EplDisplay *pdpy, const EplSurface *psurf) { if (psurf != NULL) { - if (eplRefCountUnref(&psurf->refcount)) - { - // If the refcount is zero, then that means eglDestroySurface or - // eglTerminate has already run, so the platform-specific code has - // already cleaned up the surface. - assert(psurf->deleted); - pdpy->platform->impl->FreeSurface(pdpy, psurf); - FreeBaseSurface(psurf); - } + eplDisplayUnlockSurfaceList(pdpy); } + eplDisplayRelease(pdpy); } static EGLDisplay eplGetPlatformDisplayExport(void *platformData, @@ -655,9 +678,17 @@ static EGLDisplay eplGetPlatformDisplayExport(void *platformData, goto done; } - if (!eplInitRecursiveMutex(&pdpy->mutex)) + if (pthread_rwlock_init(&pdpy->init_lock, NULL) != 0) + { + eplSetError(plat, EGL_BAD_ALLOC, "Failed to create internal locks"); + free(pdpy); + goto done; + } + + if (pthread_rwlock_init(&pdpy->surface_list_lock, NULL) != 0) { - eplSetError(plat, EGL_BAD_ALLOC, "Failed to create internal mutex"); + eplSetError(plat, EGL_BAD_ALLOC, "Failed to create internal locks"); + pthread_rwlock_destroy(&pdpy->init_lock); free(pdpy); goto done; } @@ -672,7 +703,8 @@ static EGLDisplay eplGetPlatformDisplayExport(void *platformData, if (!plat->impl->GetPlatformDisplay(plat, pdpy, nativeDisplay, remainingAttribs, &display_list)) { - pthread_mutex_destroy(&pdpy->mutex); + pthread_rwlock_destroy(&pdpy->init_lock); + pthread_rwlock_destroy(&pdpy->surface_list_lock); eplPlatformDataUnref(pdpy->platform); free(pdpy); ret = EGL_NO_DISPLAY; @@ -690,20 +722,23 @@ static EGLDisplay eplGetPlatformDisplayExport(void *platformData, static EGLBoolean HookInitialize(EGLDisplay edpy, EGLint *major, EGLint *minor) { - EplDisplay *pdpy = eplLockDisplayInternal(edpy); + EplDisplay *pdpy = eplLookupDisplay(edpy); if (pdpy == NULL) { return EGL_FALSE; } + pthread_rwlock_wrlock(&pdpy->init_lock); + if (!pdpy->initialized) { pdpy->major = 1; pdpy->minor = 5; if (!pdpy->platform->impl->InitializeDisplay(pdpy->platform, pdpy, &pdpy->major, &pdpy->minor)) { - eplDisplayRelease(pdpy); + pthread_rwlock_unlock(&pdpy->init_lock); + eplDisplayUnref(pdpy); return EGL_FALSE; } pdpy->initialized = EGL_TRUE; @@ -730,31 +765,28 @@ static EGLBoolean HookInitialize(EGLDisplay edpy, EGLint *major, EGLint *minor) *minor = pdpy->minor; } - eplDisplayRelease(pdpy); + pthread_rwlock_unlock(&pdpy->init_lock); + eplDisplayUnref(pdpy); return EGL_TRUE; } static void TerminateDisplay(EplDisplay *pdpy) { pdpy->init_count = 0; - pdpy->initialized = EGL_FALSE; - - if (pdpy->platform == NULL) + if (pdpy->initialized) { - // We've already gone through teardown, so don't try to do anything - // else. All remaining cleanup will happen in DestroyDisplay. - return; - } + pdpy->initialized = EGL_FALSE; - DestroyAllSurfaces(pdpy); - pdpy->platform->impl->TerminateDisplay(pdpy->platform, pdpy); + DestroyAllSurfaces(pdpy); + pdpy->platform->impl->TerminateDisplay(pdpy->platform, pdpy); + } } static void CheckTerminateDisplay(EplDisplay *pdpy) { if (pdpy->initialized) { - if (pdpy->init_count == 0 && pdpy->use_count == 0) + if (pdpy->init_count == 0) { TerminateDisplay(pdpy); } @@ -763,18 +795,23 @@ static void CheckTerminateDisplay(EplDisplay *pdpy) static EGLBoolean HookTerminate(EGLDisplay edpy) { - EplDisplay *pdpy = eplLockDisplayInternal(edpy); + EplDisplay *pdpy = eplLookupDisplay(edpy); if (pdpy == NULL) { return EGL_FALSE; } + pthread_rwlock_wrlock(&pdpy->init_lock); + if (pdpy->init_count > 0) { pdpy->init_count--; + CheckTerminateDisplay(pdpy); } - eplDisplayRelease(pdpy); + + pthread_rwlock_unlock(&pdpy->init_lock); + eplDisplayUnref(pdpy); return EGL_TRUE; } @@ -836,18 +873,20 @@ static EGLSurface CommonCreateSurface(EplDisplay *pdpy, psurf = AllocBaseSurface(pdpy->platform); if (psurf == NULL) { - eplDisplayRelease(pdpy); return EGL_NO_SURFACE; } psurf->type = type; + pthread_rwlock_wrlock(&pdpy->surface_list_lock); + if (type == EPL_SURFACE_TYPE_WINDOW) { if (pdpy->platform->impl->CreateWindowSurface != NULL) { psurf->internal_surface = pdpy->platform->impl->CreateWindowSurface(pdpy->platform, - pdpy, psurf, config, native_handle, attrib_list, create_platform); + pdpy, psurf, config, native_handle, attrib_list, create_platform, + &pdpy->surface_list); } else { @@ -859,7 +898,8 @@ static EGLSurface CommonCreateSurface(EplDisplay *pdpy, if (pdpy->platform->impl->CreatePixmapSurface != NULL) { psurf->internal_surface = pdpy->platform->impl->CreatePixmapSurface(pdpy->platform, - pdpy, psurf, config, native_handle, attrib_list, create_platform); + pdpy, psurf, config, native_handle, attrib_list, create_platform, + &pdpy->surface_list); } else { @@ -876,7 +916,6 @@ static EGLSurface CommonCreateSurface(EplDisplay *pdpy, { psurf->external_surface = (EGLSurface) psurf; ret = psurf->external_surface; - eplRefCountRef(&psurf->refcount); glvnd_list_add(&psurf->entry, &pdpy->surface_list); } else @@ -884,6 +923,7 @@ static EGLSurface CommonCreateSurface(EplDisplay *pdpy, FreeBaseSurface(psurf); } + pthread_rwlock_unlock(&pdpy->surface_list_lock); return ret; } @@ -989,22 +1029,18 @@ static EGLSurface HookCreatePbufferSurface(EGLDisplay edpy, EGLConfig config, co static void DeleteSurfaceCommon(EplDisplay *pdpy, EplSurface *psurf) { - assert(!psurf->deleted); + glvnd_list_del(&psurf->entry); - if (!psurf->deleted) - { - psurf->deleted = EGL_TRUE; - glvnd_list_del(&psurf->entry); - pdpy->platform->impl->DestroySurface(pdpy, psurf); + pdpy->platform->impl->DestroySurface(pdpy, psurf, &pdpy->surface_list); - eplRefCountUnref(&psurf->refcount); - } + FreeBaseSurface(psurf); } static EGLBoolean HookDestroySurface(EGLDisplay edpy, EGLSurface esurf) { EplDisplay *pdpy; - EplSurface *psurf; + EplSurface *elem; + EplSurface *psurf = NULL; EGLBoolean ret = EGL_FALSE; pdpy = eplDisplayAcquire(edpy); @@ -1013,11 +1049,20 @@ static EGLBoolean HookDestroySurface(EGLDisplay edpy, EGLSurface esurf) return EGL_FALSE; } - psurf = eplSurfaceAcquire(pdpy, esurf); + pthread_rwlock_wrlock(&pdpy->surface_list_lock); + + glvnd_list_for_each_entry(elem, &pdpy->surface_list, entry) + { + if (elem->external_surface == esurf) + { + psurf = elem; + break; + } + } + if (psurf != NULL) { DeleteSurfaceCommon(pdpy, psurf); - eplSurfaceRelease(pdpy, psurf); ret = EGL_TRUE; } else @@ -1027,6 +1072,7 @@ static EGLBoolean HookDestroySurface(EGLDisplay edpy, EGLSurface esurf) ret = pdpy->platform->egl.DestroySurface(pdpy->internal_display, esurf); } + pthread_rwlock_unlock(&pdpy->surface_list_lock); eplDisplayRelease(pdpy); return ret; } @@ -1037,8 +1083,7 @@ static EGLBoolean HookSwapBuffersWithDamage(EGLDisplay edpy, EGLSurface esurf, c EplSurface *psurf; EGLBoolean ret = EGL_FALSE; - pdpy = eplDisplayAcquire(edpy); - if (pdpy == NULL) + if (!eplHookDisplaySurface(edpy, esurf, &pdpy, &psurf)) { return EGL_FALSE; } @@ -1046,11 +1091,10 @@ static EGLBoolean HookSwapBuffersWithDamage(EGLDisplay edpy, EGLSurface esurf, c if (pdpy->platform->egl.GetCurrentDisplay() != edpy) { eplSetError(pdpy->platform, EGL_BAD_SURFACE, "EGLDisplay %p is not current", edpy); - eplDisplayRelease(pdpy); + eplHookDisplaySurfaceEnd(pdpy, psurf); return EGL_FALSE; } - psurf = eplSurfaceAcquire(pdpy, esurf); if (psurf != NULL) { if (psurf->type != EPL_SURFACE_TYPE_WINDOW) @@ -1068,8 +1112,7 @@ static EGLBoolean HookSwapBuffersWithDamage(EGLDisplay edpy, EGLSurface esurf, c ret = pdpy->platform->impl->SwapBuffers(pdpy->platform, pdpy, psurf, rects, n_rects); } - eplSurfaceRelease(pdpy, psurf); - eplDisplayRelease(pdpy); + eplHookDisplaySurfaceEnd(pdpy, psurf); } else { @@ -1081,7 +1124,7 @@ static EGLBoolean HookSwapBuffersWithDamage(EGLDisplay edpy, EGLSurface esurf, c // Release the display before calling into the driver, so that we don't // sit on the lock for a (potentially long) SwapBuffers operation. - eplDisplayRelease(pdpy); + eplHookDisplaySurfaceEnd(pdpy, psurf); if (SwapBuffersWithDamage != NULL && rects != NULL && n_rects > 0) { @@ -1104,11 +1147,15 @@ static EGLBoolean HookSwapBuffers(EGLDisplay edpy, EGLSurface esurf) static EGLBoolean HookWaitGL(void) { - EGLDisplay edpy = eplGetCurrentDisplay(); - EplDisplay *pdpy = eplDisplayAcquire(edpy); + EGLDisplay edpy = EGL_NO_DISPLAY; + EGLDisplay esurf = EGL_NO_SURFACE; + EplDisplay *pdpy = NULL; + EplSurface *psurf = NULL; EGLBoolean ret = EGL_FALSE; - if (pdpy == NULL) + eplGetCurrentSurface(&edpy, &esurf); + + if (!eplHookDisplaySurface(edpy, esurf, &pdpy, &psurf)) { return EGL_FALSE; } @@ -1116,9 +1163,7 @@ static EGLBoolean HookWaitGL(void) assert(pdpy->platform->impl->WaitGL != NULL); if (pdpy->platform->impl->WaitGL != NULL) { - EplSurface *psurf = eplSurfaceAcquire(pdpy, pdpy->platform->egl.GetCurrentSurface(EGL_DRAW)); ret = pdpy->platform->impl->WaitGL(pdpy, psurf); - eplSurfaceRelease(pdpy, psurf); } else { @@ -1126,21 +1171,22 @@ static EGLBoolean HookWaitGL(void) // we have an implementation. But, if we wanted to handle this case, // then we could just forward the call through to the driver. eplSetError(pdpy->platform, EGL_BAD_ALLOC, "Internal error: eglWaitGL hook should not be called"); - eplDisplayRelease(pdpy); - return EGL_FALSE; } - eplDisplayRelease(pdpy); + eplHookDisplaySurfaceEnd(pdpy, psurf); return ret; } static EGLBoolean HookWaitNative(void) { - EGLDisplay edpy = eplGetCurrentDisplay(); - EplDisplay *pdpy = eplDisplayAcquire(edpy); + EGLDisplay edpy = EGL_NO_DISPLAY; + EGLDisplay esurf = EGL_NO_SURFACE; + EplDisplay *pdpy = NULL; + EplSurface *psurf = NULL; EGLBoolean ret = EGL_FALSE; - if (pdpy == NULL) + eplGetCurrentSurface(&edpy, &esurf); + if (!eplHookDisplaySurface(edpy, esurf, &pdpy, &psurf)) { return EGL_FALSE; } @@ -1148,18 +1194,14 @@ static EGLBoolean HookWaitNative(void) assert(pdpy->platform->impl->WaitNative != NULL); if (pdpy->platform->impl->WaitNative != NULL) { - EplSurface *psurf = eplSurfaceAcquire(pdpy, pdpy->platform->egl.GetCurrentSurface(EGL_DRAW)); ret = pdpy->platform->impl->WaitNative(pdpy, psurf); - eplSurfaceRelease(pdpy, psurf); } else { eplSetError(pdpy->platform, EGL_BAD_ALLOC, "Internal error: eglWaitNative hook should not be called"); - eplDisplayRelease(pdpy); - return EGL_FALSE; } - eplDisplayRelease(pdpy); + eplHookDisplaySurfaceEnd(pdpy, psurf); return ret; } @@ -1197,6 +1239,62 @@ static EGLBoolean HookQueryDisplayAttrib(EGLDisplay edpy, EGLint attribute, EGLA return ret; } +static EGLBoolean HookSwapInterval(EGLDisplay edpy, EGLint interval) +{ + EplDisplay *pdpy = NULL; + EGLBoolean ret = EGL_FALSE; + EGLDisplay internal_edpy = EGL_NO_DISPLAY; + PFNEGLSWAPINTERVALPROC SwapInterval = NULL; + + pdpy = eplDisplayAcquire(edpy); + if (pdpy == NULL) + { + return EGL_FALSE; + } + + if (pdpy->platform->egl.GetCurrentDisplay() == edpy) + { + EGLSurface esurf = pdpy->platform->egl.GetCurrentSurface(EGL_DRAW); + const struct glvnd_list *surface_list = eplDisplayLockSurfaceList(pdpy); + EplSurface *psurf = eplSurfaceListLookup(surface_list, esurf); + if (psurf != NULL) + { + if (pdpy->platform->impl->SwapInterval != NULL) + { + ret = pdpy->platform->impl->SwapInterval(pdpy, psurf, interval); + } + else + { + // This should never happen: If we don't have a SwapInterval + // implementation, then we shouldn't have provided an + // eglSwapInterval hook to the driver. + assert(!"Can't happen -- no SwapInterval implementation"); + ret = EGL_TRUE; + } + } + else + { + // If we don't recognize he current EGLSurface, then we'll just + // pass the call through to the driver after we unlock everything. + internal_edpy = pdpy->internal_display; + SwapInterval = pdpy->platform->egl.SwapInterval; + } + eplDisplayUnlockSurfaceList(pdpy); + } + else + { + eplSetError(pdpy->platform, EGL_BAD_SURFACE, "EGLDisplay %p is not current", edpy); + } + + if (SwapInterval != NULL) + { + ret = SwapInterval(internal_edpy, interval); + } + + eplDisplayRelease(pdpy); + return ret; +} + static const EplHookFunc BASE_HOOK_FUNCTIONS[] = { { "eglCreatePbufferSurface", HookCreatePbufferSurface }, @@ -1244,6 +1342,10 @@ void *eplGetHookAddressExport(void *platformData, const char *name) { return HookWaitNative; } + if (plat->impl->SwapInterval != NULL && strcmp(name, "eglSwapInterval") == 0) + { + return HookSwapInterval; + } return NULL; } @@ -1289,39 +1391,52 @@ static void *eplGetInternalHandleExport(EGLDisplay edpy, EGLenum type, void *han if (type == EGL_OBJECT_DISPLAY_KHR) { - EplDisplay *pdpy = eplLockDisplayInternal(handle); + EplDisplay *pdpy = eplLookupDisplay(handle); + if (pdpy != NULL) { - ret = pdpy->internal_display; - eplDisplayRelease(pdpy); + pthread_rwlock_rdlock(&pdpy->init_lock); + if (pdpy->initialized) + { + ret = pdpy->internal_display; + } + pthread_rwlock_unlock(&pdpy->init_lock); + eplDisplayUnref(pdpy); } } else { - EplDisplay *pdpy = eplLockDisplayInternal(edpy); + EplDisplay *pdpy = eplLookupDisplay(edpy); if (pdpy != NULL) { - if (type == EGL_OBJECT_SURFACE_KHR) + pthread_rwlock_rdlock(&pdpy->init_lock); + + if (pdpy->initialized) { - EplSurface *psurf = eplSurfaceAcquire(pdpy, (EGLSurface) handle); - if (psurf != NULL) - { - ret = psurf->internal_surface; - eplSurfaceRelease(pdpy, psurf); - } - else + if (type == EGL_OBJECT_SURFACE_KHR) { - /* - * Assume that if we don't recognize the handle, then it's - * a pbuffer or stream surface, and so the driver should - * just pass it through. If the handle is invalid, then the - * driver should then set the appropriate error code on its - * own. - */ - ret = handle; + const struct glvnd_list *surface_list = eplDisplayLockSurfaceList(pdpy); + EplSurface *psurf = eplSurfaceListLookup(surface_list, (EGLSurface) handle); + if (psurf != NULL) + { + ret = psurf->internal_surface; + } + else + { + /* + * Assume that if we don't recognize the handle, then it's + * a pbuffer or stream surface, and so the driver should + * just pass it through. If the handle is invalid, then the + * driver should then set the appropriate error code on its + * own. + */ + ret = handle; + } + eplDisplayUnlockSurfaceList(pdpy); } } - eplDisplayRelease(pdpy); + pthread_rwlock_unlock(&pdpy->init_lock); + eplDisplayUnref(pdpy); } } diff --git a/src/base/platform-base.h b/src/base/platform-base.h index 63e36f8..5616321 100644 --- a/src/base/platform-base.h +++ b/src/base/platform-base.h @@ -79,14 +79,10 @@ typedef struct */ typedef struct { - EplRefCount refcount; - EGLSurface external_surface; EGLSurface internal_surface; EplSurfaceType type; - EGLBoolean deleted; - /** * Private data used by the implementation. */ @@ -139,11 +135,6 @@ typedef struct */ struct _EplPlatformData *platform; - /** - * All of the existing EplSurface structs. - */ - struct glvnd_list surface_list; - /** * Private data for the implementation. */ @@ -152,10 +143,34 @@ typedef struct // Everything after this in EplDisplay should be treated as internal to // platform-base.c. + /** + * A read/write lock to protect against concurrent calls to eglTerminate. + * + * eglInitialize, eglTerminate, and library teardown will all take the read + * lock. + * + * All other functions will take the read lock, so that they don't have to + * worry about another thread coming along and terminating the display. + */ + pthread_rwlock_t init_lock; + /** - * A mutex to control access to the display. This is a recursive mutex. + * All of the existing EplSurface structs. */ - pthread_mutex_t mutex; + struct glvnd_list surface_list; + + /** + * A read/write lock to protect the surface list. + * + * The eglCreate*Surface and eglDestroySurface hooks will hold the write + * lock, and any other functions that operate on an EGLSurface a will hold + * the read lock. + * + * This allows most functions (especially eglSwapBuffers, which may have to + * block for extended periods of time) to run concurrently, without needing + * to worry about the EGLSurface getting destroyed out from under it. + */ + pthread_rwlock_t surface_list_lock; /** * True if this display was created with EGL_TRACK_REFERENCES set. @@ -169,14 +184,6 @@ typedef struct */ unsigned int init_count; - /** - * This is a counter to keep track of whether the display is in use or not. - * - * If the app calls eglTerminate, then we defer the termination until the - * display is no longer in use. - */ - unsigned int use_count; - /// The major version number for eglInitialize in this context. EGLint major; /// The minor version number for eglInitialize in this context. @@ -210,6 +217,7 @@ typedef struct _EplPlatformData PFNEGLWAITGLPROC WaitGL; PFNEGLWAITCLIENTPROC WaitClient; PFNEGLWAITNATIVEPROC WaitNative; + PFNEGLSWAPINTERVALPROC SwapInterval; PFNEGLQUERYDEVICEATTRIBEXTPROC QueryDeviceAttribEXT; PFNEGLQUERYDEVICESTRINGEXTPROC QueryDeviceStringEXT; @@ -310,32 +318,19 @@ void eplPlatformBaseInitFail(EplPlatformData *plat); EplDisplay *eplDisplayAcquire(EGLDisplay edpy); /** - * Returns the current EGLDisplay for the current thread. + * Returns the current external EGLDisplay and EGLSurface handles for the + * calling thread. + * + * \param[out] ret_edpy Returns the current external EGLDisplay + * \param[out] ret_esurf Returns the current external EGLSurface */ -EGLDisplay eplGetCurrentDisplay(void); +void eplGetCurrentSurface(EGLDisplay *ret_edpy, EGLSurface *ret_esurf); /** * Releases a display acquired with eplDisplayAcquire. */ void eplDisplayRelease(EplDisplay *pdpy); -/** - * Unlocks the mutex for an EplDisplay, but does not decrement the reference - * count. - * - * This allows a platform library to temporarily release the mutex for an - * EplDisplay, but ensures that the EplDisplay itself sticks around. - * - * The caller must call eplDisplayLock to lock the mutex again before calling - * eplDisplayRelease. - */ -void eplDisplayUnlock(EplDisplay *pdpy); - -/** - * Re-locks the mutex for an EplDisplay. - */ -void eplDisplayLock(EplDisplay *pdpy); - /** * Looks up an internal EGLDisplay. If an EplInternalDisplay struct doesn't * already exist, then it will be created and returned. @@ -367,20 +362,59 @@ EGLBoolean eplTerminateInternalDisplay(EplPlatformData *platform, EplInternalDis void eplSetError(EplPlatformData *platform, EGLint error, const char *fmt, ...); /** - * Looks up the EplSurface struct for a surface. + * Returns the display's surface list. + * + * This will take the read lock for the surface list, and then return the list + * head. + * + * The caller must call eplDisplayUnlockSurfaceList to unlock the surface list + * afterward. + */ +const struct glvnd_list *eplDisplayLockSurfaceList(EplDisplay *pdpy); + +/** + * Unlocks the display's surface list. + */ +void eplDisplayUnlockSurfaceList(EplDisplay *pdpy); + +/** + * A convenience function for hooks that operate on an EGLSurface. + * + * This function just calls eglDisplayAcquire to look up the EplDisplay, and + * then eplDisplayLockSurfaceList and eplSurfaceListLookup to look up an + * EplSurface. + * + * The caller must then call \c eplHookDisplaySurfaceEnd afterward. * - * This will lock the surface and increment its refcount. + * \note If \p esurf is not NULL, but doesn't match any EplSurface struct, then + * this function will still succeed. In most cases, that just means that the + * hook function should pass it through to the driver. * - * The caller must release the surface with \c eplSurfaceRelease. + * \note If \p ret_psurf returns NULL, then the surface list will be left + * unlocked. * - * Note that this might return NULL if the surface is a pbuffer or stream. + * \param edpy The external EGLDisplay handle. + * \param esurf The external EGLSurface handle. + * \param[out] ret_pdpy Returns the EplDisplay pointer. + * \param[out] ret_psurf Returns the EplSurface pointer, or NULL if \p esurf + * doesn't match any EplDisplay. + * \return EGL_TRUE on success. EGL_FALSE if \p edpy is invalid, or if + * \p esurf is EGL_NO_SURFACE. */ -EplSurface *eplSurfaceAcquire(EplDisplay *pdpy, EGLSurface esurf); +EGLBoolean eplHookDisplaySurface(EGLDisplay edpy, EGLSurface esurf, + EplDisplay **ret_pdpy, EplSurface **ret_surface); + +void eplHookDisplaySurfaceEnd(EplDisplay *pdpy, const EplSurface *psurf); /** - * Decrements the refcount for an EplSurface and unlocks it. + * Looks up an EplSurface from its external EGLSurface handle. + * + * \param surface_list The surface list, as returned by \c eplDisplayLockSurfaceList. + * \param esurf The external EGLSurface handle. + * \return The corresponding EplSurface, or NULL if \p esurf doesn't match any + * surface. */ -void eplSurfaceRelease(EplDisplay *pdpy, EplSurface *psurf); +EplSurface *eplSurfaceListLookup(const struct glvnd_list *surface_list, EGLSurface esurf); /** * Replaces the current surface. diff --git a/src/base/platform-impl.h b/src/base/platform-impl.h index abd74be..e3a865d 100644 --- a/src/base/platform-impl.h +++ b/src/base/platform-impl.h @@ -183,10 +183,13 @@ typedef struct _EplImplFuncs * \param create_platform If this is true, then the call is from * eglCreatePlatformWindowSurface. If false, it's from * eglCreateWindowSurface. + * \param existing_surfaces A linked list of existing surfaces. The new + * surface will not be in this list. * \return The internal EGLSurface handle, or EGL_NO_SURFACE on failure. */ EGLSurface (* CreateWindowSurface) (EplPlatformData *plat, EplDisplay *pdpy, EplSurface *psurf, - EGLConfig config, void *native_surface, const EGLAttrib *attribs, EGLBoolean create_platform); + EGLConfig config, void *native_surface, const EGLAttrib *attribs, EGLBoolean create_platform, + const struct glvnd_list *existing_surfaces); /** * Creates an EGLSurface for a pixmap. @@ -202,35 +205,29 @@ typedef struct _EplImplFuncs * \param create_platform If this is true, then the call is from * eglCreatePlatformPixmapSurface. If false, it's from * eglCreatePixmapSurface. + * \param existing_surfaces A linked list of existing surfaces. The new + * surface will not be in this list. * \return The internal EGLSurface handle, or EGL_NO_SURFACE on failure. */ EGLSurface (* CreatePixmapSurface) (EplPlatformData *plat, EplDisplay *pdpy, EplSurface *psurf, - EGLConfig config, void *native_surface, const EGLAttrib *attribs, EGLBoolean create_platform); + EGLConfig config, void *native_surface, const EGLAttrib *attribs, EGLBoolean create_platform, + const struct glvnd_list *existing_surfaces); /** - * Called to handle eglDestroySurface and eglTerminate. + * Called from eglDestroySurface and eglTerminate to destroy a surface. * - * Note that it's possible that the EplSurface struct itself might stick around - * if another thread is holding a reference to it. + * After this, the \c EplSurface struct itself is freed. * - * \c FreeSurface is called when the refcount actually drops to zero. + * Note that this function is called with the surface list already locked, + * so it must not try to call \c eplDisplayLockSurfaceList. * - * \param plat The EplPlatformData struct * \param pdpy The EplDisplay struct + * \param psurf The EplSurface that's being destroyed. + * \param existing_surfaces A linked list of existing surfaces. \p psurf + * will not be in this list. */ - void (* DestroySurface) (EplDisplay *pdpy, EplSurface *psurf); - - /** - * Called when an EplSurface is about to be freed. - * - * At this point, it's safe to assume that no other thread is going to touch - * the surface, so the platform must free anything that it hasn't already freed - * in \c DestroySurface. - * - * \param plat The EplPlatformData struct - * \param pdpy The EplDisplay struct - */ - void (* FreeSurface) (EplDisplay *pdpy, EplSurface *psurf); + void (* DestroySurface) (EplDisplay *pdpy, EplSurface *psurf, + const struct glvnd_list *existing_surfaces); /** * Implements eglSwapBuffers and eglSwapBuffersWithDamageEXT. @@ -293,6 +290,25 @@ typedef struct _EplImplFuncs * \return EGL_TRUE on success, EGL_FALSE on failure. */ EGLBoolean (*QueryDisplayAttrib) (EplDisplay *pdpy, EGLint attrib, EGLAttrib *ret_value); + + /** + * Implements eglSwapInterval. + * + * This is only called if the current EGLSurface belongs to the platform + * library. If the current EGLSurface does not belong to the platform + * library (e.g., a pbuffer or stream), then the base library will pass the + * call through to the driver. + * + * This function is optional. If it's NULL, then the base library will not + * provide a hook function eglSwapInterval, and so the driver will follow its + * default behavior. + * + * \param pdpy The current display. + * \param psurf The current draw surface. This will never be NULL. + * \param interval The new swap interval. + * \return EGL_TRUE on success, or EGL_FALSE on failure. + */ + EGLBoolean (* SwapInterval) (EplDisplay *pdpy, EplSurface *psurf, EGLint interval); } EplImplFuncs; #ifdef __cplusplus diff --git a/src/x11/x11-pixmap.c b/src/x11/x11-pixmap.c index e86cece..be69fa4 100644 --- a/src/x11/x11-pixmap.c +++ b/src/x11/x11-pixmap.c @@ -401,11 +401,12 @@ void eplX11DestroyPixmap(EplSurface *surf) } } -static EGLBoolean CheckExistingPixmap(EplDisplay *pdpy, xcb_pixmap_t xpix) +static EGLBoolean CheckExistingPixmap(EplDisplay *pdpy, xcb_pixmap_t xpix, + const struct glvnd_list *existing_surfaces) { EplSurface *psurf; - glvnd_list_for_each_entry(psurf, &pdpy->surface_list, entry) + glvnd_list_for_each_entry(psurf, existing_surfaces, entry) { if (psurf->type == EPL_SURFACE_TYPE_PIXMAP) { @@ -423,7 +424,8 @@ static EGLBoolean CheckExistingPixmap(EplDisplay *pdpy, xcb_pixmap_t xpix) } EGLSurface eplX11CreatePixmapSurface(EplPlatformData *plat, EplDisplay *pdpy, EplSurface *surf, - EGLConfig config, void *native_surface, const EGLAttrib *attribs, EGLBoolean create_platform) + EGLConfig config, void *native_surface, const EGLAttrib *attribs, EGLBoolean create_platform, + const struct glvnd_list *existing_surfaces) { X11DisplayInstance *inst = pdpy->priv->inst; xcb_pixmap_t xpix = eplX11GetNativeXID(pdpy, native_surface, create_platform); @@ -449,7 +451,7 @@ EGLSurface eplX11CreatePixmapSurface(EplPlatformData *plat, EplDisplay *pdpy, Ep eplSetError(plat, EGL_BAD_NATIVE_PIXMAP, "Invalid native pixmap %p\n", native_surface); return EGL_NO_SURFACE; } - if (!CheckExistingPixmap(pdpy, xpix)) + if (!CheckExistingPixmap(pdpy, xpix, existing_surfaces)) { return EGL_NO_SURFACE; } diff --git a/src/x11/x11-platform.c b/src/x11/x11-platform.c index c1fa162..b88974b 100644 --- a/src/x11/x11-platform.c +++ b/src/x11/x11-platform.c @@ -77,15 +77,14 @@ static EGLBoolean eplX11GetPlatformDisplay(EplPlatformData *plat, EplDisplay *pd struct glvnd_list *existing_displays); static EGLBoolean eplX11InitializeDisplay(EplPlatformData *plat, EplDisplay *pdpy, EGLint *major, EGLint *minor); static void eplX11TerminateDisplay(EplPlatformData *plat, EplDisplay *pdpy); -static void eplX11DestroySurface(EplDisplay *pdpy, EplSurface *surf); -static void eplX11FreeSurface(EplDisplay *pdpy, EplSurface *surf); +static void eplX11DestroySurface(EplDisplay *pdpy, EplSurface *surf, + const struct glvnd_list *existing_surfaces); static EGLBoolean eplX11WaitGL(EplDisplay *pdpy, EplSurface *psurf); static const EplHookFunc X11_HOOK_FUNCTIONS[] = { { "eglChooseConfig", eplX11HookChooseConfig }, { "eglGetConfigAttrib", eplX11HookGetConfigAttrib }, - { "eglSwapInterval", eplX11SwapInterval }, }; static const int NUM_X11_HOOK_FUNCTIONS = sizeof(X11_HOOK_FUNCTIONS) / sizeof(X11_HOOK_FUNCTIONS[0]); @@ -102,9 +101,9 @@ static const EplImplFuncs X11_IMPL_FUNCS = .CreateWindowSurface = eplX11CreateWindowSurface, .CreatePixmapSurface = eplX11CreatePixmapSurface, .DestroySurface = eplX11DestroySurface, - .FreeSurface = eplX11FreeSurface, .SwapBuffers = eplX11SwapBuffers, .WaitGL = eplX11WaitGL, + .SwapInterval = eplX11SwapInterval, }; /** @@ -1269,7 +1268,8 @@ static void eplX11TerminateDisplay(EplPlatformData *plat, EplDisplay *pdpy) pdpy->priv->inst = NULL; } -static void eplX11DestroySurface(EplDisplay *pdpy, EplSurface *surf) +static void eplX11DestroySurface(EplDisplay *pdpy, EplSurface *surf, + const struct glvnd_list *existing_surfaces) { if (surf->type == EPL_SURFACE_TYPE_WINDOW) { @@ -1285,14 +1285,6 @@ static void eplX11DestroySurface(EplDisplay *pdpy, EplSurface *surf) } } -static void eplX11FreeSurface(EplDisplay *pdpy, EplSurface *surf) -{ - if (surf->type == EPL_SURFACE_TYPE_WINDOW) - { - eplX11FreeWindow(surf); - } -} - static EGLBoolean eplX11WaitGL(EplDisplay *pdpy, EplSurface *psurf) { EGLBoolean ret = EGL_TRUE; diff --git a/src/x11/x11-platform.h b/src/x11/x11-platform.h index 26c6178..d53c7ee 100644 --- a/src/x11/x11-platform.h +++ b/src/x11/x11-platform.h @@ -389,10 +389,12 @@ EGLBoolean eplX11LoadEGLExternalPlatformCommon(int major, int minor, EGLint platform_enum); EGLSurface eplX11CreatePixmapSurface(EplPlatformData *plat, EplDisplay *pdpy, EplSurface *surf, - EGLConfig config, void *native_surface, const EGLAttrib *attribs, EGLBoolean create_platform); + EGLConfig config, void *native_surface, const EGLAttrib *attribs, EGLBoolean create_platform, + const struct glvnd_list *existing_surfaces); EGLSurface eplX11CreateWindowSurface(EplPlatformData *plat, EplDisplay *pdpy, EplSurface *surf, - EGLConfig config, void *native_surface, const EGLAttrib *attribs, EGLBoolean create_platform); + EGLConfig config, void *native_surface, const EGLAttrib *attribs, EGLBoolean create_platform, + const struct glvnd_list *existing_surfaces); EGLBoolean eplX11SwapBuffers(EplPlatformData *plat, EplDisplay *pdpy, EplSurface *surf, const EGLint *rects, EGLint n_rects); @@ -443,12 +445,10 @@ EGLBoolean eplX11HookGetConfigAttrib(EGLDisplay edpy, EGLConfig config, void eplX11DestroyPixmap(EplSurface *surf); -EGLBoolean eplX11SwapInterval(EGLDisplay edpy, EGLint interval); +EGLBoolean eplX11SwapInterval(EplDisplay *pdpy, EplSurface *psurf, EGLint interval); void eplX11DestroyWindow(EplSurface *surf); -void eplX11FreeWindow(EplSurface *surf); - EGLBoolean eplX11WaitGLWindow(EplDisplay *pdpy, EplSurface *psurf); /** diff --git a/src/x11/x11-window.c b/src/x11/x11-window.c index 3b472d9..3cf48ef 100644 --- a/src/x11/x11-window.c +++ b/src/x11/x11-window.c @@ -728,9 +728,45 @@ static EGLBoolean FindSupportedModifiers(X11DisplayInstance *inst, return EGL_TRUE; } -void eplX11FreeWindow(EplSurface *surf) +void eplX11DestroyWindow(EplSurface *surf) { X11Window *pwin = (X11Window *) surf->priv; + EGLSurface internalSurf; + + assert(surf->type == EPL_SURFACE_TYPE_WINDOW); + + /* + * Lock the surface and increment skip_update_callback. After that, if + * another thread tries to call the update callback after this, then + * the update callback won't try to call into the driver. + */ + pthread_mutex_lock(&pwin->mutex); + + pwin->skip_update_callback++; + internalSurf = surf->internal_surface; + + pthread_mutex_unlock(&pwin->mutex); + + /* + * We have to unlock the surface before we call the driver's + * eglDestroySurface. + * + * If another thread tries to call the window update callback for this + * surface, then it will be holding a mutex in the driver, and then the + * callback will try to lock the surface's mutex. + * + * If we had the surface locked here, then the driver's eglDestroySurface + * implementation would try to take the driver's mutex, which would lead to + * a deadlock. + */ + if (internalSurf != EGL_NO_SURFACE) + { + pwin->inst->platform->egl.DestroySurface(pwin->inst->internal_display->edpy, internalSurf); + } + + // After the driver's eglDestroySurface returns, no other threads will call + // the update or damage callbacks, so we can finish the rest of our + // cleanup. FreeWindowBuffers(surf); @@ -854,7 +890,7 @@ static EGLBoolean CheckReallocWindow(EplSurface *surf, *was_resized = EGL_FALSE; } - if (surf->deleted || pwin->native_destroyed) + if (pwin->native_destroyed) { return EGL_TRUE; } @@ -944,7 +980,7 @@ static void PollForWindowEvents(EplSurface *surf) { X11Window *pwin = (X11Window *) surf->priv; - while (!pwin->native_destroyed && !surf->deleted) + while (!pwin->native_destroyed) { xcb_generic_event_t *xcbevt = xcb_poll_for_special_event(pwin->inst->conn, pwin->present_event); if (xcbevt == NULL) @@ -962,20 +998,6 @@ static void WindowUpdateCallback(void *param) EplSurface *surf = param; X11Window *pwin = (X11Window *) surf->priv; - /* - * Here, we lock the window mutex, but *not* the display mutex. - * - * We can't lock the display mutex, because we could run into a deadlock: - * - Thread A calls an EGL function in the platform library, which locks - * the display. The platform library calls into the driver, which tries to - * take the winsys lock. - * - Thread B calls a GL function. The driver takes the winsys lock, then - * calls the window update callback. The window update callback tries to - * take the display lock. - * - * We can safely access anything in X11DisplayInstance, because that never - * changes after it's initialized, and thus we don't need a mutex for it. - */ pthread_mutex_lock(&pwin->mutex); if (pwin->skip_update_callback != 0) @@ -1165,7 +1187,7 @@ static void WindowDamageCallback(void *param, int syncfd, unsigned int flags) // Check for any pending events first. PollForWindowEvents(surf); - if (pwin->native_destroyed || surf->deleted) + if (pwin->native_destroyed) { goto done; } @@ -1241,11 +1263,12 @@ static void WindowDamageCallback(void *param, int syncfd, unsigned int flags) pthread_mutex_unlock(&pwin->mutex); } -static EGLBoolean CheckExistingWindow(EplDisplay *pdpy, xcb_window_t xwin) +static EGLBoolean CheckExistingWindow(EplDisplay *pdpy, xcb_window_t xwin, + const struct glvnd_list *existing_surfaces) { EplSurface *psurf; - glvnd_list_for_each_entry(psurf, &pdpy->surface_list, entry) + glvnd_list_for_each_entry(psurf, existing_surfaces, entry) { if (psurf->type == EPL_SURFACE_TYPE_WINDOW) { @@ -1263,7 +1286,8 @@ static EGLBoolean CheckExistingWindow(EplDisplay *pdpy, xcb_window_t xwin) } EGLSurface eplX11CreateWindowSurface(EplPlatformData *plat, EplDisplay *pdpy, EplSurface *surf, - EGLConfig config, void *native_surface, const EGLAttrib *attribs, EGLBoolean create_platform) + EGLConfig config, void *native_surface, const EGLAttrib *attribs, EGLBoolean create_platform, + const struct glvnd_list *existing_surfaces) { X11DisplayInstance *inst = pdpy->priv->inst; xcb_window_t xwin = eplX11GetNativeXID(pdpy, native_surface, create_platform); @@ -1291,7 +1315,7 @@ EGLSurface eplX11CreateWindowSurface(EplPlatformData *plat, EplDisplay *pdpy, Ep eplSetError(plat, EGL_BAD_NATIVE_WINDOW, "Invalid native window %p\n", native_surface); return EGL_NO_SURFACE; } - if (!CheckExistingWindow(pdpy, xwin)) + if (!CheckExistingWindow(pdpy, xwin, existing_surfaces)) { return EGL_NO_SURFACE; } @@ -1450,7 +1474,7 @@ EGLSurface eplX11CreateWindowSurface(EplPlatformData *plat, EplDisplay *pdpy, Ep done: if (esurf == EGL_NO_SURFACE) { - eplX11FreeWindow(surf); + eplX11DestroyWindow(surf); } free(windowAttribReply); free(geomReply); @@ -1461,67 +1485,14 @@ EGLSurface eplX11CreateWindowSurface(EplPlatformData *plat, EplDisplay *pdpy, Ep return esurf; } -void eplX11DestroyWindow(EplSurface *surf) -{ - X11Window *pwin = (X11Window *) surf->priv; - EGLSurface internalSurf; - - assert(surf->type == EPL_SURFACE_TYPE_WINDOW); - - /* - * Lock the surface and increment skip_update_callback. After that, if - * another thread tries to call the update callback after this, then - * the update callback won't try to call into the driver. - */ - pthread_mutex_lock(&pwin->mutex); - - pwin->skip_update_callback++; - internalSurf = surf->internal_surface; - - pthread_mutex_unlock(&pwin->mutex); - - /* - * We have to unlock the surface before we call the driver's - * eglDestroySurface. - * - * If another thread tries to call the window update callback for this - * surface, then it will be holding a mutex in the driver, and then the - * callback will try to lock the surface's mutex. - * - * If we had the surface locked here, then the driver's eglDestroySurface - * implementation would try to take the driver's mutex, which would lead to - * a deadlock. - */ - if (internalSurf != EGL_NO_SURFACE) - { - pwin->inst->platform->egl.DestroySurface(pwin->inst->internal_display->edpy, internalSurf); - } -} - /** * Waits for at least one Present event to arrive. - * - * This function will unlock the surface and the display while waiting, so the - * caller must check the EplSurface::deleted flag to check whether another - * thread destroyed the surface in the meantime. */ static EGLBoolean WaitForWindowEvents(EplDisplay *pdpy, EplSurface *surf) { X11Window *pwin = (X11Window *) surf->priv; xcb_generic_event_t *xcbevt; - /* - * We don't want to block other threads while we wait, so we need to - * release our locks. - * - * The use counter in EplDisplay should prevent the display from - * getting terminated out from under us, and the refcount in EplSurface - * will ensure that the EplSurface struct itself sticks around. - * - * It's still possible that the surface will get destroyed by another - * thread in the meantime, though, so the caller needs to check for that. - */ - if (pwin->native_destroyed) { /* @@ -1530,41 +1501,12 @@ static EGLBoolean WaitForWindowEvents(EplDisplay *pdpy, EplSurface *surf) * However, a new enough X server will send a PresentConfigureNotify * event with a special flag set to notify the client when that * happens. - * - * Note that even though we unlock the window's mutex before calling - * xcb_wait_for_special_event, we should still be safe from race - * conditions. This function is only called from eglSwapBuffers, which - * means that the window must be current. Thus, only one thread will - * ever be here for this window. - * - * There is still a race condition if the X window gets destroyed - * before the server receives the PresentPixmap request, but there's - * not much that we can do about that. */ return EGL_TRUE; } - pthread_mutex_unlock(&pwin->mutex); - eplDisplayUnlock(pdpy); - xcbevt = xcb_wait_for_special_event(pwin->inst->conn, pwin->present_event); - eplDisplayLock(pdpy); - pthread_mutex_lock(&pwin->mutex); - - // Sanity check: If something called eglTerminate, then that should have - // destroyed the surface. - assert(pdpy->priv->inst == pwin->inst || surf->deleted); - - if (surf->deleted) - { - /* - * Some other thread came along and called eglDestroySurface or - * eglTerminate. - */ - return EGL_TRUE; - } - if (xcbevt == NULL) { // Note that this only happens if the X11 connection gets killed, as @@ -1788,26 +1730,10 @@ static int CheckBufferReleaseImplicit(EplDisplay *pdpy, EplSurface *surf, } } - // Release the locks while we wait, so that we don't block - // other threads. - pthread_mutex_unlock(&pwin->mutex); - eplDisplayUnlock(pdpy); - ret = poll(fds, count, timeout_ms); err = errno; - eplDisplayLock(pdpy); - pthread_mutex_lock(&pwin->mutex); - - if (surf->deleted) - { - /* - * Some other thread came along and called - * eglDestroySurface or eglTerminate. - */ - return count; - } - else if (ret > 0) + if (ret > 0) { for (i=0; imutex); - eplDisplayUnlock(pdpy); - ret = pwin->inst->platform->priv->drm.SyncobjTimelineWait( gbm_device_get_fd(pwin->inst->gbmdev), handles, points, count, timeout, @@ -1981,18 +1902,7 @@ static int CheckBufferReleaseExplicit(EplDisplay *pdpy, EplSurface *surf, &first); err = errno; - eplDisplayLock(pdpy); - pthread_mutex_lock(&pwin->mutex); - - if (surf->deleted) - { - /* - * Some other thread came along and called - * eglDestroySurface or eglTerminate. - */ - return count; - } - else if (ret == 0) + if (ret == 0) { assert(first < count); if (WaitTimelinePoint(pwin->inst, &buffers[first]->timeline)) @@ -2076,7 +1986,7 @@ static X11ColorBuffer *GetFreeBuffer(EplDisplay *pdpy, EplSurface *surf, } } - while (!surf->deleted && !pwin->native_destroyed) + while (!pwin->native_destroyed) { X11ColorBuffer *buffer = NULL; int numBuffers = 0; @@ -2171,12 +2081,7 @@ static X11ColorBuffer *GetFreeBuffer(EplDisplay *pdpy, EplSurface *surf, static EGLBoolean CheckWindowDeleted(EplSurface *surf, EGLBoolean *ret_success) { X11Window *pwin = (X11Window *) surf->priv; - if (surf->deleted) - { - *ret_success = EGL_TRUE; - return EGL_TRUE; - } - else if (pwin->native_destroyed) + if (pwin->native_destroyed) { *ret_success = EGL_FALSE; eplSetError(pwin->inst->platform, EGL_BAD_NATIVE_WINDOW, "The X11 window has been destroyed"); @@ -2351,50 +2256,23 @@ EGLBoolean eplX11SwapBuffers(EplPlatformData *plat, EplDisplay *pdpy, EplSurface return ret; } -EGLBoolean eplX11SwapInterval(EGLDisplay edpy, EGLint interval) +EGLBoolean eplX11SwapInterval(EplDisplay *pdpy, EplSurface *psurf, EGLint interval) { - EplDisplay *pdpy = eplDisplayAcquire(edpy); - EGLSurface esurf; - EGLBoolean ret = EGL_FALSE; - - if (pdpy == NULL) + if (psurf->type == EPL_SURFACE_TYPE_WINDOW) { - return EGL_FALSE; - } + X11Window *pwin = (X11Window *) psurf->priv; - esurf = pdpy->platform->egl.GetCurrentSurface(EGL_DRAW); - if (esurf != EGL_NO_SURFACE) - { - EplSurface *psurf = eplSurfaceAcquire(pdpy, esurf); - if (psurf != NULL) - { - if (psurf->type == EPL_SURFACE_TYPE_WINDOW) - { - X11Window *pwin = (X11Window *) psurf->priv; - pwin->swap_interval = interval; - if (pwin->swap_interval < 0) - { - pwin->swap_interval = 0; - } - } - eplSurfaceRelease(pdpy, psurf); - ret = EGL_TRUE; - } - else + if (interval < 0) { - // If we don't recognize he current EGLSurface, then just pass the - // call through to the driver. - ret = pdpy->platform->priv->egl.SwapInterval(pdpy->internal_display, interval); + interval = 0; } - } - else - { - eplSetError(pdpy->platform, EGL_BAD_SURFACE, "eglSwapInterval called without a current EGLSurface"); - } - eplDisplayRelease(pdpy); + pthread_mutex_lock(&pwin->mutex); + pwin->swap_interval = interval; + pthread_mutex_unlock(&pwin->mutex); + } - return ret; + return EGL_TRUE; } EGLBoolean eplX11WaitGLWindow(EplDisplay *pdpy, EplSurface *psurf) @@ -2402,7 +2280,7 @@ EGLBoolean eplX11WaitGLWindow(EplDisplay *pdpy, EplSurface *psurf) X11Window *pwin = (X11Window *) psurf->priv; while ((pwin->last_present_serial - pwin->last_complete_serial) > 0 - && !psurf->deleted && !pwin->native_destroyed) + && !pwin->native_destroyed) { if (!WaitForWindowEvents(pdpy, psurf)) {