Destroy EGLSurface immediately by focusing a placeholder surface

eglDestroySurface only destroys the surface when it's made not current.
Pass the placeholder surface in FinalShaderProgramWrapper and use it
when destroying eglSurface.

PiperOrigin-RevId: 655139661
This commit is contained in:
dancho 2024-07-23 06:18:09 -07:00 committed by Copybara-Service
parent 9c075b692e
commit 1797359950
2 changed files with 42 additions and 17 deletions

View File

@ -32,8 +32,10 @@ import android.graphics.Bitmap;
import android.graphics.SurfaceTexture;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.opengl.GLES20;
import android.opengl.GLES30;
import android.util.Pair;
import android.view.Surface;
import androidx.annotation.GuardedBy;
import androidx.annotation.IntDef;
@ -813,7 +815,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
ColorInfo.isTransferHdr(outputColorInfo)
? GlUtil.EGL_CONFIG_ATTRIBUTES_RGBA_1010102
: GlUtil.EGL_CONFIG_ATTRIBUTES_RGBA_8888;
EGLContext eglContext =
Pair<EGLContext, EGLSurface> eglContextAndPlaceholderSurface =
createFocusedEglContextWithFallback(glObjectsProvider, eglDisplay, configAttributes);
ColorInfo linearColorInfo =
@ -845,7 +847,8 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
new FinalShaderProgramWrapper(
context,
eglDisplay,
eglContext,
eglContextAndPlaceholderSurface.first,
eglContextAndPlaceholderSurface.second,
debugViewProvider,
outputColorInfo,
videoFrameProcessingTaskExecutor,
@ -860,7 +863,7 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
context,
glObjectsProvider,
eglDisplay,
eglContext,
eglContextAndPlaceholderSurface.first,
inputSwitcher,
videoFrameProcessingTaskExecutor,
listener,
@ -1100,8 +1103,12 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
}
}
/** Creates an OpenGL ES 3.0 context if possible, and an OpenGL ES 2.0 context otherwise. */
private static EGLContext createFocusedEglContextWithFallback(
/**
* Creates an OpenGL ES 3.0 context if possible, and an OpenGL ES 2.0 context otherwise.
*
* <p>See {@link #createFocusedEglContext}.
*/
private static Pair<EGLContext, EGLSurface> createFocusedEglContextWithFallback(
GlObjectsProvider glObjectsProvider, EGLDisplay eglDisplay, int[] configAttributes)
throws GlUtil.GlException {
if (SDK_INT < 29) {
@ -1121,8 +1128,10 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
/**
* Creates an {@link EGLContext} and focus it using a {@linkplain
* GlObjectsProvider#createFocusedPlaceholderEglSurface placeholder EGL Surface}.
*
* @return The {@link EGLContext} and a placeholder {@link EGLSurface} as a {@link Pair}.
*/
private static EGLContext createFocusedEglContext(
private static Pair<EGLContext, EGLSurface> createFocusedEglContext(
GlObjectsProvider glObjectsProvider,
EGLDisplay eglDisplay,
int openGlVersion,
@ -1133,8 +1142,9 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
// Some OpenGL ES 3.0 contexts returned from createEglContext may throw EGL_BAD_MATCH when being
// used to createFocusedPlaceHolderEglSurface, despite GL documentation suggesting the contexts,
// if successfully created, are valid. Check early whether the context is really valid.
glObjectsProvider.createFocusedPlaceholderEglSurface(eglContext, eglDisplay);
return eglContext;
EGLSurface eglSurface =
glObjectsProvider.createFocusedPlaceholderEglSurface(eglContext, eglDisplay);
return Pair.create(eglContext, eglSurface);
}
private static final class InputStreamInfo {

View File

@ -83,6 +83,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private final List<RgbMatrix> rgbMatrices;
private final EGLDisplay eglDisplay;
private final EGLContext eglContext;
private final EGLSurface placeholderSurface;
private final DebugViewProvider debugViewProvider;
private final ColorInfo outputColorInfo;
private final VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor;
@ -129,6 +130,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
Context context,
EGLDisplay eglDisplay,
EGLContext eglContext,
EGLSurface placeholderSurface,
DebugViewProvider debugViewProvider,
ColorInfo outputColorInfo,
VideoFrameProcessingTaskExecutor videoFrameProcessingTaskExecutor,
@ -143,6 +145,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
this.rgbMatrices = new ArrayList<>();
this.eglDisplay = eglDisplay;
this.eglContext = eglContext;
this.placeholderSurface = placeholderSurface;
this.debugViewProvider = debugViewProvider;
this.outputColorInfo = outputColorInfo;
this.videoFrameProcessingTaskExecutor = videoFrameProcessingTaskExecutor;
@ -354,16 +357,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
// outputEglSurface is a graphics buffer producer for a BufferQueue, and
// this.outputSurfaceInfo.surface is the associated consumer. The consumer owns the
// BufferQueue https://source.android.com/docs/core/graphics/arch-bq-gralloc#BufferQueue.
// If the consumer and the BufferQueue are released while the producer is still alive, EGL
// gets stuck trying to dequeue a new buffer from the released BufferQueue. This probably
// If the BufferQueue is released while the producer is still alive, EGL gets stuck trying
// to dequeue a new buffer from the released BufferQueue. This probably
// happens when the previously queued back buffer is ready for display.
try {
GlUtil.destroyEglSurface(eglDisplay, outputEglSurface);
} catch (GlUtil.GlException e) {
videoFrameProcessorListenerExecutor.execute(
() -> videoFrameProcessorListener.onError(VideoFrameProcessingException.from(e)));
}
this.outputEglSurface = null;
destroyOutputEglSurface();
}
outputSurfaceInfoChanged =
this.outputSurfaceInfo == null
@ -374,6 +371,24 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
this.outputSurfaceInfo = outputSurfaceInfo;
}
private synchronized void destroyOutputEglSurface() {
if (outputEglSurface == null) {
return;
}
try {
// outputEglSurface will be destroyed only if it's not current.
// See EGL docs. Make the placeholder surface current before destroying.
GlUtil.focusEglSurface(
eglDisplay, eglContext, placeholderSurface, /* width= */ 1, /* height= */ 1);
GlUtil.destroyEglSurface(eglDisplay, outputEglSurface);
} catch (GlUtil.GlException e) {
videoFrameProcessorListenerExecutor.execute(
() -> videoFrameProcessorListener.onError(VideoFrameProcessingException.from(e)));
} finally {
this.outputEglSurface = null;
}
}
private synchronized void renderFrame(
GlObjectsProvider glObjectsProvider,
GlTextureInfo inputTexture,