|  | /* | 
|  | * Copyright 2019 Google Inc. | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include "include/core/SkTypes.h" | 
|  |  | 
|  | #if SK_SUPPORT_GPU && defined(SK_VULKAN) | 
|  |  | 
|  | #include "include/gpu/vk/GrVkTypes.h" | 
|  | #include "src/core/SkAutoMalloc.h" | 
|  | #include "tests/Test.h" | 
|  | #include "tools/gpu/vk/VkTestUtils.h" | 
|  |  | 
|  | #define ACQUIRE_VK_PROC_NOCHECK(name, instance, device) \ | 
|  | PFN_vk##name grVk##name = reinterpret_cast<PFN_vk##name>(getProc("vk" #name, instance, device)) | 
|  |  | 
|  | #define ACQUIRE_VK_PROC(name, instance, device)                                    \ | 
|  | PFN_vk##name grVk##name =                                                      \ | 
|  | reinterpret_cast<PFN_vk##name>(getProc("vk" #name, instance, device)); \ | 
|  | do {                                                                           \ | 
|  | if (grVk##name == nullptr) {                                               \ | 
|  | if (device != VK_NULL_HANDLE) {                                        \ | 
|  | destroy_instance(getProc, inst);                                   \ | 
|  | }                                                                      \ | 
|  | return;                                                                \ | 
|  | }                                                                          \ | 
|  | } while (0) | 
|  |  | 
|  | #define ACQUIRE_VK_PROC_LOCAL(name, instance, device)                              \ | 
|  | PFN_vk##name grVk##name =                                                      \ | 
|  | reinterpret_cast<PFN_vk##name>(getProc("vk" #name, instance, device)); \ | 
|  | do {                                                                           \ | 
|  | if (grVk##name == nullptr) {                                               \ | 
|  | return;                                                                \ | 
|  | }                                                                          \ | 
|  | } while (0) | 
|  |  | 
|  | #define GET_PROC_LOCAL(F, inst, device) PFN_vk ## F F = (PFN_vk ## F) getProc("vk" #F, inst, device) | 
|  |  | 
|  | static void destroy_instance(GrVkGetProc getProc, VkInstance inst) { | 
|  | ACQUIRE_VK_PROC_LOCAL(DestroyInstance, inst, VK_NULL_HANDLE); | 
|  | grVkDestroyInstance(inst, nullptr); | 
|  | } | 
|  |  | 
|  | // If the extension VK_EXT_GLOBAL_PRIORITY is supported, this test just tries to create a VkDevice | 
|  | // using the various global priorities. The test passes if no errors are reported or the test | 
|  | // doesn't crash. | 
|  | DEF_GPUTEST(VulkanPriorityExtension, reporter, options) { | 
|  | PFN_vkGetInstanceProcAddr instProc; | 
|  | PFN_vkGetDeviceProcAddr devProc; | 
|  | if (!sk_gpu_test::LoadVkLibraryAndGetProcAddrFuncs(&instProc, &devProc)) { | 
|  | return; | 
|  | } | 
|  | auto getProc = [instProc, devProc](const char* proc_name, | 
|  | VkInstance instance, VkDevice device) { | 
|  | if (device != VK_NULL_HANDLE) { | 
|  | return devProc(device, proc_name); | 
|  | } | 
|  | return instProc(instance, proc_name); | 
|  | }; | 
|  |  | 
|  | VkResult err; | 
|  |  | 
|  | ACQUIRE_VK_PROC_NOCHECK(EnumerateInstanceVersion, VK_NULL_HANDLE, VK_NULL_HANDLE); | 
|  | uint32_t instanceVersion = 0; | 
|  | if (!grVkEnumerateInstanceVersion) { | 
|  | instanceVersion = VK_MAKE_VERSION(1, 0, 0); | 
|  | } else { | 
|  | err = grVkEnumerateInstanceVersion(&instanceVersion); | 
|  | if (err) { | 
|  | ERRORF(reporter, "failed ot enumerate instance version. Err: %d", err); | 
|  | return; | 
|  | } | 
|  | } | 
|  | SkASSERT(instanceVersion >= VK_MAKE_VERSION(1, 0, 0)); | 
|  | uint32_t apiVersion = VK_MAKE_VERSION(1, 0, 0); | 
|  | if (instanceVersion >= VK_MAKE_VERSION(1, 1, 0)) { | 
|  | // If the instance version is 1.0 we must have the apiVersion also be 1.0. However, if the | 
|  | // instance version is 1.1 or higher, we can set the apiVersion to be whatever the highest | 
|  | // api we may use in skia (technically it can be arbitrary). So for now we set it to 1.1 | 
|  | // since that is the highest vulkan version. | 
|  | apiVersion = VK_MAKE_VERSION(1, 1, 0); | 
|  | } | 
|  |  | 
|  | instanceVersion = std::min(instanceVersion, apiVersion); | 
|  |  | 
|  | VkPhysicalDevice physDev; | 
|  | VkDevice device; | 
|  | VkInstance inst; | 
|  |  | 
|  | const VkApplicationInfo app_info = { | 
|  | VK_STRUCTURE_TYPE_APPLICATION_INFO, // sType | 
|  | nullptr,                            // pNext | 
|  | "vktest",                           // pApplicationName | 
|  | 0,                                  // applicationVersion | 
|  | "vktest",                           // pEngineName | 
|  | 0,                                  // engineVersion | 
|  | apiVersion,                         // apiVersion | 
|  | }; | 
|  |  | 
|  | const VkInstanceCreateInfo instance_create = { | 
|  | VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,    // sType | 
|  | nullptr,                                   // pNext | 
|  | 0,                                         // flags | 
|  | &app_info,                                 // pApplicationInfo | 
|  | 0,                                         // enabledLayerNameCount | 
|  | nullptr,                                   // ppEnabledLayerNames | 
|  | 0,                                         // enabledExtensionNameCount | 
|  | nullptr,                                   // ppEnabledExtensionNames | 
|  | }; | 
|  |  | 
|  | ACQUIRE_VK_PROC(CreateInstance, VK_NULL_HANDLE, VK_NULL_HANDLE); | 
|  | err = grVkCreateInstance(&instance_create, nullptr, &inst); | 
|  | if (err < 0) { | 
|  | ERRORF(reporter, "Failed to create VkInstance"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | ACQUIRE_VK_PROC(EnumeratePhysicalDevices, inst, VK_NULL_HANDLE); | 
|  | ACQUIRE_VK_PROC(GetPhysicalDeviceProperties, inst, VK_NULL_HANDLE); | 
|  | ACQUIRE_VK_PROC(GetPhysicalDeviceQueueFamilyProperties, inst, VK_NULL_HANDLE); | 
|  | ACQUIRE_VK_PROC(GetPhysicalDeviceFeatures, inst, VK_NULL_HANDLE); | 
|  | ACQUIRE_VK_PROC(CreateDevice, inst, VK_NULL_HANDLE); | 
|  | ACQUIRE_VK_PROC(GetDeviceQueue, inst, VK_NULL_HANDLE); | 
|  | ACQUIRE_VK_PROC(DeviceWaitIdle, inst, VK_NULL_HANDLE); | 
|  | ACQUIRE_VK_PROC(DestroyDevice, inst, VK_NULL_HANDLE); | 
|  |  | 
|  | uint32_t gpuCount; | 
|  | err = grVkEnumeratePhysicalDevices(inst, &gpuCount, nullptr); | 
|  | if (err) { | 
|  | ERRORF(reporter, "vkEnumeratePhysicalDevices failed: %d", err); | 
|  | destroy_instance(getProc, inst); | 
|  | return; | 
|  | } | 
|  | if (!gpuCount) { | 
|  | ERRORF(reporter, "vkEnumeratePhysicalDevices returned no supported devices."); | 
|  | destroy_instance(getProc, inst); | 
|  | return; | 
|  | } | 
|  | // Just returning the first physical device instead of getting the whole array. | 
|  | // TODO: find best match for our needs | 
|  | gpuCount = 1; | 
|  | err = grVkEnumeratePhysicalDevices(inst, &gpuCount, &physDev); | 
|  | // VK_INCOMPLETE is returned when the count we provide is less than the total device count. | 
|  | if (err && VK_INCOMPLETE != err) { | 
|  | ERRORF(reporter, "vkEnumeratePhysicalDevices failed: %d", err); | 
|  | destroy_instance(getProc, inst); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // query to get the initial queue props size | 
|  | uint32_t queueCount; | 
|  | grVkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, nullptr); | 
|  | if (!queueCount) { | 
|  | ERRORF(reporter, "vkGetPhysicalDeviceQueueFamilyProperties returned no queues."); | 
|  | destroy_instance(getProc, inst); | 
|  | return; | 
|  | } | 
|  |  | 
|  | SkAutoMalloc queuePropsAlloc(queueCount * sizeof(VkQueueFamilyProperties)); | 
|  | // now get the actual queue props | 
|  | VkQueueFamilyProperties* queueProps = (VkQueueFamilyProperties*)queuePropsAlloc.get(); | 
|  |  | 
|  | grVkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, queueProps); | 
|  |  | 
|  | // iterate to find the graphics queue | 
|  | uint32_t graphicsQueueIndex = queueCount; | 
|  | for (uint32_t i = 0; i < queueCount; i++) { | 
|  | if (queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { | 
|  | graphicsQueueIndex = i; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (graphicsQueueIndex == queueCount) { | 
|  | ERRORF(reporter, "Could not find any supported graphics queues."); | 
|  | destroy_instance(getProc, inst); | 
|  | return; | 
|  | } | 
|  |  | 
|  | GET_PROC_LOCAL(EnumerateDeviceExtensionProperties, inst, VK_NULL_HANDLE); | 
|  | GET_PROC_LOCAL(EnumerateDeviceLayerProperties, inst, VK_NULL_HANDLE); | 
|  |  | 
|  | if (!EnumerateDeviceExtensionProperties || | 
|  | !EnumerateDeviceLayerProperties) { | 
|  | destroy_instance(getProc, inst); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // device extensions | 
|  | // via Vulkan implementation and implicitly enabled layers | 
|  | uint32_t extensionCount = 0; | 
|  | err = EnumerateDeviceExtensionProperties(physDev, nullptr, &extensionCount, nullptr); | 
|  | if (VK_SUCCESS != err) { | 
|  | ERRORF(reporter, "Could not  enumerate device extension properties."); | 
|  | destroy_instance(getProc, inst); | 
|  | return; | 
|  | } | 
|  | VkExtensionProperties* extensions = new VkExtensionProperties[extensionCount]; | 
|  | err = EnumerateDeviceExtensionProperties(physDev, nullptr, &extensionCount, extensions); | 
|  | if (VK_SUCCESS != err) { | 
|  | delete[] extensions; | 
|  | ERRORF(reporter, "Could not  enumerate device extension properties."); | 
|  | destroy_instance(getProc, inst); | 
|  | return; | 
|  | } | 
|  | bool hasPriorityExt = false; | 
|  | for (uint32_t i = 0; i < extensionCount; ++i) { | 
|  | if (!strcmp(extensions[i].extensionName, VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME)) { | 
|  | hasPriorityExt = true; | 
|  | } | 
|  | } | 
|  | delete[] extensions; | 
|  |  | 
|  | if (!hasPriorityExt) { | 
|  | destroy_instance(getProc, inst); | 
|  | return; | 
|  | } | 
|  |  | 
|  | const char* priorityExt = VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME; | 
|  |  | 
|  | VkPhysicalDeviceFeatures deviceFeatures; | 
|  | grVkGetPhysicalDeviceFeatures(physDev, &deviceFeatures); | 
|  |  | 
|  | // this looks like it would slow things down, | 
|  | // and we can't depend on it on all platforms | 
|  | deviceFeatures.robustBufferAccess = VK_FALSE; | 
|  |  | 
|  | float queuePriorities[1] = { 0.0 }; | 
|  |  | 
|  | VkDeviceQueueGlobalPriorityCreateInfoEXT queuePriorityCreateInfo; | 
|  | queuePriorityCreateInfo.sType = | 
|  | VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT; | 
|  | queuePriorityCreateInfo.pNext = nullptr; | 
|  |  | 
|  | VkDeviceQueueCreateInfo queueInfo = { | 
|  | VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType | 
|  | &queuePriorityCreateInfo,                   // pNext | 
|  | 0,                                          // VkDeviceQueueCreateFlags | 
|  | graphicsQueueIndex,                         // queueFamilyIndex | 
|  | 1,                                          // queueCount | 
|  | queuePriorities,                            // pQueuePriorities | 
|  | }; | 
|  |  | 
|  | for (VkQueueGlobalPriorityEXT globalPriority : { VK_QUEUE_GLOBAL_PRIORITY_LOW_EXT, | 
|  | VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_EXT, | 
|  | VK_QUEUE_GLOBAL_PRIORITY_HIGH_EXT, | 
|  | VK_QUEUE_GLOBAL_PRIORITY_REALTIME_EXT }) { | 
|  | queuePriorityCreateInfo.globalPriority = globalPriority; | 
|  |  | 
|  | const VkDeviceCreateInfo deviceInfo = { | 
|  | VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,        // sType | 
|  | nullptr,                                     // pNext | 
|  | 0,                                           // VkDeviceCreateFlags | 
|  | 1,                                           // queueCreateInfoCount | 
|  | &queueInfo,                                  // pQueueCreateInfos | 
|  | 0,                                           // layerCount | 
|  | nullptr,                                     // ppEnabledLayerNames | 
|  | 1,                                           // extensionCount | 
|  | &priorityExt,                                // ppEnabledExtensionNames | 
|  | &deviceFeatures                              // ppEnabledFeatures | 
|  | }; | 
|  |  | 
|  | err = grVkCreateDevice(physDev, &deviceInfo, nullptr, &device); | 
|  |  | 
|  | if (err != VK_SUCCESS && err != VK_ERROR_NOT_PERMITTED_EXT) { | 
|  | ERRORF(reporter, "CreateDevice failed: %d, priority %d", err, globalPriority); | 
|  | destroy_instance(getProc, inst); | 
|  | continue; | 
|  | } | 
|  | if (err != VK_ERROR_NOT_PERMITTED_EXT) { | 
|  | grVkDestroyDevice(device, nullptr); | 
|  | } | 
|  | } | 
|  | destroy_instance(getProc, inst); | 
|  | } | 
|  |  | 
|  | #endif |