Enable extension support and debug layer.

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1785813002
TBR=bsalomon@google.com

Review URL: https://codereview.chromium.org/1785813002
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index 2b5f9cd..40def5a 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -447,6 +447,8 @@
       '<(skia_src_path)/gpu/vk/GrVkCommandBuffer.h',
       '<(skia_src_path)/gpu/vk/GrVkDescriptorPool.cpp',
       '<(skia_src_path)/gpu/vk/GrVkDescriptorPool.h',
+      '<(skia_src_path)/gpu/vk/GrVkExtensions.cpp',
+      '<(skia_src_path)/gpu/vk/GrVkExtensions.h',
       '<(skia_src_path)/gpu/vk/GrVkFramebuffer.cpp',
       '<(skia_src_path)/gpu/vk/GrVkFramebuffer.h',
       '<(skia_src_path)/gpu/vk/GrVkGpu.cpp',
diff --git a/include/gpu/vk/GrVkInterface.h b/include/gpu/vk/GrVkInterface.h
index 5676b86..a6cfba7 100644
--- a/include/gpu/vk/GrVkInterface.h
+++ b/include/gpu/vk/GrVkInterface.h
@@ -10,6 +10,8 @@
 
 #include "SkRefCnt.h"
 
+#include "GrVkExtensions.h"
+
 #include "vulkan/vulkan.h"
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -54,6 +56,21 @@
     // function pointers have been initialized for Vulkan version.
     bool validate() const;
 
+    GrVkExtensions fExtensions;
+
+    bool hasInstanceExtension(const char ext[]) const {
+        return fExtensions.hasInstanceExtension(ext);
+    }
+    bool hasDeviceExtension(const char ext[]) const {
+        return fExtensions.hasDeviceExtension(ext);
+    }
+    bool hasInstanceLayer(const char ext[]) const {
+        return fExtensions.hasInstanceLayer(ext);
+    }
+    bool hasDeviceLayer(const char ext[]) const {
+        return fExtensions.hasDeviceLayer(ext);
+    }
+
     /**
      * The function pointers are in a struct so that we can have a compiler generated assignment
      * operator.
@@ -212,7 +229,11 @@
         VkPtr<PFN_vkGetDisplayPlaneCapabilitiesKHR> fGetDisplayPlaneCapabilitiesKHR;
         VkPtr<PFN_vkCreateDisplayPlaneSurfaceKHR> fCreateDisplayPlaneSurfaceKHR;
         VkPtr<PFN_vkCreateSharedSwapchainsKHR> fCreateSharedSwapchainsKHR;
+        VkPtr<PFN_vkCreateDebugReportCallbackEXT> fCreateDebugReportCallbackEXT;
+        VkPtr<PFN_vkDebugReportMessageEXT> fDebugReportMessageEXT;
+        VkPtr<PFN_vkDestroyDebugReportCallbackEXT> fDestroyDebugReportCallbackEXT;
     } fFunctions;
+
 };
 
 #endif
diff --git a/src/gpu/vk/GrVkExtensions.cpp b/src/gpu/vk/GrVkExtensions.cpp
new file mode 100644
index 0000000..8fa99cf
--- /dev/null
+++ b/src/gpu/vk/GrVkExtensions.cpp
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "vk/GrVkExtensions.h"
+#include "vk/GrVkUtil.h"
+
+#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()) {
+        return -1;
+    }
+    SkString extensionStr(ext);
+    int idx = SkTSearch<SkString, extension_compare>(&strings.front(),
+                                                     strings.count(),
+                                                     extensionStr,
+                                                     sizeof(SkString));
+    return idx;
+}
+
+GrVkExtensions::GrVkExtensions(const GrVkExtensions& that) 
+    : fInstanceExtensionStrings(new SkTArray<SkString>)
+    , fDeviceExtensionStrings(new SkTArray<SkString>)
+    , fInstanceLayerStrings(new SkTArray<SkString>)
+    , fDeviceLayerStrings(new SkTArray<SkString>) {
+    *this = that;
+}
+
+GrVkExtensions& GrVkExtensions::operator=(const GrVkExtensions& that) {
+    *fInstanceExtensionStrings = *that.fInstanceExtensionStrings;
+    *fDeviceExtensionStrings = *that.fDeviceExtensionStrings;
+    *fInstanceLayerStrings = *that.fInstanceLayerStrings;
+    *fDeviceLayerStrings = *that.fDeviceLayerStrings;
+
+    fInitialized = that.fInitialized;
+    return *this;
+}
+
+bool GrVkExtensions::init(
+    uint32_t specVersion,
+    PFN_vkEnumerateInstanceExtensionProperties enumerateInstanceExtensionProperties,
+    PFN_vkEnumerateDeviceExtensionProperties enumerateDeviceExtensionProperties,
+    PFN_vkEnumerateInstanceLayerProperties enumerateInstanceLayerProperties,
+    PFN_vkEnumerateDeviceLayerProperties enumerateDeviceLayerProperties) {
+    fInitialized = false;
+    this->reset();
+
+    if (!enumerateInstanceExtensionProperties ||
+        !enumerateDeviceExtensionProperties ||
+        !enumerateInstanceLayerProperties ||
+        !enumerateDeviceLayerProperties) {
+        return false;
+    }
+
+    // instance extensions
+    uint32_t extensionCount = 0;
+    VkResult res = enumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
+
+    VkExtensionProperties* extensions = new VkExtensionProperties[extensionCount];
+    res = enumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions);
+
+    fInstanceExtensionStrings->push_back_n(extensionCount);
+    for (uint32_t i = 0; i < extensionCount; ++i) {
+        if (specVersion >= extensions[i].specVersion) {
+            (*fInstanceExtensionStrings)[i] = extensions[i].extensionName;
+        }
+    }
+    delete [] extensions;
+
+    if (!fInstanceExtensionStrings->empty()) {
+        SkTLessFunctionToFunctorAdaptor<SkString, extension_compare> cmp;
+        SkTQSort(&fInstanceExtensionStrings->front(), &fInstanceExtensionStrings->back(), cmp);
+    }
+
+    fInitialized = true;
+    return true;
+}
+
+
+bool GrVkExtensions::hasInstanceExtension(const char ext[]) const {
+    SkASSERT(fInitialized);
+
+    return find_string(*fInstanceExtensionStrings, ext) >= 0;
+}
+
+bool GrVkExtensions::hasDeviceExtension(const char ext[]) const {
+    SkASSERT(fInitialized);
+
+    return find_string(*fDeviceExtensionStrings, ext) >= 0;
+}
+
+bool GrVkExtensions::hasInstanceLayer(const char ext[]) const {
+    SkASSERT(fInitialized);
+
+    return find_string(*fInstanceLayerStrings, ext) >= 0;
+}
+
+bool GrVkExtensions::hasDeviceLayer(const char ext[]) const {
+    SkASSERT(fInitialized);
+
+    return find_string(*fDeviceLayerStrings, ext) >= 0;
+}
+
+
+bool GrVkExtensions::removeInstanceExtension(const char ext[]) {
+    SkASSERT(fInitialized);
+    int idx = find_string(*fInstanceExtensionStrings, ext);
+    if (idx >= 0) {
+        // This is not terribly effecient but we really only expect this function to be called at
+        // most a handful of times when our test programs start.
+        SkAutoTDelete< SkTArray<SkString> > oldStrings(fInstanceExtensionStrings.release());
+        fInstanceExtensionStrings.reset(new SkTArray<SkString>(oldStrings->count() - 1));
+        fInstanceExtensionStrings->push_back_n(idx, &oldStrings->front());
+        fInstanceExtensionStrings->push_back_n(oldStrings->count() - idx-1, &(*oldStrings)[idx]+1);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+bool GrVkExtensions::removeDeviceExtension(const char ext[]) {
+    SkASSERT(fInitialized);
+    int idx = find_string(*fDeviceExtensionStrings, ext);
+    if (idx >= 0) {
+        // This is not terribly effecient but we really only expect this function to be called at
+        // most a handful of times when our test programs start.
+        SkAutoTDelete< SkTArray<SkString> > oldStrings(fDeviceExtensionStrings.release());
+        fDeviceExtensionStrings.reset(new SkTArray<SkString>(oldStrings->count() - 1));
+        fDeviceExtensionStrings->push_back_n(idx, &oldStrings->front());
+        fDeviceExtensionStrings->push_back_n(oldStrings->count() - idx-1, &(*oldStrings)[idx] + 1);
+        return true;
+    }
+    else {
+        return false;
+    }
+}
+
+bool GrVkExtensions::removeInstanceLayer(const char ext[]) {
+    SkASSERT(fInitialized);
+    int idx = find_string(*fInstanceLayerStrings, ext);
+    if (idx >= 0) {
+        // This is not terribly effecient but we really only expect this function to be called at
+        // most a handful of times when our test programs start.
+        SkAutoTDelete< SkTArray<SkString> > oldStrings(fInstanceLayerStrings.release());
+        fInstanceLayerStrings.reset(new SkTArray<SkString>(oldStrings->count() - 1));
+        fInstanceLayerStrings->push_back_n(idx, &oldStrings->front());
+        fInstanceLayerStrings->push_back_n(oldStrings->count() - idx - 1, &(*oldStrings)[idx] + 1);
+        return true;
+    }
+    else {
+        return false;
+    }
+}
+
+bool GrVkExtensions::removeDeviceLayer(const char ext[]) {
+    SkASSERT(fInitialized);
+    int idx = find_string(*fDeviceLayerStrings, ext);
+    if (idx >= 0) {
+        // This is not terribly effecient but we really only expect this function to be called at
+        // most a handful of times when our test programs start.
+        SkAutoTDelete< SkTArray<SkString> > oldStrings(fDeviceLayerStrings.release());
+        fDeviceLayerStrings.reset(new SkTArray<SkString>(oldStrings->count() - 1));
+        fDeviceLayerStrings->push_back_n(idx, &oldStrings->front());
+        fDeviceLayerStrings->push_back_n(oldStrings->count() - idx - 1, &(*oldStrings)[idx] + 1);
+        return true;
+    }
+    else {
+        return false;
+    }
+}
+
+void GrVkExtensions::addInstanceExtension(const char ext[]) {
+    int idx = find_string(*fInstanceExtensionStrings, ext);
+    if (idx < 0) {
+        // This is not the most effecient approach since we end up doing a full sort of the
+        // extensions after the add
+        fInstanceExtensionStrings->push_back().set(ext);
+        SkTLessFunctionToFunctorAdaptor<SkString, extension_compare> cmp;
+        SkTQSort(&fInstanceExtensionStrings->front(), &fInstanceExtensionStrings->back(), cmp);
+    }
+}
+
+void GrVkExtensions::addDeviceExtension(const char ext[]) {
+    int idx = find_string(*fDeviceExtensionStrings, ext);
+    if (idx < 0) {
+        // This is not the most effecient approach since we end up doing a full sort of the
+        // extensions after the add
+        fDeviceExtensionStrings->push_back().set(ext);
+        SkTLessFunctionToFunctorAdaptor<SkString, extension_compare> cmp;
+        SkTQSort(&fDeviceExtensionStrings->front(), &fDeviceExtensionStrings->back(), cmp);
+    }
+}
+
+void GrVkExtensions::addInstanceLayer(const char ext[]) {
+    int idx = find_string(*fInstanceLayerStrings, ext);
+    if (idx < 0) {
+        // This is not the most effecient approach since we end up doing a full sort of the
+        // extensions after the add
+        fInstanceLayerStrings->push_back().set(ext);
+        SkTLessFunctionToFunctorAdaptor<SkString, extension_compare> cmp;
+        SkTQSort(&fInstanceLayerStrings->front(), &fInstanceLayerStrings->back(), cmp);
+    }
+}
+
+void GrVkExtensions::addDeviceLayer(const char ext[]) {
+    int idx = find_string(*fDeviceLayerStrings, ext);
+    if (idx < 0) {
+        // This is not the most effecient approach since we end up doing a full sort of the
+        // extensions after the add
+        fDeviceLayerStrings->push_back().set(ext);
+        SkTLessFunctionToFunctorAdaptor<SkString, extension_compare> cmp;
+        SkTQSort(&fDeviceLayerStrings->front(), &fDeviceLayerStrings->back(), cmp);
+    }
+}
+
+void GrVkExtensions::print(const char* sep) const {
+    if (nullptr == sep) {
+        sep = " ";
+    }
+    int cnt = fInstanceExtensionStrings->count();
+    SkDebugf("Instance Extensions: ");
+    for (int i = 0; i < cnt; ++i) {
+        SkDebugf("%s%s", (*fInstanceExtensionStrings)[i].c_str(), (i < cnt - 1) ? sep : "");
+    }
+    cnt = fDeviceExtensionStrings->count();
+    SkDebugf("\nDevice Extensions: ");
+    for (int i = 0; i < cnt; ++i) {
+        SkDebugf("%s%s", (*fDeviceExtensionStrings)[i].c_str(), (i < cnt - 1) ? sep : "");
+    }
+    cnt = fInstanceLayerStrings->count();
+    SkDebugf("\nInstance Layers: ");
+    for (int i = 0; i < cnt; ++i) {
+        SkDebugf("%s%s", (*fInstanceLayerStrings)[i].c_str(), (i < cnt - 1) ? sep : "");
+    }
+    cnt = fDeviceLayerStrings->count();
+    SkDebugf("\nDevice Layers: ");
+    for (int i = 0; i < cnt; ++i) {
+        SkDebugf("%s%s", (*fDeviceLayerStrings)[i].c_str(), (i < cnt - 1) ? sep : "");
+    }
+}
diff --git a/src/gpu/vk/GrVkExtensions.h b/src/gpu/vk/GrVkExtensions.h
new file mode 100644
index 0000000..d1d57e4
--- /dev/null
+++ b/src/gpu/vk/GrVkExtensions.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrVkExtensions_DEFINED
+#define GrVkExtensions_DEFINED
+
+#include "../../private/SkTArray.h"
+#include "SkString.h"
+#include "vulkan/vulkan.h"
+
+/**
+ * This helper queries the current Vulkan context for its extensions and layers, remembers them, 
+ * and can be queried. It supports queries for both instance and device extensions and layers.
+ */
+class SK_API GrVkExtensions {
+public:
+    GrVkExtensions() : fInitialized(false)
+                     , fInstanceExtensionStrings(new SkTArray<SkString>)
+                     , fDeviceExtensionStrings(new SkTArray<SkString>)
+                     , fInstanceLayerStrings(new SkTArray<SkString>)
+                     , fDeviceLayerStrings(new SkTArray<SkString>) {}
+
+    GrVkExtensions(const GrVkExtensions&);
+
+    GrVkExtensions& operator=(const GrVkExtensions&);
+
+    void swap(GrVkExtensions* that) {
+        fInstanceExtensionStrings.swap(that->fInstanceExtensionStrings);
+        fDeviceExtensionStrings.swap(that->fDeviceExtensionStrings);
+        fInstanceLayerStrings.swap(that->fInstanceLayerStrings);
+        fDeviceLayerStrings.swap(that->fDeviceLayerStrings);
+
+        SkTSwap(fInitialized, that->fInitialized);
+    }
+
+    /**
+     * We sometimes need to use this class without having yet created a GrVkInterface. 
+     */
+    bool init(uint32_t specVersion,
+              PFN_vkEnumerateInstanceExtensionProperties enumerateInstanceExtensionProperties,
+              PFN_vkEnumerateDeviceExtensionProperties enumerateDeviceExtensionProperties,
+              PFN_vkEnumerateInstanceLayerProperties enumerateInstanceLayerProperties,
+              PFN_vkEnumerateDeviceLayerProperties enumerateDeviceLayerProperties);
+
+    bool isInitialized() const { return fInitialized; }
+
+    /**
+     * Queries whether an extension or layer is present. Will fail if init() has not been called.
+     */
+    bool hasInstanceExtension(const char[]) const;
+    bool hasDeviceExtension(const char[]) const;
+    bool hasInstanceLayer(const char[]) const;
+    bool hasDeviceLayer(const char[]) const;
+
+    /**
+     * Removes an extension or layer if present. Returns true if it was present before the call.
+     */
+    bool removeInstanceExtension(const char[]);
+    bool removeDeviceExtension(const char[]);
+    bool removeInstanceLayer(const char[]);
+    bool removeDeviceLayer(const char[]);
+
+    /**
+     * Adds an extension or layer to list
+     */
+    void addInstanceExtension(const char[]);
+    void addDeviceExtension(const char[]);
+    void addInstanceLayer(const char[]);
+    void addDeviceLayer(const char[]);
+
+    void reset() {
+        fInstanceExtensionStrings->reset();
+        fDeviceExtensionStrings->reset();
+        fInstanceLayerStrings->reset();
+        fDeviceLayerStrings->reset();
+    }
+
+    void print(const char* sep = "\n") const;
+
+private:
+    bool                                fInitialized;
+    SkAutoTDelete<SkTArray<SkString> >  fInstanceExtensionStrings;
+    SkAutoTDelete<SkTArray<SkString> >  fDeviceExtensionStrings;
+    SkAutoTDelete<SkTArray<SkString> >  fInstanceLayerStrings;
+    SkAutoTDelete<SkTArray<SkString> >  fDeviceLayerStrings;
+};
+
+#endif
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 16526f6..ee87f49 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -43,6 +43,124 @@
 ////////////////////////////////////////////////////////////////////////////////
 // Stuff used to set up a GrVkGpu secrectly for now.
 
+
+#ifdef ENABLE_VK_LAYERS
+VKAPI_ATTR VkBool32 VKAPI_CALL DebugReportCallback(
+    VkDebugReportFlagsEXT       flags,
+    VkDebugReportObjectTypeEXT  objectType,
+    uint64_t                    object,
+    size_t                      location,
+    int32_t                     messageCode,
+    const char*                 pLayerPrefix,
+    const char*                 pMessage,
+    void*                       pUserData) {
+    if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
+        SkDebugf("Vulkan error [%s]: code: %d: %s\n", pLayerPrefix, messageCode, pMessage);
+    } else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
+        SkDebugf("Vulkan warning [%s]: code: %d: %s\n", pLayerPrefix, messageCode, pMessage);
+    } else if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) {
+        SkDebugf("Vulkan perf warning [%s]: code: %d: %s\n", pLayerPrefix, messageCode, pMessage);
+    } else {
+        SkDebugf("Vulkan info/debug [%s]: code: %d: %s\n", pLayerPrefix, messageCode, pMessage);
+    }
+    return VK_FALSE;
+}
+
+const char* kEnabledLayerNames[] = {
+    // elements of VK_LAYER_LUNARG_standard_validation
+    "VK_LAYER_LUNARG_threading",
+    "VK_LAYER_LUNARG_param_checker",
+    "VK_LAYER_LUNARG_device_limits",
+    "VK_LAYER_LUNARG_object_tracker",
+    "VK_LAYER_LUNARG_image",
+    "VK_LAYER_LUNARG_mem_tracker",
+    "VK_LAYER_LUNARG_draw_state",
+    "VK_LAYER_LUNARG_swapchain",
+    "VK_LAYER_GOOGLE_unique_objects",
+    // not included in standard_validation
+    //"VK_LAYER_LUNARG_api_dump",
+};
+const char* kEnabledInstanceExtensionNames[] = {
+    VK_EXT_DEBUG_REPORT_EXTENSION_NAME
+};
+
+bool verify_instance_layers() {
+    // make sure we can actually use the extensions and layers above
+    uint32_t extensionCount;
+    VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
+    if (VK_SUCCESS != res) {
+        return false;
+    }
+    VkExtensionProperties* extensions = new VkExtensionProperties[extensionCount];
+    res = vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions);
+    if (VK_SUCCESS != res) {
+        return false;
+    }
+    int instanceExtensionsFound = 0;
+    for (uint32_t j = 0; j < ARRAYSIZE(kEnabledInstanceExtensionNames); ++j) {
+        for (uint32_t i = 0; i < extensionCount; ++i) {
+            if (!strncmp(extensions[i].extensionName, kEnabledInstanceExtensionNames[j],
+                         strlen(kEnabledInstanceExtensionNames[j]))) {
+                ++instanceExtensionsFound;
+                break;
+            }
+        }
+    }
+    delete[] extensions;
+
+    uint32_t layerCount;
+    res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
+    if (VK_SUCCESS != res) {
+        return false;
+    }
+    VkLayerProperties* layers = new VkLayerProperties[layerCount];
+    res = vkEnumerateInstanceLayerProperties(&layerCount, layers);
+    if (VK_SUCCESS != res) {
+        return false;
+    }
+    int instanceLayersFound = 0;
+    for (uint32_t j = 0; j < ARRAYSIZE(kEnabledLayerNames); ++j) {
+        for (uint32_t i = 0; i < layerCount; ++i) {
+            if (!strncmp(layers[i].layerName, kEnabledLayerNames[j],
+                         strlen(kEnabledLayerNames[j]))) {
+                ++instanceLayersFound;
+                break;
+            }
+        }
+    }
+    delete[] layers;
+
+    return instanceExtensionsFound == ARRAYSIZE(kEnabledInstanceExtensionNames) &&
+           instanceLayersFound == ARRAYSIZE(kEnabledLayerNames);
+}
+
+bool verify_device_layers(VkPhysicalDevice physDev) {
+    uint32_t layerCount;
+    VkResult res = vkEnumerateDeviceLayerProperties(physDev, &layerCount, nullptr);
+    if (VK_SUCCESS != res) {
+        return false;
+    }
+    VkLayerProperties* layers = new VkLayerProperties[layerCount];
+    res = vkEnumerateDeviceLayerProperties(physDev, &layerCount, layers);
+    if (VK_SUCCESS != res) {
+        return false;
+    }
+    int deviceLayersFound = 0;
+    for (uint32_t j = 0; j < ARRAYSIZE(kEnabledLayerNames); ++j) {
+        for (uint32_t i = 0; i < layerCount; ++i) {
+            if (!strncmp(layers[i].layerName, kEnabledLayerNames[j],
+                         strlen(kEnabledLayerNames[j]))) {
+                ++deviceLayersFound;
+                break;
+            }
+        }
+    }
+    delete[] layers;
+
+    return deviceLayersFound == ARRAYSIZE(kEnabledLayerNames);
+}
+#endif
+
 // For now the VkGpuCreate is using the same signature as GL. This is mostly for ease of
 // hiding this code from offical skia. In the end the VkGpuCreate will not take a GrBackendContext
 // and mostly likely would take an optional device and queues to use.
@@ -62,18 +180,33 @@
         0,                                  // applicationVersion
         "vktest",                           // pEngineName
         0,                                  // engineVerison
-        VK_API_VERSION,                     // apiVersion
+        kGrVkMinimumVersion,                // apiVersion
     };
+
+    const char** enabledLayerNames = nullptr;
+    int enabledLayerCount = 0;
+    const char** enabledInstanceExtensionNames = nullptr;
+    int enabledInstanceExtensionCount = 0;
+#ifdef ENABLE_VK_LAYERS
+    if (verify_instance_layers()) {
+        enabledLayerNames = kEnabledLayerNames;
+        enabledLayerCount = ARRAYSIZE(kEnabledLayerNames);
+        enabledInstanceExtensionNames = kEnabledInstanceExtensionNames;
+        enabledInstanceExtensionCount = ARRAYSIZE(kEnabledInstanceExtensionNames);
+    }
+#endif
+
     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
+        enabledLayerCount,                      // enabledLayerNameCount
+        enabledLayerNames,                      // ppEnabledLayerNames
+        enabledInstanceExtensionCount,          // enabledExtensionNameCount
+        enabledInstanceExtensionNames,          // ppEnabledExtensionNames
     };
+
     err = vkCreateInstance(&instance_create, nullptr, &inst);
     if (err < 0) {
         SkDebugf("vkCreateInstanced failed: %d\n", err);
@@ -116,6 +249,14 @@
     }
     SkASSERT(graphicsQueueIndex < queueCount);
 
+#ifdef ENABLE_VK_LAYERS
+    // unlikely that the device will have different layers than the instance, but good to check
+    if (!verify_device_layers(physDev)) {
+        enabledLayerNames = nullptr;
+        enabledLayerCount = 0;
+    }
+#endif
+
     float queuePriorities[1] = { 0.0 };
     const VkDeviceQueueCreateInfo queueInfo = {
         VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType
@@ -131,8 +272,8 @@
         0,                                     // VkDeviceCreateFlags
         1,                                     // queueCreateInfoCount
         &queueInfo,                            // pQueueCreateInfos
-        0,                                     // layerCount
-        nullptr,                               // ppEnabledLayerNames
+        enabledLayerCount,                     // layerCount
+        enabledLayerNames,                     // ppEnabledLayerNames
         0,                                     // extensionCount
         nullptr,                               // ppEnabledExtensionNames
         nullptr                                // ppEnabledFeatures
@@ -188,6 +329,25 @@
     fCurrentCmdBuffer->begin(this);
     VK_CALL(GetPhysicalDeviceMemoryProperties(physDev, &fPhysDevMemProps));
 
+#ifdef ENABLE_VK_LAYERS
+    if (fInterface->hasInstanceExtension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) {
+        /* Setup callback creation information */
+        VkDebugReportCallbackCreateInfoEXT callbackCreateInfo;
+        callbackCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
+        callbackCreateInfo.pNext = nullptr;
+        callbackCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT |
+                                   VK_DEBUG_REPORT_WARNING_BIT_EXT |
+                                   //VK_DEBUG_REPORT_INFORMATION_BIT_EXT |
+                                   //VK_DEBUG_REPORT_DEBUG_BIT_EXT |
+                                   VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
+        callbackCreateInfo.pfnCallback = &DebugReportCallback;
+        callbackCreateInfo.pUserData = nullptr;
+
+        /* Register the callback */
+        GR_VK_CALL_ERRCHECK(fInterface, CreateDebugReportCallbackEXT(inst, &callbackCreateInfo,
+                                                                     nullptr, &fCallback));
+    }
+#endif
 }
 
 GrVkGpu::~GrVkGpu() {
@@ -202,6 +362,10 @@
     // must call this just before we destroy the VkDevice
     fResourceProvider.destroyResources();
 
+#ifdef SK_DEBUG
+    VK_CALL(DestroyDebugReportCallbackEXT(fVkInstance, fCallback, nullptr));
+#endif
+
     VK_CALL(DestroyCommandPool(fDevice, fCmdPool, nullptr));
     VK_CALL(DestroyDevice(fDevice, nullptr));
     VK_CALL(DestroyInstance(fVkInstance, nullptr));
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index 3785b15..c39973a 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -30,6 +30,10 @@
 class GrVkTexture;
 struct GrVkInterface;
 
+#ifdef SK_DEBUG
+#define ENABLE_VK_LAYERS
+#endif
+
 class GrVkGpu : public GrGpu {
 public:
     // Currently passing in the inst so that we can properly delete it when we are done.
@@ -218,6 +222,11 @@
     GrVkCommandBuffer*                fCurrentCmdBuffer;
     GrVkResourceProvider              fResourceProvider;
 
+#ifdef ENABLE_VK_LAYERS
+    // For reporting validation layer errors
+    VkDebugReportCallbackEXT          fCallback;
+#endif
+
     // Shaderc compiler used for compiling glsl in spirv. We only want to create the compiler once
     // since there is significant overhead to the first compile of any compiler.
     shaderc_compiler_t fCompiler;
diff --git a/src/gpu/vk/GrVkInterface.cpp b/src/gpu/vk/GrVkInterface.cpp
index 07e85fc..4b12e13 100644
--- a/src/gpu/vk/GrVkInterface.cpp
+++ b/src/gpu/vk/GrVkInterface.cpp
@@ -6,14 +6,30 @@
  */
 
 #include "vk/GrVkInterface.h"
+#include "vk/GrVkUtil.h"
 
 GrVkInterface::GrVkInterface() {
 }
 
 #define GET_PROC(F) functions->f ## F = (PFN_vk ## F) vkGetInstanceProcAddr(instance, "vk" #F)
+#define GET_PROC_LOCAL(inst, F) PFN_vk ## F F = (PFN_vk ## F) vkGetInstanceProcAddr(inst, "vk" #F)
 
 const GrVkInterface* GrVkCreateInterface(VkInstance instance) {
 
+    GET_PROC_LOCAL(nullptr, EnumerateInstanceExtensionProperties);
+    GET_PROC_LOCAL(instance, EnumerateDeviceExtensionProperties);
+    GET_PROC_LOCAL(nullptr, EnumerateInstanceLayerProperties);
+    GET_PROC_LOCAL(instance, EnumerateDeviceLayerProperties);
+
+    GrVkExtensions extensions;
+    if (!extensions.init(kGrVkMinimumVersion,
+                         EnumerateInstanceExtensionProperties,
+                         EnumerateDeviceExtensionProperties,
+                         EnumerateInstanceLayerProperties,
+                         EnumerateDeviceLayerProperties)) {
+        return nullptr;
+    }
+
     GrVkInterface* interface = new GrVkInterface();
     GrVkInterface::Functions* functions = &interface->fFunctions;
 
@@ -152,6 +168,7 @@
     GET_PROC(CmdNextSubpass);
     GET_PROC(CmdEndRenderPass);
     GET_PROC(CmdExecuteCommands);
+    // TODO: break these out with extension checks
     GET_PROC(DestroySurfaceKHR);
     GET_PROC(GetPhysicalDeviceSurfaceSupportKHR);
     GET_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
@@ -171,6 +188,14 @@
     GET_PROC(CreateDisplayPlaneSurfaceKHR);
     GET_PROC(CreateSharedSwapchainsKHR);
 
+    if (extensions.hasInstanceExtension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) {
+        GET_PROC(CreateDebugReportCallbackEXT);
+        GET_PROC(DebugReportMessageEXT);
+        GET_PROC(DestroyDebugReportCallbackEXT);
+    }
+
+    interface->fExtensions.swap(&extensions);
+
     return interface;
 }
 
@@ -332,7 +357,10 @@
         NULL == fFunctions.fCreateDisplayModeKHR ||
         NULL == fFunctions.fGetDisplayPlaneCapabilitiesKHR ||
         NULL == fFunctions.fCreateDisplayPlaneSurfaceKHR ||
-        NULL == fFunctions.fCreateSharedSwapchainsKHR) {
+        NULL == fFunctions.fCreateSharedSwapchainsKHR ||
+        NULL == fFunctions.fCreateDebugReportCallbackEXT ||
+        NULL == fFunctions.fDebugReportMessageEXT ||
+        NULL == fFunctions.fDestroyDebugReportCallbackEXT) {
         return false;
     }
     return true;
diff --git a/src/gpu/vk/GrVkUtil.h b/src/gpu/vk/GrVkUtil.h
index 4fee310..0b760ca 100644
--- a/src/gpu/vk/GrVkUtil.h
+++ b/src/gpu/vk/GrVkUtil.h
@@ -14,6 +14,9 @@
 
 #include "vulkan/vulkan.h"
 
+// the minimum version of Vulkan supported
+const uint32_t kGrVkMinimumVersion = VK_MAKE_VERSION(1, 0, 3);
+
 // makes a Vk call on the interface
 #define GR_VK_CALL(IFACE, X) (IFACE)->fFunctions.f##X;
 // same as GR_VK_CALL but checks for success