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:
@ -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:
|
||||||
|
|||||||
@ -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() {
|
||||||
|
|||||||
@ -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() {
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user