Home | History | Annotate | Download | only in cts
      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