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