1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.opengl.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertTrue;
22 
23 import android.app.ActivityManager;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.pm.ConfigurationInfo;
27 import android.content.pm.FeatureInfo;
28 import android.content.pm.PackageManager;
29 import android.content.res.Configuration;
30 import android.util.Log;
31 
32 import androidx.test.filters.LargeTest;
33 import androidx.test.rule.ActivityTestRule;
34 import androidx.test.runner.AndroidJUnit4;
35 
36 import com.android.compatibility.common.util.AdoptShellPermissionsRule;
37 import com.android.compatibility.common.util.CddTest;
38 
39 import org.junit.Before;
40 import org.junit.Rule;
41 import org.junit.Test;
42 import org.junit.runner.RunWith;
43 
44 import java.util.regex.Matcher;
45 import java.util.regex.Pattern;
46 
47 import javax.microedition.khronos.egl.EGL10;
48 import javax.microedition.khronos.egl.EGLConfig;
49 import javax.microedition.khronos.egl.EGLContext;
50 import javax.microedition.khronos.egl.EGLDisplay;
51 
52 /**
53  * Test for checking whether the ro.opengles.version property is set to the correct value.
54  */
55 @LargeTest
56 @RunWith(AndroidJUnit4.class)
57 public class OpenGlEsVersionTest {
58 
59     private static final String TAG = OpenGlEsVersionTest.class.getSimpleName();
60 
61     // TODO: switch to android.opengl.EGL14/EGLExt and use the constants from there
62     private static final int EGL_OPENGL_ES_BIT = 0x0001;
63     private static final int EGL_OPENGL_ES2_BIT = 0x0004;
64     private static final int EGL_OPENGL_ES3_BIT_KHR = 0x0040;
65 
66     private OpenGlEsVersionCtsActivity mActivity;
67 
68     @Rule(order = 0)
69     public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule(
70             androidx.test.platform.app.InstrumentationRegistry
71                     .getInstrumentation().getUiAutomation(),
72             android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX);
73 
74     @Rule(order = 1)
75     public ActivityTestRule<OpenGlEsVersionCtsActivity> mActivityRule =
76             new ActivityTestRule<>(OpenGlEsVersionCtsActivity.class);
77 
78     @Rule(order = 2)
79     public ActivityTestRule<OpenGlEsVersionCtsActivity> mActivityRelaunchRule =
80             new ActivityTestRule<>(OpenGlEsVersionCtsActivity.class, false, false);
81 
82     @Before
setup()83     public void setup() {
84         mActivity = mActivityRule.getActivity();
85     }
86 
87     @CddTest(requirement="7.1.4.1/C-0-1")
88     @Test
testOpenGlEsVersion()89     public void testOpenGlEsVersion() throws InterruptedException {
90         int detectedMajorVersion = getDetectedMajorVersion();
91         int reportedVersion = getVersionFromActivityManager(mActivity);
92 
93         assertEquals("Reported OpenGL ES version from ActivityManager differs from PackageManager",
94                 reportedVersion, getVersionFromPackageManager(mActivity));
95 
96         verifyGlVersionString(1, 1);
97         if (detectedMajorVersion == 2) {
98             restartActivityWithClientVersion(2);
99             verifyGlVersionString(2, getMinorVersion(reportedVersion));
100         } else if (detectedMajorVersion == 3) {
101             restartActivityWithClientVersion(3);
102             verifyGlVersionString(3, getMinorVersion(reportedVersion));
103         }
104     }
105 
106     @CddTest(requirement="7.1.4.1/C-2-2")
107     @Test
testRequiredExtensions()108     public void testRequiredExtensions() throws InterruptedException {
109         int reportedVersion = getVersionFromActivityManager(mActivity);
110 
111         if (getMajorVersion(reportedVersion) < 3)
112             return;
113 
114         restartActivityWithClientVersion(3);
115 
116         String extensions = mActivity.getExtensionsString();
117 
118         if (getMajorVersion(reportedVersion) != 3 || getMinorVersion(reportedVersion) < 1)
119             return;
120 
121         final String es31RequiredList[] = {
122             "EXT_texture_sRGB_decode",
123             "KHR_blend_equation_advanced",
124             "KHR_debug",
125             "OES_shader_image_atomic",
126             "OES_texture_stencil8",
127             "OES_texture_storage_multisample_2d_array"
128         };
129 
130         for (int i = 0; i < es31RequiredList.length; ++i) {
131             assertTrue("OpenGL ES version 3.1+ is missing extension " + es31RequiredList[i],
132                     hasExtension(extensions, es31RequiredList[i]));
133         }
134     }
135 
136     @CddTest(requirement="7.1.4.1/C-2-1,C-5-1,C-4-1")
137     @Test
testExtensionPack()138     public void testExtensionPack() throws InterruptedException {
139         // Requirements:
140         // 1. If the device claims support for the system feature, the extension must be available.
141         // 2. If the extension is available, the device must claim support for it.
142         // 3. If the extension is available, it must be correct:
143         //    - ES 3.1+ must be supported
144         //    - All included extensions must be available
145 
146         int reportedVersion = getVersionFromActivityManager(mActivity);
147         boolean hasAepFeature = mActivity.getPackageManager().hasSystemFeature(
148                 PackageManager.FEATURE_OPENGLES_EXTENSION_PACK);
149 
150         if (getMajorVersion(reportedVersion) != 3 || getMinorVersion(reportedVersion) < 1) {
151             assertFalse("FEATURE_OPENGLES_EXTENSION_PACK is available without OpenGL ES 3.1+",
152                     hasAepFeature);
153             return;
154         }
155 
156         restartActivityWithClientVersion(3);
157 
158         String extensions = mActivity.getExtensionsString();
159         boolean hasAepExtension = hasExtension(extensions, "GL_ANDROID_extension_pack_es31a");
160         assertEquals("System feature FEATURE_OPENGLES_EXTENSION_PACK is "
161             + (hasAepFeature ? "" : "not ") + "available, but extension GL_ANDROID_extension_pack_es31a is "
162             + (hasAepExtension ? "" : "not ") + "in the OpenGL ES extension list.",
163             hasAepFeature, hasAepExtension);
164     }
165     @CddTest(requirement="7.9.2/C-1-4")
166     @Test
testOpenGlEsVersionForVrHighPerformance()167     public void testOpenGlEsVersionForVrHighPerformance() throws InterruptedException {
168         if (!supportsVrHighPerformance())
169             return;
170         restartActivityWithClientVersion(3);
171 
172         int reportedVersion = getVersionFromActivityManager(mActivity);
173         int major = getMajorVersion(reportedVersion);
174         int minor = getMinorVersion(reportedVersion);
175 
176         assertTrue("OpenGL ES version 3.2 or higher is required for VR high-performance devices " +
177             " but this device supports only version " + major + "." + minor,
178             (major == 3 && minor >= 2) || major > 3);
179     }
180 
181     @CddTest(requirement="7.9.2/C-1-6,C-1-8")
182     @Test
testRequiredExtensionsForVrHighPerformance()183     public void testRequiredExtensionsForVrHighPerformance() throws InterruptedException {
184         if (!supportsVrHighPerformance())
185             return;
186         restartActivityWithClientVersion(3);
187         final boolean isVrHeadset = (mActivity.getResources().getConfiguration().uiMode
188             & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_VR_HEADSET;
189 
190         String extensions = mActivity.getExtensionsString();
191         final String requiredList[] = {
192             "GL_EXT_multisampled_render_to_texture",
193             "GL_EXT_protected_textures",
194             "GL_OVR_multiview",
195             "GL_OVR_multiview2",
196             "GL_OVR_multiview_multisampled_render_to_texture",
197         };
198         final String vrHeadsetRequiredList[] = {
199             "GL_EXT_EGL_image_array",
200             "GL_EXT_external_buffer",
201             "GL_EXT_multisampled_render_to_texture2",
202         };
203 
204         for (String requiredExtension : requiredList) {
205             assertTrue("Required extension for VR high-performance is missing: " + requiredExtension,
206                     hasExtension(extensions, requiredExtension));
207         }
208         if (isVrHeadset) {
209             for (String requiredExtension : vrHeadsetRequiredList) {
210                 assertTrue("Required extension for VR high-performance is missing: " + requiredExtension,
211                         hasExtension(extensions, requiredExtension));
212             }
213         }
214 
215         EGL10 egl = (EGL10) EGLContext.getEGL();
216         EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
217         extensions = egl.eglQueryString(display, EGL10.EGL_EXTENSIONS);
218         final String requiredEglList[] = {
219             "EGL_ANDROID_front_buffer_auto_refresh",
220             "EGL_ANDROID_get_native_client_buffer",
221             "EGL_EXT_protected_content",
222             "EGL_IMG_context_priority",
223             "EGL_KHR_fence_sync",
224             "EGL_KHR_mutable_render_buffer",
225             "EGL_KHR_wait_sync",
226         };
227         final String vrHeadsetRequiredEglList[] = {
228             "EGL_EXT_image_gl_colorspace",
229         };
230 
231         for (String requiredExtension : requiredEglList) {
232             assertTrue("Required EGL extension for VR high-performance is missing: " + requiredExtension,
233                     hasExtension(extensions, requiredExtension));
234         }
235         if (isVrHeadset) {
236             for (String requiredExtension : vrHeadsetRequiredEglList) {
237                 assertTrue("Required EGL extension for VR high-performance is missing: " + requiredExtension,
238                         hasExtension(extensions, requiredExtension));
239             }
240         }
241     }
242     @CddTest(requirement="7.1.4.1/C-6-1")
243     @Test
testRequiredEglExtensions()244     public void testRequiredEglExtensions() {
245         // See CDD section 7.1.4
246         final String requiredEglList[] = {
247             "EGL_KHR_image",
248             "EGL_KHR_image_base",
249             "EGL_ANDROID_image_native_buffer",
250             "EGL_ANDROID_get_native_client_buffer",
251             "EGL_KHR_wait_sync",
252             "EGL_KHR_get_all_proc_addresses",
253             "EGL_ANDROID_presentation_time",
254             "EGL_KHR_swap_buffers_with_damage"
255         };
256 
257         EGL10 egl = (EGL10) EGLContext.getEGL();
258         EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
259 
260         if (egl.eglInitialize(display, null)) {
261             try {
262                 String eglExtensions = egl.eglQueryString(display, EGL10.EGL_EXTENSIONS);
263                 for (int i = 0; i < requiredEglList.length; ++i) {
264                     assertTrue("EGL Extension required by CDD section 7.1.4 missing: " +
265                                requiredEglList[i], hasExtension(eglExtensions, requiredEglList[i]));
266                 }
267                 if (hasExtension(eglExtensions, "EGL_KHR_mutable_render_buffer")) {
268                     assertTrue("Devices exposes EGL_KHR_mutable_render_buffer but not EGL_ANDROID_front_buffer_auto_refresh", hasExtension(eglExtensions, "EGL_ANDROID_front_buffer_auto_refresh"));
269                 }
270             } finally {
271                 egl.eglTerminate(display);
272             }
273         } else {
274             Log.e(TAG, "Couldn't initialize EGL.");
275         }
276     }
277 
278     @CddTest(requirement="7.1.4.5/H-1-1")
279     @Test
testRequiredEglExtensionsForHdrCapableDisplay()280     public void testRequiredEglExtensionsForHdrCapableDisplay() {
281         // See CDD section 7.1.4
282         // This test covers the EGL portion of the CDD requirement. The VK portion of the
283         // requirement is covered elsewhere.
284         final String requiredEglList[] = {
285             "EGL_EXT_gl_colorspace_bt2020_pq",
286             "EGL_EXT_surface_SMPTE2086_metadata",
287             "EGL_EXT_surface_CTA861_3_metadata",
288         };
289 
290         // This requirement only applies if device is handheld and claims to be HDR capable.
291         boolean isHdrCapable = mActivity.getResources().getConfiguration().isScreenHdr();
292         if (!isHdrCapable || !isHandheld())
293             return;
294 
295         EGL10 egl = (EGL10) EGLContext.getEGL();
296         EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
297 
298         if (egl.eglInitialize(display, null)) {
299             try {
300                 String eglExtensions = egl.eglQueryString(display, EGL10.EGL_EXTENSIONS);
301                 for (int i = 0; i < requiredEglList.length; ++i) {
302                     assertTrue("EGL extension required by CDD section 7.1.4.5 missing: " +
303                         requiredEglList[i], hasExtension(eglExtensions, requiredEglList[i]));
304                 }
305             } finally {
306                 egl.eglTerminate(display);
307             }
308         } else {
309             Log.e(TAG, "Couldn't initialize EGL.");
310         }
311     }
312 
313     @CddTest(requirement="7.1.4.5/C-1-4")
314     @Test
testRequiredGLESVersion()315     public void testRequiredGLESVersion() {
316         // This requirement only applies if device claims to be wide color capable.
317         boolean isWideColorCapable =
318             mActivity.getResources().getConfiguration().isScreenWideColorGamut();
319         if (!isWideColorCapable)
320             return;
321 
322         int reportedVersion = getVersionFromActivityManager(mActivity);
323         assertEquals("Reported OpenGL ES major version doesn't meet the requirement of" +
324             " CDD 7.1.4.5/C-1-4", 3, getMajorVersion(reportedVersion));
325         assertTrue("Reported OpenGL ES minor version doesn't meet the requirement of" +
326             " CDD 7.1.4.5/C-1-4", 1 == getMinorVersion(reportedVersion) ||
327                                   2 == getMinorVersion(reportedVersion));
328     }
329 
330     @CddTest(requirement="7.1.4.5/C-1-5")
331     @Test
testRequiredEglExtensionsForWideColorDisplay()332     public void testRequiredEglExtensionsForWideColorDisplay() {
333         // See CDD section 7.1.4.5
334         // This test covers the EGL portion of the CDD requirement. The VK portion of the
335         // requirement is covered elsewhere.
336         final String requiredEglList[] = {
337             "EGL_KHR_no_config_context",
338             "EGL_EXT_pixel_format_float",
339             "EGL_KHR_gl_colorspace",
340             "EGL_EXT_gl_colorspace_scrgb",
341             "EGL_EXT_gl_colorspace_scrgb_linear",
342             "EGL_EXT_gl_colorspace_display_p3",
343             "EGL_EXT_gl_colorspace_display_p3_linear",
344             "EGL_EXT_gl_colorspace_display_p3_passthrough",
345         };
346 
347         // This requirement only applies if device claims to be wide color capable.
348         boolean isWideColorCapable = mActivity.getResources().getConfiguration().isScreenWideColorGamut();
349         if (!isWideColorCapable)
350             return;
351 
352         EGL10 egl = (EGL10) EGLContext.getEGL();
353         EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
354 
355         if (egl.eglInitialize(display, null)) {
356             try {
357                 String eglExtensions = egl.eglQueryString(display, EGL10.EGL_EXTENSIONS);
358                 for (int i = 0; i < requiredEglList.length; ++i) {
359                     assertTrue("EGL extension required by CDD section 7.1.4.5 missing: " +
360                         requiredEglList[i], hasExtension(eglExtensions, requiredEglList[i]));
361                 }
362             } finally {
363                 egl.eglTerminate(display);
364             }
365         } else {
366             Log.e(TAG, "Couldn't initialize EGL.");
367         }
368     }
369 
isHandheld()370     private boolean isHandheld() {
371         // handheld nature is not exposed to package manager, for now
372         // we check for touchscreen and NOT watch and NOT tv
373         PackageManager pm = mActivity.getPackageManager();
374         return pm.hasSystemFeature(pm.FEATURE_TOUCHSCREEN)
375                 && !pm.hasSystemFeature(pm.FEATURE_WATCH)
376                 && !pm.hasSystemFeature(pm.FEATURE_TELEVISION);
377     }
378 
hasExtension(String extensions, String name)379     private static boolean hasExtension(String extensions, String name) {
380         return OpenGlEsVersionCtsActivity.hasExtension(extensions, name);
381     }
382 
383     /** @return OpenGL ES major version 1, 2, or 3 or some non-positive number for error */
getDetectedMajorVersion()384     private static int getDetectedMajorVersion() {
385         /*
386          * Get all the device configurations and check the EGL_RENDERABLE_TYPE attribute
387          * to determine the highest ES version supported by any config. The
388          * EGL_KHR_create_context extension is required to check for ES3 support; if the
389          * extension is not present this test will fail to detect ES3 support. This
390          * effectively makes the extension mandatory for ES3-capable devices.
391          */
392 
393         EGL10 egl = (EGL10) EGLContext.getEGL();
394         EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
395         int[] numConfigs = new int[1];
396 
397         if (egl.eglInitialize(display, null)) {
398             try {
399                 boolean checkES3 = hasExtension(egl.eglQueryString(display, EGL10.EGL_EXTENSIONS),
400                         "EGL_KHR_create_context");
401                 if (egl.eglGetConfigs(display, null, 0, numConfigs)) {
402                     EGLConfig[] configs = new EGLConfig[numConfigs[0]];
403                     if (egl.eglGetConfigs(display, configs, numConfigs[0], numConfigs)) {
404                         int highestEsVersion = 0;
405                         int[] value = new int[1];
406                         for (int i = 0; i < numConfigs[0]; i++) {
407                             if (egl.eglGetConfigAttrib(display, configs[i],
408                                     EGL10.EGL_RENDERABLE_TYPE, value)) {
409                                 if (checkES3 && ((value[0] & EGL_OPENGL_ES3_BIT_KHR) ==
410                                         EGL_OPENGL_ES3_BIT_KHR)) {
411                                     if (highestEsVersion < 3) highestEsVersion = 3;
412                                 } else if ((value[0] & EGL_OPENGL_ES2_BIT) == EGL_OPENGL_ES2_BIT) {
413                                     if (highestEsVersion < 2) highestEsVersion = 2;
414                                 } else if ((value[0] & EGL_OPENGL_ES_BIT) == EGL_OPENGL_ES_BIT) {
415                                     if (highestEsVersion < 1) highestEsVersion = 1;
416                                 }
417                             } else {
418                                 Log.w(TAG, "Getting config attribute with "
419                                         + "EGL10#eglGetConfigAttrib failed "
420                                         + "(" + i + "/" + numConfigs[0] + "): "
421                                         + egl.eglGetError());
422                             }
423                         }
424                         return highestEsVersion;
425                     } else {
426                         Log.e(TAG, "Getting configs with EGL10#eglGetConfigs failed: "
427                                 + egl.eglGetError());
428                         return -1;
429                     }
430                 } else {
431                     Log.e(TAG, "Getting number of configs with EGL10#eglGetConfigs failed: "
432                             + egl.eglGetError());
433                     return -2;
434                 }
435               } finally {
436                   egl.eglTerminate(display);
437               }
438         } else {
439             Log.e(TAG, "Couldn't initialize EGL.");
440             return -3;
441         }
442     }
443 
getVersionFromActivityManager(Context context)444     private static int getVersionFromActivityManager(Context context) {
445         ActivityManager activityManager =
446             (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
447         ConfigurationInfo configInfo = activityManager.getDeviceConfigurationInfo();
448         if (configInfo.reqGlEsVersion != ConfigurationInfo.GL_ES_VERSION_UNDEFINED) {
449             return configInfo.reqGlEsVersion;
450         } else {
451             return 1 << 16; // Lack of property means OpenGL ES version 1
452         }
453     }
454 
getVersionFromPackageManager(Context context)455     private static int getVersionFromPackageManager(Context context) {
456         PackageManager packageManager = context.getPackageManager();
457         FeatureInfo[] featureInfos = packageManager.getSystemAvailableFeatures();
458         if (featureInfos != null && featureInfos.length > 0) {
459             for (FeatureInfo featureInfo : featureInfos) {
460                 // Null feature name means this feature is the open gl es version feature.
461                 if (featureInfo.name == null) {
462                     if (featureInfo.reqGlEsVersion != FeatureInfo.GL_ES_VERSION_UNDEFINED) {
463                         return featureInfo.reqGlEsVersion;
464                     } else {
465                         return 1 << 16; // Lack of property means OpenGL ES version 1
466                     }
467                 }
468             }
469         }
470         return 1;
471     }
472 
473     /** @see FeatureInfo#getGlEsVersion() */
getMajorVersion(int glEsVersion)474     private static int getMajorVersion(int glEsVersion) {
475         return ((glEsVersion & 0xffff0000) >> 16);
476     }
477 
478     /** @see FeatureInfo#getGlEsVersion() */
getMinorVersion(int glEsVersion)479     private static int getMinorVersion(int glEsVersion) {
480         return glEsVersion & 0xffff;
481     }
482 
483     /**
484      * Check that the version string has the form "OpenGL ES(-CM)? (\d+)\.(\d+)", where the two
485      * numbers match the major and minor parameters.
486      */
487     @CddTest(requirement="7.1.4.1/C-0-1")
verifyGlVersionString(int major, int minor)488     private void verifyGlVersionString(int major, int minor) throws InterruptedException {
489         Matcher matcher = Pattern.compile("OpenGL ES(?:-CM)? (\\d+)\\.(\\d+).*")
490                                  .matcher(mActivity.getVersionString());
491         assertTrue("OpenGL ES version string is not of the required form "
492             + "'OpenGL ES(-CM)? (\\d+)\\.(\\d+).*'",
493             matcher.matches());
494         int stringMajor = Integer.parseInt(matcher.group(1));
495         int stringMinor = Integer.parseInt(matcher.group(2));
496         assertEquals("GL_VERSION string doesn't match ActivityManager major version (check ro.opengles.version property)",
497             major, stringMajor);
498         assertEquals("GL_VERSION string doesn't match ActivityManager minor version (check ro.opengles.version property)",
499             minor, stringMinor);
500     }
501 
502     /** Restart {@link GLSurfaceViewCtsActivity} with a specific client version. */
restartActivityWithClientVersion(int version)503     private void restartActivityWithClientVersion(int version) {
504         mActivity.finish();
505 
506         Intent intent = OpenGlEsVersionCtsActivity.createIntent(version);
507         mActivity = mActivityRelaunchRule.launchActivity(intent);
508     }
509 
510     /**
511      * Return whether the system supports FEATURE_VR_MODE_HIGH_PERFORMANCE.
512      * This is used to skip some tests.
513      */
supportsVrHighPerformance()514     private boolean supportsVrHighPerformance() {
515         PackageManager pm = mActivity.getPackageManager();
516         return pm.hasSystemFeature(PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE);
517     }
518 }
519