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