summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/egl/drivers/dri2/egl_dri2.c111
-rw-r--r--src/egl/drivers/dri2/egl_dri2.h4
2 files changed, 95 insertions, 20 deletions
diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
index 030227d8477..a5cab68e278 100644
--- a/src/egl/drivers/dri2/egl_dri2.c
+++ b/src/egl/drivers/dri2/egl_dri2.c
@@ -757,10 +757,33 @@ dri2_create_screen(_EGLDisplay *disp)
/**
* Called via eglInitialize(), GLX_drv->API.Initialize().
+ *
+ * This must be guaranteed to be called exactly once, even if eglInitialize is
+ * called many times (without a eglTerminate in between).
*/
static EGLBoolean
dri2_initialize(_EGLDriver *drv, _EGLDisplay *disp)
{
+ EGLBoolean ret = EGL_FALSE;
+ struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+
+ /* In the case where the application calls eglMakeCurrent(context1),
+ * eglTerminate, then eglInitialize again (without a call to eglReleaseThread
+ * or eglMakeCurrent(NULL) before that), dri2_dpy structure is still
+ * initialized, as we need it to be able to free context1 correctly.
+ *
+ * It would probably be safest to forcibly release the display with
+ * dri2_display_release, to make sure the display is reinitialized correctly.
+ * However, the EGL spec states that we need to keep a reference to the
+ * current context (so we cannot call dri2_make_current(NULL)), and therefore
+ * we would leak context1 as we would be missing the old display connection
+ * to free it up correctly.
+ */
+ if (dri2_dpy) {
+ dri2_dpy->ref_count++;
+ return EGL_TRUE;
+ }
+
/* not until swrast_dri is supported */
if (disp->Options.UseFallback)
return EGL_FALSE;
@@ -769,52 +792,75 @@ dri2_initialize(_EGLDriver *drv, _EGLDisplay *disp)
#ifdef HAVE_SURFACELESS_PLATFORM
case _EGL_PLATFORM_SURFACELESS:
if (disp->Options.TestOnly)
- return EGL_TRUE;
- return dri2_initialize_surfaceless(drv, disp);
+ ret = EGL_TRUE;
+ else
+ ret = dri2_initialize_surfaceless(drv, disp);
+ break;
#endif
-
#ifdef HAVE_X11_PLATFORM
case _EGL_PLATFORM_X11:
if (disp->Options.TestOnly)
- return EGL_TRUE;
- return dri2_initialize_x11(drv, disp);
+ ret = EGL_TRUE;
+ else
+ ret = dri2_initialize_x11(drv, disp);
+ break;
#endif
-
#ifdef HAVE_DRM_PLATFORM
case _EGL_PLATFORM_DRM:
if (disp->Options.TestOnly)
- return EGL_TRUE;
- return dri2_initialize_drm(drv, disp);
+ ret = EGL_TRUE;
+ else
+ ret = dri2_initialize_drm(drv, disp);
+ break;
#endif
#ifdef HAVE_WAYLAND_PLATFORM
case _EGL_PLATFORM_WAYLAND:
if (disp->Options.TestOnly)
- return EGL_TRUE;
- return dri2_initialize_wayland(drv, disp);
+ ret = EGL_TRUE;
+ else
+ ret = dri2_initialize_wayland(drv, disp);
+ break;
#endif
#ifdef HAVE_ANDROID_PLATFORM
case _EGL_PLATFORM_ANDROID:
if (disp->Options.TestOnly)
- return EGL_TRUE;
- return dri2_initialize_android(drv, disp);
+ ret = EGL_TRUE;
+ else
+ ret = dri2_initialize_android(drv, disp);
+ break;
#endif
-
default:
_eglLog(_EGL_WARNING, "No EGL platform enabled.");
return EGL_FALSE;
}
+
+ if (ret) {
+ dri2_dpy = dri2_egl_display(disp);
+
+ if (!dri2_dpy) {
+ return EGL_FALSE;
+ }
+
+ dri2_dpy->ref_count++;
+ }
+
+ return ret;
}
/**
- * Called via eglTerminate(), drv->API.Terminate().
+ * Decrement display reference count, and free up display if necessary.
*/
-static EGLBoolean
-dri2_terminate(_EGLDriver *drv, _EGLDisplay *disp)
-{
+static void
+dri2_display_release(_EGLDisplay *disp) {
struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
unsigned i;
- _eglReleaseDisplayResources(drv, disp);
+ assert(dri2_dpy->ref_count > 0);
+ dri2_dpy->ref_count--;
+
+ if (dri2_dpy->ref_count > 0)
+ return;
+
_eglCleanupDisplay(disp);
if (dri2_dpy->own_dri_screen)
@@ -869,6 +915,21 @@ dri2_terminate(_EGLDriver *drv, _EGLDisplay *disp)
}
free(dri2_dpy);
disp->DriverData = NULL;
+}
+
+/**
+ * Called via eglTerminate(), drv->API.Terminate().
+ *
+ * This must be guaranteed to be called exactly once, even if eglTerminate is
+ * called many times (without a eglInitialize in between).
+ */
+static EGLBoolean
+dri2_terminate(_EGLDriver *drv, _EGLDisplay *disp)
+{
+ /* Release all non-current Context/Surfaces. */
+ _eglReleaseDisplayResources(drv, disp);
+
+ dri2_display_release(disp);
return EGL_TRUE;
}
@@ -1188,6 +1249,10 @@ dri2_make_current(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *dsurf,
_EGLSurface *tmp_dsurf, *tmp_rsurf;
__DRIdrawable *ddraw, *rdraw;
__DRIcontext *cctx;
+ EGLBoolean unbind;
+
+ if (!dri2_dpy)
+ return _eglError(EGL_NOT_INITIALIZED, "eglMakeCurrent");
/* make new bindings */
if (!_eglBindContext(ctx, dsurf, rsurf, &old_ctx, &old_dsurf, &old_rsurf)) {
@@ -1208,8 +1273,9 @@ dri2_make_current(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *dsurf,
dri2_dpy->core->unbindContext(old_cctx);
}
- if ((cctx == NULL && ddraw == NULL && rdraw == NULL) ||
- dri2_dpy->core->bindContext(cctx, ddraw, rdraw)) {
+ unbind = (cctx == NULL && ddraw == NULL && rdraw == NULL);
+
+ if (unbind || dri2_dpy->core->bindContext(cctx, ddraw, rdraw)) {
if (old_dsurf)
drv->API.DestroySurface(drv, disp, old_dsurf);
if (old_rsurf)
@@ -1217,6 +1283,11 @@ dri2_make_current(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *dsurf,
if (old_ctx)
drv->API.DestroyContext(drv, disp, old_ctx);
+ if (!unbind)
+ dri2_dpy->ref_count++;
+ if (old_dsurf || old_rsurf || old_ctx)
+ dri2_display_release(disp);
+
return EGL_TRUE;
} else {
/* undo the previous _eglBindContext */
diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
index 317de069ecd..45778756f38 100644
--- a/src/egl/drivers/dri2/egl_dri2.h
+++ b/src/egl/drivers/dri2/egl_dri2.h
@@ -177,6 +177,10 @@ struct dri2_egl_display
const __DRI2interopExtension *interop;
int fd;
+ /* dri2_initialize/dri2_terminate increment/decrement this count, so does
+ * dri2_make_current (tracks if there are active contexts/surfaces). */
+ int ref_count;
+
int own_device;
int invalidate_available;
int min_swap_interval;