• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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