• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 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.graphics.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertNotNull;
21 import static org.junit.Assert.assertNull;
22 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assume.assumeTrue;
24 
25 import android.content.pm.FeatureInfo;
26 import android.content.pm.PackageManager;
27 import android.util.Log;
28 
29 import androidx.test.InstrumentationRegistry;
30 import androidx.test.filters.SmallTest;
31 import androidx.test.runner.AndroidJUnit4;
32 
33 import com.android.compatibility.common.util.CddTest;
34 import com.android.compatibility.common.util.PropertyUtil;
35 
36 import org.json.JSONArray;
37 import org.json.JSONException;
38 import org.json.JSONObject;
39 import org.junit.Before;
40 import org.junit.Test;
41 import org.junit.runner.RunWith;
42 
43 /**
44  * Test that the Vulkan loader is present, supports the required extensions, and that system
45  * features accurately indicate the capabilities of the Vulkan driver if one exists.
46  */
47 @SmallTest
48 @RunWith(AndroidJUnit4.class)
49 public class VulkanFeaturesTest {
50 
51     static {
52         System.loadLibrary("ctsgraphics_jni");
53     }
54 
55     private static final String TAG = VulkanFeaturesTest.class.getSimpleName();
56     private static final boolean DEBUG = false;
57 
58     // Require patch version 3 for Vulkan 1.0: It was the first publicly available version,
59     // and there was an important bugfix relative to 1.0.2.
60     private static final int VULKAN_1_0 = 0x00400003; // 1.0.3
61     private static final int VULKAN_1_1 = 0x00401000; // 1.1.0
62     private static final int VULKAN_1_2 = 0x00402000; // 1.2.0
63     private static final int VULKAN_1_3 = 0x00403000; // 1.3.0
64 
65     private static final String VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME =
66             "VK_ANDROID_external_memory_android_hardware_buffer";
67     private static final int VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_SPEC_VERSION = 2;
68 
69     private static final String VK_KHR_SURFACE = "VK_KHR_surface";
70     private static final int VK_KHR_SURFACE_SPEC_VERSION = 25;
71 
72     private static final String VK_KHR_ANDROID_SURFACE = "VK_KHR_android_surface";
73     private static final int VK_KHR_ANDROID_SURFACE_SPEC_VERSION = 6;
74 
75     private static final String VK_KHR_SWAPCHAIN = "VK_KHR_swapchain";
76     private static final int VK_KHR_SWAPCHAIN_SPEC_VERSION = 68;
77 
78     private static final String VK_KHR_MAINTENANCE1 = "VK_KHR_maintenance1";
79     private static final int VK_KHR_MAINTENANCE1_SPEC_VERSION = 1;
80 
81     private static final String VK_KHR_INCREMENTAL_PRESENT = "VK_KHR_incremental_present";
82     private static final int VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION = 1;
83 
84     private static final int VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT = 0x8;
85     private static final int VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT = 0x10;
86     private static final int VK_PHYSICAL_DEVICE_TYPE_CPU = 4;
87 
88     private static final int API_LEVEL_BEFORE_ANDROID_HARDWARE_BUFFER_REQ = 28;
89 
90     private PackageManager mPm;
91     private FeatureInfo mVulkanHardwareLevel = null;
92     private FeatureInfo mVulkanHardwareVersion = null;
93     private FeatureInfo mVulkanHardwareCompute = null;
94     private JSONObject mVkJSON = null;
95     private JSONObject mVulkanDevices[];
96     private JSONObject mBestDevice = null;
97 
98     @Before
setup()99     public void setup() throws Throwable {
100         mPm = InstrumentationRegistry.getTargetContext().getPackageManager();
101         FeatureInfo features[] = mPm.getSystemAvailableFeatures();
102         if (features != null) {
103             for (FeatureInfo feature : features) {
104                 if (PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL.equals(feature.name)) {
105                     mVulkanHardwareLevel = feature;
106                     if (DEBUG) {
107                         Log.d(TAG, feature.name + "=" + feature.version);
108                     }
109                 } else if (PackageManager.FEATURE_VULKAN_HARDWARE_VERSION.equals(feature.name)) {
110                     mVulkanHardwareVersion = feature;
111                     if (DEBUG) {
112                         Log.d(TAG, feature.name + "=0x" + Integer.toHexString(feature.version));
113                     }
114                 } else if (PackageManager.FEATURE_VULKAN_HARDWARE_COMPUTE.equals(feature.name)) {
115                     mVulkanHardwareCompute = feature;
116                     if (DEBUG) {
117                         Log.d(TAG, feature.name + "=" + feature.version);
118                     }
119                 }
120             }
121         }
122 
123         mVkJSON = new JSONObject(nativeGetVkJSON());
124         mVulkanDevices = getVulkanDevices(mVkJSON);
125         mBestDevice = getBestDevice();
126     }
127 
128     @CddTest(requirement = "7.1.4.2/C-1-1,C-2-1")
129     @Test
testVulkanHardwareFeatures()130     public void testVulkanHardwareFeatures() throws JSONException {
131         if (DEBUG) {
132             Log.d(TAG, "Inspecting " + mVulkanDevices.length + " devices");
133         }
134         if (mVulkanDevices.length == 0) {
135             assertNull("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL +
136                        " is supported, but no Vulkan physical devices are available",
137                        mVulkanHardwareLevel);
138             assertNull("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_VERSION +
139                        " is supported, but no Vulkan physical devices are available",
140                        mVulkanHardwareLevel);
141             assertNull("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_COMPUTE +
142                        " is supported, but no Vulkan physical devices are available",
143                        mVulkanHardwareCompute);
144             return;
145         }
146 
147         if (hasOnlyCpuDevice()) {
148             return;
149         }
150 
151         assertNotNull("Vulkan physical devices are available, but system feature " +
152                       PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL + " is not supported",
153                       mVulkanHardwareLevel);
154         assertNotNull("Vulkan physical devices are available, but system feature " +
155                       PackageManager.FEATURE_VULKAN_HARDWARE_VERSION + " is not supported",
156                       mVulkanHardwareVersion);
157         if (mVulkanHardwareLevel == null || mVulkanHardwareVersion == null) {
158             return;
159         }
160 
161         assertTrue("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL +
162                    " version " + mVulkanHardwareLevel.version + " is not one of the defined " +
163                    " versions [0..1]",
164                    mVulkanHardwareLevel.version >= 0 && mVulkanHardwareLevel.version <= 1);
165         assertTrue("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_VERSION +
166                    " version 0x" + Integer.toHexString(mVulkanHardwareVersion.version) + " is not" +
167                    " one of the versions allowed",
168                    isHardwareVersionAllowed(mVulkanHardwareVersion.version));
169         if (mVulkanHardwareCompute != null) {
170             assertTrue("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_COMPUTE +
171                        " version " + mVulkanHardwareCompute.version +
172                        " is not one of the versions allowed",
173                        mVulkanHardwareCompute.version == 0);
174         }
175 
176         int bestDeviceLevel = determineHardwareLevel(mBestDevice);
177         int bestComputeLevel = determineHardwareCompute(mBestDevice);
178         int bestDeviceVersion = determineHardwareVersion(mBestDevice);
179 
180         assertEquals("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL +
181             " version " + mVulkanHardwareLevel.version + " doesn't match best physical device " +
182             " hardware level " + bestDeviceLevel,
183             bestDeviceLevel, mVulkanHardwareLevel.version);
184         assertTrue(
185             "System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_VERSION +
186             " version 0x" + Integer.toHexString(mVulkanHardwareVersion.version) +
187             " isn't close enough (same major and minor version, less or equal patch version)" +
188             " to best physical device version 0x" + Integer.toHexString(bestDeviceVersion),
189             isVersionCompatible(bestDeviceVersion, mVulkanHardwareVersion.version));
190         if (mVulkanHardwareCompute == null) {
191             assertEquals("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_COMPUTE +
192                 " not present, but required features are supported",
193                 bestComputeLevel, -1);
194         } else {
195             assertEquals("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_COMPUTE +
196                 " version " + mVulkanHardwareCompute.version +
197                 " doesn't match best physical device (version: " + bestComputeLevel + ")",
198                 bestComputeLevel, mVulkanHardwareCompute.version);
199         }
200     }
201 
202     @CddTest(requirement = "3.3.1/C-0-12")
203     @Test
testVulkanApplicationBinaryInterfaceRequirements()204     public void testVulkanApplicationBinaryInterfaceRequirements() throws JSONException {
205         assumeTrue("Skipping because Vulkan is not supported", mVulkanHardwareVersion != null);
206 
207         if (hasOnlyCpuDevice()) {
208             return;
209         }
210 
211         assertTrue("Devices must support the core Vulkan 1.1",
212                 mVulkanHardwareVersion.version >= VULKAN_1_1);
213     }
214 
215     @CddTest(requirement = "7.1.4.2/C-1-3")
216     @Test
testVulkanApiForEachDevice()217     public void testVulkanApiForEachDevice() throws JSONException {
218         for (JSONObject device : mVulkanDevices) {
219             assertTrue("All enumerated VPhysicalDevice must support Vulkan 1.1",
220                     determineHardwareVersion(device) >= VULKAN_1_1);
221         }
222     }
223 
224     @CddTest(requirement = "7.1.4.2/C-3-1")
225     @Test
testVulkan1_1Requirements()226     public void testVulkan1_1Requirements() throws JSONException {
227         if (mVulkanHardwareVersion == null || mVulkanHardwareVersion.version < VULKAN_1_1
228                 || !PropertyUtil.isVendorApiLevelNewerThan(
229                         API_LEVEL_BEFORE_ANDROID_HARDWARE_BUFFER_REQ)) {
230             return;
231         }
232         assertTrue("Devices with Vulkan 1.1 must support sampler YCbCr conversion",
233                 mBestDevice.getJSONObject("samplerYcbcrConversionFeatures")
234                            .getInt("samplerYcbcrConversion") != 0);
235 
236         if (hasOnlyCpuDevice()) {
237             return;
238         }
239         assertTrue("Devices with Vulkan 1.1 must support " +
240                 VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME +
241                 " (version >= " + VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_SPEC_VERSION +
242                 ")",
243                 hasDeviceExtension(mBestDevice,
244                     VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
245                     VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_SPEC_VERSION));
246         assertTrue("Devices with Vulkan 1.1 must support SYNC_FD external semaphores",
247                 hasHandleType(mBestDevice.getJSONArray("externalSemaphoreProperties"),
248                     VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
249                     "externalSemaphoreFeatures", 0x3 /* importable + exportable */));
250         assertTrue("Devices with Vulkan 1.1 must support SYNC_FD external fences",
251                 hasHandleType(mBestDevice.getJSONArray("externalFenceProperties"),
252                     VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT,
253                     "externalFenceFeatures", 0x3 /* importable + exportable */));
254     }
255 
256     @CddTest(requirement = "7.1.4.2/C-1-7, 3.3.1/C-0-12")
257     @Test
testVulkanRequiredExtensions()258     public void testVulkanRequiredExtensions() throws JSONException {
259         assumeTrue("Skipping because Vulkan is not supported", mVulkanDevices.length > 0);
260 
261         assertVulkanInstanceExtension(VK_KHR_SURFACE, VK_KHR_SURFACE_SPEC_VERSION);
262         assertVulkanInstanceExtension(VK_KHR_ANDROID_SURFACE, VK_KHR_ANDROID_SURFACE_SPEC_VERSION);
263 
264         assertVulkanDeviceExtension(VK_KHR_SWAPCHAIN, VK_KHR_SWAPCHAIN_SPEC_VERSION);
265         assertVulkanDeviceExtension(VK_KHR_INCREMENTAL_PRESENT,
266                 VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION);
267         assertVulkanDeviceExtension(VK_KHR_MAINTENANCE1, VK_KHR_MAINTENANCE1_SPEC_VERSION);
268     }
269 
270     @CddTest(requirement = "7.9.2/C-1-5")
271     @Test
testVulkanVersionForVrHighPerformance()272     public void testVulkanVersionForVrHighPerformance() {
273         if (!mPm.hasSystemFeature(PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE))
274             return;
275         assertTrue(
276             "VR high-performance devices must support Vulkan 1.0 with Hardware Level 0, " +
277             "but this device does not.",
278             mVulkanHardwareVersion != null && mVulkanHardwareVersion.version >= VULKAN_1_0 &&
279             mVulkanHardwareLevel != null && mVulkanHardwareLevel.version >= 0);
280     }
281 
282     @CddTest(requirement = "7.1.4.2/C-1-11")
283     @Test
testVulkanBlockedExtensions()284     public void testVulkanBlockedExtensions() throws JSONException {
285         assertNoVulkanDeviceExtension("VK_KHR_performance_query");
286         assertNoVulkanDeviceExtension("VK_KHR_video_queue");
287         assertNoVulkanDeviceExtension("VK_KHR_video_decode_queue");
288         assertNoVulkanDeviceExtension("VK_KHR_video_encode_queue");
289     }
290 
291     @CddTest(requirement = "7.1.4.2")
292     @Test
testVulkanVariantSupport()293     public void testVulkanVariantSupport() throws JSONException {
294         if (mVulkanHardwareVersion == null) {
295             return;
296         }
297         int expectedVariant = 0x0;
298         int actualVariant = (mVulkanHardwareVersion.version >> 29) & 0x7;
299         assertEquals(expectedVariant, actualVariant);
300     }
301 
nativeGetABPSupport()302     private static native String nativeGetABPSupport();
nativeGetABPCpuOnlySupport()303     private static native String nativeGetABPCpuOnlySupport();
304 
305     @CddTest(requirement = "7.1.4.2/C-1-13")
306     @Test
testAndroidBaselineProfile2021Support()307     public void testAndroidBaselineProfile2021Support() throws JSONException {
308         assumeTrue("Skipping because Vulkan is not supported", mVulkanHardwareVersion != null);
309 
310         if (!hasOnlyCpuDevice()) {
311             assertEquals("This device must support the ABP 2021.", "", nativeGetABPSupport());
312         } else {
313             assertEquals("This device must support the ABP 2021.", "",
314                     nativeGetABPCpuOnlySupport());
315         }
316     }
317 
getBestDevice()318     private JSONObject getBestDevice() throws JSONException {
319         JSONObject bestDevice = null;
320         int bestDeviceLevel = -1;
321         int bestComputeLevel = -1;
322         int bestDeviceVersion = -1;
323         for (JSONObject device : mVulkanDevices) {
324             int level = determineHardwareLevel(device);
325             int compute = determineHardwareCompute(device);
326             int version = determineHardwareVersion(device);
327             if (DEBUG) {
328                 Log.d(TAG, device.getJSONObject("properties").getString("deviceName") +
329                     ": level=" + level + " compute=" + compute +
330                     " version=0x" + Integer.toHexString(version));
331             }
332             if (level >= bestDeviceLevel && compute >= bestComputeLevel &&
333                     version >= bestDeviceVersion) {
334                 bestDevice = device;
335                 bestDeviceLevel = level;
336                 bestComputeLevel = compute;
337                 bestDeviceVersion = version;
338             }
339         }
340         return bestDevice;
341     }
342 
hasOnlyCpuDevice()343     private boolean hasOnlyCpuDevice() throws JSONException {
344         for (JSONObject device : mVulkanDevices) {
345             if (device.getJSONObject("properties").getInt("deviceType")
346                     != VK_PHYSICAL_DEVICE_TYPE_CPU) {
347                 return false;
348             }
349         }
350         return true;
351     }
352 
determineHardwareLevel(JSONObject device)353     private int determineHardwareLevel(JSONObject device) throws JSONException {
354         JSONObject features = device.getJSONObject("features");
355         boolean textureCompressionETC2 = features.getInt("textureCompressionETC2") != 0;
356         boolean fullDrawIndexUint32 = features.getInt("fullDrawIndexUint32") != 0;
357         boolean imageCubeArray = features.getInt("imageCubeArray") != 0;
358         boolean independentBlend = features.getInt("independentBlend") != 0;
359         boolean geometryShader = features.getInt("geometryShader") != 0;
360         boolean tessellationShader = features.getInt("tessellationShader") != 0;
361         boolean sampleRateShading = features.getInt("sampleRateShading") != 0;
362         boolean textureCompressionASTC_LDR = features.getInt("textureCompressionASTC_LDR") != 0;
363         boolean fragmentStoresAndAtomics = features.getInt("fragmentStoresAndAtomics") != 0;
364         boolean shaderImageGatherExtended = features.getInt("shaderImageGatherExtended") != 0;
365         boolean shaderUniformBufferArrayDynamicIndexing = features.getInt("shaderUniformBufferArrayDynamicIndexing") != 0;
366         boolean shaderSampledImageArrayDynamicIndexing = features.getInt("shaderSampledImageArrayDynamicIndexing") != 0;
367         if (!textureCompressionETC2) {
368             return -1;
369         }
370         if (!fullDrawIndexUint32 ||
371             !imageCubeArray ||
372             !independentBlend ||
373             !geometryShader ||
374             !tessellationShader ||
375             !sampleRateShading ||
376             !textureCompressionASTC_LDR ||
377             !fragmentStoresAndAtomics ||
378             !shaderImageGatherExtended ||
379             !shaderUniformBufferArrayDynamicIndexing ||
380             !shaderSampledImageArrayDynamicIndexing) {
381             return 0;
382         }
383         return 1;
384     }
385 
determineHardwareCompute(JSONObject device)386     private int determineHardwareCompute(JSONObject device) throws JSONException {
387         boolean variablePointers = false;
388         try {
389             variablePointers = device.getJSONObject("variablePointerFeatures")
390                                              .getInt("variablePointers") != 0;
391         } catch (JSONException exp) {
392             try {
393                 variablePointers = device.getJSONObject("VK_KHR_variable_pointers")
394                                                  .getJSONObject("variablePointerFeaturesKHR")
395                                                  .getInt("variablePointers") != 0;
396             }  catch (JSONException exp2) {
397                 variablePointers = false;
398             }
399         }
400         JSONObject limits = device.getJSONObject("properties").getJSONObject("limits");
401         int maxPerStageDescriptorStorageBuffers = limits.getInt("maxPerStageDescriptorStorageBuffers");
402         if (DEBUG) {
403             Log.d(TAG, device.getJSONObject("properties").getString("deviceName") +
404                 ": variablePointers=" + variablePointers +
405                 " maxPerStageDescriptorStorageBuffers=" + maxPerStageDescriptorStorageBuffers);
406         }
407         if (!variablePointers || maxPerStageDescriptorStorageBuffers < 16)
408             return -1;
409         return 0;
410     }
411 
determineHardwareVersion(JSONObject device)412     private int determineHardwareVersion(JSONObject device) throws JSONException {
413         return device.getJSONObject("properties").getInt("apiVersion");
414     }
415 
isVersionCompatible(int expected, int actual)416     private boolean isVersionCompatible(int expected, int actual) {
417         int expectedVariant = (expected >> 29) & 0x7;
418         int expectedMajor   = (expected >> 22) & 0x7F;
419         int expectedMinor   = (expected >> 12) & 0x3FF;
420         int expectedPatch   = (expected >>  0) & 0xFFF;
421         int actualVariant = (actual >> 29) & 0x7;
422         int actualMajor   = (actual >> 22) & 0x7F;
423         int actualMinor   = (actual >> 12) & 0x3FF;
424         int actualPatch   = (actual >>  0) & 0xFFF;
425         return (actualVariant == expectedVariant)
426             && (actualMajor == expectedMajor)
427             && (actualMinor == expectedMinor)
428             && (actualPatch <= expectedPatch);
429     }
430 
isHardwareVersionAllowed(int actual)431     private boolean isHardwareVersionAllowed(int actual) {
432         // Limit which system feature hardware versions are allowed. If a new major/minor version
433         // is released, we don't want devices claiming support for it until tests for the new
434         // version are available. And only claiming support for a base patch level per major/minor
435         // pair reduces fragmentation seen by developers. Patch-level changes are supposed to be
436         // forwards and backwards compatible; if a developer *really* needs to alter behavior based
437         // on the patch version, they can do so at runtime, but must be able to handle previous
438         // patch versions.
439         final int[] ALLOWED_HARDWARE_VERSIONS = {
440             VULKAN_1_0,
441             VULKAN_1_1,
442             VULKAN_1_2,
443             VULKAN_1_3,
444         };
445         for (int expected : ALLOWED_HARDWARE_VERSIONS) {
446             if (actual == expected) {
447                 return true;
448             }
449         }
450         return false;
451     }
452 
assertVulkanDeviceExtension(final String name, final int minVersion)453     private void assertVulkanDeviceExtension(final String name, final int minVersion)
454             throws JSONException {
455         assertTrue(
456                 String.format(
457                         "Devices with Vulkan must support device extension %s (version >= %d)",
458                         name,
459                         minVersion),
460                 hasDeviceExtension(mBestDevice, name, minVersion));
461     }
462 
assertNoVulkanDeviceExtension(final String name)463     private void assertNoVulkanDeviceExtension(final String name)
464             throws JSONException {
465         for (JSONObject device : mVulkanDevices) {
466             assertTrue(
467                     String.format("Devices must not support Vulkan device extension %s", name),
468                     !hasDeviceExtension(device, name, 0));
469         }
470     }
471 
assertVulkanInstanceExtension(final String name, final int minVersion)472     private void assertVulkanInstanceExtension(final String name, final int minVersion)
473             throws JSONException {
474         assertTrue(
475                 String.format(
476                         "Devices with Vulkan must support instance extension %s (version >= %d)",
477                         name,
478                         minVersion),
479                 hasInstanceExtension(name, minVersion));
480     }
481 
hasDeviceExtension( final JSONObject device, final String name, final int minVersion)482     private static boolean hasDeviceExtension(
483             final JSONObject device,
484             final String name,
485             final int minVersion) throws JSONException {
486         final JSONArray deviceExtensions = device.getJSONArray("extensions");
487         return hasExtension(deviceExtensions, name, minVersion);
488     }
489 
hasInstanceExtension( final String name, final int minVersion)490     private boolean hasInstanceExtension(
491             final String name,
492             final int minVersion) throws JSONException {
493         // Instance extensions are in the top-level vkjson object.
494         final JSONArray instanceExtensions = mVkJSON.getJSONArray("extensions");
495         return hasExtension(instanceExtensions, name, minVersion);
496     }
497 
hasExtension( final JSONArray extensions, final String name, final int minVersion)498     private static boolean hasExtension(
499             final JSONArray extensions,
500             final String name,
501             final int minVersion) throws JSONException {
502         for (int i = 0; i < extensions.length(); i++) {
503             JSONObject ext = extensions.getJSONObject(i);
504             if (ext.getString("extensionName").equals(name) &&
505                     ext.getInt("specVersion") >= minVersion)
506                 return true;
507         }
508         return false;
509     }
510 
hasHandleType(JSONArray handleTypes, int type, String featuresName, int requiredFeatures)511     private boolean hasHandleType(JSONArray handleTypes, int type,
512             String featuresName, int requiredFeatures) throws JSONException {
513         for (int i = 0; i < handleTypes.length(); i++) {
514             JSONArray typeRecord = handleTypes.getJSONArray(i);
515             if (typeRecord.getInt(0) == type) {
516                 JSONObject typeInfo = typeRecord.getJSONObject(1);
517                 if ((typeInfo.getInt(featuresName) & requiredFeatures) == requiredFeatures)
518                     return true;
519             }
520         }
521         return false;
522     }
523 
nativeGetVkJSON()524     private static native String nativeGetVkJSON();
525 
getVulkanDevices(final JSONObject vkJSON)526     private static JSONObject[] getVulkanDevices(final JSONObject vkJSON) throws JSONException {
527         JSONArray devicesArray = vkJSON.getJSONArray("devices");
528         JSONObject[] devices = new JSONObject[devicesArray.length()];
529         for (int i = 0; i < devicesArray.length(); i++) {
530             devices[i] = devicesArray.getJSONObject(i);
531         }
532         return devices;
533     }
534 }
535