| /* |
| * 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 defined(SK_GANESH) && defined(SK_VULKAN) |
| |
| #include "include/core/SkTypes.h" |
| #include "include/gpu/vk/VulkanTypes.h" |
| #include "src/base/SkAutoMalloc.h" |
| #include "tests/CtsEnforcement.h" |
| #include "tests/Test.h" |
| #include "tools/gpu/vk/VkTestUtils.h" |
| |
| #include <algorithm> |
| #include <cstdint> |
| #include <functional> |
| #include <initializer_list> |
| #include <string> |
| #include <vulkan/vulkan_core.h> |
| |
| struct GrContextOptions; |
| |
| #define ACQUIRE_VK_PROC_NOCHECK(name, instance) \ |
| PFN_vk##name grVk##name = \ |
| reinterpret_cast<PFN_vk##name>(getProc("vk" #name, instance, VK_NULL_HANDLE)) |
| |
| #define ACQUIRE_VK_PROC(name, instance) \ |
| PFN_vk##name grVk##name = \ |
| reinterpret_cast<PFN_vk##name>(getProc("vk" #name, instance, VK_NULL_HANDLE)); \ |
| do { \ |
| if (grVk##name == nullptr) { \ |
| if (instance != VK_NULL_HANDLE) { \ |
| destroy_instance(getProc, instance); \ |
| } \ |
| return; \ |
| } \ |
| } while (0) |
| |
| #define ACQUIRE_VK_PROC_LOCAL(name, instance) \ |
| PFN_vk##name grVk##name = \ |
| reinterpret_cast<PFN_vk##name>(getProc("vk" #name, instance, VK_NULL_HANDLE)); \ |
| do { \ |
| if (grVk##name == nullptr) { \ |
| return; \ |
| } \ |
| } while (0) |
| |
| #define GET_PROC_LOCAL(F, inst) PFN_vk ## F F = (PFN_vk ## F) getProc("vk" #F, inst, VK_NULL_HANDLE) |
| |
| static void destroy_instance(skgpu::VulkanGetProc getProc, VkInstance inst) { |
| ACQUIRE_VK_PROC_LOCAL(DestroyInstance, inst); |
| 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_GANESH_TEST_FOR_VULKAN_CONTEXT(VulkanPriorityExtension, |
| reporter, |
| context_info, |
| CtsEnforcement::kApiLevel_T) { |
| PFN_vkGetInstanceProcAddr instProc; |
| if (!sk_gpu_test::LoadVkLibraryAndGetProcAddrFuncs(&instProc)) { |
| return; |
| } |
| // This isn't the most effecient but we just use the instProc to get all ptrs. |
| auto getProc = [instProc](const char* proc_name, VkInstance instance, VkDevice) { |
| return instProc(instance, proc_name); |
| }; |
| |
| VkResult err; |
| |
| ACQUIRE_VK_PROC_NOCHECK(EnumerateInstanceVersion, 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); |
| err = grVkCreateInstance(&instance_create, nullptr, &inst); |
| if (err < 0) { |
| ERRORF(reporter, "Failed to create VkInstance"); |
| return; |
| } |
| |
| ACQUIRE_VK_PROC(EnumeratePhysicalDevices, inst); |
| ACQUIRE_VK_PROC(GetPhysicalDeviceProperties, inst); |
| ACQUIRE_VK_PROC(GetPhysicalDeviceQueueFamilyProperties, inst); |
| ACQUIRE_VK_PROC(GetPhysicalDeviceFeatures, inst); |
| ACQUIRE_VK_PROC(CreateDevice, inst); |
| ACQUIRE_VK_PROC(GetDeviceQueue, inst); |
| ACQUIRE_VK_PROC(DeviceWaitIdle, inst); |
| ACQUIRE_VK_PROC(DestroyDevice, inst); |
| |
| uint32_t gpuCount; |
| err = grVkEnumeratePhysicalDevices(inst, &gpuCount, nullptr); |
| if (err) { |
| ERRORF(reporter, "vkEnumeratePhysicalDevices failed: %d", err); |
| destroy_instance(getProc, inst); |
| return; |
| } |
| if (!gpuCount) { |
| // Don't throw and error here, because this behavior is allowed by Android CTS. A device |
| // count of 0 effectively means Vulkan is not supported. |
| 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); |
| GET_PROC_LOCAL(EnumerateDeviceLayerProperties, inst); |
| |
| 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 |