diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi index 2b5f9cd8d3..40def5acb7 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 5676b86e3a..a6cfba73ab 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 @@ public: // 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 @@ public: VkPtr fGetDisplayPlaneCapabilitiesKHR; VkPtr fCreateDisplayPlaneSurfaceKHR; VkPtr fCreateSharedSwapchainsKHR; + VkPtr fCreateDebugReportCallbackEXT; + VkPtr fDebugReportMessageEXT; + VkPtr fDestroyDebugReportCallbackEXT; } fFunctions; + }; #endif diff --git a/src/gpu/vk/GrVkExtensions.cpp b/src/gpu/vk/GrVkExtensions.cpp new file mode 100644 index 0000000000..8fa99cf9fc --- /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& strings, const char ext[]) { + if (strings.empty()) { + return -1; + } + SkString extensionStr(ext); + int idx = SkTSearch(&strings.front(), + strings.count(), + extensionStr, + sizeof(SkString)); + return idx; +} + +GrVkExtensions::GrVkExtensions(const GrVkExtensions& that) + : fInstanceExtensionStrings(new SkTArray) + , fDeviceExtensionStrings(new SkTArray) + , fInstanceLayerStrings(new SkTArray) + , fDeviceLayerStrings(new SkTArray) { + *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 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 > oldStrings(fInstanceExtensionStrings.release()); + fInstanceExtensionStrings.reset(new SkTArray(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 > oldStrings(fDeviceExtensionStrings.release()); + fDeviceExtensionStrings.reset(new SkTArray(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 > oldStrings(fInstanceLayerStrings.release()); + fInstanceLayerStrings.reset(new SkTArray(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 > oldStrings(fDeviceLayerStrings.release()); + fDeviceLayerStrings.reset(new SkTArray(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 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 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 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 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 0000000000..d1d57e424a --- /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) + , fDeviceExtensionStrings(new SkTArray) + , fInstanceLayerStrings(new SkTArray) + , fDeviceLayerStrings(new SkTArray) {} + + 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 > fInstanceExtensionStrings; + SkAutoTDelete > fDeviceExtensionStrings; + SkAutoTDelete > fInstanceLayerStrings; + SkAutoTDelete > fDeviceLayerStrings; +}; + +#endif diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp index 16526f6613..ee87f49b07 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 @@ GrGpu* vk_gpu_create(GrBackendContext backendContext, const GrContextOptions& op 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 @@ GrGpu* vk_gpu_create(GrBackendContext backendContext, const GrContextOptions& op } 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 @@ GrGpu* vk_gpu_create(GrBackendContext backendContext, const GrContextOptions& op 0, // VkDeviceCreateFlags 1, // queueCreateInfoCount &queueInfo, // pQueueCreateInfos - 0, // layerCount - nullptr, // ppEnabledLayerNames + enabledLayerCount, // layerCount + enabledLayerNames, // ppEnabledLayerNames 0, // extensionCount nullptr, // ppEnabledExtensionNames nullptr // ppEnabledFeatures @@ -188,6 +329,25 @@ GrVkGpu::GrVkGpu(GrContext* context, const GrContextOptions& options, 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 @@ GrVkGpu::~GrVkGpu() { // 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 3785b15af1..c39973a4e7 100644 --- a/src/gpu/vk/GrVkGpu.h +++ b/src/gpu/vk/GrVkGpu.h @@ -30,6 +30,10 @@ class GrVkRenderPass; 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 @@ private: 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 07e85fcd5b..4b12e13f0c 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 @@ const GrVkInterface* GrVkCreateInterface(VkInstance instance) { 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 @@ const GrVkInterface* GrVkCreateInterface(VkInstance instance) { 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 @@ bool GrVkInterface::validate() const { 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 4fee31046d..0b760cabe2 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