Add unit test to check if we can create a VkDevice using global priority extension.
Bug: b/126595186
Change-Id: I0a23ca2a7cc47bd02bb535566aa37627899249ee
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/197201
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Stan Iliev <stani@google.com>
Cherry-pick: 39847ccfd0351149070a1ba00129d1430a1c193f
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/197843
Reviewed-by: Derek Sollenberger <djsollen@google.com>
diff --git a/gn/tests.gni b/gn/tests.gni
index 412da6d..281c1dd 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -285,6 +285,7 @@
"$_tests/VkDrawableTest.cpp",
"$_tests/VkHardwareBufferTest.cpp",
"$_tests/VkMakeCopyPipelineTest.cpp",
+ "$_tests/VkPriorityExtensionTest.cpp",
"$_tests/VkWrapTests.cpp",
"$_tests/VptrTest.cpp",
"$_tests/WindowRectanglesTest.cpp",
diff --git a/tests/VkPriorityExtensionTest.cpp b/tests/VkPriorityExtensionTest.cpp
new file mode 100644
index 0000000..622620a
--- /dev/null
+++ b/tests/VkPriorityExtensionTest.cpp
@@ -0,0 +1,280 @@
+/*
+ * 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 "SkTypes.h"
+
+#if SK_SUPPORT_GPU && defined(SK_VULKAN)
+
+#include "SkAutoMalloc.h"
+#include "Test.h"
+#include "vk/GrVkTypes.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 = SkTMin(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