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