mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Fix @RequiresApi annotations in GlUtil
`EGL14` was added in API 17, so move all references into the `Api17` wrapper class. `GLES30` requires API 18. Remove some defensive calls to check that a context is current. Checking for GL errors after calls should catch the case of calling GL methods without a context (which is also only likely to come up early in development), and these checks were not done consistently across all methods anyway. That allows removing the API 17 requirement from `SceneRenderer` (where previously it wasn't documented). PiperOrigin-RevId: 548669946
This commit is contained in:
parent
2d3f464008
commit
81ef52763d
1 changed files with 111 additions and 88 deletions
|
|
@ -178,9 +178,7 @@ public final class GlUtil {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
return Api17.isExtensionSupported(EXTENSION_PROTECTED_CONTENT);
|
||||||
@Nullable String eglExtensions = EGL14.eglQueryString(display, EGL10.EGL_EXTENSIONS);
|
|
||||||
return eglExtensions != null && eglExtensions.contains(EXTENSION_PROTECTED_CONTENT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -191,12 +189,7 @@ public final class GlUtil {
|
||||||
* EGLContext)}.
|
* EGLContext)}.
|
||||||
*/
|
*/
|
||||||
public static boolean isSurfacelessContextExtensionSupported() {
|
public static boolean isSurfacelessContextExtensionSupported() {
|
||||||
if (Util.SDK_INT < 17) {
|
return Util.SDK_INT >= 17 && Api17.isExtensionSupported(EXTENSION_SURFACELESS_CONTEXT);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
|
||||||
@Nullable String eglExtensions = EGL14.eglQueryString(display, EGL10.EGL_EXTENSIONS);
|
|
||||||
return eglExtensions != null && eglExtensions.contains(EXTENSION_SURFACELESS_CONTEXT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -209,9 +202,8 @@ public final class GlUtil {
|
||||||
if (Util.SDK_INT < 17) {
|
if (Util.SDK_INT < 17) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable String glExtensions;
|
@Nullable String glExtensions;
|
||||||
if (Util.areEqual(EGL14.eglGetCurrentContext(), EGL14.EGL_NO_CONTEXT)) {
|
if (Util.areEqual(Api17.getCurrentContext(), EGL14.EGL_NO_CONTEXT)) {
|
||||||
// Create a placeholder context and make it current to allow calling GLES20.glGetString().
|
// Create a placeholder context and make it current to allow calling GLES20.glGetString().
|
||||||
try {
|
try {
|
||||||
EGLDisplay eglDisplay = getDefaultEglDisplay();
|
EGLDisplay eglDisplay = getDefaultEglDisplay();
|
||||||
|
|
@ -231,9 +223,7 @@ public final class GlUtil {
|
||||||
|
|
||||||
/** Returns whether {@link #EXTENSION_COLORSPACE_BT2020_PQ} is supported. */
|
/** Returns whether {@link #EXTENSION_COLORSPACE_BT2020_PQ} is supported. */
|
||||||
public static boolean isBt2020PqExtensionSupported() {
|
public static boolean isBt2020PqExtensionSupported() {
|
||||||
EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
return Util.SDK_INT >= 17 && Api17.isExtensionSupported(EXTENSION_COLORSPACE_BT2020_PQ);
|
||||||
@Nullable String eglExtensions = EGL14.eglQueryString(display, EGL10.EGL_EXTENSIONS);
|
|
||||||
return eglExtensions != null && eglExtensions.contains(EXTENSION_COLORSPACE_BT2020_PQ);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns an initialized default {@link EGLDisplay}. */
|
/** Returns an initialized default {@link EGLDisplay}. */
|
||||||
|
|
@ -360,9 +350,9 @@ public final class GlUtil {
|
||||||
@RequiresApi(17)
|
@RequiresApi(17)
|
||||||
public static EGLSurface createFocusedPlaceholderEglSurface(
|
public static EGLSurface createFocusedPlaceholderEglSurface(
|
||||||
EGLContext eglContext, EGLDisplay eglDisplay) throws GlException {
|
EGLContext eglContext, EGLDisplay eglDisplay) throws GlException {
|
||||||
// EGL_CONFIG_ATTRIBUTES_RGBA_1010102 could be used for HDR input, but
|
// EGL_CONFIG_ATTRIBUTES_RGBA_1010102 could be used for HDR input, but EGL14.EGL_NO_SURFACE
|
||||||
// EGL14.EGL_NO_SURFACE support was added before EGL 2, so HDR-capable devices should have
|
// support was added before EGL 2, so HDR-capable devices should have support for EGL_NO_SURFACE
|
||||||
// support for EGL_NO_SURFACE and therefore configAttributes shouldn't matter for HDR.
|
// and therefore configAttributes shouldn't matter for HDR.
|
||||||
int[] configAttributes = EGL_CONFIG_ATTRIBUTES_RGBA_8888;
|
int[] configAttributes = EGL_CONFIG_ATTRIBUTES_RGBA_8888;
|
||||||
EGLSurface eglSurface =
|
EGLSurface eglSurface =
|
||||||
isSurfacelessContextExtensionSupported()
|
isSurfacelessContextExtensionSupported()
|
||||||
|
|
@ -379,26 +369,10 @@ public final class GlUtil {
|
||||||
* <p>Returns {@code 0} if the operation failed or the {@link EGLContext} version is less than
|
* <p>Returns {@code 0} if the operation failed or the {@link EGLContext} version is less than
|
||||||
* 3.0.
|
* 3.0.
|
||||||
*/
|
*/
|
||||||
|
@RequiresApi(17)
|
||||||
public static long createGlSyncFence() throws GlException {
|
public static long createGlSyncFence() throws GlException {
|
||||||
int[] currentEglContextVersion = new int[1];
|
// If the context is an OpenGL 3.0 context, we must be running API 18 or later.
|
||||||
EGL14.eglQueryContext(
|
return Api17.getContextMajorVersion() >= 3 ? Api18.createSyncFence() : 0;
|
||||||
EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY),
|
|
||||||
EGL14.eglGetCurrentContext(),
|
|
||||||
EGL_CONTEXT_CLIENT_VERSION,
|
|
||||||
currentEglContextVersion,
|
|
||||||
/* offset= */ 0);
|
|
||||||
checkGlError();
|
|
||||||
if (currentEglContextVersion[0] < 3) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
long syncObject = GLES30.glFenceSync(GLES30.GL_SYNC_GPU_COMMANDS_COMPLETE, /* flags= */ 0);
|
|
||||||
checkGlError();
|
|
||||||
// Due to specifics of OpenGL, it might happen that the fence creation command is not yet
|
|
||||||
// sent into the GPU command queue, which can cause other threads to wait infinitely if
|
|
||||||
// the glSyncWait/glClientSyncWait command went into the GPU earlier. Hence, we have to
|
|
||||||
// call glFlush to ensure that glFenceSync is inside of the GPU command queue.
|
|
||||||
GLES20.glFlush();
|
|
||||||
return syncObject;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -407,14 +381,22 @@ public final class GlUtil {
|
||||||
* <p>The {@code syncObject} must not be used after deletion.
|
* <p>The {@code syncObject} must not be used after deletion.
|
||||||
*/
|
*/
|
||||||
public static void deleteSyncObject(long syncObject) throws GlException {
|
public static void deleteSyncObject(long syncObject) throws GlException {
|
||||||
GLES30.glDeleteSync(syncObject);
|
// If the sync object is set, we must be running API 18 or later.
|
||||||
checkGlError();
|
if (Util.SDK_INT >= 18) {
|
||||||
|
Api18.deleteSyncObject(syncObject);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Releases the GL sync object if set, suppressing any error. */
|
/** Releases the GL sync object if set, suppressing any error. */
|
||||||
public static void deleteSyncObjectQuietly(long syncObject) {
|
public static void deleteSyncObjectQuietly(long syncObject) {
|
||||||
|
if (Util.SDK_INT >= 18) {
|
||||||
|
try {
|
||||||
// glDeleteSync ignores a 0-valued sync object.
|
// glDeleteSync ignores a 0-valued sync object.
|
||||||
GLES30.glDeleteSync(syncObject);
|
Api18.deleteSyncObject(syncObject);
|
||||||
|
} catch (GlException unused) {
|
||||||
|
// Suppress exceptions.
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -427,14 +409,15 @@ public final class GlUtil {
|
||||||
// Fallback to using glFinish for synchronization when fence creation failed.
|
// Fallback to using glFinish for synchronization when fence creation failed.
|
||||||
GLES20.glFinish();
|
GLES20.glFinish();
|
||||||
} else {
|
} else {
|
||||||
GLES30.glWaitSync(syncObject, /* flags= */ 0, GLES30.GL_TIMEOUT_IGNORED);
|
// If the sync object is set, we must be running API 18 or later.
|
||||||
checkGlError();
|
Api18.waitSync(syncObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the current {@link EGLContext context}. */
|
/** Gets the current {@link EGLContext context}. */
|
||||||
|
@RequiresApi(17)
|
||||||
public static EGLContext getCurrentContext() {
|
public static EGLContext getCurrentContext() {
|
||||||
return EGL14.eglGetCurrentContext();
|
return Api17.getCurrentContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -543,10 +526,16 @@ public final class GlUtil {
|
||||||
* @param width The viewport width, in pixels.
|
* @param width The viewport width, in pixels.
|
||||||
* @param height The viewport height, in pixels.
|
* @param height The viewport height, in pixels.
|
||||||
*/
|
*/
|
||||||
@RequiresApi(17)
|
|
||||||
public static void focusFramebufferUsingCurrentContext(int framebuffer, int width, int height)
|
public static void focusFramebufferUsingCurrentContext(int framebuffer, int width, int height)
|
||||||
throws GlException {
|
throws GlException {
|
||||||
Api17.focusFramebufferUsingCurrentContext(framebuffer, width, height);
|
int[] boundFramebuffer = new int[1];
|
||||||
|
GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, boundFramebuffer, /* offset= */ 0);
|
||||||
|
if (boundFramebuffer[0] != framebuffer) {
|
||||||
|
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebuffer);
|
||||||
|
}
|
||||||
|
checkGlError();
|
||||||
|
GLES20.glViewport(/* x= */ 0, /* y= */ 0, width, height);
|
||||||
|
checkGlError();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -630,9 +619,6 @@ public final class GlUtil {
|
||||||
|
|
||||||
/** Returns a new GL texture identifier. */
|
/** Returns a new GL texture identifier. */
|
||||||
private static int generateTexture() throws GlException {
|
private static int generateTexture() throws GlException {
|
||||||
checkGlException(
|
|
||||||
!Util.areEqual(EGL14.eglGetCurrentContext(), EGL14.EGL_NO_CONTEXT), "No current context");
|
|
||||||
|
|
||||||
int[] texId = new int[1];
|
int[] texId = new int[1];
|
||||||
GLES20.glGenTextures(/* n= */ 1, texId, /* offset= */ 0);
|
GLES20.glGenTextures(/* n= */ 1, texId, /* offset= */ 0);
|
||||||
checkGlError();
|
checkGlError();
|
||||||
|
|
@ -667,9 +653,6 @@ public final class GlUtil {
|
||||||
* @param texId The identifier of the texture to attach to the framebuffer.
|
* @param texId The identifier of the texture to attach to the framebuffer.
|
||||||
*/
|
*/
|
||||||
public static int createFboForTexture(int texId) throws GlException {
|
public static int createFboForTexture(int texId) throws GlException {
|
||||||
checkGlException(
|
|
||||||
!Util.areEqual(EGL14.eglGetCurrentContext(), EGL14.EGL_NO_CONTEXT), "No current context");
|
|
||||||
|
|
||||||
int[] fboId = new int[1];
|
int[] fboId = new int[1];
|
||||||
GLES20.glGenFramebuffers(/* n= */ 1, fboId, /* offset= */ 0);
|
GLES20.glGenFramebuffers(/* n= */ 1, fboId, /* offset= */ 0);
|
||||||
checkGlError();
|
checkGlError();
|
||||||
|
|
@ -737,13 +720,6 @@ public final class GlUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkEglException(String errorMessage) throws GlException {
|
|
||||||
int error = EGL14.eglGetError();
|
|
||||||
if (error != EGL14.EGL_SUCCESS) {
|
|
||||||
throw new GlException(errorMessage + ", error code: 0x" + Integer.toHexString(error));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(17)
|
@RequiresApi(17)
|
||||||
private static final class Api17 {
|
private static final class Api17 {
|
||||||
private Api17() {}
|
private Api17() {}
|
||||||
|
|
@ -787,6 +763,49 @@ public final class GlUtil {
|
||||||
return eglContext;
|
return eglContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DoNotInline
|
||||||
|
public static EGLContext getCurrentContext() {
|
||||||
|
return EGL14.eglGetCurrentContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DoNotInline
|
||||||
|
private static EGLConfig getEglConfig(EGLDisplay eglDisplay, int[] attributes)
|
||||||
|
throws GlException {
|
||||||
|
EGLConfig[] eglConfigs = new EGLConfig[1];
|
||||||
|
if (!EGL14.eglChooseConfig(
|
||||||
|
eglDisplay,
|
||||||
|
attributes,
|
||||||
|
/* attrib_listOffset= */ 0,
|
||||||
|
eglConfigs,
|
||||||
|
/* configsOffset= */ 0,
|
||||||
|
/* config_size= */ 1,
|
||||||
|
/* unusedNumConfig */ new int[1],
|
||||||
|
/* num_configOffset= */ 0)) {
|
||||||
|
throw new GlException("eglChooseConfig failed.");
|
||||||
|
}
|
||||||
|
return eglConfigs[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@DoNotInline
|
||||||
|
public static boolean isExtensionSupported(String extensionName) {
|
||||||
|
EGLDisplay display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
||||||
|
@Nullable String eglExtensions = EGL14.eglQueryString(display, EGL10.EGL_EXTENSIONS);
|
||||||
|
return eglExtensions != null && eglExtensions.contains(extensionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DoNotInline
|
||||||
|
public static int getContextMajorVersion() throws GlException {
|
||||||
|
int[] currentEglContextVersion = new int[1];
|
||||||
|
EGL14.eglQueryContext(
|
||||||
|
EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY),
|
||||||
|
EGL14.eglGetCurrentContext(),
|
||||||
|
EGL_CONTEXT_CLIENT_VERSION,
|
||||||
|
currentEglContextVersion,
|
||||||
|
/* offset= */ 0);
|
||||||
|
checkGlError();
|
||||||
|
return currentEglContextVersion[0];
|
||||||
|
}
|
||||||
|
|
||||||
@DoNotInline
|
@DoNotInline
|
||||||
public static EGLSurface createEglSurface(
|
public static EGLSurface createEglSurface(
|
||||||
EGLDisplay eglDisplay, Object surface, int[] configAttributes, int[] windowAttributes)
|
EGLDisplay eglDisplay, Object surface, int[] configAttributes, int[] windowAttributes)
|
||||||
|
|
@ -829,22 +848,6 @@ public final class GlUtil {
|
||||||
focusFramebufferUsingCurrentContext(framebuffer, width, height);
|
focusFramebufferUsingCurrentContext(framebuffer, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DoNotInline
|
|
||||||
public static void focusFramebufferUsingCurrentContext(int framebuffer, int width, int height)
|
|
||||||
throws GlException {
|
|
||||||
checkGlException(
|
|
||||||
!Util.areEqual(EGL14.eglGetCurrentContext(), EGL14.EGL_NO_CONTEXT), "No current context");
|
|
||||||
|
|
||||||
int[] boundFramebuffer = new int[1];
|
|
||||||
GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING, boundFramebuffer, /* offset= */ 0);
|
|
||||||
if (boundFramebuffer[0] != framebuffer) {
|
|
||||||
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebuffer);
|
|
||||||
}
|
|
||||||
checkGlError();
|
|
||||||
GLES20.glViewport(/* x= */ 0, /* y= */ 0, width, height);
|
|
||||||
checkGlError();
|
|
||||||
}
|
|
||||||
|
|
||||||
@DoNotInline
|
@DoNotInline
|
||||||
public static void destroyEglContext(
|
public static void destroyEglContext(
|
||||||
@Nullable EGLDisplay eglDisplay, @Nullable EGLContext eglContext) throws GlException {
|
@Nullable EGLDisplay eglDisplay, @Nullable EGLContext eglContext) throws GlException {
|
||||||
|
|
@ -875,21 +878,41 @@ public final class GlUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
@DoNotInline
|
@DoNotInline
|
||||||
private static EGLConfig getEglConfig(EGLDisplay eglDisplay, int[] attributes)
|
public static void checkEglException(String errorMessage) throws GlException {
|
||||||
throws GlException {
|
int error = EGL14.eglGetError();
|
||||||
EGLConfig[] eglConfigs = new EGLConfig[1];
|
if (error != EGL14.EGL_SUCCESS) {
|
||||||
if (!EGL14.eglChooseConfig(
|
throw new GlException(errorMessage + ", error code: 0x" + Integer.toHexString(error));
|
||||||
eglDisplay,
|
|
||||||
attributes,
|
|
||||||
/* attrib_listOffset= */ 0,
|
|
||||||
eglConfigs,
|
|
||||||
/* configsOffset= */ 0,
|
|
||||||
/* config_size= */ 1,
|
|
||||||
/* unusedNumConfig */ new int[1],
|
|
||||||
/* num_configOffset= */ 0)) {
|
|
||||||
throw new GlException("eglChooseConfig failed.");
|
|
||||||
}
|
}
|
||||||
return eglConfigs[0];
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(18)
|
||||||
|
private static final class Api18 {
|
||||||
|
private Api18() {}
|
||||||
|
|
||||||
|
@DoNotInline
|
||||||
|
public static long createSyncFence() throws GlException {
|
||||||
|
long syncObject = GLES30.glFenceSync(GLES30.GL_SYNC_GPU_COMMANDS_COMPLETE, /* flags= */ 0);
|
||||||
|
checkGlError();
|
||||||
|
// Due to specifics of OpenGL, it might happen that the fence creation command is not yet
|
||||||
|
// sent into the GPU command queue, which can cause other threads to wait infinitely if
|
||||||
|
// the glSyncWait/glClientSyncWait command went into the GPU earlier. Hence, we have to
|
||||||
|
// call glFlush to ensure that glFenceSync is inside of the GPU command queue.
|
||||||
|
GLES20.glFlush();
|
||||||
|
checkGlError();
|
||||||
|
return syncObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
@DoNotInline
|
||||||
|
public static void deleteSyncObject(long syncObject) throws GlException {
|
||||||
|
GLES30.glDeleteSync(syncObject);
|
||||||
|
checkGlError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@DoNotInline
|
||||||
|
public static void waitSync(long syncObject) throws GlException {
|
||||||
|
GLES30.glWaitSync(syncObject, /* flags= */ 0, GLES30.GL_TIMEOUT_IGNORED);
|
||||||
|
checkGlError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue