Allow EglBase instances to share EGLConnection.

This enables clients of EglBase to keep using it but
share underlying EGLContext with other clients.
go/meet-android-eglcontext-reduction

Bug: b/225229697
Change-Id: I42719f25be7db169c39878b57a5f1487e3c1894e
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/301941
Reviewed-by: Magnus Jedvert <magjed@webrtc.org>
Commit-Queue: Linus Nilsson <lnilsson@webrtc.org>
Reviewed-by: Xavier Lepaul‎ <xalep@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#39961}
diff --git a/sdk/android/api/org/webrtc/EglBase.java b/sdk/android/api/org/webrtc/EglBase.java
index 64771d0..3b45e35 100644
--- a/sdk/android/api/org/webrtc/EglBase.java
+++ b/sdk/android/api/org/webrtc/EglBase.java
@@ -34,6 +34,38 @@
     long getNativeEglContext();
   }
 
+  /**
+   * Wraps the objects needed to interact with EGL that are independent of a particular EGLSurface.
+   * In practice this means EGLContext, EGLDisplay and EGLConfig objects. Separating them out in a
+   * standalone object allows for multiple EglBase instances to use the same underlying EGLContext,
+   * while still operating on their own EGLSurface.
+   */
+  public interface EglConnection extends RefCounted {
+    /** Analogous to corresponding EglBase#create below. */
+    public static EglConnection create(@Nullable Context sharedContext, int[] configAttributes) {
+      if (sharedContext == null) {
+        return EglConnection.createEgl14(configAttributes);
+      } else if (sharedContext instanceof EglBase14.Context) {
+        return new EglBase14Impl.EglConnection(
+            ((EglBase14.Context) sharedContext).getRawContext(), configAttributes);
+      } else if (sharedContext instanceof EglBase10.Context) {
+        return new EglBase10Impl.EglConnection(
+            ((EglBase10.Context) sharedContext).getRawContext(), configAttributes);
+      }
+      throw new IllegalArgumentException("Unrecognized Context");
+    }
+
+    /** Analogous to corresponding EglBase#createEgl10 below. */
+    public static EglConnection createEgl10(int[] configAttributes) {
+      return new EglBase10Impl.EglConnection(/* sharedContext= */ null, configAttributes);
+    }
+
+    /** Analogous to corresponding EglBase#createEgl14 below. */
+    public static EglConnection createEgl14(int[] configAttributes) {
+      return new EglBase14Impl.EglConnection(/* sharedContext= */ null, configAttributes);
+    }
+  }
+
   // According to the documentation, EGL can be used from multiple threads at the same time if each
   // thread has its own EGLContext, but in practice it deadlocks on some devices when doing this.
   // Therefore, synchronize on this global lock before calling dangerous EGL functions that might
@@ -146,6 +178,24 @@
   }
 
   /**
+   * Creates a new EglBase with a shared EglConnection. EglBase instances sharing the same
+   * EglConnection should be used on the same thread to avoid the underlying EGLContext being made
+   * current on multiple threads. It is up to the client of EglBase to ensure that instances with a
+   * shared EglConnection are current on that thread before each use since other EglBase instances
+   * may have used the same EGLContext since the last interaction.
+   */
+  public static EglBase create(EglConnection eglConnection) {
+    if (eglConnection == null) {
+      return create();
+    } else if (eglConnection instanceof EglBase14Impl.EglConnection) {
+      return new EglBase14Impl((EglBase14Impl.EglConnection) eglConnection);
+    } else if (eglConnection instanceof EglBase10Impl.EglConnection) {
+      return new EglBase10Impl((EglBase10Impl.EglConnection) eglConnection);
+    }
+    throw new IllegalArgumentException("Unrecognized EglConnection");
+  }
+
+  /**
    * Create a new context with the specified config attributes, sharing data with `sharedContext`.
    * If `sharedContext` is null, a root EGL 1.4 context is created.
    */
diff --git a/sdk/android/api/org/webrtc/EglBase10.java b/sdk/android/api/org/webrtc/EglBase10.java
index f8b0a3c..ad2eb1c 100644
--- a/sdk/android/api/org/webrtc/EglBase10.java
+++ b/sdk/android/api/org/webrtc/EglBase10.java
@@ -10,11 +10,24 @@
 
 package org.webrtc;
 
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
 import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
 
 /** EGL 1.0 implementation of EglBase. */
 public interface EglBase10 extends EglBase {
   interface Context extends EglBase.Context {
     EGLContext getRawContext();
   }
+
+  interface EglConnection extends EglBase.EglConnection {
+    EGL10 getEgl();
+
+    EGLContext getContext();
+
+    EGLDisplay getDisplay();
+
+    EGLConfig getConfig();
+  }
 }
diff --git a/sdk/android/api/org/webrtc/EglBase14.java b/sdk/android/api/org/webrtc/EglBase14.java
index 69c89c4..7455362 100644
--- a/sdk/android/api/org/webrtc/EglBase14.java
+++ b/sdk/android/api/org/webrtc/EglBase14.java
@@ -10,11 +10,21 @@
 
 package org.webrtc;
 
+import android.opengl.EGLConfig;
 import android.opengl.EGLContext;
+import android.opengl.EGLDisplay;
 
 /** EGL 1.4 implementation of EglBase. */
 public interface EglBase14 extends EglBase {
   interface Context extends EglBase.Context {
     EGLContext getRawContext();
   }
+
+  interface EglConnection extends EglBase.EglConnection {
+    EGLContext getContext();
+
+    EGLDisplay getDisplay();
+
+    EGLConfig getConfig();
+  }
 }
diff --git a/sdk/android/src/java/org/webrtc/EglBase10Impl.java b/sdk/android/src/java/org/webrtc/EglBase10Impl.java
index 254a17c..5f3cd82 100644
--- a/sdk/android/src/java/org/webrtc/EglBase10Impl.java
+++ b/sdk/android/src/java/org/webrtc/EglBase10Impl.java
@@ -13,7 +13,6 @@
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
-import android.opengl.EGL14;
 import android.opengl.GLException;
 import android.view.Surface;
 import android.view.SurfaceHolder;
@@ -33,11 +32,10 @@
   // This constant is taken from EGL14.EGL_CONTEXT_CLIENT_VERSION.
   private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
 
-  private final EGL10 egl;
-  private EGLContext eglContext;
-  @Nullable private EGLConfig eglConfig;
-  private EGLDisplay eglDisplay;
+  private static final EglConnection EGL_NO_CONNECTION = new EglConnection();
+
   private EGLSurface eglSurface = EGL10.EGL_NO_SURFACE;
+  private EglConnection eglConnection;
 
   // EGL wrapper for an actual EGLContext.
   private static class Context implements EglBase10.Context {
@@ -90,14 +88,80 @@
     }
   }
 
+  public static class EglConnection implements EglBase10.EglConnection {
+    private final EGL10 egl;
+    private final EGLContext eglContext;
+    private final EGLDisplay eglDisplay;
+    private final EGLConfig eglConfig;
+    private final RefCountDelegate refCountDelegate;
+
+    public EglConnection(EGLContext sharedContext, int[] configAttributes) {
+      egl = (EGL10) EGLContext.getEGL();
+      eglDisplay = getEglDisplay(egl);
+      eglConfig = getEglConfig(egl, eglDisplay, configAttributes);
+      final int openGlesVersion = EglBase.getOpenGlesVersionFromConfig(configAttributes);
+      Logging.d(TAG, "Using OpenGL ES version " + openGlesVersion);
+      eglContext = createEglContext(egl, sharedContext, eglDisplay, eglConfig, openGlesVersion);
+
+      // Ref count delegate with release callback.
+      refCountDelegate = new RefCountDelegate(() -> {
+        synchronized (EglBase.lock) {
+          egl.eglMakeCurrent(
+              eglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
+        }
+        egl.eglDestroyContext(eglDisplay, eglContext);
+        egl.eglTerminate(eglDisplay);
+      });
+    }
+
+    // Returns a "null" EglConnection. Useful to represent a released instance with default values.
+    private EglConnection() {
+      egl = (EGL10) EGLContext.getEGL();
+      eglContext = EGL10.EGL_NO_CONTEXT;
+      eglDisplay = EGL10.EGL_NO_DISPLAY;
+      eglConfig = null;
+      refCountDelegate = new RefCountDelegate(() -> {});
+    }
+
+    @Override
+    public void retain() {
+      refCountDelegate.retain();
+    }
+
+    @Override
+    public void release() {
+      refCountDelegate.release();
+    }
+
+    @Override
+    public EGL10 getEgl() {
+      return egl;
+    }
+
+    @Override
+    public EGLContext getContext() {
+      return eglContext;
+    }
+
+    @Override
+    public EGLDisplay getDisplay() {
+      return eglDisplay;
+    }
+
+    @Override
+    public EGLConfig getConfig() {
+      return eglConfig;
+    }
+  }
+
   // Create a new context with the specified config type, sharing data with sharedContext.
   public EglBase10Impl(EGLContext sharedContext, int[] configAttributes) {
-    this.egl = (EGL10) EGLContext.getEGL();
-    eglDisplay = getEglDisplay();
-    eglConfig = getEglConfig(egl, eglDisplay, configAttributes);
-    final int openGlesVersion = EglBase.getOpenGlesVersionFromConfig(configAttributes);
-    Logging.d(TAG, "Using OpenGL ES version " + openGlesVersion);
-    eglContext = createEglContext(sharedContext, eglDisplay, eglConfig, openGlesVersion);
+    this.eglConnection = new EglConnection(sharedContext, configAttributes);
+  }
+
+  public EglBase10Impl(EglConnection eglConnection) {
+    this.eglConnection = eglConnection;
+    this.eglConnection.retain();
   }
 
   @Override
@@ -186,8 +250,11 @@
     if (eglSurface != EGL10.EGL_NO_SURFACE) {
       throw new RuntimeException("Already has an EGLSurface");
     }
+
+    EGL10 egl = eglConnection.getEgl();
     int[] surfaceAttribs = {EGL10.EGL_NONE};
-    eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig, nativeWindow, surfaceAttribs);
+    eglSurface = egl.eglCreateWindowSurface(
+        eglConnection.getDisplay(), eglConnection.getConfig(), nativeWindow, surfaceAttribs);
     if (eglSurface == EGL10.EGL_NO_SURFACE) {
       throw new GLException(egl.eglGetError(),
           "Failed to create window surface: 0x" + Integer.toHexString(egl.eglGetError()));
@@ -206,8 +273,10 @@
     if (eglSurface != EGL10.EGL_NO_SURFACE) {
       throw new RuntimeException("Already has an EGLSurface");
     }
+    EGL10 egl = eglConnection.getEgl();
     int[] surfaceAttribs = {EGL10.EGL_WIDTH, width, EGL10.EGL_HEIGHT, height, EGL10.EGL_NONE};
-    eglSurface = egl.eglCreatePbufferSurface(eglDisplay, eglConfig, surfaceAttribs);
+    eglSurface = egl.eglCreatePbufferSurface(
+        eglConnection.getDisplay(), eglConnection.getConfig(), surfaceAttribs);
     if (eglSurface == EGL10.EGL_NO_SURFACE) {
       throw new GLException(egl.eglGetError(),
           "Failed to create pixel buffer surface with size " + width + "x" + height + ": 0x"
@@ -217,7 +286,8 @@
 
   @Override
   public org.webrtc.EglBase.Context getEglBaseContext() {
-    return new Context(egl, eglContext, eglConfig);
+    return new Context(
+        eglConnection.getEgl(), eglConnection.getContext(), eglConnection.getConfig());
   }
 
   @Override
@@ -228,28 +298,29 @@
   @Override
   public int surfaceWidth() {
     final int widthArray[] = new int[1];
-    egl.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_WIDTH, widthArray);
+    eglConnection.getEgl().eglQuerySurface(
+        eglConnection.getDisplay(), eglSurface, EGL10.EGL_WIDTH, widthArray);
     return widthArray[0];
   }
 
   @Override
   public int surfaceHeight() {
     final int heightArray[] = new int[1];
-    egl.eglQuerySurface(eglDisplay, eglSurface, EGL10.EGL_HEIGHT, heightArray);
+    eglConnection.getEgl().eglQuerySurface(
+        eglConnection.getDisplay(), eglSurface, EGL10.EGL_HEIGHT, heightArray);
     return heightArray[0];
   }
 
   @Override
   public void releaseSurface() {
     if (eglSurface != EGL10.EGL_NO_SURFACE) {
-      egl.eglDestroySurface(eglDisplay, eglSurface);
+      eglConnection.getEgl().eglDestroySurface(eglConnection.getDisplay(), eglSurface);
       eglSurface = EGL10.EGL_NO_SURFACE;
     }
   }
 
   private void checkIsNotReleased() {
-    if (eglDisplay == EGL10.EGL_NO_DISPLAY || eglContext == EGL10.EGL_NO_CONTEXT
-        || eglConfig == null) {
+    if (eglConnection == EGL_NO_CONNECTION) {
       throw new RuntimeException("This object has been released");
     }
   }
@@ -258,12 +329,8 @@
   public void release() {
     checkIsNotReleased();
     releaseSurface();
-    detachCurrent();
-    egl.eglDestroyContext(eglDisplay, eglContext);
-    egl.eglTerminate(eglDisplay);
-    eglContext = EGL10.EGL_NO_CONTEXT;
-    eglDisplay = EGL10.EGL_NO_DISPLAY;
-    eglConfig = null;
+    eglConnection.release();
+    eglConnection = EGL_NO_CONNECTION;
   }
 
   @Override
@@ -273,7 +340,9 @@
       throw new RuntimeException("No EGLSurface - can't make current");
     }
     synchronized (EglBase.lock) {
-      if (!egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
+      EGL10 egl = eglConnection.getEgl();
+      if (!egl.eglMakeCurrent(
+              eglConnection.getDisplay(), eglSurface, eglSurface, eglConnection.getContext())) {
         throw new GLException(egl.eglGetError(),
             "eglMakeCurrent failed: 0x" + Integer.toHexString(egl.eglGetError()));
       }
@@ -284,8 +353,9 @@
   @Override
   public void detachCurrent() {
     synchronized (EglBase.lock) {
-      if (!egl.eglMakeCurrent(
-              eglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT)) {
+      EGL10 egl = eglConnection.getEgl();
+      if (!egl.eglMakeCurrent(eglConnection.getDisplay(), EGL10.EGL_NO_SURFACE,
+              EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT)) {
         throw new GLException(egl.eglGetError(),
             "eglDetachCurrent failed: 0x" + Integer.toHexString(egl.eglGetError()));
       }
@@ -299,7 +369,7 @@
       throw new RuntimeException("No EGLSurface - can't swap buffers");
     }
     synchronized (EglBase.lock) {
-      egl.eglSwapBuffers(eglDisplay, eglSurface);
+      eglConnection.getEgl().eglSwapBuffers(eglConnection.getDisplay(), eglSurface);
     }
   }
 
@@ -310,7 +380,7 @@
   }
 
   // Return an EGLDisplay, or die trying.
-  private EGLDisplay getEglDisplay() {
+  private static EGLDisplay getEglDisplay(EGL10 egl) {
     EGLDisplay eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
     if (eglDisplay == EGL10.EGL_NO_DISPLAY) {
       throw new GLException(egl.eglGetError(),
@@ -343,8 +413,8 @@
   }
 
   // Return an EGLConfig, or die trying.
-  private EGLContext createEglContext(@Nullable EGLContext sharedContext, EGLDisplay eglDisplay,
-      EGLConfig eglConfig, int openGlesVersion) {
+  private static EGLContext createEglContext(EGL10 egl, @Nullable EGLContext sharedContext,
+      EGLDisplay eglDisplay, EGLConfig eglConfig, int openGlesVersion) {
     if (sharedContext != null && sharedContext == EGL10.EGL_NO_CONTEXT) {
       throw new RuntimeException("Invalid sharedContext");
     }
diff --git a/sdk/android/src/java/org/webrtc/EglBase14Impl.java b/sdk/android/src/java/org/webrtc/EglBase14Impl.java
index e53dda6..4c0a3e9 100644
--- a/sdk/android/src/java/org/webrtc/EglBase14Impl.java
+++ b/sdk/android/src/java/org/webrtc/EglBase14Impl.java
@@ -18,10 +18,8 @@
 import android.opengl.EGLExt;
 import android.opengl.EGLSurface;
 import android.opengl.GLException;
-import android.os.Build;
 import android.view.Surface;
 import androidx.annotation.Nullable;
-import org.webrtc.EglBase;
 
 /**
  * Holds EGL state and utility methods for handling an EGL14 EGLContext, an EGLDisplay,
@@ -30,10 +28,10 @@
 @SuppressWarnings("ReferenceEquality") // We want to compare to EGL14 constants.
 class EglBase14Impl implements EglBase14 {
   private static final String TAG = "EglBase14Impl";
-  private EGLContext eglContext;
-  @Nullable private EGLConfig eglConfig;
-  private EGLDisplay eglDisplay;
+  private static final EglConnection EGL_NO_CONNECTION = new EglConnection();
+
   private EGLSurface eglSurface = EGL14.EGL_NO_SURFACE;
+  private EglConnection eglConnection;
 
   public static class Context implements EglBase14.Context {
     private final EGLContext egl14Context;
@@ -53,14 +51,74 @@
     }
   }
 
+  public static class EglConnection implements EglBase14.EglConnection {
+    private final EGLContext eglContext;
+    private final EGLDisplay eglDisplay;
+    private final EGLConfig eglConfig;
+    private final RefCountDelegate refCountDelegate;
+
+    public EglConnection(EGLContext sharedContext, int[] configAttributes) {
+      eglDisplay = getEglDisplay();
+      eglConfig = getEglConfig(eglDisplay, configAttributes);
+      final int openGlesVersion = EglBase.getOpenGlesVersionFromConfig(configAttributes);
+      Logging.d(TAG, "Using OpenGL ES version " + openGlesVersion);
+      eglContext = createEglContext(sharedContext, eglDisplay, eglConfig, openGlesVersion);
+
+      // Ref count delegate with release callback.
+      refCountDelegate = new RefCountDelegate(() -> {
+        synchronized (EglBase.lock) {
+          EGL14.eglMakeCurrent(
+              eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
+          EGL14.eglDestroyContext(eglDisplay, eglContext);
+        }
+        EGL14.eglReleaseThread();
+        EGL14.eglTerminate(eglDisplay);
+      });
+    }
+
+    // Returns a "null" EglConnection. Useful to represent a released instance with default values.
+    private EglConnection() {
+      eglContext = EGL14.EGL_NO_CONTEXT;
+      eglDisplay = EGL14.EGL_NO_DISPLAY;
+      eglConfig = null;
+      refCountDelegate = new RefCountDelegate(() -> {});
+    }
+
+    @Override
+    public void retain() {
+      refCountDelegate.retain();
+    }
+
+    @Override
+    public void release() {
+      refCountDelegate.release();
+    }
+
+    @Override
+    public EGLContext getContext() {
+      return eglContext;
+    }
+
+    @Override
+    public EGLDisplay getDisplay() {
+      return eglDisplay;
+    }
+
+    @Override
+    public EGLConfig getConfig() {
+      return eglConfig;
+    }
+  }
   // Create a new context with the specified config type, sharing data with sharedContext.
   // `sharedContext` may be null.
   public EglBase14Impl(EGLContext sharedContext, int[] configAttributes) {
-    eglDisplay = getEglDisplay();
-    eglConfig = getEglConfig(eglDisplay, configAttributes);
-    final int openGlesVersion = EglBase.getOpenGlesVersionFromConfig(configAttributes);
-    Logging.d(TAG, "Using OpenGL ES version " + openGlesVersion);
-    eglContext = createEglContext(sharedContext, eglDisplay, eglConfig, openGlesVersion);
+    this.eglConnection = new EglConnection(sharedContext, configAttributes);
+  }
+
+  // Create a new EglBase using an existing, possibly externally managed, EglConnection.
+  public EglBase14Impl(EglConnection eglConnection) {
+    this.eglConnection = eglConnection;
+    this.eglConnection.retain();
   }
 
   // Create EGLSurface from the Android Surface.
@@ -85,7 +143,8 @@
       throw new RuntimeException("Already has an EGLSurface");
     }
     int[] surfaceAttribs = {EGL14.EGL_NONE};
-    eglSurface = EGL14.eglCreateWindowSurface(eglDisplay, eglConfig, surface, surfaceAttribs, 0);
+    eglSurface = EGL14.eglCreateWindowSurface(
+        eglConnection.getDisplay(), eglConnection.getConfig(), surface, surfaceAttribs, 0);
     if (eglSurface == EGL14.EGL_NO_SURFACE) {
       throw new GLException(EGL14.eglGetError(),
           "Failed to create window surface: 0x" + Integer.toHexString(EGL14.eglGetError()));
@@ -104,7 +163,8 @@
       throw new RuntimeException("Already has an EGLSurface");
     }
     int[] surfaceAttribs = {EGL14.EGL_WIDTH, width, EGL14.EGL_HEIGHT, height, EGL14.EGL_NONE};
-    eglSurface = EGL14.eglCreatePbufferSurface(eglDisplay, eglConfig, surfaceAttribs, 0);
+    eglSurface = EGL14.eglCreatePbufferSurface(
+        eglConnection.getDisplay(), eglConnection.getConfig(), surfaceAttribs, 0);
     if (eglSurface == EGL14.EGL_NO_SURFACE) {
       throw new GLException(EGL14.eglGetError(),
           "Failed to create pixel buffer surface with size " + width + "x" + height + ": 0x"
@@ -114,7 +174,7 @@
 
   @Override
   public Context getEglBaseContext() {
-    return new Context(eglContext);
+    return new Context(eglConnection.getContext());
   }
 
   @Override
@@ -124,29 +184,28 @@
 
   @Override
   public int surfaceWidth() {
-    final int widthArray[] = new int[1];
-    EGL14.eglQuerySurface(eglDisplay, eglSurface, EGL14.EGL_WIDTH, widthArray, 0);
+    final int[] widthArray = new int[1];
+    EGL14.eglQuerySurface(eglConnection.getDisplay(), eglSurface, EGL14.EGL_WIDTH, widthArray, 0);
     return widthArray[0];
   }
 
   @Override
   public int surfaceHeight() {
-    final int heightArray[] = new int[1];
-    EGL14.eglQuerySurface(eglDisplay, eglSurface, EGL14.EGL_HEIGHT, heightArray, 0);
+    final int[] heightArray = new int[1];
+    EGL14.eglQuerySurface(eglConnection.getDisplay(), eglSurface, EGL14.EGL_HEIGHT, heightArray, 0);
     return heightArray[0];
   }
 
   @Override
   public void releaseSurface() {
     if (eglSurface != EGL14.EGL_NO_SURFACE) {
-      EGL14.eglDestroySurface(eglDisplay, eglSurface);
+      EGL14.eglDestroySurface(eglConnection.getDisplay(), eglSurface);
       eglSurface = EGL14.EGL_NO_SURFACE;
     }
   }
 
   private void checkIsNotReleased() {
-    if (eglDisplay == EGL14.EGL_NO_DISPLAY || eglContext == EGL14.EGL_NO_CONTEXT
-        || eglConfig == null) {
+    if (eglConnection == EGL_NO_CONNECTION) {
       throw new RuntimeException("This object has been released");
     }
   }
@@ -155,15 +214,8 @@
   public void release() {
     checkIsNotReleased();
     releaseSurface();
-    detachCurrent();
-    synchronized (EglBase.lock) {
-      EGL14.eglDestroyContext(eglDisplay, eglContext);
-    }
-    EGL14.eglReleaseThread();
-    EGL14.eglTerminate(eglDisplay);
-    eglContext = EGL14.EGL_NO_CONTEXT;
-    eglDisplay = EGL14.EGL_NO_DISPLAY;
-    eglConfig = null;
+    eglConnection.release();
+    eglConnection = EGL_NO_CONNECTION;
   }
 
   @Override
@@ -173,7 +225,8 @@
       throw new RuntimeException("No EGLSurface - can't make current");
     }
     synchronized (EglBase.lock) {
-      if (!EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
+      if (!EGL14.eglMakeCurrent(
+              eglConnection.getDisplay(), eglSurface, eglSurface, eglConnection.getContext())) {
         throw new GLException(EGL14.eglGetError(),
             "eglMakeCurrent failed: 0x" + Integer.toHexString(EGL14.eglGetError()));
       }
@@ -184,8 +237,8 @@
   @Override
   public void detachCurrent() {
     synchronized (EglBase.lock) {
-      if (!EGL14.eglMakeCurrent(
-              eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT)) {
+      if (!EGL14.eglMakeCurrent(eglConnection.getDisplay(), EGL14.EGL_NO_SURFACE,
+              EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT)) {
         throw new GLException(EGL14.eglGetError(),
             "eglDetachCurrent failed: 0x" + Integer.toHexString(EGL14.eglGetError()));
       }
@@ -199,7 +252,7 @@
       throw new RuntimeException("No EGLSurface - can't swap buffers");
     }
     synchronized (EglBase.lock) {
-      EGL14.eglSwapBuffers(eglDisplay, eglSurface);
+      EGL14.eglSwapBuffers(eglConnection.getDisplay(), eglSurface);
     }
   }
 
@@ -212,8 +265,8 @@
     synchronized (EglBase.lock) {
       // See
       // https://android.googlesource.com/platform/frameworks/native/+/tools_r22.2/opengl/specs/EGL_ANDROID_presentation_time.txt
-      EGLExt.eglPresentationTimeANDROID(eglDisplay, eglSurface, timeStampNs);
-      EGL14.eglSwapBuffers(eglDisplay, eglSurface);
+      EGLExt.eglPresentationTimeANDROID(eglConnection.getDisplay(), eglSurface, timeStampNs);
+      EGL14.eglSwapBuffers(eglConnection.getDisplay(), eglSurface);
     }
   }