blob: e60b8cf6717451d16641abed6de8525b660fcbf6 [file] [log] [blame]
/*
* Copyright 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.graphics.cts;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.lessThan;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.hardware.SyncFence;
import android.opengl.EGL14;
import android.opengl.EGL15;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLExt;
import android.opengl.EGLImage;
import android.opengl.EGLSurface;
import android.opengl.EGLSync;
import android.opengl.GLES20;
import android.os.SystemProperties;
import androidx.test.filters.SmallTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
@SmallTest
@RunWith(BlockJUnit4ClassRunner.class)
public class EGL15Test {
static {
System.loadLibrary("ctsgraphics_jni");
}
private static final String TAG = EGL15Test.class.getSimpleName();
private static final boolean DEBUG = false;
private EGLDisplay mEglDisplay = EGL15.EGL_NO_DISPLAY;
private EGLConfig mEglConfig = null;
private EGLSurface mEglPbuffer = EGL15.EGL_NO_SURFACE;
private EGLContext mEglContext = EGL15.EGL_NO_CONTEXT;
private int mEglVersion = 0;
@Before
public void setup() throws Throwable {
int error;
mEglDisplay = EGL15.eglGetPlatformDisplay(EGL15.EGL_PLATFORM_ANDROID_KHR,
EGL14.EGL_DEFAULT_DISPLAY,
new long[] {
EGL14.EGL_NONE },
0);
if (mEglDisplay == EGL15.EGL_NO_DISPLAY) {
throw new RuntimeException("no EGL display");
}
error = EGL14.eglGetError();
if (error != EGL14.EGL_SUCCESS) {
throw new RuntimeException("eglGetPlatformDisplay failed");
}
int[] major = new int[1];
int[] minor = new int[1];
if (!EGL14.eglInitialize(mEglDisplay, major, 0, minor, 0)) {
throw new RuntimeException("error in eglInitialize");
}
error = EGL14.eglGetError();
if (error != EGL14.EGL_SUCCESS) {
throw new RuntimeException("eglInitialize failed");
}
mEglVersion = major[0] * 10 + minor[0];
// If we could rely on having EGL_KHR_surfaceless_context and EGL_KHR_context_no_config, we
// wouldn't have to create a config or pbuffer at all.
int[] numConfigs = new int[1];
EGLConfig[] configs = new EGLConfig[1];
if (!EGL14.eglChooseConfig(mEglDisplay,
new int[] {
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT,
EGL14.EGL_NONE},
0, configs, 0, 1, numConfigs, 0)) {
throw new RuntimeException("eglChooseConfig failed");
}
error = EGL14.eglGetError();
if (error != EGL14.EGL_SUCCESS) {
throw new RuntimeException("eglChooseConfig failed");
}
mEglConfig = configs[0];
mEglPbuffer = EGL14.eglCreatePbufferSurface(mEglDisplay, mEglConfig,
new int[] {EGL14.EGL_WIDTH, 1, EGL14.EGL_HEIGHT, 1, EGL14.EGL_NONE}, 0);
if (mEglPbuffer == EGL15.EGL_NO_SURFACE) {
throw new RuntimeException("eglCreatePbufferSurface failed");
}
error = EGL14.eglGetError();
if (error != EGL14.EGL_SUCCESS) {
throw new RuntimeException("eglCreatePbufferSurface failed");
}
mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig, EGL14.EGL_NO_CONTEXT,
new int[] {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE}, 0);
if (mEglContext == EGL15.EGL_NO_CONTEXT) {
throw new RuntimeException("eglCreateContext failed");
}
error = EGL14.eglGetError();
if (error != EGL14.EGL_SUCCESS) {
throw new RuntimeException("eglCreateContext failed");
}
if (!EGL14.eglMakeCurrent(mEglDisplay, mEglPbuffer, mEglPbuffer, mEglContext)) {
throw new RuntimeException("eglMakeCurrent failed");
}
error = EGL14.eglGetError();
if (error != EGL14.EGL_SUCCESS) {
throw new RuntimeException("eglMakeCurrent failed");
}
}
@After
public void teardown() throws Throwable {
EGL14.eglTerminate(mEglDisplay);
}
@Test
public void testEGL15SyncFence() {
if (mEglVersion < 15) {
return;
}
EGLSync sync = EGL15.eglCreateSync(mEglDisplay, EGL15.EGL_SYNC_FENCE,
new long[] {
EGL14.EGL_NONE },
0);
if (sync == EGL15.EGL_NO_SYNC) {
throw new RuntimeException("eglCreateSync failed");
}
int error = EGL14.eglGetError();
if (error != EGL14.EGL_SUCCESS) {
throw new RuntimeException("eglCreateSync failed");
}
GLES20.glFlush();
error = GLES20.glGetError();
if (error != GLES20.GL_NO_ERROR) {
throw new RuntimeException("glFlush failed");
}
int status = EGL15.eglClientWaitSync(mEglDisplay, sync, 0, EGL15.EGL_FOREVER);
if (status != EGL15.EGL_CONDITION_SATISFIED) {
throw new RuntimeException("eglClientWaitSync failed");
}
error = EGL14.eglGetError();
if (error != EGL14.EGL_SUCCESS) {
throw new RuntimeException("eglClientWaitSync failed");
}
if (!EGL15.eglDestroySync(mEglDisplay, sync)) {
throw new RuntimeException("eglDestroySync failed");
}
error = EGL14.eglGetError();
if (error != EGL14.EGL_SUCCESS) {
throw new RuntimeException("eglDestroySync failed");
}
}
@Test
public void testEGL15AndroidNativeFence() {
if (mEglVersion < 15) {
return;
}
String eglExtensions = EGL14.eglQueryString(mEglDisplay, EGL14.EGL_EXTENSIONS);
if (!eglExtensions.contains("EGL_ANDROID_native_fence_sync")) {
return;
}
long before = System.nanoTime();
EGLSync sync = EGL15.eglCreateSync(mEglDisplay, EGLExt.EGL_SYNC_NATIVE_FENCE_ANDROID,
new long[] {
EGL14.EGL_NONE },
0);
assertNotEquals(sync, EGL15.EGL_NO_SYNC);
assertEquals(EGL14.EGL_SUCCESS, EGL14.eglGetError());
SyncFence nativeFence = EGLExt.eglDupNativeFenceFDANDROID(mEglDisplay, sync);
assertNotNull(nativeFence);
assertEquals(EGL14.EGL_SUCCESS, EGL14.eglGetError());
// If the fence isn't valid, trigger a flush & try again
if (!nativeFence.isValid()) {
GLES20.glFlush();
assertEquals(GLES20.GL_NO_ERROR, GLES20.glGetError());
// Have flushed, native fence should be populated
nativeFence = EGLExt.eglDupNativeFenceFDANDROID(mEglDisplay, sync);
assertNotNull(nativeFence);
assertTrue(nativeFence.isValid());
assertEquals(EGL14.EGL_SUCCESS, EGL14.eglGetError());
}
long signaledTime = nativeFence.getSignalTime();
// We don't know if the fence has or hasn't signaled yet. But either way it must be
// greater than before the fence was created (either signaled, or PENDING which is LONG_MAX)
assertThat("getSignalTime before waiting", signaledTime, greaterThan(before));
// Similarly, if the fence has signaled it must be less than now
assertThat("getSignalTime before waiting", signaledTime,
anyOf(equalTo(SyncFence.SIGNAL_TIME_PENDING), lessThan(System.nanoTime())));
assertTrue(EGL15.eglDestroySync(mEglDisplay, sync));
assertEquals(EGL14.EGL_SUCCESS, EGL14.eglGetError());
assertTrue(nativeFence.awaitForever());
signaledTime = nativeFence.getSignalTime();
assertNotEquals(SyncFence.SIGNAL_TIME_INVALID, signaledTime);
assertThat("getSignalTime after waiting", signaledTime, greaterThan(before));
nativeFence.close();
assertFalse(nativeFence.isValid());
assertEquals(SyncFence.SIGNAL_TIME_INVALID, nativeFence.getSignalTime());
}
@Test
public void testEGL15GetSyncType() {
if (mEglVersion < 15) {
return;
}
EGLSync sync = EGL15.eglCreateSync(mEglDisplay, EGL15.EGL_SYNC_FENCE,
new long[] {
EGL14.EGL_NONE },
0);
if (sync == EGL15.EGL_NO_SYNC) {
throw new RuntimeException("eglCreateSync failed");
}
int error = EGL14.eglGetError();
if (error != EGL14.EGL_SUCCESS) {
throw new RuntimeException("eglCreateSync failed");
}
long[] type = new long[1];
boolean success = EGL15.eglGetSyncAttrib(mEglDisplay, sync, EGL15.EGL_SYNC_TYPE, type, 0);
if (!success) {
throw new RuntimeException("eglGetSyncAttrib failed (returned false)");
}
error = EGL14.eglGetError();
if (error != EGL14.EGL_SUCCESS) {
throw new RuntimeException("eglGetSyncAttrib failed");
}
assertEquals(type[0], EGL15.EGL_SYNC_FENCE);
if (!EGL15.eglDestroySync(mEglDisplay, sync)) {
throw new RuntimeException("eglDestroySync failed");
}
error = EGL14.eglGetError();
if (error != EGL14.EGL_SUCCESS) {
throw new RuntimeException("eglDestroySync failed");
}
}
@Test
public void testEGL15WaitSync() {
if (mEglVersion < 15) {
return;
}
EGLSync sync = EGL15.eglCreateSync(mEglDisplay, EGL15.EGL_SYNC_FENCE,
new long[] {
EGL14.EGL_NONE },
0);
if (sync == EGL15.EGL_NO_SYNC) {
throw new RuntimeException("eglCreateSync failed");
}
int error = EGL14.eglGetError();
if (error != EGL14.EGL_SUCCESS) {
throw new RuntimeException("eglCreateSync failed");
}
boolean success = EGL15.eglWaitSync(mEglDisplay, sync, 0);
if (!success) {
throw new RuntimeException("eglWaitSync failed (returned false)");
}
error = EGL14.eglGetError();
if (error != EGL14.EGL_SUCCESS) {
throw new RuntimeException("eglWaitSync failed");
}
if (!EGL15.eglDestroySync(mEglDisplay, sync)) {
throw new RuntimeException("eglDestroySync failed");
}
error = EGL14.eglGetError();
if (error != EGL14.EGL_SUCCESS) {
throw new RuntimeException("eglDestroySync failed");
}
}
@Test
public void testEGL15CreateImage() {
if (mEglVersion < 15) {
return;
}
int error;
int srcTex = 1;
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, srcTex);
error = GLES20.glGetError();
if (error != GLES20.GL_NO_ERROR) {
throw new RuntimeException("glBindTexture failed");
}
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, 64, 64, 0,
GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
error = GLES20.glGetError();
if (error != GLES20.GL_NO_ERROR) {
throw new RuntimeException("glTexImage2D failed");
}
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_LINEAR);
error = GLES20.glGetError();
if (error != GLES20.GL_NO_ERROR) {
throw new RuntimeException("glTexParameteri failed");
}
long[] attribs = new long[] {
EGL15.EGL_GL_TEXTURE_LEVEL, 0,
EGL14.EGL_NONE };
EGLImage image = EGL15.eglCreateImage(mEglDisplay, mEglContext,
EGL15.EGL_GL_TEXTURE_2D, srcTex, attribs, 0);
if (image == EGL15.EGL_NO_IMAGE) {
throw new RuntimeException("eglCreateImage failed, got EGL_NO_IMAGE");
}
error = EGL14.eglGetError();
if (error != EGL14.EGL_SUCCESS) {
throw new RuntimeException("eglCreateImage failed");
}
boolean success = EGL15.eglDestroyImage(mEglDisplay, image);
if (!success) {
throw new RuntimeException("eglDestroyImage failed (returned false)");
}
error = EGL14.eglGetError();
if (error != EGL14.EGL_SUCCESS) {
throw new RuntimeException("eglDestroyImage failed");
}
}
@Test
public void testEGL15CreatePlatformPixmap() {
if (mEglVersion < 15) {
return;
}
long[] attribs = new long[] { EGL14.EGL_NONE };
boolean unsupported = false;
try {
EGLSurface surface = EGL15.eglCreatePlatformPixmapSurface(mEglDisplay,
mEglConfig, null, attribs, 0);
} catch (UnsupportedOperationException e) {
unsupported = true;
}
if (!unsupported) {
throw new RuntimeException("eglCreatePlatformPixmapSurface is not supported on Android,"
+ " why did call not report that!");
}
}
@Test
public void testEGL15CreateDebugContext() {
int error;
if (mEglVersion < 15) {
return;
}
// Required functionality for devices released with Android R (11),
// skip if launched with older version of Android.
if (SystemProperties.getInt("ro.product.first_api_level", 0) < 30) {
return;
}
mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig, EGL14.EGL_NO_CONTEXT,
new int[] { EGL15.EGL_CONTEXT_OPENGL_DEBUG, EGL14.EGL_FALSE, EGL14.EGL_NONE }, 0);
if (mEglContext == EGL15.EGL_NO_CONTEXT) {
throw new RuntimeException("eglCreateContext failed");
}
error = EGL14.eglGetError();
if (error != EGL14.EGL_SUCCESS) {
throw new RuntimeException("eglCreateContext failed");
}
EGL14.eglDestroyContext(mEglDisplay, mEglContext);
mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig, EGL14.EGL_NO_CONTEXT,
new int[] { EGL15.EGL_CONTEXT_OPENGL_DEBUG, EGL14.EGL_TRUE, EGL14.EGL_NONE }, 0);
if (mEglContext == EGL15.EGL_NO_CONTEXT) {
throw new RuntimeException("eglCreateContext failed");
}
error = EGL14.eglGetError();
if (error != EGL14.EGL_SUCCESS) {
throw new RuntimeException("eglCreateContext failed");
}
EGL14.eglDestroyContext(mEglDisplay, mEglContext);
}
}
// Note: Need to add tests for eglCreatePlatformWindowSurface