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