Android EGL: Synchronize calls to eglSwapBuffers and eglMakeCurrent

BUG=webrtc:5702
R=glaznev@webrtc.org, perkj@webrtc.org

Review URL: https://codereview.webrtc.org/1848483002 .

Cr-Commit-Position: refs/heads/master@{#12178}
This commit is contained in:
Magnus Jedvert
2016-03-31 13:17:11 +02:00
parent 71bdda0ede
commit 3db6f9b4df
4 changed files with 41 additions and 18 deletions

View File

@ -25,6 +25,12 @@ public abstract class EglBase {
public static class Context { public static class Context {
} }
// 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
// deadlock. See https://bugs.chromium.org/p/webrtc/issues/detail?id=5702 for more info.
public static final Object lock = new Object();
// These constants are taken from EGL14.EGL_OPENGL_ES2_BIT and EGL14.EGL_CONTEXT_CLIENT_VERSION. // These constants are taken from EGL14.EGL_OPENGL_ES2_BIT and EGL14.EGL_CONTEXT_CLIENT_VERSION.
// https://android.googlesource.com/platform/frameworks/base/+/master/opengl/java/android/opengl/EGL14.java // https://android.googlesource.com/platform/frameworks/base/+/master/opengl/java/android/opengl/EGL14.java
// This is similar to how GlSurfaceView does: // This is similar to how GlSurfaceView does:

View File

@ -220,17 +220,21 @@ final class EglBase10 extends EglBase {
if (eglSurface == EGL10.EGL_NO_SURFACE) { if (eglSurface == EGL10.EGL_NO_SURFACE) {
throw new RuntimeException("No EGLSurface - can't make current"); throw new RuntimeException("No EGLSurface - can't make current");
} }
synchronized (EglBase.lock) {
if (!egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) { if (!egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
throw new RuntimeException("eglMakeCurrent failed"); throw new RuntimeException("eglMakeCurrent failed");
} }
} }
}
// Detach the current EGL context, so that it can be made current on another thread. // Detach the current EGL context, so that it can be made current on another thread.
@Override @Override
public void detachCurrent() { public void detachCurrent() {
synchronized (EglBase.lock) {
if (!egl.eglMakeCurrent( if (!egl.eglMakeCurrent(
eglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT)) { eglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT)) {
throw new RuntimeException("eglMakeCurrent failed"); throw new RuntimeException("eglDetachCurrent failed");
}
} }
} }
@ -240,8 +244,10 @@ final class EglBase10 extends EglBase {
if (eglSurface == EGL10.EGL_NO_SURFACE) { if (eglSurface == EGL10.EGL_NO_SURFACE) {
throw new RuntimeException("No EGLSurface - can't swap buffers"); throw new RuntimeException("No EGLSurface - can't swap buffers");
} }
synchronized (EglBase.lock) {
egl.eglSwapBuffers(eglDisplay, eglSurface); egl.eglSwapBuffers(eglDisplay, eglSurface);
} }
}
// Return an EGLDisplay, or die trying. // Return an EGLDisplay, or die trying.
private EGLDisplay getEglDisplay() { private EGLDisplay getEglDisplay() {

View File

@ -168,17 +168,21 @@ public final class EglBase14 extends EglBase {
if (eglSurface == EGL14.EGL_NO_SURFACE) { if (eglSurface == EGL14.EGL_NO_SURFACE) {
throw new RuntimeException("No EGLSurface - can't make current"); throw new RuntimeException("No EGLSurface - can't make current");
} }
synchronized (EglBase.lock) {
if (!EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) { if (!EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) {
throw new RuntimeException("eglMakeCurrent failed"); throw new RuntimeException("eglMakeCurrent failed");
} }
} }
}
// Detach the current EGL context, so that it can be made current on another thread. // Detach the current EGL context, so that it can be made current on another thread.
@Override @Override
public void detachCurrent() { public void detachCurrent() {
synchronized (EglBase.lock) {
if (!EGL14.eglMakeCurrent( if (!EGL14.eglMakeCurrent(
eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT)) { eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT)) {
throw new RuntimeException("eglMakeCurrent failed"); throw new RuntimeException("eglDetachCurrent failed");
}
} }
} }
@ -188,18 +192,22 @@ public final class EglBase14 extends EglBase {
if (eglSurface == EGL14.EGL_NO_SURFACE) { if (eglSurface == EGL14.EGL_NO_SURFACE) {
throw new RuntimeException("No EGLSurface - can't swap buffers"); throw new RuntimeException("No EGLSurface - can't swap buffers");
} }
synchronized (EglBase.lock) {
EGL14.eglSwapBuffers(eglDisplay, eglSurface); EGL14.eglSwapBuffers(eglDisplay, eglSurface);
} }
}
public void swapBuffers(long timeStampNs) { public void swapBuffers(long timeStampNs) {
checkIsNotReleased(); checkIsNotReleased();
if (eglSurface == EGL14.EGL_NO_SURFACE) { if (eglSurface == EGL14.EGL_NO_SURFACE) {
throw new RuntimeException("No EGLSurface - can't swap buffers"); throw new RuntimeException("No EGLSurface - can't swap buffers");
} }
synchronized (EglBase.lock) {
// See https://android.googlesource.com/platform/frameworks/native/+/tools_r22.2/opengl/specs/EGL_ANDROID_presentation_time.txt // See https://android.googlesource.com/platform/frameworks/native/+/tools_r22.2/opengl/specs/EGL_ANDROID_presentation_time.txt
EGLExt.eglPresentationTimeANDROID(eglDisplay, eglSurface, timeStampNs); EGLExt.eglPresentationTimeANDROID(eglDisplay, eglSurface, timeStampNs);
EGL14.eglSwapBuffers(eglDisplay, eglSurface); EGL14.eglSwapBuffers(eglDisplay, eglSurface);
} }
}
// Return an EGLDisplay, or die trying. // Return an EGLDisplay, or die trying.
private static EGLDisplay getEglDisplay() { private static EGLDisplay getEglDisplay() {

View File

@ -451,8 +451,12 @@ class SurfaceTextureHelper {
isTextureInUse = true; isTextureInUse = true;
hasPendingTexture = false; hasPendingTexture = false;
eglBase.makeCurrent(); // SurfaceTexture.updateTexImage apparently can compete and deadlock with eglSwapBuffers,
// as observed on Nexus 5. Therefore, synchronize it with the EGL functions.
// See https://bugs.chromium.org/p/webrtc/issues/detail?id=5702 for more info.
synchronized (EglBase.lock) {
surfaceTexture.updateTexImage(); surfaceTexture.updateTexImage();
}
final float[] transformMatrix = new float[16]; final float[] transformMatrix = new float[16];
surfaceTexture.getTransformMatrix(transformMatrix); surfaceTexture.getTransformMatrix(transformMatrix);
@ -473,7 +477,6 @@ class SurfaceTextureHelper {
if (yuvConverter != null) if (yuvConverter != null)
yuvConverter.release(); yuvConverter.release();
} }
eglBase.makeCurrent();
GLES20.glDeleteTextures(1, new int[] {oesTextureId}, 0); GLES20.glDeleteTextures(1, new int[] {oesTextureId}, 0);
surfaceTexture.release(); surfaceTexture.release();
eglBase.release(); eglBase.release();