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.platform.test.annotations.AppModeFull; 28 import android.util.ArrayMap; 29 import android.util.Log; 30 31 import androidx.test.InstrumentationRegistry; 32 import androidx.test.filters.SmallTest; 33 import androidx.test.runner.AndroidJUnit4; 34 35 import com.android.compatibility.common.util.CddTest; 36 import com.android.compatibility.common.util.PropertyUtil; 37 38 import org.json.JSONArray; 39 import org.json.JSONException; 40 import org.json.JSONObject; 41 import org.junit.Before; 42 import org.junit.Test; 43 import org.junit.runner.RunWith; 44 45 import java.util.Arrays; 46 import java.util.HashSet; 47 import java.util.Map; 48 import java.util.Set; 49 50 /** 51 * Test that the Vulkan loader is present, supports the required extensions, and that system 52 * features accurately indicate the capabilities of the Vulkan driver if one exists. 53 */ 54 @SmallTest 55 @AppModeFull 56 @RunWith(AndroidJUnit4.class) 57 public class VulkanFeaturesTest { 58 59 static { 60 System.loadLibrary("ctsgraphics_jni"); 61 } 62 63 private static final String TAG = VulkanFeaturesTest.class.getSimpleName(); 64 private static final boolean DEBUG = false; 65 66 // Require patch version 3 for Vulkan 1.0: It was the first publicly available version, 67 // and there was an important bugfix relative to 1.0.2. 68 private static final int VULKAN_1_0 = 0x00400003; // 1.0.3 69 private static final int VULKAN_1_1 = 0x00401000; // 1.1.0 70 private static final int VULKAN_1_2 = 0x00402000; // 1.2.0 71 private static final int VULKAN_1_3 = 0x00403000; // 1.3.0 72 private static final int VULKAN_1_4 = 0x00404000; // 1.4.0 73 74 private static final String VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME = 75 "VK_ANDROID_external_memory_android_hardware_buffer"; 76 private static final int VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_SPEC_VERSION = 2; 77 78 private static final String VK_KHR_SURFACE = "VK_KHR_surface"; 79 private static final int VK_KHR_SURFACE_SPEC_VERSION = 25; 80 81 private static final String VK_KHR_ANDROID_SURFACE = "VK_KHR_android_surface"; 82 private static final int VK_KHR_ANDROID_SURFACE_SPEC_VERSION = 6; 83 84 private static final String VK_KHR_SWAPCHAIN = "VK_KHR_swapchain"; 85 private static final int VK_KHR_SWAPCHAIN_SPEC_VERSION = 68; 86 87 private static final String VK_KHR_MAINTENANCE1 = "VK_KHR_maintenance1"; 88 private static final int VK_KHR_MAINTENANCE1_SPEC_VERSION = 1; 89 90 private static final String VK_KHR_INCREMENTAL_PRESENT = "VK_KHR_incremental_present"; 91 private static final int VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION = 1; 92 93 private static final int VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT = 0x8; 94 private static final int VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT = 0x10; 95 private static final int VK_PHYSICAL_DEVICE_TYPE_CPU = 4; 96 97 private static final int API_LEVEL_BEFORE_ANDROID_HARDWARE_BUFFER_REQ = 28; 98 99 private static final int DEQP_LEVEL_FOR_B = 0x7E90301; 100 private static final int DEQP_LEVEL_FOR_V = 0x7E80301; 101 private static final int DEQP_LEVEL_FOR_U = 0x7E70301; 102 private static final int DEQP_LEVEL_FOR_T = 0x7E60301; 103 private static final int DEQP_LEVEL_FOR_S = 0x7E50301; 104 private static final int DEQP_LEVEL_FOR_R = 0x7E40301; 105 private static final int DEQP_LEVEL_BEFORE_R = 0; 106 107 private static final Map<Integer, String[]> DEQP_EXTENSIONS_MAP = new ArrayMap<>(); 108 static { DEQP_EXTENSIONS_MAP.put( DEQP_LEVEL_FOR_B, new String[] { "VK_KHR_compute_shader_derivatives", "VK_KHR_maintenance7", "VK_KHR_pipeline_binary", "VK_KHR_pipeline_executable_properties", "VK_KHR_shader_relaxed_extended_instructions", "VK_KHR_video_encode_av1", "VK_KHR_video_encode_quantization_map"})109 DEQP_EXTENSIONS_MAP.put( 110 DEQP_LEVEL_FOR_B, 111 new String[] { 112 "VK_KHR_compute_shader_derivatives", 113 "VK_KHR_maintenance7", 114 "VK_KHR_pipeline_binary", 115 "VK_KHR_pipeline_executable_properties", 116 "VK_KHR_shader_relaxed_extended_instructions", 117 "VK_KHR_video_encode_av1", 118 "VK_KHR_video_encode_quantization_map"}); DEQP_EXTENSIONS_MAP.put( DEQP_LEVEL_FOR_V, new String[] { "VK_KHR_calibrated_timestamps", "VK_KHR_cooperative_matrix", "VK_KHR_index_type_uint8", "VK_KHR_line_rasterization", "VK_KHR_load_store_op_none", "VK_KHR_maintenance5", "VK_KHR_maintenance6", "VK_KHR_map_memory2", "VK_KHR_ray_tracing_position_fetch", "VK_KHR_shader_expect_assume", "VK_KHR_shader_quad_control", "VK_KHR_vertex_attribute_divisor", "VK_ANDROID_external_format_resolve", "VK_KHR_dynamic_rendering_local_read", "VK_KHR_shader_float_controls2", "VK_KHR_shader_maximal_reconvergence", "VK_KHR_shader_subgroup_rotate", "VK_KHR_video_decode_av1", "VK_KHR_video_encode_h264", "VK_KHR_video_encode_h265", "VK_KHR_video_encode_queue", "VK_KHR_video_maintenance1"})119 DEQP_EXTENSIONS_MAP.put( 120 DEQP_LEVEL_FOR_V, 121 new String[] { 122 "VK_KHR_calibrated_timestamps", 123 "VK_KHR_cooperative_matrix", 124 "VK_KHR_index_type_uint8", 125 "VK_KHR_line_rasterization", 126 "VK_KHR_load_store_op_none", 127 "VK_KHR_maintenance5", 128 "VK_KHR_maintenance6", 129 "VK_KHR_map_memory2", 130 "VK_KHR_ray_tracing_position_fetch", 131 "VK_KHR_shader_expect_assume", 132 "VK_KHR_shader_quad_control", 133 "VK_KHR_vertex_attribute_divisor", 134 "VK_ANDROID_external_format_resolve", 135 "VK_KHR_dynamic_rendering_local_read", 136 "VK_KHR_shader_float_controls2", 137 "VK_KHR_shader_maximal_reconvergence", 138 "VK_KHR_shader_subgroup_rotate", 139 "VK_KHR_video_decode_av1", 140 "VK_KHR_video_encode_h264", 141 "VK_KHR_video_encode_h265", 142 "VK_KHR_video_encode_queue", 143 "VK_KHR_video_maintenance1"}); DEQP_EXTENSIONS_MAP.put( DEQP_LEVEL_FOR_U, new String[] { "VK_KHR_fragment_shader_barycentric", "VK_KHR_ray_tracing_maintenance1", "VK_KHR_video_decode_h264", "VK_KHR_video_decode_h265", "VK_KHR_video_decode_queue", "VK_KHR_video_queue", "VK_GOOGLE_user_type"})144 DEQP_EXTENSIONS_MAP.put( 145 DEQP_LEVEL_FOR_U, 146 new String[] { 147 "VK_KHR_fragment_shader_barycentric", 148 "VK_KHR_ray_tracing_maintenance1", 149 "VK_KHR_video_decode_h264", 150 "VK_KHR_video_decode_h265", 151 "VK_KHR_video_decode_queue", 152 "VK_KHR_video_queue", 153 "VK_GOOGLE_user_type"}); DEQP_EXTENSIONS_MAP.put( DEQP_LEVEL_FOR_T, new String[] { "VK_KHR_dynamic_rendering", "VK_KHR_format_feature_flags2", "VK_KHR_global_priority", "VK_KHR_maintenance4", "VK_KHR_portability_subset", "VK_KHR_present_id", "VK_KHR_present_wait", "VK_KHR_shader_subgroup_uniform_control_flow", "VK_KHR_portability_enumeration"})154 DEQP_EXTENSIONS_MAP.put( 155 DEQP_LEVEL_FOR_T, 156 new String[] { 157 "VK_KHR_dynamic_rendering", 158 "VK_KHR_format_feature_flags2", 159 "VK_KHR_global_priority", 160 "VK_KHR_maintenance4", 161 "VK_KHR_portability_subset", 162 "VK_KHR_present_id", 163 "VK_KHR_present_wait", 164 "VK_KHR_shader_subgroup_uniform_control_flow", 165 "VK_KHR_portability_enumeration"}); DEQP_EXTENSIONS_MAP.put( DEQP_LEVEL_FOR_S, new String[] { "VK_KHR_copy_commands2", "VK_KHR_shader_terminate_invocation", "VK_KHR_ray_tracing_pipeline", "VK_KHR_ray_query", "VK_KHR_acceleration_structure", "VK_KHR_pipeline_library", "VK_KHR_deferred_host_operations", "VK_KHR_fragment_shading_rate", "VK_KHR_zero_initialize_workgroup_memory", "VK_KHR_workgroup_memory_explicit_layout", "VK_KHR_synchronization2", "VK_KHR_shader_integer_dot_product"})166 DEQP_EXTENSIONS_MAP.put( 167 DEQP_LEVEL_FOR_S, 168 new String[] { 169 "VK_KHR_copy_commands2", 170 "VK_KHR_shader_terminate_invocation", 171 "VK_KHR_ray_tracing_pipeline", 172 "VK_KHR_ray_query", 173 "VK_KHR_acceleration_structure", 174 "VK_KHR_pipeline_library", 175 "VK_KHR_deferred_host_operations", 176 "VK_KHR_fragment_shading_rate", 177 "VK_KHR_zero_initialize_workgroup_memory", 178 "VK_KHR_workgroup_memory_explicit_layout", 179 "VK_KHR_synchronization2", 180 "VK_KHR_shader_integer_dot_product"}); DEQP_EXTENSIONS_MAP.put( DEQP_LEVEL_FOR_R, new String[] { "VK_KHR_swapchain", "VK_KHR_swapchain_mutable_format", "VK_KHR_display_swapchain", "VK_KHR_sampler_mirror_clamp_to_edge", "VK_KHR_external_memory_win32", "VK_KHR_external_memory_fd", "VK_KHR_win32_keyed_mutex", "VK_KHR_external_semaphore_win32", "VK_KHR_external_semaphore_fd", "VK_KHR_push_descriptor", "VK_KHR_shader_float16_int8", "VK_KHR_incremental_present", "VK_KHR_8bit_storage", "VK_KHR_create_renderpass2", "VK_KHR_shared_presentable_image", "VK_KHR_external_fence_win32", "VK_KHR_external_fence_fd", "VK_KHR_image_format_list", "VK_KHR_driver_properties", "VK_KHR_shader_float_controls", "VK_KHR_depth_stencil_resolve", "VK_KHR_draw_indirect_count", "VK_KHR_shader_atomic_int64", "VK_KHR_vulkan_memory_model", "VK_KHR_uniform_buffer_standard_layout", "VK_KHR_imageless_framebuffer", "VK_KHR_shader_subgroup_extended_types", "VK_KHR_buffer_device_address", "VK_KHR_separate_depth_stencil_layouts", "VK_KHR_timeline_semaphore", "VK_KHR_spirv_1_4", "VK_KHR_pipeline_executable_properties", "VK_KHR_shader_clock", "VK_KHR_performance_query", "VK_KHR_shader_non_semantic_info", "VK_KHR_surface", "VK_KHR_display", "VK_KHR_xlib_surface", "VK_KHR_xcb_surface", "VK_KHR_wayland_surface", "VK_KHR_mir_surface", "VK_KHR_android_surface", "VK_KHR_win32_surface", "VK_KHR_get_surface_capabilities2", "VK_KHR_get_display_properties2", "VK_KHR_surface_protected_capabilities", "VK_GOOGLE_decorate_string", "VK_GOOGLE_hlsl_functionality1"})181 DEQP_EXTENSIONS_MAP.put( 182 DEQP_LEVEL_FOR_R, 183 new String[] { 184 "VK_KHR_swapchain", 185 "VK_KHR_swapchain_mutable_format", 186 "VK_KHR_display_swapchain", 187 "VK_KHR_sampler_mirror_clamp_to_edge", 188 "VK_KHR_external_memory_win32", 189 "VK_KHR_external_memory_fd", 190 "VK_KHR_win32_keyed_mutex", 191 "VK_KHR_external_semaphore_win32", 192 "VK_KHR_external_semaphore_fd", 193 "VK_KHR_push_descriptor", 194 "VK_KHR_shader_float16_int8", 195 "VK_KHR_incremental_present", 196 "VK_KHR_8bit_storage", 197 "VK_KHR_create_renderpass2", 198 "VK_KHR_shared_presentable_image", 199 "VK_KHR_external_fence_win32", 200 "VK_KHR_external_fence_fd", 201 "VK_KHR_image_format_list", 202 "VK_KHR_driver_properties", 203 "VK_KHR_shader_float_controls", 204 "VK_KHR_depth_stencil_resolve", 205 "VK_KHR_draw_indirect_count", 206 "VK_KHR_shader_atomic_int64", 207 "VK_KHR_vulkan_memory_model", 208 "VK_KHR_uniform_buffer_standard_layout", 209 "VK_KHR_imageless_framebuffer", 210 "VK_KHR_shader_subgroup_extended_types", 211 "VK_KHR_buffer_device_address", 212 "VK_KHR_separate_depth_stencil_layouts", 213 "VK_KHR_timeline_semaphore", 214 "VK_KHR_spirv_1_4", 215 "VK_KHR_pipeline_executable_properties", 216 "VK_KHR_shader_clock", 217 "VK_KHR_performance_query", 218 "VK_KHR_shader_non_semantic_info", 219 "VK_KHR_surface", 220 "VK_KHR_display", 221 "VK_KHR_xlib_surface", 222 "VK_KHR_xcb_surface", 223 "VK_KHR_wayland_surface", 224 "VK_KHR_mir_surface", 225 "VK_KHR_android_surface", 226 "VK_KHR_win32_surface", 227 "VK_KHR_get_surface_capabilities2", 228 "VK_KHR_get_display_properties2", 229 "VK_KHR_surface_protected_capabilities", 230 "VK_GOOGLE_decorate_string", 231 "VK_GOOGLE_hlsl_functionality1"}); DEQP_EXTENSIONS_MAP.put( DEQP_LEVEL_BEFORE_R, new String[] { "VK_KHR_multiview", "VK_KHR_device_group", "VK_KHR_shader_draw_parameters", "VK_KHR_maintenance1", "VK_KHR_external_memory", "VK_KHR_external_semaphore", "VK_KHR_16bit_storage", "VK_KHR_descriptor_update_template", "VK_KHR_external_fence", "VK_KHR_maintenance2", "VK_KHR_variable_pointers", "VK_KHR_dedicated_allocation", "VK_KHR_storage_buffer_storage_class", "VK_KHR_relaxed_block_layout", "VK_KHR_get_memory_requirements2", "VK_KHR_sampler_ycbcr_conversion", "VK_KHR_bind_memory2", "VK_KHR_maintenance3", "VK_KHR_get_physical_device_properties2", "VK_KHR_device_group_creation", "VK_KHR_external_memory_capabilities", "VK_KHR_external_semaphore_capabilities", "VK_KHR_external_fence_capabilities", "VK_ANDROID_external_memory_android_hardware_buffer", "VK_GOOGLE_display_timing"})232 DEQP_EXTENSIONS_MAP.put( 233 DEQP_LEVEL_BEFORE_R, 234 new String[] { 235 "VK_KHR_multiview", 236 "VK_KHR_device_group", 237 "VK_KHR_shader_draw_parameters", 238 "VK_KHR_maintenance1", 239 "VK_KHR_external_memory", 240 "VK_KHR_external_semaphore", 241 "VK_KHR_16bit_storage", 242 "VK_KHR_descriptor_update_template", 243 "VK_KHR_external_fence", 244 "VK_KHR_maintenance2", 245 "VK_KHR_variable_pointers", 246 "VK_KHR_dedicated_allocation", 247 "VK_KHR_storage_buffer_storage_class", 248 "VK_KHR_relaxed_block_layout", 249 "VK_KHR_get_memory_requirements2", 250 "VK_KHR_sampler_ycbcr_conversion", 251 "VK_KHR_bind_memory2", 252 "VK_KHR_maintenance3", 253 "VK_KHR_get_physical_device_properties2", 254 "VK_KHR_device_group_creation", 255 "VK_KHR_external_memory_capabilities", 256 "VK_KHR_external_semaphore_capabilities", 257 "VK_KHR_external_fence_capabilities", 258 "VK_ANDROID_external_memory_android_hardware_buffer", 259 "VK_GOOGLE_display_timing"}); 260 } 261 262 private PackageManager mPm; 263 private FeatureInfo mVulkanHardwareLevel = null; 264 private FeatureInfo mVulkanHardwareVersion = null; 265 private FeatureInfo mVulkanHardwareCompute = null; 266 private FeatureInfo mVulkanDeqpLevel = null; 267 private JSONObject mVkJSON = null; 268 private JSONObject mVulkanDevices[]; 269 private JSONObject mBestDevice = null; 270 private boolean mIsTV = false; 271 private boolean mIsWatch = false; 272 private boolean mIsAutomotive = false; 273 private boolean mHasTouchscreen = false; 274 275 @Before setup()276 public void setup() throws Throwable { 277 mPm = InstrumentationRegistry.getTargetContext().getPackageManager(); 278 FeatureInfo features[] = mPm.getSystemAvailableFeatures(); 279 if (features != null) { 280 for (FeatureInfo feature : features) { 281 if (PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL.equals(feature.name)) { 282 mVulkanHardwareLevel = feature; 283 if (DEBUG) { 284 Log.d(TAG, feature.name + "=" + feature.version); 285 } 286 } else if (PackageManager.FEATURE_VULKAN_HARDWARE_VERSION.equals(feature.name)) { 287 mVulkanHardwareVersion = feature; 288 if (DEBUG) { 289 Log.d(TAG, feature.name + "=0x" + Integer.toHexString(feature.version)); 290 } 291 } else if (PackageManager.FEATURE_VULKAN_HARDWARE_COMPUTE.equals(feature.name)) { 292 mVulkanHardwareCompute = feature; 293 if (DEBUG) { 294 Log.d(TAG, feature.name + "=" + feature.version); 295 } 296 } else if (PackageManager.FEATURE_VULKAN_DEQP_LEVEL.equals(feature.name)) { 297 mVulkanDeqpLevel = feature; 298 if (DEBUG) { 299 Log.d(TAG, feature.name + "=" + feature.version); 300 } 301 } else if (PackageManager.FEATURE_LEANBACK.equals(feature.name)) { 302 mIsTV = true; 303 } else if (PackageManager.FEATURE_WATCH.equals(feature.name)) { 304 mIsWatch = true; 305 } else if (PackageManager.FEATURE_TOUCHSCREEN.equals(feature.name)) { 306 mHasTouchscreen = true; 307 } else if (PackageManager.FEATURE_AUTOMOTIVE.equals(feature.name)) { 308 mIsAutomotive = true; 309 } 310 } 311 } 312 313 mVkJSON = new JSONObject(nativeGetVkJSON()); 314 mVulkanDevices = getVulkanDevices(mVkJSON); 315 mBestDevice = getBestDevice(); 316 } 317 318 @CddTest(requirements = {"7.1.4.2/C-1-1,C-2-1"}) 319 @Test testVulkanHardwareFeatures()320 public void testVulkanHardwareFeatures() throws JSONException { 321 if (DEBUG) { 322 Log.d(TAG, "Inspecting " + mVulkanDevices.length + " devices"); 323 } 324 if (mVulkanDevices.length == 0) { 325 assertNull("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL + 326 " is supported, but no Vulkan physical devices are available", 327 mVulkanHardwareLevel); 328 assertNull("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_VERSION + 329 " is supported, but no Vulkan physical devices are available", 330 mVulkanHardwareLevel); 331 assertNull("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_COMPUTE + 332 " is supported, but no Vulkan physical devices are available", 333 mVulkanHardwareCompute); 334 return; 335 } 336 337 if (hasOnlyCpuDevice()) { 338 return; 339 } 340 341 assertNotNull("Vulkan physical devices are available, but system feature " + 342 PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL + " is not supported", 343 mVulkanHardwareLevel); 344 assertNotNull("Vulkan physical devices are available, but system feature " + 345 PackageManager.FEATURE_VULKAN_HARDWARE_VERSION + " is not supported", 346 mVulkanHardwareVersion); 347 if (mVulkanHardwareLevel == null || mVulkanHardwareVersion == null) { 348 return; 349 } 350 351 assertTrue("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL + 352 " version " + mVulkanHardwareLevel.version + " is not one of the defined " + 353 " versions [0..1]", 354 mVulkanHardwareLevel.version >= 0 && mVulkanHardwareLevel.version <= 1); 355 assertTrue("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_VERSION + 356 " version 0x" + Integer.toHexString(mVulkanHardwareVersion.version) + " is not" + 357 " one of the versions allowed", 358 isHardwareVersionAllowed(mVulkanHardwareVersion.version)); 359 if (mVulkanHardwareCompute != null) { 360 assertTrue("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_COMPUTE + 361 " version " + mVulkanHardwareCompute.version + 362 " is not one of the versions allowed", 363 mVulkanHardwareCompute.version == 0); 364 } 365 366 int bestDeviceLevel = determineHardwareLevel(mBestDevice); 367 int bestComputeLevel = determineHardwareCompute(mBestDevice); 368 int bestDeviceVersion = determineHardwareVersion(mBestDevice); 369 370 assertEquals("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_LEVEL + 371 " version " + mVulkanHardwareLevel.version + " doesn't match best physical device " + 372 " hardware level " + bestDeviceLevel, 373 bestDeviceLevel, mVulkanHardwareLevel.version); 374 assertTrue( 375 "System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_VERSION + 376 " version 0x" + Integer.toHexString(mVulkanHardwareVersion.version) + 377 " isn't close enough (same major and minor version, less or equal patch version)" + 378 " to best physical device version 0x" + Integer.toHexString(bestDeviceVersion), 379 isVersionCompatible(bestDeviceVersion, mVulkanHardwareVersion.version)); 380 if (mVulkanHardwareCompute == null) { 381 assertEquals("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_COMPUTE + 382 " not present, but required features are supported", 383 bestComputeLevel, -1); 384 } else { 385 assertEquals("System feature " + PackageManager.FEATURE_VULKAN_HARDWARE_COMPUTE + 386 " version " + mVulkanHardwareCompute.version + 387 " doesn't match best physical device (version: " + bestComputeLevel + ")", 388 bestComputeLevel, mVulkanHardwareCompute.version); 389 } 390 } 391 392 @CddTest(requirements = {"3.3.1/C-0-12"}) 393 @Test testVulkanApplicationBinaryInterfaceRequirements()394 public void testVulkanApplicationBinaryInterfaceRequirements() throws JSONException { 395 assumeTrue("Skipping because Vulkan is not supported", mVulkanHardwareVersion != null); 396 397 if (hasOnlyCpuDevice()) { 398 return; 399 } 400 401 assertTrue("Devices must support the core Vulkan 1.1", 402 mVulkanHardwareVersion.version >= VULKAN_1_1); 403 } 404 405 @CddTest(requirements = {"7.1.4.2/C-1-3"}) 406 @Test testVulkanApiForEachDevice()407 public void testVulkanApiForEachDevice() throws JSONException { 408 for (JSONObject device : mVulkanDevices) { 409 assertTrue("All enumerated VPhysicalDevice must support Vulkan 1.1", 410 determineHardwareVersion(device) >= VULKAN_1_1); 411 } 412 } 413 414 @CddTest(requirements = {"7.1.4.2/C-3-1"}) 415 @Test testVulkan1_1Requirements()416 public void testVulkan1_1Requirements() throws JSONException { 417 if (mVulkanHardwareVersion == null || mVulkanHardwareVersion.version < VULKAN_1_1 418 || !PropertyUtil.isVendorApiLevelNewerThan( 419 API_LEVEL_BEFORE_ANDROID_HARDWARE_BUFFER_REQ)) { 420 return; 421 } 422 assertTrue("Devices with Vulkan 1.1 must support sampler YCbCr conversion", 423 mBestDevice.getJSONObject("samplerYcbcrConversionFeatures") 424 .getInt("samplerYcbcrConversion") != 0); 425 426 if (hasOnlyCpuDevice()) { 427 return; 428 } 429 assertTrue("Devices with Vulkan 1.1 must support " + 430 VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME + 431 " (version >= " + VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_SPEC_VERSION + 432 ")", 433 hasDeviceExtension(mBestDevice, 434 VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME, 435 VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_SPEC_VERSION)); 436 assertTrue("Devices with Vulkan 1.1 must support SYNC_FD external semaphores", 437 hasHandleType(mBestDevice.getJSONArray("externalSemaphoreProperties"), 438 VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, 439 "externalSemaphoreFeatures", 0x3 /* importable + exportable */)); 440 assertTrue("Devices with Vulkan 1.1 must support SYNC_FD external fences", 441 hasHandleType(mBestDevice.getJSONArray("externalFenceProperties"), 442 VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT, 443 "externalFenceFeatures", 0x3 /* importable + exportable */)); 444 } 445 446 @CddTest(requirements = {"7.1.4.2/C-1-7", "3.3.1/C-0-12"}) 447 @Test testVulkanRequiredExtensions()448 public void testVulkanRequiredExtensions() throws JSONException { 449 assumeTrue("Skipping because Vulkan is not supported", mVulkanDevices.length > 0); 450 451 assertVulkanInstanceExtension(VK_KHR_SURFACE, VK_KHR_SURFACE_SPEC_VERSION); 452 assertVulkanInstanceExtension(VK_KHR_ANDROID_SURFACE, VK_KHR_ANDROID_SURFACE_SPEC_VERSION); 453 454 assertVulkanDeviceExtension(VK_KHR_SWAPCHAIN, VK_KHR_SWAPCHAIN_SPEC_VERSION); 455 assertVulkanDeviceExtension(VK_KHR_INCREMENTAL_PRESENT, 456 VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION); 457 assertVulkanDeviceExtension(VK_KHR_MAINTENANCE1, VK_KHR_MAINTENANCE1_SPEC_VERSION); 458 } 459 460 @CddTest(requirements = {"7.9.2/C-1-5"}) 461 @Test testVulkanVersionForVrHighPerformance()462 public void testVulkanVersionForVrHighPerformance() { 463 if (!mPm.hasSystemFeature(PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE)) 464 return; 465 assertTrue( 466 "VR high-performance devices must support Vulkan 1.0 with Hardware Level 0, " + 467 "but this device does not.", 468 mVulkanHardwareVersion != null && mVulkanHardwareVersion.version >= VULKAN_1_0 && 469 mVulkanHardwareLevel != null && mVulkanHardwareLevel.version >= 0); 470 } 471 472 @CddTest(requirements = {"7.1.4.2/C-1-11"}) 473 @Test testVulkanBlockedExtensions()474 public void testVulkanBlockedExtensions() throws JSONException { 475 assertNoVulkanDeviceExtension("VK_KHR_performance_query"); 476 assertNoVulkanDeviceExtension("VK_KHR_video_queue"); 477 assertNoVulkanDeviceExtension("VK_KHR_video_decode_queue"); 478 assertNoVulkanDeviceExtension("VK_KHR_video_encode_queue"); 479 } 480 481 @CddTest(requirements = {"7.1.4.2"}) 482 @Test testVulkanVariantSupport()483 public void testVulkanVariantSupport() throws JSONException { 484 assumeTrue("Skipping because Vulkan is not supported", mVulkanHardwareVersion != null); 485 486 int expectedVariant = 0x0; 487 int actualVariant = (mVulkanHardwareVersion.version >> 29) & 0x7; 488 assertEquals(expectedVariant, actualVariant); 489 } 490 491 @CddTest(requirements = {"7.1.4.2/C-1-14"}) 492 @Test testVulkanExposedDeviceExtensions()493 public void testVulkanExposedDeviceExtensions() throws JSONException { 494 assumeTrue("Skipping because Vulkan is not supported", mVulkanHardwareVersion != null); 495 496 if (hasOnlyCpuDevice()) { 497 return; 498 } 499 500 // Determine the set of device-side extensions that can be exposed 501 // Note this only includes VK_KHR, VK_GOOGLE, VK_ANDROID 502 final int deviceDeqpLevel = mVulkanDeqpLevel.version; 503 Set<String> allowedDeviceExtensions = new HashSet<String>(); 504 for (Integer level : DEQP_EXTENSIONS_MAP.keySet()) { 505 if (deviceDeqpLevel >= level) { 506 allowedDeviceExtensions.addAll(Arrays.asList(DEQP_EXTENSIONS_MAP.get(level))); 507 } 508 } 509 510 // Get the set of all device-side extensions exposed by the device 511 final JSONArray deviceExtensions = mBestDevice.getJSONArray("extensions"); 512 // Search for any device extensions that should not be exposed 513 Set<String> untestedExtensions = new HashSet<String>(); 514 for (int i = 0; i < deviceExtensions.length(); i++) { 515 JSONObject extension = deviceExtensions.getJSONObject(i); 516 String deviceExtension = extension.getString("extensionName"); 517 boolean vk_android = deviceExtension.startsWith("VK_ANDROID"); 518 boolean vk_google = deviceExtension.startsWith("VK_GOOGLE"); 519 boolean vk_khr = deviceExtension.startsWith("VK_KHR"); 520 if (!vk_android && !vk_google && !vk_khr) { 521 if (DEBUG) { 522 Log.d(TAG, "Device extension exposed is not KHR, GOOGLE, or ANDROID: " 523 + deviceExtension); 524 } 525 continue; 526 } 527 if (!allowedDeviceExtensions.contains(deviceExtension)) { 528 if (DEBUG) { 529 Log.d(TAG, "Device extension exposed on device not found in dEQP level " 530 + deviceDeqpLevel + ": " + deviceExtension); 531 } 532 untestedExtensions.add(deviceExtension); 533 } 534 } 535 536 assertEquals("This device exposes the extensions:\n" + untestedExtensions 537 + "\n that are not tested under its claimed dEQP level: " + deviceDeqpLevel, 538 0, untestedExtensions.size()); 539 } 540 nativeGetABPSupport()541 private static native String nativeGetABPSupport(); nativeGetABPCpuOnlySupport()542 private static native String nativeGetABPCpuOnlySupport(); 543 isHandheld()544 private boolean isHandheld() { 545 // There is no PM feature for "handheld" 546 return mHasTouchscreen && !mIsTV && !mIsWatch && !mIsAutomotive; 547 } 548 549 @CddTest(requirements = {"7.1.4.2/H-1-1"}) 550 @Test testAndroidBaselineProfile2021Support()551 public void testAndroidBaselineProfile2021Support() throws JSONException { 552 final int apiLevel = PropertyUtil.getVsrApiLevel(); 553 assumeTrue("Skipping because Vulkan is not supported", mVulkanHardwareVersion != null); 554 assumeTrue("Skipping because ABP is only required of handheld devices", isHandheld()); 555 556 if (!hasOnlyCpuDevice()) { 557 assertEquals("This device must support the ABP 2021.", "", nativeGetABPSupport()); 558 } else { 559 assertEquals("This device must support the ABP 2021.", "", 560 nativeGetABPCpuOnlySupport()); 561 } 562 } 563 getBestDevice()564 private JSONObject getBestDevice() throws JSONException { 565 JSONObject bestDevice = null; 566 int bestDeviceLevel = -1; 567 int bestComputeLevel = -1; 568 int bestDeviceVersion = -1; 569 for (JSONObject device : mVulkanDevices) { 570 int level = determineHardwareLevel(device); 571 int compute = determineHardwareCompute(device); 572 int version = determineHardwareVersion(device); 573 if (DEBUG) { 574 Log.d(TAG, device.getJSONObject("properties").getString("deviceName") + 575 ": level=" + level + " compute=" + compute + 576 " version=0x" + Integer.toHexString(version)); 577 } 578 if (level >= bestDeviceLevel && compute >= bestComputeLevel && 579 version >= bestDeviceVersion) { 580 bestDevice = device; 581 bestDeviceLevel = level; 582 bestComputeLevel = compute; 583 bestDeviceVersion = version; 584 } 585 } 586 return bestDevice; 587 } 588 hasOnlyCpuDevice()589 private boolean hasOnlyCpuDevice() throws JSONException { 590 for (JSONObject device : mVulkanDevices) { 591 if (device.getJSONObject("properties").getInt("deviceType") 592 != VK_PHYSICAL_DEVICE_TYPE_CPU) { 593 return false; 594 } 595 } 596 return true; 597 } 598 determineHardwareLevel(JSONObject device)599 private int determineHardwareLevel(JSONObject device) throws JSONException { 600 JSONObject features = device.getJSONObject("features"); 601 boolean textureCompressionETC2 = features.getInt("textureCompressionETC2") != 0; 602 boolean fullDrawIndexUint32 = features.getInt("fullDrawIndexUint32") != 0; 603 boolean imageCubeArray = features.getInt("imageCubeArray") != 0; 604 boolean independentBlend = features.getInt("independentBlend") != 0; 605 boolean geometryShader = features.getInt("geometryShader") != 0; 606 boolean tessellationShader = features.getInt("tessellationShader") != 0; 607 boolean sampleRateShading = features.getInt("sampleRateShading") != 0; 608 boolean textureCompressionASTC_LDR = features.getInt("textureCompressionASTC_LDR") != 0; 609 boolean fragmentStoresAndAtomics = features.getInt("fragmentStoresAndAtomics") != 0; 610 boolean shaderImageGatherExtended = features.getInt("shaderImageGatherExtended") != 0; 611 boolean shaderUniformBufferArrayDynamicIndexing = 612 features.getInt("shaderUniformBufferArrayDynamicIndexing") != 0; 613 boolean shaderSampledImageArrayDynamicIndexing = 614 features.getInt("shaderSampledImageArrayDynamicIndexing") != 0; 615 if (!textureCompressionETC2) { 616 return -1; 617 } 618 if (!fullDrawIndexUint32 || 619 !imageCubeArray || 620 !independentBlend || 621 !geometryShader || 622 !tessellationShader || 623 !sampleRateShading || 624 !textureCompressionASTC_LDR || 625 !fragmentStoresAndAtomics || 626 !shaderImageGatherExtended || 627 !shaderUniformBufferArrayDynamicIndexing || 628 !shaderSampledImageArrayDynamicIndexing) { 629 return 0; 630 } 631 return 1; 632 } 633 determineHardwareCompute(JSONObject device)634 private int determineHardwareCompute(JSONObject device) throws JSONException { 635 boolean variablePointers = false; 636 try { 637 variablePointers = device.getJSONObject("variablePointersFeatures") 638 .getInt("variablePointers") != 0; 639 } catch (JSONException exp) { 640 try { 641 variablePointers = device.getJSONObject("VK_KHR_variable_pointers") 642 .getJSONObject("variablePointerFeaturesKHR") 643 .getInt("variablePointers") != 0; 644 } catch (JSONException exp2) { 645 try { 646 variablePointers = device.getJSONObject("VK_KHR_variable_pointers") 647 .getJSONObject("variablePointersFeaturesKHR") 648 .getInt("variablePointers") != 0; 649 } catch (JSONException exp3) { 650 variablePointers = false; 651 } 652 } 653 } 654 JSONObject limits = device.getJSONObject("properties").getJSONObject("limits"); 655 int maxPerStageDescriptorStorageBuffers = 656 limits.getInt("maxPerStageDescriptorStorageBuffers"); 657 if (DEBUG) { 658 Log.d(TAG, device.getJSONObject("properties").getString("deviceName") + 659 ": variablePointers=" + variablePointers + 660 " maxPerStageDescriptorStorageBuffers=" + maxPerStageDescriptorStorageBuffers); 661 } 662 if (!variablePointers || maxPerStageDescriptorStorageBuffers < 16) 663 return -1; 664 return 0; 665 } 666 determineHardwareVersion(JSONObject device)667 private int determineHardwareVersion(JSONObject device) throws JSONException { 668 return device.getJSONObject("properties").getInt("apiVersion"); 669 } 670 isVersionCompatible(int expected, int actual)671 private boolean isVersionCompatible(int expected, int actual) { 672 int expectedVariant = (expected >> 29) & 0x7; 673 int expectedMajor = (expected >> 22) & 0x7F; 674 int expectedMinor = (expected >> 12) & 0x3FF; 675 int expectedPatch = (expected >> 0) & 0xFFF; 676 int actualVariant = (actual >> 29) & 0x7; 677 int actualMajor = (actual >> 22) & 0x7F; 678 int actualMinor = (actual >> 12) & 0x3FF; 679 int actualPatch = (actual >> 0) & 0xFFF; 680 return (actualVariant == expectedVariant) 681 && (actualMajor == expectedMajor) 682 && (actualMinor == expectedMinor) 683 && (actualPatch <= expectedPatch); 684 } 685 isHardwareVersionAllowed(int actual)686 private boolean isHardwareVersionAllowed(int actual) { 687 // Limit which system feature hardware versions are allowed. If a new major/minor version 688 // is released, we don't want devices claiming support for it until tests for the new 689 // version are available. And only claiming support for a base patch level per major/minor 690 // pair reduces fragmentation seen by developers. Patch-level changes are supposed to be 691 // forwards and backwards compatible; if a developer *really* needs to alter behavior based 692 // on the patch version, they can do so at runtime, but must be able to handle previous 693 // patch versions. 694 final int[] ALLOWED_HARDWARE_VERSIONS = { 695 VULKAN_1_0, 696 VULKAN_1_1, 697 VULKAN_1_2, 698 VULKAN_1_3, 699 VULKAN_1_4, 700 }; 701 for (int expected : ALLOWED_HARDWARE_VERSIONS) { 702 if (actual == expected) { 703 return true; 704 } 705 } 706 return false; 707 } 708 assertVulkanDeviceExtension(final String name, final int minVersion)709 private void assertVulkanDeviceExtension(final String name, final int minVersion) 710 throws JSONException { 711 assertTrue( 712 String.format( 713 "Devices with Vulkan must support device extension %s (version >= %d)", 714 name, 715 minVersion), 716 hasDeviceExtension(mBestDevice, name, minVersion)); 717 } 718 assertNoVulkanDeviceExtension(final String name)719 private void assertNoVulkanDeviceExtension(final String name) 720 throws JSONException { 721 for (JSONObject device : mVulkanDevices) { 722 assertTrue( 723 String.format("Devices must not support Vulkan device extension %s", name), 724 !hasDeviceExtension(device, name, 0)); 725 } 726 } 727 assertVulkanInstanceExtension(final String name, final int minVersion)728 private void assertVulkanInstanceExtension(final String name, final int minVersion) 729 throws JSONException { 730 assertTrue( 731 String.format( 732 "Devices with Vulkan must support instance extension %s (version >= %d)", 733 name, 734 minVersion), 735 hasInstanceExtension(name, minVersion)); 736 } 737 hasDeviceExtension( final JSONObject device, final String name, final int minVersion)738 private static boolean hasDeviceExtension( 739 final JSONObject device, 740 final String name, 741 final int minVersion) throws JSONException { 742 final JSONArray deviceExtensions = device.getJSONArray("extensions"); 743 return hasExtension(deviceExtensions, name, minVersion); 744 } 745 hasInstanceExtension( final String name, final int minVersion)746 private boolean hasInstanceExtension( 747 final String name, 748 final int minVersion) throws JSONException { 749 // Instance extensions are in the top-level vkjson object. 750 final JSONArray instanceExtensions = mVkJSON.getJSONArray("extensions"); 751 return hasExtension(instanceExtensions, name, minVersion); 752 } 753 hasExtension( final JSONArray extensions, final String name, final int minVersion)754 private static boolean hasExtension( 755 final JSONArray extensions, 756 final String name, 757 final int minVersion) throws JSONException { 758 for (int i = 0; i < extensions.length(); i++) { 759 JSONObject ext = extensions.getJSONObject(i); 760 if (ext.getString("extensionName").equals(name) && 761 ext.getInt("specVersion") >= minVersion) 762 return true; 763 } 764 return false; 765 } 766 hasHandleType(JSONArray handleTypes, int type, String featuresName, int requiredFeatures)767 private boolean hasHandleType(JSONArray handleTypes, int type, 768 String featuresName, int requiredFeatures) throws JSONException { 769 for (int i = 0; i < handleTypes.length(); i++) { 770 JSONArray typeRecord = handleTypes.getJSONArray(i); 771 if (typeRecord.getInt(0) == type) { 772 JSONObject typeInfo = typeRecord.getJSONObject(1); 773 if ((typeInfo.getInt(featuresName) & requiredFeatures) == requiredFeatures) 774 return true; 775 } 776 } 777 return false; 778 } 779 nativeGetVkJSON()780 private static native String nativeGetVkJSON(); 781 getVulkanDevices(final JSONObject vkJSON)782 private static JSONObject[] getVulkanDevices(final JSONObject vkJSON) throws JSONException { 783 JSONArray devicesArray = vkJSON.getJSONArray("devices"); 784 JSONObject[] devices = new JSONObject[devicesArray.length()]; 785 for (int i = 0; i < devicesArray.length(); i++) { 786 devices[i] = devicesArray.getJSONObject(i); 787 } 788 return devices; 789 } 790 } 791