Android: Add error callback for GL_OUT_OF_MEMORY in EglRenderer

Encountering GL_OUT_OF_MEMORY is relatively common and we should give
clients a chance to deal with it in a non-fatal way.

Bug: webrtc:8154
Change-Id: Ifa9ca74392f21083692b02a5144dc5632a88d34d
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/144561
Commit-Queue: Magnus Jedvert <magjed@webrtc.org>
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28495}
This commit is contained in:
Magnus Jedvert
2019-07-05 14:33:12 +02:00
committed by Commit Bot
parent 48b1b18065
commit ecae9cd1a7
2 changed files with 59 additions and 19 deletions

View File

@ -37,6 +37,12 @@ public class EglRenderer implements VideoSink {
public interface FrameListener { void onFrame(Bitmap frame); }
/** Callback for clients to be notified about errors encountered during rendering. */
public static interface ErrorCallback {
/** Called if GLES20.GL_OUT_OF_MEMORY is encountered during rendering. */
void onGlOutOfMemory();
}
private static class FrameListenerAndParams {
public final FrameListener listener;
public final float scale;
@ -112,6 +118,8 @@ public class EglRenderer implements VideoSink {
private final ArrayList<FrameListenerAndParams> frameListeners = new ArrayList<>();
private volatile ErrorCallback errorCallback;
// Variables for fps reduction.
private final Object fpsReductionLock = new Object();
// Time for when next frame should be rendered.
@ -485,6 +493,11 @@ public class EglRenderer implements VideoSink {
ThreadUtils.awaitUninterruptibly(latch);
}
/** Can be set in order to be notified about errors encountered during rendering. */
public void setErrorCallback(ErrorCallback errorCallback) {
this.errorCallback = errorCallback;
}
// VideoSink interface.
@Override
public void onFrame(VideoFrame frame) {
@ -642,29 +655,44 @@ public class EglRenderer implements VideoSink {
drawMatrix.preScale(scaleX, scaleY);
drawMatrix.preTranslate(-0.5f, -0.5f);
if (shouldRenderFrame) {
GLES20.glClearColor(0 /* red */, 0 /* green */, 0 /* blue */, 0 /* alpha */);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
frameDrawer.drawFrame(frame, drawer, drawMatrix, 0 /* viewportX */, 0 /* viewportY */,
eglBase.surfaceWidth(), eglBase.surfaceHeight());
try {
if (shouldRenderFrame) {
GLES20.glClearColor(0 /* red */, 0 /* green */, 0 /* blue */, 0 /* alpha */);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
frameDrawer.drawFrame(frame, drawer, drawMatrix, 0 /* viewportX */, 0 /* viewportY */,
eglBase.surfaceWidth(), eglBase.surfaceHeight());
final long swapBuffersStartTimeNs = System.nanoTime();
if (usePresentationTimeStamp) {
eglBase.swapBuffers(frame.getTimestampNs());
} else {
eglBase.swapBuffers();
final long swapBuffersStartTimeNs = System.nanoTime();
if (usePresentationTimeStamp) {
eglBase.swapBuffers(frame.getTimestampNs());
} else {
eglBase.swapBuffers();
}
final long currentTimeNs = System.nanoTime();
synchronized (statisticsLock) {
++framesRendered;
renderTimeNs += (currentTimeNs - startTimeNs);
renderSwapBufferTimeNs += (currentTimeNs - swapBuffersStartTimeNs);
}
}
final long currentTimeNs = System.nanoTime();
synchronized (statisticsLock) {
++framesRendered;
renderTimeNs += (currentTimeNs - startTimeNs);
renderSwapBufferTimeNs += (currentTimeNs - swapBuffersStartTimeNs);
notifyCallbacks(frame, shouldRenderFrame);
} catch (GlUtil.GlOutOfMemoryException e) {
logE("Error while drawing frame", e);
final ErrorCallback errorCallback = this.errorCallback;
if (errorCallback != null) {
errorCallback.onGlOutOfMemory();
}
// Attempt to free up some resources.
drawer.release();
frameDrawer.release();
bitmapTextureFramebuffer.release();
// Continue here on purpose and retry again for next frame. In worst case, this is a continous
// problem and no more frames will be drawn.
} finally {
frame.release();
}
notifyCallbacks(frame, shouldRenderFrame);
frame.release();
}
private void notifyCallbacks(VideoFrame frame, boolean wasRendered) {
@ -743,6 +771,10 @@ public class EglRenderer implements VideoSink {
}
}
private void logE(String string, Throwable e) {
Logging.e(TAG, name + string, e);
}
private void logD(String string) {
Logging.d(TAG, name + string);
}