Make sure we check the version number of Vulkan Extensions.

Bug: skia:
Change-Id: Iff02911491cc61e39f94370c644b6666e5f9118f
Reviewed-on: https://skia-review.googlesource.com/137881
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/include/gpu/vk/GrVkBackendContext.h b/include/gpu/vk/GrVkBackendContext.h
index eb4f43d..67ed5f0 100644
--- a/include/gpu/vk/GrVkBackendContext.h
+++ b/include/gpu/vk/GrVkBackendContext.h
@@ -8,7 +8,6 @@
 #ifndef GrVkBackendContext_DEFINED
 #define GrVkBackendContext_DEFINED
 
-#include <functional>
 #include "GrVkTypes.h"
 #include "SkRefCnt.h"
 #include "vk/GrVkMemoryAllocator.h"
@@ -31,12 +30,6 @@
     kSampleRateShading_GrVkFeatureFlag = 0x0004,
 };
 
-using GrVkGetProc = std::function<PFN_vkVoidFunction(
-        const char*, // function name
-        VkInstance,  // instance or VK_NULL_HANDLE
-        VkDevice     // device or VK_NULL_HANDLE
-        )>;
-
 // The BackendContext contains all of the base Vulkan objects needed by the GrVkGpu. The assumption
 // is that the client will set these up and pass them to the GrVkGpu constructor. The VkDevice
 // created must support at least one graphics queue, which is passed in as well.
diff --git a/include/gpu/vk/GrVkExtensions.h b/include/gpu/vk/GrVkExtensions.h
index 2a133ab..08bed50 100644
--- a/include/gpu/vk/GrVkExtensions.h
+++ b/include/gpu/vk/GrVkExtensions.h
@@ -8,24 +8,45 @@
 #ifndef GrVkExtensions_DEFINED
 #define GrVkExtensions_DEFINED
 
-#include "../private/SkTArray.h"
+#include "../../private/SkTArray.h"
 #include "SkString.h"
+#include "vk/GrVkTypes.h"
 
 /**
  * Helper class that eats in an array of extensions strings for instance and device and allows for
  * quicker querying if an extension is present.
  */
-class GrVkExtensions {
+class SK_API GrVkExtensions {
 public:
     GrVkExtensions() {}
 
-    void init(uint32_t instanceExtensionCount, const char* const* instanceExtensions,
+    void init(GrVkGetProc, VkInstance, VkPhysicalDevice,
+              uint32_t instanceExtensionCount, const char* const* instanceExtensions,
               uint32_t deviceExtensionCount, const char* const* deviceExtensions);
 
-    bool hasExtension(const char[]) const;
+    bool hasExtension(const char[], uint32_t minVersion) const;
+
+    struct Info {
+        Info() {}
+        Info(const char* name) : fName(name), fSpecVersion(0) {}
+
+        SkString fName;
+        uint32_t fSpecVersion;
+
+        struct Less {
+            bool operator() (const Info& a, const SkString& b) {
+                return strcmp(a.fName.c_str(), b.c_str()) < 0;
+            }
+            bool operator() (const SkString& a, const GrVkExtensions::Info& b) {
+                return strcmp(a.c_str(), b.fName.c_str()) < 0;
+            }
+        };
+    };
 
 private:
-    SkTArray<SkString>  fExtensionStrings;
+    void getSpecVersions(GrVkGetProc getProc, VkInstance, VkPhysicalDevice);
+
+    SkTArray<Info>  fExtensions;
 };
 
 #endif
diff --git a/include/gpu/vk/GrVkTypes.h b/include/gpu/vk/GrVkTypes.h
index d734c2b..7f9258d 100644
--- a/include/gpu/vk/GrVkTypes.h
+++ b/include/gpu/vk/GrVkTypes.h
@@ -9,24 +9,10 @@
 #ifndef GrVkTypes_DEFINED
 #define GrVkTypes_DEFINED
 
+#include <functional>
 #include "GrTypes.h"
 #include "GrVkDefines.h"
 
-/**
- * KHR_debug
- */
-/*typedef void (GR_GL_FUNCTION_TYPE* GrVkDEBUGPROC)(GrVkenum source,
-                                                  GrVkenum type,
-                                                  GrVkuint id,
-                                                  GrVkenum severity,
-                                                  GrVksizei length,
-                                                  const GrVkchar* message,
-                                                  const void* userParam);*/
-
-
-
-///////////////////////////////////////////////////////////////////////////////
-
 typedef intptr_t GrVkBackendMemory;
 
 /**
@@ -115,4 +101,11 @@
     }
 };
 
+using GrVkGetProc = std::function<PFN_vkVoidFunction(
+        const char*, // function name
+        VkInstance,  // instance or VK_NULL_HANDLE
+        VkDevice     // device or VK_NULL_HANDLE
+        )>;
+
+
 #endif
diff --git a/src/gpu/vk/GrVkExtensions.cpp b/src/gpu/vk/GrVkExtensions.cpp
index 6cc20e6..4d7fc11 100644
--- a/src/gpu/vk/GrVkExtensions.cpp
+++ b/src/gpu/vk/GrVkExtensions.cpp
@@ -13,50 +13,119 @@
 #include "SkTSearch.h"
 #include "SkTSort.h"
 
-namespace { // This cannot be static because it is used as a template parameter.
-inline bool extension_compare(const SkString& a, const SkString& b) {
-    return strcmp(a.c_str(), b.c_str()) < 0;
-}
-}
-
-// finds the index of ext in strings or a negative result if ext is not found.
-static int find_string(const SkTArray<SkString>& strings, const char ext[]) {
-    if (strings.empty()) {
+// finds the index of ext in infos or a negative result if ext is not found.
+static int find_info(const SkTArray<GrVkExtensions::Info>& infos, const char ext[]) {
+    if (infos.empty()) {
         return -1;
     }
     SkString extensionStr(ext);
-    int idx = SkTSearch<SkString, extension_compare>(&strings.front(),
-                                                     strings.count(),
-                                                     extensionStr,
-                                                     sizeof(SkString));
+    GrVkExtensions::Info::Less less;
+    int idx = SkTSearch<GrVkExtensions::Info, SkString, GrVkExtensions::Info::Less>(
+            &infos.front(), infos.count(), extensionStr, sizeof(GrVkExtensions::Info),
+            less);
     return idx;
 }
 
-void GrVkExtensions::init(uint32_t instanceExtensionCount,
+namespace { // This cannot be static because it is used as a template parameter.
+inline bool extension_compare(const GrVkExtensions::Info& a, const GrVkExtensions::Info& b) {
+    return strcmp(a.fName.c_str(), b.fName.c_str()) < 0;
+}
+}
+
+void GrVkExtensions::init(GrVkGetProc getProc,
+                          VkInstance instance,
+                          VkPhysicalDevice physDev,
+                          uint32_t instanceExtensionCount,
                           const char* const* instanceExtensions,
                           uint32_t deviceExtensionCount,
                           const char* const* deviceExtensions) {
-    SkTLessFunctionToFunctorAdaptor<SkString, extension_compare> cmp;
+    SkTLessFunctionToFunctorAdaptor<GrVkExtensions::Info, extension_compare> cmp;
 
     for (uint32_t i = 0; i < instanceExtensionCount; ++i) {
         const char* extension = instanceExtensions[i];
         // if not already in the list, add it
-        if (find_string(fExtensionStrings, extension) < 0) {
-            fExtensionStrings.push_back() = extension;
-            SkTQSort(&fExtensionStrings.front(), &fExtensionStrings.back(), cmp);
+        if (find_info(fExtensions, extension) < 0) {
+            fExtensions.push_back() = Info(extension);
+            SkTQSort(&fExtensions.front(), &fExtensions.back(), cmp);
         }
     }
     for (uint32_t i = 0; i < deviceExtensionCount; ++i) {
         const char* extension = deviceExtensions[i];
         // if not already in the list, add it
-        if (find_string(fExtensionStrings, extension) < 0) {
-            fExtensionStrings.push_back() = extension;
-            SkTQSort(&fExtensionStrings.front(), &fExtensionStrings.back(), cmp);
+        if (find_info(fExtensions, extension) < 0) {
+            fExtensions.push_back() = Info(extension);
+            SkTQSort(&fExtensions.front(), &fExtensions.back(), cmp);
         }
     }
+    this->getSpecVersions(getProc, instance, physDev);
 }
 
-bool GrVkExtensions::hasExtension(const char ext[]) const {
-    return find_string(fExtensionStrings, ext) >= 0;
+#define GET_PROC(F, inst)                                                        \
+        PFN_vk##F grVk##F = (PFN_vk ## F) getProc("vk" #F, inst, VK_NULL_HANDLE)
+
+void GrVkExtensions::getSpecVersions(GrVkGetProc getProc, VkInstance instance,
+                                     VkPhysicalDevice physDevice) {
+    // We grab all the extensions for the VkInstance and VkDevice so we can look up what spec
+    // version each of the supported extensions are. We do not grab the extensions for layers
+    // because we don't know what layers the client has enabled and in general we don't do anything
+    // special for those extensions.
+
+    if (instance == VK_NULL_HANDLE) {
+        return;
+    }
+    GET_PROC(EnumerateInstanceExtensionProperties, VK_NULL_HANDLE);
+    SkASSERT(grVkEnumerateInstanceExtensionProperties);
+
+    VkResult res;
+    // instance extensions
+    uint32_t extensionCount = 0;
+    res = grVkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
+    if (VK_SUCCESS != res) {
+        return;
+    }
+    VkExtensionProperties* extensions = new VkExtensionProperties[extensionCount];
+    res = grVkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions);
+    if (VK_SUCCESS != res) {
+        delete[] extensions;
+        return;
+    }
+    for (uint32_t i = 0; i < extensionCount; ++i) {
+        int idx = find_info(fExtensions, extensions[i].extensionName);
+        if (idx >= 0) {
+            fExtensions[idx].fSpecVersion = extensions[i].specVersion;
+        }
+    }
+    delete[] extensions;
+
+    if (physDevice == VK_NULL_HANDLE) {
+        return;
+    }
+    GET_PROC(EnumerateDeviceExtensionProperties, instance);
+    SkASSERT(grVkEnumerateDeviceExtensionProperties);
+
+    // device extensions
+    extensionCount = 0;
+    res = grVkEnumerateDeviceExtensionProperties(physDevice, nullptr, &extensionCount, nullptr);
+    if (VK_SUCCESS != res) {
+        return;
+    }
+    extensions = new VkExtensionProperties[extensionCount];
+    res = grVkEnumerateDeviceExtensionProperties(physDevice, nullptr, &extensionCount, extensions);
+    if (VK_SUCCESS != res) {
+        delete[] extensions;
+        return;
+    }
+    for (uint32_t i = 0; i < extensionCount; ++i) {
+        int idx = find_info(fExtensions, extensions[i].extensionName);
+        if (idx >= 0) {
+            fExtensions[idx].fSpecVersion = extensions[i].specVersion;
+        }
+    }
+    delete[] extensions;
+}
+
+bool GrVkExtensions::hasExtension(const char ext[], uint32_t minVersion) const {
+    int idx = find_info(fExtensions, ext);
+    return  idx >= 0 && fExtensions[idx].fSpecVersion >= minVersion;
 }
 
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 5a19713..6a37c27 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -37,6 +37,7 @@
 #include "SkSLCompiler.h"
 #include "SkTo.h"
 
+#include "vk/GrVkExtensions.h"
 #include "vk/GrVkTypes.h"
 
 #include <utility>
diff --git a/src/gpu/vk/GrVkInterface.cpp b/src/gpu/vk/GrVkInterface.cpp
index 7ffa8c0..bbdb873 100644
--- a/src/gpu/vk/GrVkInterface.cpp
+++ b/src/gpu/vk/GrVkInterface.cpp
@@ -7,6 +7,7 @@
 
 #include "GrVkInterface.h"
 #include "vk/GrVkBackendContext.h"
+#include "vk/GrVkExtensions.h"
 #include "vk/GrVkUtil.h"
 
 #define ACQUIRE_PROC(name, instance, device) fFunctions.f##name = \
@@ -39,13 +40,6 @@
     ACQUIRE_PROC(EnumerateDeviceExtensionProperties, instance, VK_NULL_HANDLE);
     ACQUIRE_PROC(EnumerateDeviceLayerProperties, instance, VK_NULL_HANDLE);
 
-    if (extensions->hasExtension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) {
-        // Also instance Procs.
-        ACQUIRE_PROC(CreateDebugReportCallbackEXT, instance, VK_NULL_HANDLE);
-        ACQUIRE_PROC(DebugReportMessageEXT, instance, VK_NULL_HANDLE);
-        ACQUIRE_PROC(DestroyDebugReportCallbackEXT, instance, VK_NULL_HANDLE);
-    }
-
     // Device Procs.
     ACQUIRE_PROC(GetDeviceQueue, VK_NULL_HANDLE, device);
     ACQUIRE_PROC(QueueSubmit, VK_NULL_HANDLE, device);
@@ -318,13 +312,6 @@
         RETURN_FALSE_INTERFACE
     }
 
-    if (extensions->hasExtension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) {
-        if (nullptr == fFunctions.fCreateDebugReportCallbackEXT ||
-            nullptr == fFunctions.fDebugReportMessageEXT ||
-            nullptr == fFunctions.fDestroyDebugReportCallbackEXT) {
-            RETURN_FALSE_INTERFACE
-        }
-    }
     return true;
 }
 
diff --git a/src/gpu/vk/GrVkInterface.h b/src/gpu/vk/GrVkInterface.h
index 22991ac..3345831 100644
--- a/src/gpu/vk/GrVkInterface.h
+++ b/src/gpu/vk/GrVkInterface.h
@@ -12,7 +12,9 @@
 
 #include "vk/GrVkBackendContext.h"
 #include "vk/GrVkTypes.h"
-#include "vk/GrVkExtensions.h"
+#include "vk/GrVkDefines.h"
+
+class GrVkExtensions;
 
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -22,7 +24,7 @@
  * available based on the Vulkan's version must be non-NULL or GrContext creation
  * will fail. This can be tested with the validate() method.
  */
-struct SK_API GrVkInterface : public SkRefCnt {
+struct GrVkInterface : public SkRefCnt {
 private:
     // simple wrapper class that exists only to initialize a pointer to NULL
     template <typename FNPTR_TYPE> class VkPtr {
@@ -186,10 +188,6 @@
         VkPtr<PFN_vkCmdNextSubpass> fCmdNextSubpass;
         VkPtr<PFN_vkCmdEndRenderPass> fCmdEndRenderPass;
         VkPtr<PFN_vkCmdExecuteCommands> fCmdExecuteCommands;
-
-        VkPtr<PFN_vkCreateDebugReportCallbackEXT> fCreateDebugReportCallbackEXT;
-        VkPtr<PFN_vkDebugReportMessageEXT> fDebugReportMessageEXT;
-        VkPtr<PFN_vkDestroyDebugReportCallbackEXT> fDestroyDebugReportCallbackEXT;
     } fFunctions;
 };
 
diff --git a/tools/gpu/vk/VkTestContext.cpp b/tools/gpu/vk/VkTestContext.cpp
index 9d3bdc5..db78e85 100644
--- a/tools/gpu/vk/VkTestContext.cpp
+++ b/tools/gpu/vk/VkTestContext.cpp
@@ -153,6 +153,7 @@
         GrVkExtensions* extensions;
         bool ownsContext = true;
         VkDebugReportCallbackEXT debugCallback = VK_NULL_HANDLE;
+        PFN_vkDestroyDebugReportCallbackEXT destroyCallback = nullptr;
         if (sharedContext) {
             backendContext = sharedContext->getVkBackendContext();
             extensions = const_cast<GrVkExtensions*>(sharedContext->getVkExtensions());
@@ -177,8 +178,13 @@
                                                      &debugCallback)) {
                 return nullptr;
             }
+            if (debugCallback != VK_NULL_HANDLE) {
+                destroyCallback = (PFN_vkDestroyDebugReportCallbackEXT) instProc(
+                        backendContext.fInstance, "vkDestroyDebugReportCallbackEXT");
+            }
         }
-        return new VkTestContextImpl(backendContext, extensions, ownsContext, debugCallback);
+        return new VkTestContextImpl(backendContext, extensions, ownsContext, debugCallback,
+                                     destroyCallback);
     }
 
     ~VkTestContextImpl() override { this->teardown(); }
@@ -225,8 +231,10 @@
 
 private:
     VkTestContextImpl(const GrVkBackendContext& backendContext, const GrVkExtensions* extensions,
-                      bool ownsContext, VkDebugReportCallbackEXT debugCallback)
-            : VkTestContext(backendContext, extensions, ownsContext, debugCallback) {
+                      bool ownsContext, VkDebugReportCallbackEXT debugCallback,
+                      PFN_vkDestroyDebugReportCallbackEXT destroyCallback)
+            : VkTestContext(backendContext, extensions, ownsContext, debugCallback,
+                            destroyCallback) {
         fFenceSync.reset(new VkFenceSync(fVk.fGetProc, fVk.fDevice, fVk.fQueue,
                                          fVk.fGraphicsQueueIndex));
     }
diff --git a/tools/gpu/vk/VkTestContext.h b/tools/gpu/vk/VkTestContext.h
index 17c4215..426dd6f 100644
--- a/tools/gpu/vk/VkTestContext.h
+++ b/tools/gpu/vk/VkTestContext.h
@@ -32,16 +32,19 @@
 
 protected:
     VkTestContext(const GrVkBackendContext& vk, const GrVkExtensions* extensions, bool ownsContext,
-                  VkDebugReportCallbackEXT debugCallback)
+                  VkDebugReportCallbackEXT debugCallback,
+                  PFN_vkDestroyDebugReportCallbackEXT destroyCallback)
             : fVk(vk)
             , fExtensions(extensions)
             , fOwnsContext(ownsContext)
-            , fDebugCallback(debugCallback) {}
+            , fDebugCallback(debugCallback)
+            , fDestroyDebugReportCallbackEXT(destroyCallback) {}
 
-    GrVkBackendContext       fVk;
-    const GrVkExtensions*    fExtensions;
-    bool                     fOwnsContext;
-    VkDebugReportCallbackEXT fDebugCallback = VK_NULL_HANDLE;
+    GrVkBackendContext                  fVk;
+    const GrVkExtensions*               fExtensions;
+    bool                                fOwnsContext;
+    VkDebugReportCallbackEXT            fDebugCallback = VK_NULL_HANDLE;
+    PFN_vkDestroyDebugReportCallbackEXT fDestroyDebugReportCallbackEXT = nullptr;
 
 private:
     typedef TestContext INHERITED;
diff --git a/tools/gpu/vk/VkTestUtils.cpp b/tools/gpu/vk/VkTestUtils.cpp
index b3a3665..faf7e90 100644
--- a/tools/gpu/vk/VkTestUtils.cpp
+++ b/tools/gpu/vk/VkTestUtils.cpp
@@ -571,7 +571,8 @@
     VkQueue queue;
     grVkGetDeviceQueue(device, graphicsQueueIndex, 0, &queue);
 
-    extensions->init((uint32_t) instanceExtensionNames.count(),
+    extensions->init(getProc, inst, physDev,
+                     (uint32_t) instanceExtensionNames.count(),
                      instanceExtensionNames.begin(),
                      (uint32_t) deviceExtensionNames.count(),
                      deviceExtensionNames.begin());
diff --git a/tools/sk_app/VulkanWindowContext.cpp b/tools/sk_app/VulkanWindowContext.cpp
index db5cc1e..01129bc 100644
--- a/tools/sk_app/VulkanWindowContext.cpp
+++ b/tools/sk_app/VulkanWindowContext.cpp
@@ -14,8 +14,8 @@
 
 #include "vk/GrVkExtensions.h"
 #include "vk/GrVkImage.h"
-#include "vk/GrVkUtil.h"
 #include "vk/GrVkTypes.h"
+#include "vk/GrVkUtil.h"
 
 #ifdef VK_USE_PLATFORM_WIN32_KHR
 // windows wants to define this as CreateSemaphoreA or CreateSemaphoreW
@@ -66,8 +66,8 @@
         return;
     }
 
-    if (!extensions.hasExtension(VK_KHR_SURFACE_EXTENSION_NAME) ||
-        !extensions.hasExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
+    if (!extensions.hasExtension(VK_KHR_SURFACE_EXTENSION_NAME, 25) ||
+        !extensions.hasExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, 68)) {
         return;
     }
 
@@ -80,6 +80,9 @@
                                        &extensions));
 
     GET_PROC(DestroyInstance);
+    if (fDebugCallback != VK_NULL_HANDLE) {
+        GET_PROC(DestroyDebugReportCallbackEXT);
+    }
     GET_PROC(DestroySurfaceKHR);
     GET_PROC(GetPhysicalDeviceSurfaceSupportKHR);
     GET_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
@@ -453,8 +456,7 @@
 
 #ifdef SK_ENABLE_VK_LAYERS
     if (fDebugCallback != VK_NULL_HANDLE) {
-        GR_VK_CALL(fInterface, DestroyDebugReportCallbackEXT(fInstance, fDebugCallback,
-                                                             nullptr));
+        fDestroyDebugReportCallbackEXT(fInstance, fDebugCallback, nullptr);
     }
 #endif
 
diff --git a/tools/sk_app/VulkanWindowContext.h b/tools/sk_app/VulkanWindowContext.h
index 2e01e29..0b4d5ce 100644
--- a/tools/sk_app/VulkanWindowContext.h
+++ b/tools/sk_app/VulkanWindowContext.h
@@ -93,6 +93,7 @@
 
     PFN_vkDestroyInstance fDestroyInstance = nullptr;
     PFN_vkDeviceWaitIdle fDeviceWaitIdle = nullptr;
+    PFN_vkDestroyDebugReportCallbackEXT fDestroyDebugReportCallbackEXT = nullptr;
     PFN_vkQueueWaitIdle fQueueWaitIdle = nullptr;
     PFN_vkDestroyDevice fDestroyDevice = nullptr;
     PFN_vkGetDeviceQueue fGetDeviceQueue = nullptr;