blob: f9111b0374051d16855b2ce320cdf391f0a4e93f [file] [log] [blame]
/*
* Copyright 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.graphics.cts;
import android.content.Context;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
import android.test.AndroidTestCase;
import android.util.Log;
import java.io.UnsupportedEncodingException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Test that the Vulkan loader is present, supports the required extensions, and that system
* features accurately indicate the capabilities of the Vulkan driver if one exists.
*/
public class VulkanFeaturesTest extends AndroidTestCase {
static {
System.loadLibrary("ctsgraphics_jni");
}
private static final String TAG = VulkanFeaturesTest.class.getSimpleName();
private static final boolean DEBUG = false;
// Require patch version 3 for Vulkan 1.0: It was the first publicly available version,
// and there was an important bugfix relative to 1.0.2.
private static final int VULKAN_1_0 = 0x00400003; // 1.0.3
PackageManager mPm;
FeatureInfo mVulkanHardwareLevel = null;
FeatureInfo mVulkanHardwareVersion = null;
JSONObject mVulkanDevices[];
public VulkanFeaturesTest() {
super();
}
@Override
protected void setUp() throws Exception {
super.setUp();
mPm = getContext().getPackageManager();
FeatureInfo features[] = mPm.getSystemAvailableFeatures();
if (features != null) {
for (FeatureInfo feature : features) {
if (PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL.equals(feature.name)) {
mVulkanHardwareLevel = feature;
if (DEBUG) {
Log.d(TAG, feature.name + "=" + feature.version);
}
} else if (PackageManager.FEATURE_VULKAN_HARDWARE_VERSION.equals(feature.name)) {
mVulkanHardwareVersion = feature;
if (DEBUG) {
Log.d(TAG, feature.name + "=0x" + Integer.toHexString(feature.version));
}
}
}
}
mVulkanDevices = getVulkanDevices();
}
public void testVulkanHardwareFeatures() throws JSONException {
if (DEBUG) {
Log.d(TAG, "Inspecting " + mVulkanDevices.length + " devices");
}
if (mVulkanDevices.length == 0) {
assertNull("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL +
" is supported, but no Vulkan physical devices are available",
mVulkanHardwareLevel);
assertNull("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_VERSION +
" is supported, but no Vulkan physical devices are available",
mVulkanHardwareLevel);
return;
}
assertNotNull("Vulkan physical devices are available, but system feature " +
PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL + " is not supported",
mVulkanHardwareLevel);
assertNotNull("Vulkan physical devices are available, but system feature " +
PackageManager.FEATURE_VULKAN_HARDWARE_VERSION + " is not supported",
mVulkanHardwareVersion);
if (mVulkanHardwareLevel == null || mVulkanHardwareVersion == null) {
return;
}
assertTrue("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL +
" version " + mVulkanHardwareLevel.version + " is not one of the defined " +
" versions [0..1]",
mVulkanHardwareLevel.version >= 0 && mVulkanHardwareLevel.version <= 1);
assertTrue("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_VERSION +
" version 0x" + Integer.toHexString(mVulkanHardwareVersion.version) + " is not" +
" one of the versions allowed",
isHardwareVersionAllowed(mVulkanHardwareVersion.version));
JSONObject bestDevice = null;
int bestDeviceLevel = -1;
int bestDeviceVersion = -1;
for (JSONObject device : mVulkanDevices) {
int level = determineHardwareLevel(device);
int version = determineHardwareVersion(device);
if (DEBUG) {
Log.d(TAG, device.getJSONObject("properties").getString("deviceName") +
": level=" + level + " version=0x" + Integer.toHexString(version));
}
if (level >= bestDeviceLevel && version >= bestDeviceVersion) {
bestDevice = device;
bestDeviceLevel = level;
bestDeviceVersion = version;
}
}
assertEquals("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL +
" version " + mVulkanHardwareLevel.version + " doesn't match best physical device " +
" hardware level " + bestDeviceLevel,
bestDeviceLevel, mVulkanHardwareLevel.version);
assertTrue(
"System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_VERSION +
" version 0x" + Integer.toHexString(mVulkanHardwareVersion.version) +
" isn't close enough (same major and minor version, less or equal patch version)" +
" to best physical device version 0x" + Integer.toHexString(bestDeviceVersion),
isVersionCompatible(bestDeviceVersion, mVulkanHardwareVersion.version));
}
private int determineHardwareLevel(JSONObject device) throws JSONException {
JSONObject features = device.getJSONObject("features");
boolean textureCompressionETC2 = features.getInt("textureCompressionETC2") != 0;
boolean fullDrawIndexUint32 = features.getInt("fullDrawIndexUint32") != 0;
boolean imageCubeArray = features.getInt("imageCubeArray") != 0;
boolean independentBlend = features.getInt("independentBlend") != 0;
boolean geometryShader = features.getInt("geometryShader") != 0;
boolean tessellationShader = features.getInt("tessellationShader") != 0;
boolean sampleRateShading = features.getInt("sampleRateShading") != 0;
boolean textureCompressionASTC_LDR = features.getInt("textureCompressionASTC_LDR") != 0;
boolean fragmentStoresAndAtomics = features.getInt("fragmentStoresAndAtomics") != 0;
boolean shaderImageGatherExtended = features.getInt("shaderImageGatherExtended") != 0;
boolean shaderUniformBufferArrayDynamicIndexing = features.getInt("shaderUniformBufferArrayDynamicIndexing") != 0;
boolean shaderSampledImageArrayDynamicIndexing = features.getInt("shaderSampledImageArrayDynamicIndexing") != 0;
if (!textureCompressionETC2) {
return -1;
}
if (!fullDrawIndexUint32 ||
!imageCubeArray ||
!independentBlend ||
!geometryShader ||
!tessellationShader ||
!sampleRateShading ||
!textureCompressionASTC_LDR ||
!fragmentStoresAndAtomics ||
!shaderImageGatherExtended ||
!shaderUniformBufferArrayDynamicIndexing ||
!shaderSampledImageArrayDynamicIndexing) {
return 0;
}
return 1;
}
private int determineHardwareVersion(JSONObject device) throws JSONException {
return device.getJSONObject("properties").getInt("apiVersion");
}
private boolean isVersionCompatible(int expected, int actual) {
int expectedMajor = (expected >> 22) & 0x3FF;
int expectedMinor = (expected >> 12) & 0x3FF;
int expectedPatch = (expected >> 0) & 0xFFF;
int actualMajor = (actual >> 22) & 0x3FF;
int actualMinor = (actual >> 12) & 0x3FF;
int actualPatch = (actual >> 0) & 0xFFF;
return (actualMajor == expectedMajor) &&
(actualMinor == expectedMinor) &&
(actualPatch <= expectedPatch);
}
private boolean isHardwareVersionAllowed(int actual) {
// Limit which system feature hardware versions are allowed. If a new major/minor version
// is released, we don't want devices claiming support for it until tests for the new
// version are available. And only claiming support for a base patch level per major/minor
// pair reduces fragmentation seen by developers. Patch-level changes are supposed to be
// forwards and backwards compatible; if a developer *really* needs to alter behavior based
// on the patch version, they can do so at runtime, but must be able to handle previous
// patch versions.
final int[] ALLOWED_HARDWARE_VERSIONS = {
VULKAN_1_0,
};
for (int expected : ALLOWED_HARDWARE_VERSIONS) {
if (actual == expected) {
return true;
}
}
return false;
}
private static native String nativeGetVkJSON();
private JSONObject[] getVulkanDevices() throws JSONException, UnsupportedEncodingException {
JSONArray vkjson = new JSONArray(nativeGetVkJSON());
JSONObject[] devices = new JSONObject[vkjson.length()];
for (int i = 0; i < vkjson.length(); i++) {
devices[i] = vkjson.getJSONObject(i);
}
return devices;
}
}