GL: Request OpenGL ES 3.0 context on API 29+, with fallback to 2.0.

Despite GL 3.0 not being required on API 29+, it is experimentally
determined to always be supported on our testing devices, on API 29+.

That said, still fall back to OpenGL 2.0 if 3.0 is not supported,
just in case.

PiperOrigin-RevId: 590569772
This commit is contained in:
huangdarwin 2023-12-13 05:58:48 -08:00 committed by Copybara-Service
parent 2fa5430417
commit a15dfd75be
3 changed files with 78 additions and 21 deletions

View File

@ -366,11 +366,23 @@ public final class GlUtil {
return eglSurface; return eglSurface;
} }
/**
* Returns the {@link EGL14#EGL_CONTEXT_CLIENT_VERSION} of the current context.
*
* <p>Returns {@code 0} if no {@link EGLContext} {@linkplain #createFocusedPlaceholderEglSurface
* is focused}.
*/
@RequiresApi(17)
public static long getContextMajorVersion() throws GlException {
return Api17.getContextMajorVersion();
}
/** /**
* Returns a newly created sync object and inserts it into the GL command stream. * Returns a newly created sync object and inserts it into the GL command stream.
* *
* <p>Returns {@code 0} if the operation failed or the {@link EGLContext} version is less than * <p>Returns {@code 0} if the operation failed, no {@link EGLContext} {@linkplain
* 3.0. * #createFocusedPlaceholderEglSurface is focused}, or the focused {@link EGLContext} version is
* less than 3.0.
*/ */
@RequiresApi(17) @RequiresApi(17)
public static long createGlSyncFence() throws GlException { public static long createGlSyncFence() throws GlException {

View File

@ -19,6 +19,7 @@ import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkState;
import static androidx.media3.common.util.Assertions.checkStateNotNull; import static androidx.media3.common.util.Assertions.checkStateNotNull;
import static androidx.media3.common.util.Util.SDK_INT;
import static androidx.media3.effect.DebugTraceUtil.EVENT_VFP_RECEIVE_END_OF_INPUT; import static androidx.media3.effect.DebugTraceUtil.EVENT_VFP_RECEIVE_END_OF_INPUT;
import static androidx.media3.effect.DebugTraceUtil.EVENT_VFP_REGISTER_NEW_INPUT_STREAM; import static androidx.media3.effect.DebugTraceUtil.EVENT_VFP_REGISTER_NEW_INPUT_STREAM;
import static androidx.media3.effect.DebugTraceUtil.EVENT_VFP_SIGNAL_ENDED; import static androidx.media3.effect.DebugTraceUtil.EVENT_VFP_SIGNAL_ENDED;
@ -637,18 +638,20 @@ public final class DefaultVideoFrameProcessor implements VideoFrameProcessor {
ColorInfo.isTransferHdr(outputColorInfo) ColorInfo.isTransferHdr(outputColorInfo)
? GlUtil.EGL_CONFIG_ATTRIBUTES_RGBA_1010102 ? GlUtil.EGL_CONFIG_ATTRIBUTES_RGBA_1010102
: GlUtil.EGL_CONFIG_ATTRIBUTES_RGBA_8888; : GlUtil.EGL_CONFIG_ATTRIBUTES_RGBA_8888;
int openGlVersion =
ColorInfo.isTransferHdr(inputColorInfo) || ColorInfo.isTransferHdr(outputColorInfo) ? 3 : 2;
EGLContext eglContext = EGLContext eglContext =
glObjectsProvider.createEglContext(eglDisplay, openGlVersion, configAttributes); createFocusedEglContextWithFallback(glObjectsProvider, eglDisplay, configAttributes);
glObjectsProvider.createFocusedPlaceholderEglSurface(eglContext, eglDisplay); if ((ColorInfo.isTransferHdr(inputColorInfo) || ColorInfo.isTransferHdr(outputColorInfo))
&& GlUtil.getContextMajorVersion() != 3) {
throw new VideoFrameProcessingException(
"OpenGL ES 3.0 context support is required for HDR input or output.");
}
// Not renderFramesAutomatically means outputting to a display surface. HDR display surfaces // Not renderFramesAutomatically means outputting to a display surface. HDR display surfaces
// require the BT2020 PQ GL extension. // require the BT2020 PQ GL extension.
if (!renderFramesAutomatically && ColorInfo.isTransferHdr(outputColorInfo)) { if (!renderFramesAutomatically && ColorInfo.isTransferHdr(outputColorInfo)) {
// Display hardware supports PQ only. // Display hardware supports PQ only.
checkArgument(outputColorInfo.colorTransfer == C.COLOR_TRANSFER_ST2084); checkArgument(outputColorInfo.colorTransfer == C.COLOR_TRANSFER_ST2084);
if (Util.SDK_INT < 33 || !GlUtil.isBt2020PqExtensionSupported()) { if (SDK_INT < 33 || !GlUtil.isBt2020PqExtensionSupported()) {
GlUtil.destroyEglContext(eglDisplay, eglContext); GlUtil.destroyEglContext(eglDisplay, eglContext);
// On API<33, the system cannot display PQ content correctly regardless of whether BT2020 PQ // On API<33, the system cannot display PQ content correctly regardless of whether BT2020 PQ
// GL extension is supported. // GL extension is supported.
@ -882,6 +885,43 @@ 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(
GlObjectsProvider glObjectsProvider, EGLDisplay eglDisplay, int[] configAttributes)
throws GlUtil.GlException {
if (SDK_INT < 29) {
return createFocusedEglContext(
glObjectsProvider, eglDisplay, /* openGlVersion= */ 2, configAttributes);
}
try {
return createFocusedEglContext(
glObjectsProvider, eglDisplay, /* openGlVersion= */ 3, configAttributes);
} catch (GlUtil.GlException e) {
return createFocusedEglContext(
glObjectsProvider, eglDisplay, /* openGlVersion= */ 2, configAttributes);
}
}
/**
* Creates an {@link EGLContext} and focus it using a {@linkplain
* GlObjectsProvider#createFocusedPlaceholderEglSurface placeholder EGL Surface}.
*/
private static EGLContext createFocusedEglContext(
GlObjectsProvider glObjectsProvider,
EGLDisplay eglDisplay,
int openGlVersion,
int[] configAttributes)
throws GlUtil.GlException {
EGLContext eglContext =
glObjectsProvider.createEglContext(eglDisplay, openGlVersion, configAttributes);
// 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;
}
private static final class InputStreamInfo { private static final class InputStreamInfo {
public final @InputType int inputType; public final @InputType int inputType;
public final List<Effect> effects; public final List<Effect> effects;

View File

@ -48,6 +48,7 @@ import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -268,13 +269,15 @@ public final class HdrEditingTest {
assertThat(isToneMappingFallbackApplied.get()).isTrue(); assertThat(isToneMappingFallbackApplied.get()).isTrue();
assertFileHasColorTransfer(context, exportTestResult.filePath, C.COLOR_TRANSFER_SDR); assertFileHasColorTransfer(context, exportTestResult.filePath, C.COLOR_TRANSFER_SDR);
} catch (ExportException exception) { } catch (ExportException exception) {
if (exception.getCause() != null if (exception.getCause() != null) {
&& (Objects.equals( @Nullable String message = exception.getCause().getMessage();
exception.getCause().getMessage(), if (message != null
"Decoding HDR is not supported on this device.") && (Objects.equals(message, "Decoding HDR is not supported on this device.")
|| Objects.equals( || message.contains(
exception.getCause().getMessage(), "Device lacks YUV extension support."))) { "OpenGL ES 3.0 context support is required for HDR input or output.")
return; || Objects.equals(message, "Device lacks YUV extension support."))) {
return;
}
} }
throw exception; throw exception;
} }
@ -330,13 +333,15 @@ public final class HdrEditingTest {
assertThat(isToneMappingFallbackApplied.get()).isTrue(); assertThat(isToneMappingFallbackApplied.get()).isTrue();
assertFileHasColorTransfer(context, exportTestResult.filePath, C.COLOR_TRANSFER_SDR); assertFileHasColorTransfer(context, exportTestResult.filePath, C.COLOR_TRANSFER_SDR);
} catch (ExportException exception) { } catch (ExportException exception) {
if (exception.getCause() != null if (exception.getCause() != null) {
&& (Objects.equals( @Nullable String message = exception.getCause().getMessage();
exception.getCause().getMessage(), if (message != null
"Decoding HDR is not supported on this device.") && (Objects.equals(message, "Decoding HDR is not supported on this device.")
|| Objects.equals( || message.contains(
exception.getCause().getMessage(), "Device lacks YUV extension support."))) { "OpenGL ES 3.0 context support is required for HDR input or output.")
return; || Objects.equals(message, "Device lacks YUV extension support."))) {
return;
}
} }
throw exception; throw exception;
} }