xref: /aosp_15_r20/external/skia/tests/VkPriorityExtensionTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2019 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkTypes.h"
9 
10 #if defined(SK_GANESH) && defined(SK_VULKAN)
11 
12 #include "include/core/SkTypes.h"
13 #include "include/gpu/vk/VulkanTypes.h"
14 #include "src/base/SkAutoMalloc.h"
15 #include "tests/CtsEnforcement.h"
16 #include "tests/Test.h"
17 #include "tools/gpu/vk/VkTestUtils.h"
18 
19 #include <algorithm>
20 #include <cstdint>
21 #include <cstring>
22 #include <functional>
23 #include <initializer_list>
24 #include <vulkan/vulkan_core.h>
25 
26 struct GrContextOptions;
27 
28 #define ACQUIRE_VK_PROC_NOCHECK(name, instance) \
29     PFN_vk##name grVk##name =                                                              \
30             reinterpret_cast<PFN_vk##name>(getProc("vk" #name, instance, VK_NULL_HANDLE))
31 
32 #define ACQUIRE_VK_PROC(name, instance)                                                    \
33     PFN_vk##name grVk##name =                                                              \
34             reinterpret_cast<PFN_vk##name>(getProc("vk" #name, instance, VK_NULL_HANDLE)); \
35     do {                                                                                   \
36         if (grVk##name == nullptr) {                                                       \
37             if (instance != VK_NULL_HANDLE) {                                              \
38                 destroy_instance(getProc, instance);                                       \
39             }                                                                              \
40             return;                                                                        \
41         }                                                                                  \
42     } while (0)
43 
44 #define ACQUIRE_VK_PROC_LOCAL(name, instance)                                              \
45     PFN_vk##name grVk##name =                                                              \
46             reinterpret_cast<PFN_vk##name>(getProc("vk" #name, instance, VK_NULL_HANDLE)); \
47     do {                                                                                   \
48         if (grVk##name == nullptr) {                                                       \
49             return;                                                                        \
50         }                                                                                  \
51     } while (0)
52 
53 #define GET_PROC_LOCAL(F, inst) PFN_vk ## F F = (PFN_vk ## F) getProc("vk" #F, inst, VK_NULL_HANDLE)
54 
destroy_instance(const skgpu::VulkanGetProc & getProc,VkInstance inst)55 static void destroy_instance(const skgpu::VulkanGetProc& getProc, VkInstance inst) {
56     ACQUIRE_VK_PROC_LOCAL(DestroyInstance, inst);
57     grVkDestroyInstance(inst, nullptr);
58 }
59 
60 // If the extension VK_EXT_GLOBAL_PRIORITY is supported, this test just tries to create a VkDevice
61 // using the various global priorities. The test passes if no errors are reported or the test
62 // doesn't crash.
DEF_GANESH_TEST_FOR_VULKAN_CONTEXT(VulkanPriorityExtension,reporter,context_info,CtsEnforcement::kApiLevel_T)63 DEF_GANESH_TEST_FOR_VULKAN_CONTEXT(VulkanPriorityExtension,
64                                    reporter,
65                                    context_info,
66                                    CtsEnforcement::kApiLevel_T) {
67     PFN_vkGetInstanceProcAddr instProc;
68     if (!sk_gpu_test::LoadVkLibraryAndGetProcAddrFuncs(&instProc)) {
69         return;
70     }
71     // This isn't the most effecient but we just use the instProc to get all ptrs.
72     auto getProc = [instProc](const char* proc_name, VkInstance instance, VkDevice) {
73         return instProc(instance, proc_name);
74     };
75 
76     VkResult err;
77 
78     ACQUIRE_VK_PROC_NOCHECK(EnumerateInstanceVersion, VK_NULL_HANDLE);
79     uint32_t instanceVersion = 0;
80     if (!grVkEnumerateInstanceVersion) {
81         instanceVersion = VK_MAKE_VERSION(1, 0, 0);
82     } else {
83         err = grVkEnumerateInstanceVersion(&instanceVersion);
84         if (err) {
85             ERRORF(reporter, "failed ot enumerate instance version. Err: %d", err);
86             return;
87         }
88     }
89     SkASSERT(instanceVersion >= VK_MAKE_VERSION(1, 0, 0));
90     uint32_t apiVersion = VK_MAKE_VERSION(1, 0, 0);
91     if (instanceVersion >= VK_MAKE_VERSION(1, 1, 0)) {
92         // If the instance version is 1.0 we must have the apiVersion also be 1.0. However, if the
93         // instance version is 1.1 or higher, we can set the apiVersion to be whatever the highest
94         // api we may use in skia (technically it can be arbitrary). So for now we set it to 1.1
95         // since that is the highest vulkan version.
96         apiVersion = VK_MAKE_VERSION(1, 1, 0);
97     }
98 
99     instanceVersion = std::min(instanceVersion, apiVersion);
100 
101     VkPhysicalDevice physDev;
102     VkDevice device;
103     VkInstance inst;
104 
105     const VkApplicationInfo app_info = {
106         VK_STRUCTURE_TYPE_APPLICATION_INFO, // sType
107         nullptr,                            // pNext
108         "vktest",                           // pApplicationName
109         0,                                  // applicationVersion
110         "vktest",                           // pEngineName
111         0,                                  // engineVersion
112         apiVersion,                         // apiVersion
113     };
114 
115     const VkInstanceCreateInfo instance_create = {
116         VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,    // sType
117         nullptr,                                   // pNext
118         0,                                         // flags
119         &app_info,                                 // pApplicationInfo
120         0,                                         // enabledLayerNameCount
121         nullptr,                                   // ppEnabledLayerNames
122         0,                                         // enabledExtensionNameCount
123         nullptr,                                   // ppEnabledExtensionNames
124     };
125 
126     ACQUIRE_VK_PROC(CreateInstance, VK_NULL_HANDLE);
127     err = grVkCreateInstance(&instance_create, nullptr, &inst);
128     if (err < 0) {
129         ERRORF(reporter, "Failed to create VkInstance");
130         return;
131     }
132 
133     ACQUIRE_VK_PROC(EnumeratePhysicalDevices, inst);
134     ACQUIRE_VK_PROC(GetPhysicalDeviceProperties, inst);
135     ACQUIRE_VK_PROC(GetPhysicalDeviceQueueFamilyProperties, inst);
136     ACQUIRE_VK_PROC(GetPhysicalDeviceFeatures, inst);
137     ACQUIRE_VK_PROC(CreateDevice, inst);
138     ACQUIRE_VK_PROC(GetDeviceQueue, inst);
139     ACQUIRE_VK_PROC(DeviceWaitIdle, inst);
140     ACQUIRE_VK_PROC(DestroyDevice, inst);
141 
142     uint32_t gpuCount;
143     err = grVkEnumeratePhysicalDevices(inst, &gpuCount, nullptr);
144     if (err) {
145         ERRORF(reporter, "vkEnumeratePhysicalDevices failed: %d", err);
146         destroy_instance(getProc, inst);
147         return;
148     }
149     if (!gpuCount) {
150         // Don't throw and error here, because this behavior is allowed by Android CTS. A device
151         // count of 0 effectively means Vulkan is not supported.
152         destroy_instance(getProc, inst);
153         return;
154     }
155     // Just returning the first physical device instead of getting the whole array.
156     // TODO: find best match for our needs
157     gpuCount = 1;
158     err = grVkEnumeratePhysicalDevices(inst, &gpuCount, &physDev);
159     // VK_INCOMPLETE is returned when the count we provide is less than the total device count.
160     if (err && VK_INCOMPLETE != err) {
161         ERRORF(reporter, "vkEnumeratePhysicalDevices failed: %d", err);
162         destroy_instance(getProc, inst);
163         return;
164     }
165 
166     // query to get the initial queue props size
167     uint32_t queueCount;
168     grVkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, nullptr);
169     if (!queueCount) {
170         ERRORF(reporter, "vkGetPhysicalDeviceQueueFamilyProperties returned no queues.");
171         destroy_instance(getProc, inst);
172         return;
173     }
174 
175     SkAutoMalloc queuePropsAlloc(queueCount * sizeof(VkQueueFamilyProperties));
176     // now get the actual queue props
177     VkQueueFamilyProperties* queueProps = (VkQueueFamilyProperties*)queuePropsAlloc.get();
178 
179     grVkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, queueProps);
180 
181     // iterate to find the graphics queue
182     uint32_t graphicsQueueIndex = queueCount;
183     for (uint32_t i = 0; i < queueCount; i++) {
184         if (queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
185             graphicsQueueIndex = i;
186             break;
187         }
188     }
189     if (graphicsQueueIndex == queueCount) {
190         ERRORF(reporter, "Could not find any supported graphics queues.");
191         destroy_instance(getProc, inst);
192         return;
193     }
194 
195     GET_PROC_LOCAL(EnumerateDeviceExtensionProperties, inst);
196     GET_PROC_LOCAL(EnumerateDeviceLayerProperties, inst);
197 
198     if (!EnumerateDeviceExtensionProperties ||
199         !EnumerateDeviceLayerProperties) {
200         destroy_instance(getProc, inst);
201         return;
202     }
203 
204     // device extensions
205     // via Vulkan implementation and implicitly enabled layers
206     uint32_t extensionCount = 0;
207     err = EnumerateDeviceExtensionProperties(physDev, nullptr, &extensionCount, nullptr);
208     if (VK_SUCCESS != err) {
209         ERRORF(reporter, "Could not  enumerate device extension properties.");
210         destroy_instance(getProc, inst);
211         return;
212     }
213     VkExtensionProperties* extensions = new VkExtensionProperties[extensionCount];
214     err = EnumerateDeviceExtensionProperties(physDev, nullptr, &extensionCount, extensions);
215     if (VK_SUCCESS != err) {
216         delete[] extensions;
217         ERRORF(reporter, "Could not  enumerate device extension properties.");
218         destroy_instance(getProc, inst);
219         return;
220     }
221     bool hasPriorityExt = false;
222     for (uint32_t i = 0; i < extensionCount; ++i) {
223         if (!strcmp(extensions[i].extensionName, VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME)) {
224             hasPriorityExt = true;
225         }
226     }
227     delete[] extensions;
228 
229     if (!hasPriorityExt) {
230         destroy_instance(getProc, inst);
231         return;
232     }
233 
234     const char* priorityExt = VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME;
235 
236     VkPhysicalDeviceFeatures deviceFeatures;
237     grVkGetPhysicalDeviceFeatures(physDev, &deviceFeatures);
238 
239     // this looks like it would slow things down,
240     // and we can't depend on it on all platforms
241     deviceFeatures.robustBufferAccess = VK_FALSE;
242 
243     float queuePriorities[1] = { 0.0 };
244 
245     VkDeviceQueueGlobalPriorityCreateInfoEXT queuePriorityCreateInfo;
246     queuePriorityCreateInfo.sType =
247             VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT;
248     queuePriorityCreateInfo.pNext = nullptr;
249 
250     VkDeviceQueueCreateInfo queueInfo = {
251         VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType
252         &queuePriorityCreateInfo,                   // pNext
253         0,                                          // VkDeviceQueueCreateFlags
254         graphicsQueueIndex,                         // queueFamilyIndex
255         1,                                          // queueCount
256         queuePriorities,                            // pQueuePriorities
257     };
258 
259     for (VkQueueGlobalPriorityEXT globalPriority : { VK_QUEUE_GLOBAL_PRIORITY_LOW_EXT,
260                                                      VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_EXT,
261                                                      VK_QUEUE_GLOBAL_PRIORITY_HIGH_EXT,
262                                                      VK_QUEUE_GLOBAL_PRIORITY_REALTIME_EXT }) {
263         queuePriorityCreateInfo.globalPriority = globalPriority;
264 
265         const VkDeviceCreateInfo deviceInfo = {
266             VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,        // sType
267             nullptr,                                     // pNext
268             0,                                           // VkDeviceCreateFlags
269             1,                                           // queueCreateInfoCount
270             &queueInfo,                                  // pQueueCreateInfos
271             0,                                           // layerCount
272             nullptr,                                     // ppEnabledLayerNames
273             1,                                           // extensionCount
274             &priorityExt,                                // ppEnabledExtensionNames
275             &deviceFeatures                              // ppEnabledFeatures
276         };
277 
278         err = grVkCreateDevice(physDev, &deviceInfo, nullptr, &device);
279 
280         if (err != VK_SUCCESS && err != VK_ERROR_NOT_PERMITTED_EXT) {
281             ERRORF(reporter, "CreateDevice failed: %d, priority %d", err, globalPriority);
282             destroy_instance(getProc, inst);
283             continue;
284         }
285         if (err != VK_ERROR_NOT_PERMITTED_EXT) {
286             grVkDestroyDevice(device, nullptr);
287         }
288     }
289     destroy_instance(getProc, inst);
290 }
291 
292 #endif
293