1 /*
2  * Copyright (C) 2023 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 package com.android.gpu.vts;
17 
18 import static org.junit.Assert.assertEquals;
19 import static org.junit.Assert.assertNotNull;
20 import static org.junit.Assert.assertTrue;
21 import static org.junit.Assert.fail;
22 import static org.junit.Assume.assumeTrue;
23 
24 import android.platform.test.annotations.RequiresDevice;
25 import com.android.compatibility.common.util.ApiLevelUtil;
26 import com.android.compatibility.common.util.FeatureUtil;
27 import com.android.compatibility.common.util.PropertyUtil;
28 import com.android.compatibility.common.util.VsrTest;
29 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
30 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
31 import org.json.JSONArray;
32 import org.json.JSONObject;
33 import org.junit.Before;
34 import org.junit.Test;
35 import org.junit.runner.RunWith;
36 
37 /*
38  * VTS test for Vulkan requirements.
39  */
40 @RequiresDevice
41 @RunWith(DeviceJUnit4ClassRunner.class)
42 public class VulkanTest extends BaseHostJUnit4Test {
43     private static final int VK_PHYSICAL_DEVICE_TYPE_CPU = 4;
44     private static final int VULKAN_1_1_API_VERSION = 0x401000;
45     private static final int VULKAN_1_3_API_VERSION = 0x403000;
46 
47     // Feature version corresponding to dEQP level for 2024-03-01.
48     public static final int DEQP_LEVEL_FOR_V = 0x7E80301;
49 
50     // Feature version corresponding to dEQP level for 2023-03-01.
51     public static final int DEQP_LEVEL_FOR_U = 0x7E70301;
52 
53     // Feature version corresponding to dEQP level for 2022-03-01.
54     public static final int DEQP_LEVEL_FOR_T = 0x7E60301;
55 
56     // Feature version corresponding to dEQP level for 2021-03-01.
57     public static final int DEQP_LEVEL_FOR_S = 0x7E50301;
58 
59     // Feature version corresponding to dEQP level for 2020-03-01.
60     public static final int DEQP_LEVEL_FOR_R = 0x7E40301;
61 
62     private static final String VK_EXT_DEVICE_MEMORY_REPORT_EXTENSION_NAME =
63             "VK_EXT_device_memory_report";
64     private static final int VK_EXT_DEVICE_MEMORY_REPORT_SPEC_VERSION = 1;
65 
66     private static final String VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME = "VK_EXT_global_priority";
67     private static final int VK_EXT_GLOBAL_PRIORITY_SPEC_VERSION = 1;
68     private static final String VK_KHR_GLOBAL_PRIORITY_EXTENSION_NAME = "VK_KHR_global_priority";
69     private static final int VK_KHR_GLOBAL_PRIORITY_SPEC_VERSION = 1;
70 
71     // the string to parse to confirm that Skia is using vulkan
72     private static final String SKIA_PIPELINE = "Pipeline=Skia";
73     private static final String SKIA_VULKAN_PIPELINE = "Pipeline=Skia (Vulkan)";
74 
75     private JSONObject[] mVulkanDevices;
76     private JSONObject mVulkanProfiles;
77 
78     /**
79      * Test specific setup
80      */
81     @Before
setUp()82     public void setUp() throws Exception {
83         final String output = getDevice().executeShellCommand("cmd gpu vkjson");
84         final JSONArray vkjson;
85         // vkjson output in Android N is JSONArray.
86         if (ApiLevelUtil.isBefore(getDevice(), Build.OC)) {
87             vkjson = (new JSONArray(output));
88         } else {
89             vkjson = (new JSONObject(output)).getJSONArray("devices");
90         }
91         mVulkanDevices = new JSONObject[vkjson.length()];
92         for (int i = 0; i < vkjson.length(); i++) {
93             mVulkanDevices[i] = vkjson.getJSONObject(i);
94         }
95 
96         final String profiles = getDevice().executeShellCommand("cmd gpu vkprofiles");
97         mVulkanProfiles = new JSONObject(profiles);
98     }
99 
100     /**
101      * 64-bit SoCs released with Q must support Vulkan 1.1.
102      */
103     @VsrTest(requirements = {"VSR-3.2.1-001.001"})
104     @Test
checkVulkan1_1Requirements()105     public void checkVulkan1_1Requirements() throws Exception {
106         // Only test for new 64-bit SoCs that are Q and above.
107         assumeTrue("Test does not apply for SoCs released before Q",
108                 PropertyUtil.getVsrApiLevel(getDevice()) >= Build.QT);
109         assumeTrue("Test does not apply for 32-bit SoCs",
110                 getDevice().getProperty("ro.product.cpu.abi").contains("64"));
111 
112         assertTrue(mVulkanDevices.length > 0);
113 
114         int bestApiVersion = 0;
115         for (JSONObject device : mVulkanDevices) {
116             final int apiVersion = device.getJSONObject("properties").getInt("apiVersion");
117             if (bestApiVersion < apiVersion) {
118                 bestApiVersion = apiVersion;
119             }
120         }
121         assertTrue("Supported Vulkan version must be at least 1.1",
122                 bestApiVersion >= VULKAN_1_1_API_VERSION);
123     }
124 
125     /**
126      * 64-bit SoCs released with U must support Vulkan 1.3.
127      * All SoCs released with V must support Vulkan 1.3.
128      */
129     @VsrTest(requirements = {"VSR-3.2.1-001.003", "VSR-3.2.1-007"})
130     @Test
checkVulkan1_3Requirements()131     public void checkVulkan1_3Requirements() throws Exception {
132         assumeTrue("Test does not apply for SoCs released before U",
133                 PropertyUtil.getVsrApiLevel(getDevice()) >= Build.UDC);
134         assumeTrue("Test does not apply for automotive devices released before V",
135                 !FeatureUtil.isAutomotive(getDevice())
136                         || PropertyUtil.getVsrApiLevel(getDevice()) > Build.UDC);
137 
138         // Don't test if an SoC released during U is 32 bit
139         // If an SoC is released with V then both 32 and 64 bit are to be tested
140         if (PropertyUtil.getVsrApiLevel(getDevice()) == Build.UDC) {
141             assumeTrue("Test does not apply for 32-bit SoCs released with Android U",
142                     getDevice().getProperty("ro.product.cpu.abi").contains("64"));
143         }
144 
145         assertTrue(mVulkanDevices.length > 0);
146 
147         int bestApiVersion = 0;
148         for (JSONObject device : mVulkanDevices) {
149             final int apiVersion = device.getJSONObject("properties").getInt("apiVersion");
150             if (bestApiVersion < apiVersion) {
151                 bestApiVersion = apiVersion;
152             }
153         }
154         assertTrue("Supported Vulkan version must be at least 1.3",
155                 bestApiVersion >= VULKAN_1_3_API_VERSION);
156     }
157 
158     /**
159      * SoCs with only CPU Vulkan must properly set "ro.cpuvulkan.version" property.
160      */
161     @VsrTest(requirements = {"VSR-3.2.1-002"})
162     @Test
checkCpuVulkanRequirements()163     public void checkCpuVulkanRequirements() throws Exception {
164         // Only test for new SoCs that are Q and above.
165         assumeTrue("Test does not apply for SoCs released before Q",
166                 PropertyUtil.getVsrApiLevel(getDevice()) >= Build.QT);
167 
168         if (mVulkanDevices.length == 0) {
169             return;
170         }
171 
172         boolean hasOnlyCpuDevice = true;
173         int bestApiVersion = 0;
174         for (JSONObject device : mVulkanDevices) {
175             if (device.getJSONObject("properties").getInt("deviceType")
176                     != VK_PHYSICAL_DEVICE_TYPE_CPU) {
177                 hasOnlyCpuDevice = false;
178             } else {
179                 final int apiVersion = device.getJSONObject("properties").getInt("apiVersion");
180                 if (bestApiVersion < apiVersion) {
181                     bestApiVersion = apiVersion;
182                 }
183             }
184         }
185 
186         if (!hasOnlyCpuDevice) {
187             return;
188         }
189 
190         final int advertisedApiVersion =
191                 Integer.parseInt(getDevice().getProperty("ro.cpuvulkan.version"));
192         assertEquals("Advertised CPU Vulkan api version " + advertisedApiVersion
193                         + " doesn't match the best physical device api version " + bestApiVersion,
194                 bestApiVersion, advertisedApiVersion);
195     }
196 
197     /**
198      * Verify that FEATURE_VULKAN_DEQP_LEVEL (feature:android.software.vulkan.deqp.level) has a
199      * sufficiently high version in relation to the vendor and first product API level.
200      */
201     @VsrTest(requirements = {"VSR-3.2.2-001", "VSR-3.2.2-002", "VSR-3.2.2-003", "VSR-3.2.2-004",
202                      "VSR-3.2.2-005", "VSR-3.2.2-006"})
203     @Test
204     public void
checkVulkanDeqpLevelIsHighEnough()205     checkVulkanDeqpLevelIsHighEnough() throws Exception {
206         final int apiLevel = Util.getVendorApiLevelOrFirstProductApiLevel(getDevice());
207 
208         assumeTrue("Test does not apply for API level lower than R", apiLevel >= Build.RVC);
209         assumeTrue("Test does not apply for SoCs without Vulkan", mVulkanDevices.length > 0);
210 
211         // Map from API level to required dEQP level.
212         final int requiredVulkanDeqpLevel;
213         switch (apiLevel) {
214             case Build.RVC:
215                 requiredVulkanDeqpLevel = DEQP_LEVEL_FOR_R;
216                 break;
217             case Build.SC:
218             case Build.SC_V2:
219                 requiredVulkanDeqpLevel = DEQP_LEVEL_FOR_S;
220                 break;
221             case Build.TM:
222                 requiredVulkanDeqpLevel = DEQP_LEVEL_FOR_T;
223                 break;
224             case Build.UDC:
225                 requiredVulkanDeqpLevel = DEQP_LEVEL_FOR_U;
226                 break;
227             case Build.VENDOR_24Q2:
228                 requiredVulkanDeqpLevel = DEQP_LEVEL_FOR_V;
229                 break;
230             default:
231                 fail("Test should only run for API levels: R, S, Sv2, TM, UDC, 202404...");
232                 return;
233         }
234 
235         // Check that the feature flag is present and its value is at least the required dEQP level.
236         final String output = getDevice().executeShellCommand(String.format(
237                 "pm has-feature android.software.vulkan.deqp.level %d", requiredVulkanDeqpLevel));
238 
239         if (!output.trim().equals("true")) {
240             final String message = String.format(
241                     "Advertised Vulkan dEQP level feature is too low or does not exist.\n"
242                             + "Expected:\nfeature:android.software.vulkan.deqp.level>=%d\n"
243                             + "Actual:\n%s",
244                     requiredVulkanDeqpLevel,
245                     getDevice().executeShellCommand("pm list features | grep deqp"));
246 
247             fail(message);
248         }
249     }
250 
251     /**
252      * For SoCs launching with Android 12 or higher, if the SoC supports Vulkan 1.1 or higher,
253      * VK_EXT_device_memory_report extension must be supported.
254      */
255     @VsrTest(requirements = {"VSR-3.2.1-006"})
256     @Test
checkVulkanDeviceMemoryReportSupport()257     public void checkVulkanDeviceMemoryReportSupport() throws Exception {
258         final int apiLevel = Util.getVendorApiLevelOrFirstProductApiLevel(getDevice());
259 
260         assumeTrue("Test does not apply for API level lower than S", apiLevel >= Build.SC);
261 
262         assumeTrue(
263                 "Test does not apply for SoCs without Vulkan support", mVulkanDevices.length > 0);
264 
265         for (int i = 0; i < mVulkanDevices.length; ++i) {
266             final JSONObject device = mVulkanDevices[i];
267             // Skip extension check if Vulkan device's API version is < 1.1.
268             if (device.getJSONObject("properties").getInt("apiVersion") < VULKAN_1_1_API_VERSION) {
269                 continue;
270             }
271 
272             final boolean isSupported =
273                     hasExtension(device, VK_EXT_DEVICE_MEMORY_REPORT_EXTENSION_NAME,
274                             VK_EXT_DEVICE_MEMORY_REPORT_SPEC_VERSION);
275 
276             if (!isSupported) {
277                 final String message =
278                         String.format("Vulkan devices with API version >= 1.1 must support %s "
279                                         + "but the device at index %d does not. "
280                                         + "Check 'adb shell cmd gpu vkjson'.",
281                                 VK_EXT_DEVICE_MEMORY_REPORT_EXTENSION_NAME, i);
282 
283                 fail(message);
284             }
285         }
286     }
287 
288     /**
289      * All SoCs released with V must support Skia Vulkan with HWUI
290      */
291     @VsrTest(requirements = {"VSR-3.2.1-009"})
292     @Test
checkSkiaVulkanSupport()293     public void checkSkiaVulkanSupport() throws Exception {
294         final int apiLevel = PropertyUtil.getVendorApiLevel(getDevice());
295 
296         assumeTrue("Test does not apply for SoCs launched before V", apiLevel >= Build.VENDOR_24Q2);
297 
298         final String gfxinfo = getDevice().executeShellCommand("dumpsys gfxinfo");
299         assertNotNull(gfxinfo);
300         assertTrue(gfxinfo.length() > 0);
301 
302         int skiaDataIndex = gfxinfo.indexOf(SKIA_PIPELINE);
303         assertTrue("The SoCs adb shell dumpsys gfxinfo must contain a Skia pipeline",
304                 skiaDataIndex >= 0);
305 
306         String tmpinfo = gfxinfo;
307         while (skiaDataIndex != -1) {
308             // Remove string before next Skia pipeline
309             tmpinfo = tmpinfo.substring(skiaDataIndex);
310 
311             // Get the pipeline descriptor line
312             final int newlinecharacter = tmpinfo.indexOf(System.getProperty("line.separator"));
313             String line = tmpinfo.substring(0, newlinecharacter);
314 
315             // Confirm that the pipeline uses Vulkan
316             assertTrue("All Skia pipelines must use Vulkan", line.equals(SKIA_VULKAN_PIPELINE));
317 
318             // Remove line and find next pipeline
319             tmpinfo = tmpinfo.substring(newlinecharacter + 1);
320             skiaDataIndex = tmpinfo.indexOf(SKIA_PIPELINE);
321         }
322     }
323 
324     /**
325      * All SoCs released with V must support ABP 2022
326      */
327     @VsrTest(requirements = {"VSR-3.2.1-008"})
328     @Test
checkAndroidBaselineProfile2022Support()329     public void checkAndroidBaselineProfile2022Support() throws Exception {
330         final int apiLevel = PropertyUtil.getVendorApiLevel(getDevice());
331 
332         assumeTrue("Test does not apply for SoCs launched before V", apiLevel >= Build.VENDOR_24Q2);
333 
334         boolean hasOnlyCpuDevice = true;
335         for (JSONObject device : mVulkanDevices) {
336             if (device.getJSONObject("properties").getInt("deviceType")
337                     != VK_PHYSICAL_DEVICE_TYPE_CPU) {
338                 hasOnlyCpuDevice = false;
339             }
340         }
341 
342         if (hasOnlyCpuDevice) {
343             return;
344         }
345 
346         String supported = mVulkanProfiles.getString("VP_ANDROID_baseline_2022");
347         assertEquals("This SoC must support VP_ANDROID_baseline_2022.", "SUPPORTED", supported);
348     }
349 
350     /**
351      * All SoCs released with V must support VPA15
352      */
353     @VsrTest(requirements = {"VSR-3.2.1-008"})
354     @Test
checkVpAndroid15MinimumsSupport()355     public void checkVpAndroid15MinimumsSupport() throws Exception {
356         final int apiLevel = PropertyUtil.getVendorApiLevel(getDevice());
357 
358         assumeTrue("Test does not apply for SoCs launched before V", apiLevel >= Build.VENDOR_24Q2);
359 
360         boolean hasOnlyCpuDevice = true;
361         for (JSONObject device : mVulkanDevices) {
362             if (device.getJSONObject("properties").getInt("deviceType")
363                     != VK_PHYSICAL_DEVICE_TYPE_CPU) {
364                 hasOnlyCpuDevice = false;
365             }
366         }
367 
368         if (hasOnlyCpuDevice) {
369             return;
370         }
371 
372         String supported = mVulkanProfiles.getString("VP_ANDROID_15_minimums");
373         assertEquals("This SoC must support VP_ANDROID_15_minimums.", "SUPPORTED", supported);
374     }
375 
376     /**
377      * All SoCs released with V must support protectedMemory and VK_EXT_global_priority
378      */
379     @VsrTest(requirements = {"VSR-3.2.1-011"})
380     @Test
checkProtectedMemoryAndGlobalPrioritySupport()381     public void checkProtectedMemoryAndGlobalPrioritySupport() throws Exception {
382         final int apiLevel = PropertyUtil.getVendorApiLevel(getDevice());
383 
384         assumeTrue("Test does not apply for SoCs launched before V", apiLevel >= Build.VENDOR_24Q2);
385 
386         assertTrue(mVulkanDevices.length > 0);
387 
388         for (JSONObject device : mVulkanDevices) {
389             if (device.getJSONObject("properties").getInt("deviceType")
390                     != VK_PHYSICAL_DEVICE_TYPE_CPU) {
391                 continue;
392             }
393 
394             final boolean extGlobalPriority = hasExtension(device,
395                     VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, VK_EXT_GLOBAL_PRIORITY_SPEC_VERSION);
396             final boolean khrGlobalPriority = hasExtension(device,
397                     VK_KHR_GLOBAL_PRIORITY_EXTENSION_NAME, VK_KHR_GLOBAL_PRIORITY_SPEC_VERSION);
398             assertTrue("All non-cpu Vulkan devices must support global_priority",
399                     extGlobalPriority || khrGlobalPriority);
400 
401             final int protectedMemory =
402                     device.getJSONObject("protectedMemoryFeatures").getInt("protectedMemory");
403             assertTrue("All non-cpu Vulkan devices must support protectedMemory",
404                     protectedMemory == 1);
405         }
406     }
407 
hasExtension(JSONObject device, String name, int minVersion)408     private boolean hasExtension(JSONObject device, String name, int minVersion) throws Exception {
409         JSONArray extensions = device.getJSONArray("extensions");
410         for (int i = 0; i < extensions.length(); i++) {
411             JSONObject ext = extensions.getJSONObject(i);
412             if (ext.getString("extensionName").equals(name)
413                     && ext.getInt("specVersion") >= minVersion)
414                 return true;
415         }
416         return false;
417     }
418 }
419