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 android.content.Context; 20 import android.content.pm.FeatureInfo; 21 import android.content.pm.PackageManager; 22 import android.test.AndroidTestCase; 23 import android.util.Log; 24 import java.io.UnsupportedEncodingException; 25 import org.json.JSONArray; 26 import org.json.JSONException; 27 import org.json.JSONObject; 28 29 /** 30 * Test that the Vulkan loader is present, supports the required extensions, and that system 31 * features accurately indicate the capabilities of the Vulkan driver if one exists. 32 */ 33 public class VulkanFeaturesTest extends AndroidTestCase { 34 35 static { 36 System.loadLibrary("ctsgraphics_jni"); 37 } 38 39 private static final String TAG = VulkanFeaturesTest.class.getSimpleName(); 40 private static final boolean DEBUG = false; 41 42 // Require patch version 3 for Vulkan 1.0: It was the first publicly available version, 43 // and there was an important bugfix relative to 1.0.2. 44 private static final int VULKAN_1_0 = 0x00400003; // 1.0.3 45 46 PackageManager mPm; 47 FeatureInfo mVulkanHardwareLevel = null; 48 FeatureInfo mVulkanHardwareVersion = null; 49 JSONObject mVulkanDevices[]; 50 51 public VulkanFeaturesTest() { 52 super(); 53 } 54 55 @Override 56 protected void setUp() throws Exception { 57 super.setUp(); 58 59 mPm = getContext().getPackageManager(); 60 FeatureInfo features[] = mPm.getSystemAvailableFeatures(); 61 if (features != null) { 62 for (FeatureInfo feature : features) { 63 if (PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL.equals(feature.name)) { 64 mVulkanHardwareLevel = feature; 65 if (DEBUG) { 66 Log.d(TAG, feature.name + "=" + feature.version); 67 } 68 } else if (PackageManager.FEATURE_VULKAN_HARDWARE_VERSION.equals(feature.name)) { 69 mVulkanHardwareVersion = feature; 70 if (DEBUG) { 71 Log.d(TAG, feature.name + "=0x" + Integer.toHexString(feature.version)); 72 } 73 } 74 } 75 } 76 77 mVulkanDevices = getVulkanDevices(); 78 } 79 80 public void testVulkanHardwareFeatures() throws JSONException { 81 if (DEBUG) { 82 Log.d(TAG, "Inspecting " + mVulkanDevices.length + " devices"); 83 } 84 if (mVulkanDevices.length == 0) { 85 assertNull("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL + 86 " is supported, but no Vulkan physical devices are available", 87 mVulkanHardwareLevel); 88 assertNull("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_VERSION + 89 " is supported, but no Vulkan physical devices are available", 90 mVulkanHardwareLevel); 91 return; 92 } 93 assertNotNull("Vulkan physical devices are available, but system feature " + 94 PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL + " is not supported", 95 mVulkanHardwareLevel); 96 assertNotNull("Vulkan physical devices are available, but system feature " + 97 PackageManager.FEATURE_VULKAN_HARDWARE_VERSION + " is not supported", 98 mVulkanHardwareVersion); 99 if (mVulkanHardwareLevel == null || mVulkanHardwareVersion == null) { 100 return; 101 } 102 103 assertTrue("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL + 104 " version " + mVulkanHardwareLevel.version + " is not one of the defined " + 105 " versions [0..1]", 106 mVulkanHardwareLevel.version >= 0 && mVulkanHardwareLevel.version <= 1); 107 assertTrue("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_VERSION + 108 " version 0x" + Integer.toHexString(mVulkanHardwareVersion.version) + " is not" + 109 " one of the versions allowed", 110 isHardwareVersionAllowed(mVulkanHardwareVersion.version)); 111 112 JSONObject bestDevice = null; 113 int bestDeviceLevel = -1; 114 int bestDeviceVersion = -1; 115 for (JSONObject device : mVulkanDevices) { 116 int level = determineHardwareLevel(device); 117 int version = determineHardwareVersion(device); 118 if (DEBUG) { 119 Log.d(TAG, device.getJSONObject("properties").getString("deviceName") + 120 ": level=" + level + " version=0x" + Integer.toHexString(version)); 121 } 122 if (level >= bestDeviceLevel && version >= bestDeviceVersion) { 123 bestDevice = device; 124 bestDeviceLevel = level; 125 bestDeviceVersion = version; 126 } 127 } 128 129 assertEquals("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL + 130 " version " + mVulkanHardwareLevel.version + " doesn't match best physical device " + 131 " hardware level " + bestDeviceLevel, 132 bestDeviceLevel, mVulkanHardwareLevel.version); 133 assertTrue( 134 "System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_VERSION + 135 " version 0x" + Integer.toHexString(mVulkanHardwareVersion.version) + 136 " isn't close enough (same major and minor version, less or equal patch version)" + 137 " to best physical device version 0x" + Integer.toHexString(bestDeviceVersion), 138 isVersionCompatible(bestDeviceVersion, mVulkanHardwareVersion.version)); 139 } 140 141 public void testVulkanVersionForVrHighPerformance() { 142 if (!mPm.hasSystemFeature(PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) 143 return; 144 assertTrue( 145 "VR high-performance devices must support Vulkan 1.0 with Hardware Level 0, " + 146 "but this device does not.", 147 mVulkanHardwareVersion != null && mVulkanHardwareVersion.version >= VULKAN_1_0 && 148 mVulkanHardwareLevel != null && mVulkanHardwareLevel.version >= 0); 149 } 150 151 private int determineHardwareLevel(JSONObject device) throws JSONException { 152 JSONObject features = device.getJSONObject("features"); 153 boolean textureCompressionETC2 = features.getInt("textureCompressionETC2") != 0; 154 boolean fullDrawIndexUint32 = features.getInt("fullDrawIndexUint32") != 0; 155 boolean imageCubeArray = features.getInt("imageCubeArray") != 0; 156 boolean independentBlend = features.getInt("independentBlend") != 0; 157 boolean geometryShader = features.getInt("geometryShader") != 0; 158 boolean tessellationShader = features.getInt("tessellationShader") != 0; 159 boolean sampleRateShading = features.getInt("sampleRateShading") != 0; 160 boolean textureCompressionASTC_LDR = features.getInt("textureCompressionASTC_LDR") != 0; 161 boolean fragmentStoresAndAtomics = features.getInt("fragmentStoresAndAtomics") != 0; 162 boolean shaderImageGatherExtended = features.getInt("shaderImageGatherExtended") != 0; 163 boolean shaderUniformBufferArrayDynamicIndexing = features.getInt("shaderUniformBufferArrayDynamicIndexing") != 0; 164 boolean shaderSampledImageArrayDynamicIndexing = features.getInt("shaderSampledImageArrayDynamicIndexing") != 0; 165 if (!textureCompressionETC2) { 166 return -1; 167 } 168 if (!fullDrawIndexUint32 || 169 !imageCubeArray || 170 !independentBlend || 171 !geometryShader || 172 !tessellationShader || 173 !sampleRateShading || 174 !textureCompressionASTC_LDR || 175 !fragmentStoresAndAtomics || 176 !shaderImageGatherExtended || 177 !shaderUniformBufferArrayDynamicIndexing || 178 !shaderSampledImageArrayDynamicIndexing) { 179 return 0; 180 } 181 return 1; 182 } 183 184 private int determineHardwareVersion(JSONObject device) throws JSONException { 185 return device.getJSONObject("properties").getInt("apiVersion"); 186 } 187 188 private boolean isVersionCompatible(int expected, int actual) { 189 int expectedMajor = (expected >> 22) & 0x3FF; 190 int expectedMinor = (expected >> 12) & 0x3FF; 191 int expectedPatch = (expected >> 0) & 0xFFF; 192 int actualMajor = (actual >> 22) & 0x3FF; 193 int actualMinor = (actual >> 12) & 0x3FF; 194 int actualPatch = (actual >> 0) & 0xFFF; 195 return (actualMajor == expectedMajor) && 196 (actualMinor == expectedMinor) && 197 (actualPatch <= expectedPatch); 198 } 199 200 private boolean isHardwareVersionAllowed(int actual) { 201 // Limit which system feature hardware versions are allowed. If a new major/minor version 202 // is released, we don't want devices claiming support for it until tests for the new 203 // version are available. And only claiming support for a base patch level per major/minor 204 // pair reduces fragmentation seen by developers. Patch-level changes are supposed to be 205 // forwards and backwards compatible; if a developer *really* needs to alter behavior based 206 // on the patch version, they can do so at runtime, but must be able to handle previous 207 // patch versions. 208 final int[] ALLOWED_HARDWARE_VERSIONS = { 209 VULKAN_1_0, 210 }; 211 for (int expected : ALLOWED_HARDWARE_VERSIONS) { 212 if (actual == expected) { 213 return true; 214 } 215 } 216 return false; 217 } 218 219 private static native String nativeGetVkJSON(); 220 221 private JSONObject[] getVulkanDevices() throws JSONException, UnsupportedEncodingException { 222 JSONArray vkjson = new JSONArray(nativeGetVkJSON()); 223 JSONObject[] devices = new JSONObject[vkjson.length()]; 224 for (int i = 0; i < vkjson.length(); i++) { 225 devices[i] = vkjson.getJSONObject(i); 226 } 227 return devices; 228 } 229 } 230