blob: 8a8555543c970cf05b97dbcfdbe41455d56e635b [file] [log] [blame]
/*
* Copyright (C) 2010 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.opengl.cts;
import android.app.Activity;
import android.content.Intent;
import android.opengl.GLES31;
import android.opengl.GLES31Ext;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.util.Log;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
/**
* {@link Activity} that queries the device's display attributes to determine what version of
* OpenGL ES is supported and returns what the GL version string reports.
*/
public class OpenGlEsVersionCtsActivity extends Activity {
private static String TAG = "OpenGlEsVersionCtsActivity";
private static final String EGL_CONTEXT_CLIENT_VERSION = "eglContextClientVersion";
/** Timeout to wait for the surface to be created and the version queried. */
private static final int TIMEOUT_SECONDS = 10;
/** Version string reported by glGetString. */
private String mVersionString;
/** Extensions string reported by glGetString. */
private String mExtensionsString;
/** Whether GL_ANDROID_extension_pack_es31a is correctly supported. */
private boolean mAepEs31Support = false;
/** Latch that is unlocked when the activity is done finding the version. */
private CountDownLatch mSurfaceCreatedLatch = new CountDownLatch(1);
public static Intent createIntent(int eglContextClientVersion) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.putExtra(EGL_CONTEXT_CLIENT_VERSION, eglContextClientVersion);
return intent;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GLSurfaceView view = new GLSurfaceView(this);
Intent intent = getIntent();
int eglContextClientVersion = intent.getIntExtra(EGL_CONTEXT_CLIENT_VERSION, -1);
if (eglContextClientVersion > 0) {
view.setEGLContextClientVersion(eglContextClientVersion);
}
view.setRenderer(new Renderer());
setContentView(view);
}
public String getVersionString() throws InterruptedException {
mSurfaceCreatedLatch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
synchronized (this) {
return mVersionString;
}
}
public String getExtensionsString() throws InterruptedException {
mSurfaceCreatedLatch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
synchronized (this) {
return mExtensionsString;
}
}
public boolean getAepEs31Support() throws InterruptedException {
mSurfaceCreatedLatch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
synchronized (this) {
return mAepEs31Support;
}
}
public static boolean hasExtension(String extensions, String name) {
int start = extensions.indexOf(name);
while (start >= 0) {
// check that we didn't find a prefix of a longer extension name
int end = start + name.length();
if (end == extensions.length() || extensions.charAt(end) == ' ') {
return true;
}
start = extensions.indexOf(name, end);
}
return false;
}
private class Renderer implements GLSurfaceView.Renderer {
/**
* These shaders test at least one feature of each of the underlying extension, to verify
* that enabling GL_ANDROID_extension_pack_es31a correctly enables all of them.
*/
private final String mAepEs31VertexShader =
"#version 310 es\n" +
"#extension GL_ANDROID_extension_pack_es31a : require\n" +
"void main() {\n" +
" gl_Position = vec4(1, 0, 0, 1);\n" +
"}\n";
private final String mAepEs31TessellationControlShader =
"#version 310 es\n" +
"#extension GL_ANDROID_extension_pack_es31a : require\n" +
"layout(vertices = 3) out;\n" + // GL_EXT_tessellation_shader
"void main() {\n" +
" gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" + // GL_EXT_shader_io_blocks
" if (gl_InvocationID == 0) {\n" +
" gl_BoundingBoxEXT[0] = gl_in[0].gl_Position;\n" + // GL_EXT_primitive_bounding_box
" gl_BoundingBoxEXT[1] = gl_in[1].gl_Position;\n" +
" }\n" +
"}\n";
private final String mAepEs31TessellationEvaluationShader =
"#version 310 es\n" +
"#extension GL_ANDROID_extension_pack_es31a : require\n" +
"layout(triangles, equal_spacing, cw) in;\n" +
"void main() {\n" +
" gl_Position = gl_in[0].gl_Position * gl_TessCoord.x +\n" +
" gl_in[1].gl_Position * gl_TessCoord.y +\n" +
" gl_in[2].gl_Position * gl_TessCoord.z;\n" +
"}\n";
private final String mAepEs31GeometryShader =
"#version 310 es\n" +
"#extension GL_ANDROID_extension_pack_es31a : require\n" +
"layout(triangles) in;\n" + // GL_EXT_geometry_shader
"layout(triangle_strip, max_vertices = 3) out;\n" +
"sample out vec4 perSampleColor;\n" +
"void main() {\n" +
" for (int i = 0; i < gl_in.length(); ++i) {\n" +
" gl_Position = gl_in[i].gl_Position;\n" +
" perSampleColor = gl_in[i].gl_Position;\n" +
" EmitVertex();\n" +
" }\n" +
"}\n";
private final String mAepEs31FragmentShader =
"#version 310 es\n" +
"#extension GL_ANDROID_extension_pack_es31a : require\n" +
"precision mediump float;\n" +
"layout(blend_support_all_equations) out;\n" + // GL_KHR_blend_equation_advanced
"sample in vec4 perSampleColor;\n" + // GL_OES_shader_multisample_interpolation
"layout(r32ui) coherent uniform mediump uimage2D image;\n" +
"uniform mediump sampler2DMSArray mySamplerMSArray;\n" + // GL_OES_texture_storage_multisample_2d_array
"uniform mediump samplerBuffer mySamplerBuffer;\n" + // GL_EXT_texture_buffer
"uniform mediump samplerCubeArray mySamplerCubeArray;\n" + // GL_EXT_texture_cube_map_array
"out vec4 color;\n" +
"void main() {\n" +
" imageAtomicAdd(image, ivec2(1, 1), 1u);\n" + // GL_OES_shader_image_atomic
" vec4 color = vec4(gl_SamplePosition.x, 0, 0, 1);\n" + // GL_OES_sample_variables
" vec4 color2 = texelFetch(mySamplerMSArray, ivec3(1, 1, 1), 3);\n" +
" vec4 color3 = texelFetch(mySamplerBuffer, 3);\n" +
" vec4 color4 = texture(mySamplerCubeArray, vec4(1, 1, 1, 1));\n" +
" color = fma(color + color2, color3 + color4, perSampleColor);" + // GL_EXT_gpu_shader5
"}\n";
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
synchronized (OpenGlEsVersionCtsActivity.this) {
try {
mVersionString = gl.glGetString(GL10.GL_VERSION);
mExtensionsString = gl.glGetString(GL10.GL_EXTENSIONS);
if (hasExtension(mExtensionsString, "ANDROID_extension_pack_es31a"))
mAepEs31Support = checkAepEs31Support();
} finally {
mSurfaceCreatedLatch.countDown();
}
}
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
}
public void onDrawFrame(GL10 gl) {
}
private boolean compileShaderAndAttach(int program, int shaderType, String source) {
int shader = GLES31.glCreateShader(shaderType);
if (shader == 0) {
Log.e(TAG, "Unable to create shaders of type " + shaderType);
return false;
}
GLES31.glShaderSource(shader, source);
GLES31.glCompileShader(shader);
int[] compiled = new int[1];
GLES31.glGetShaderiv(shader, GLES31.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
Log.e(TAG, "Unable to compile shader " + shaderType + ":");
Log.e(TAG, GLES31.glGetShaderInfoLog(shader));
GLES31.glDeleteShader(shader);
return false;
}
GLES31.glAttachShader(program, shader);
GLES31.glDeleteShader(shader);
return true;
}
private boolean checkAepEs31Support() {
final String requiredList[] = {
"EXT_copy_image",
"EXT_draw_buffers_indexed",
"EXT_geometry_shader",
"EXT_gpu_shader5",
"EXT_primitive_bounding_box",
"EXT_shader_io_blocks",
"EXT_tessellation_shader",
"EXT_texture_border_clamp",
"EXT_texture_buffer",
"EXT_texture_cube_map_array",
"EXT_texture_sRGB_decode",
"KHR_blend_equation_advanced",
"KHR_debug",
"KHR_texture_compression_astc_ldr",
"OES_sample_shading",
"OES_sample_variables",
"OES_shader_image_atomic",
"OES_shader_multisample_interpolation",
"OES_texture_stencil8",
"OES_texture_storage_multisample_2d_array"
};
for (int i = 0; i < requiredList.length; ++i) {
if (!hasExtension(mExtensionsString, requiredList[i])) {
Log.e(TAG,"ANDROID_extension_pack_es31a is present but extension " +
requiredList[i] + " is missing");
return false;
}
}
int[] value = new int[1];
GLES31.glGetIntegerv(GLES31.GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS, value, 0);
if (value[0] < 1) {
Log.e(TAG, "ANDROID_extension_pack_es31a is present, but the " +
"GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS value is " + value[0] + " < 1");
return false;
}
GLES31.glGetIntegerv(GLES31.GL_MAX_FRAGMENT_ATOMIC_COUNTERS, value, 0);
if (value[0] < 8) {
Log.e(TAG, "ANDROID_extension_pack_es31a is present, but the " +
"GL_MAX_FRAGMENT_ATOMIC_COUNTERS value is " + value[0] + " < 8");
return false;
}
GLES31.glGetIntegerv(GLES31.GL_MAX_FRAGMENT_IMAGE_UNIFORMS, value, 0);
if (value[0] < 4) {
Log.e(TAG, "ANDROID_extension_pack_es31a is present, but the " +
"GL_MAX_FRAGMENT_IMAGE_UNIFORMS value is " + value[0] + " < 4");
return false;
}
GLES31.glGetIntegerv(GLES31.GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, value, 0);
if (value[0] < 4) {
Log.e(TAG, "ANDROID_extension_pack_es31a is present, but the " +
"GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS value is " + value[0] + " < 4");
return false;
}
int program = GLES31.glCreateProgram();
try {
if (!compileShaderAndAttach(program, GLES31.GL_VERTEX_SHADER, mAepEs31VertexShader) ||
!compileShaderAndAttach(program, GLES31Ext.GL_TESS_CONTROL_SHADER_EXT, mAepEs31TessellationControlShader) ||
!compileShaderAndAttach(program, GLES31Ext.GL_TESS_EVALUATION_SHADER_EXT, mAepEs31TessellationEvaluationShader) ||
!compileShaderAndAttach(program, GLES31Ext.GL_GEOMETRY_SHADER_EXT, mAepEs31GeometryShader) ||
!compileShaderAndAttach(program, GLES31.GL_FRAGMENT_SHADER, mAepEs31FragmentShader))
return false;
GLES31.glLinkProgram(program);
GLES31.glGetProgramiv(program, GLES31.GL_LINK_STATUS, value, 0);
if (value[0] == 0) {
Log.e(TAG, "Unable to link program :");
Log.e(TAG, GLES31.glGetProgramInfoLog(program));
return false;
}
} finally {
GLES31.glDeleteProgram(program);
}
return true;
}
}
}