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