From 6d9a3b82c6afdda92566d9c6194be9126fca155e Mon Sep 17 00:00:00 2001 From: Corentin Wallez Date: Mon, 20 Nov 2017 10:51:23 -0500 Subject: [PATCH] Vulkan: Create VkInstance and register debug report --- src/backend/vulkan/VulkanBackend.cpp | 101 +++++++++++++++++++++++++ src/backend/vulkan/VulkanBackend.h | 15 ++++ src/backend/vulkan/VulkanFunctions.cpp | 30 ++++++++ src/backend/vulkan/VulkanFunctions.h | 26 +++++++ src/backend/vulkan/VulkanInfo.cpp | 32 ++++++++ src/backend/vulkan/VulkanInfo.h | 16 +++- 6 files changed, 218 insertions(+), 2 deletions(-) diff --git a/src/backend/vulkan/VulkanBackend.cpp b/src/backend/vulkan/VulkanBackend.cpp index 52bc35fd30..7500329fc7 100644 --- a/src/backend/vulkan/VulkanBackend.cpp +++ b/src/backend/vulkan/VulkanBackend.cpp @@ -19,6 +19,8 @@ #include +#include + #if NXT_PLATFORM_LINUX const char kVulkanLibName[] = "libvulkan.so.1"; #elif NXT_PLATFORM_WINDOWS @@ -58,9 +60,38 @@ namespace vulkan { ASSERT(false); return; } + + KnownGlobalVulkanExtensions usedGlobals; + if (!CreateInstance(&usedGlobals)) { + ASSERT(false); + return; + } + + if (!functions->LoadInstanceProcs(instance, usedGlobals)) { + ASSERT(false); + return; + } + + vulkanInfo->SetUsedGlobals(usedGlobals); + + if(usedGlobals.debugReport) { + if (!RegisterDebugReport()) { + ASSERT(false); + return; + } + } } Device::~Device() { + if (debugReportCallback != VK_NULL_HANDLE) { + fn.DestroyDebugReportCallbackEXT(instance, debugReportCallback, nullptr); + debugReportCallback = VK_NULL_HANDLE; + } + + if (instance != VK_NULL_HANDLE) { + fn.DestroyInstance(instance, nullptr); + instance = VK_NULL_HANDLE; + } } BindGroupBase* Device::CreateBindGroup(BindGroupBuilder* builder) { @@ -129,6 +160,76 @@ namespace vulkan { void Device::TickImpl() { } + bool Device::CreateInstance(KnownGlobalVulkanExtensions* usedGlobals) { + std::vector layersToRequest; + std::vector extensionsToRequest; + + #if defined(NXT_ENABLE_ASSERTS) + if (info.global.standardValidation) { + layersToRequest.push_back(kLayerNameLunargStandardValidation); + usedGlobals->standardValidation = true; + } + if (info.global.debugReport) { + extensionsToRequest.push_back(kExtensionNameExtDebugReport); + usedGlobals->debugReport = true; + } + #endif + + VkApplicationInfo appInfo; + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + appInfo.pNext = nullptr; + appInfo.pApplicationName = nullptr; + appInfo.applicationVersion = 0; + appInfo.pEngineName = nullptr; + appInfo.engineVersion = 0; + appInfo.apiVersion = VK_API_VERSION_1_0; + + VkInstanceCreateInfo createInfo; + createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + createInfo.pNext = nullptr; + createInfo.flags = 0; + createInfo.pApplicationInfo = &appInfo; + createInfo.enabledLayerCount = static_cast(layersToRequest.size()); + createInfo.ppEnabledLayerNames = layersToRequest.data(); + createInfo.enabledExtensionCount = static_cast(extensionsToRequest.size()); + createInfo.ppEnabledExtensionNames = extensionsToRequest.data(); + + if (fn.CreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { + return false; + } + + return true; + } + + bool Device::RegisterDebugReport() { + VkDebugReportCallbackCreateInfoEXT createInfo; + createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; + createInfo.pNext = nullptr; + createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; + createInfo.pfnCallback = Device::OnDebugReportCallback; + createInfo.pUserData = this; + + if (fn.CreateDebugReportCallbackEXT(instance, &createInfo, nullptr, &debugReportCallback) != VK_SUCCESS) { + return false; + } + + return true; + } + + VkBool32 Device::OnDebugReportCallback(VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT /*objectType*/, + uint64_t /*object*/, + size_t /*location*/, + int32_t /*messageCode*/, + const char* /*pLayerPrefix*/, + const char* pMessage, + void* /*pUserdata*/) { + std::cout << pMessage << std::endl; + ASSERT((flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) == 0); + + return VK_FALSE; + } + VulkanFunctions* Device::GetMutableFunctions() { return const_cast(&fn); } diff --git a/src/backend/vulkan/VulkanBackend.h b/src/backend/vulkan/VulkanBackend.h index c27d708485..9833e56c6a 100644 --- a/src/backend/vulkan/VulkanBackend.h +++ b/src/backend/vulkan/VulkanBackend.h @@ -125,12 +125,27 @@ namespace vulkan { const VulkanInfo info; private: + bool CreateInstance(KnownGlobalVulkanExtensions* usedGlobals); + bool RegisterDebugReport(); + + static VkBool32 OnDebugReportCallback(VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT objectType, + uint64_t object, + size_t location, + int32_t messageCode, + const char* pLayerPrefix, + const char* pMessage, + void* pUserdata); + // To make it easier to use fn it is a public const member. However // the Device is allowed to mutate them through these private methods. VulkanFunctions* GetMutableFunctions(); VulkanInfo* GetMutableInfo(); DynamicLib vulkanLib; + + VkInstance instance = VK_NULL_HANDLE; + VkDebugReportCallbackEXT debugReportCallback = VK_NULL_HANDLE; }; class Buffer : public BufferBase { diff --git a/src/backend/vulkan/VulkanFunctions.cpp b/src/backend/vulkan/VulkanFunctions.cpp index 113c871f25..62a8d8e812 100644 --- a/src/backend/vulkan/VulkanFunctions.cpp +++ b/src/backend/vulkan/VulkanFunctions.cpp @@ -14,6 +14,7 @@ #include "backend/vulkan/VulkanFunctions.h" +#include "backend/vulkan/VulkanInfo.h" #include "common/DynamicLib.h" namespace backend { @@ -38,5 +39,34 @@ namespace vulkan { return true; } + #define GET_INSTANCE_PROC(name) \ + name = reinterpret_cast(GetInstanceProcAddr(instance, "vk" #name)); \ + if (name == nullptr) { \ + return false; \ + } + + bool VulkanFunctions::LoadInstanceProcs(VkInstance instance, const KnownGlobalVulkanExtensions& usedGlobals) { + GET_INSTANCE_PROC(CreateDevice); + GET_INSTANCE_PROC(DestroyDevice); + GET_INSTANCE_PROC(EnumerateDeviceExtensionProperties); + GET_INSTANCE_PROC(EnumerateDeviceLayerProperties); + GET_INSTANCE_PROC(EnumeratePhysicalDevices); + GET_INSTANCE_PROC(GetPhysicalDeviceFeatures); + GET_INSTANCE_PROC(GetPhysicalDeviceFormatProperties); + GET_INSTANCE_PROC(GetPhysicalDeviceImageFormatProperties); + GET_INSTANCE_PROC(GetPhysicalDeviceMemoryProperties); + GET_INSTANCE_PROC(GetPhysicalDeviceProperties); + GET_INSTANCE_PROC(GetPhysicalDeviceQueueFamilyProperties); + GET_INSTANCE_PROC(GetPhysicalDeviceSparseImageFormatProperties); + + if (usedGlobals.debugReport) { + GET_INSTANCE_PROC(CreateDebugReportCallbackEXT); + GET_INSTANCE_PROC(DebugReportMessageEXT); + GET_INSTANCE_PROC(DestroyDebugReportCallbackEXT); + } + + return true; + } + } } diff --git a/src/backend/vulkan/VulkanFunctions.h b/src/backend/vulkan/VulkanFunctions.h index 0060b55ae0..7ca10a5628 100644 --- a/src/backend/vulkan/VulkanFunctions.h +++ b/src/backend/vulkan/VulkanFunctions.h @@ -22,10 +22,13 @@ class DynamicLib; namespace backend { namespace vulkan { + struct KnownGlobalVulkanExtensions; + // Stores the Vulkan entry points. Also loads them from the dynamic library // and the vkGet*ProcAddress entry points. struct VulkanFunctions { bool LoadGlobalProcs(const DynamicLib& vulkanLib); + bool LoadInstanceProcs(VkInstance instance, const KnownGlobalVulkanExtensions& usedGlobals); // Initial proc from which we can get all the others PFN_vkGetInstanceProcAddr GetInstanceProcAddr = nullptr; @@ -37,7 +40,30 @@ namespace vulkan { // DestroyInstance isn't technically a global proc but we want to be able to use it // before querying the instance procs in case we need to error out during initialization. PFN_vkDestroyInstance DestroyInstance = nullptr; + + // Instance procs + PFN_vkCreateDevice CreateDevice = nullptr; + PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties = nullptr; + PFN_vkEnumerateDeviceLayerProperties EnumerateDeviceLayerProperties = nullptr; + PFN_vkEnumeratePhysicalDevices EnumeratePhysicalDevices = nullptr; + PFN_vkGetPhysicalDeviceFeatures GetPhysicalDeviceFeatures = nullptr; + PFN_vkGetPhysicalDeviceFormatProperties GetPhysicalDeviceFormatProperties = nullptr; + PFN_vkGetPhysicalDeviceImageFormatProperties GetPhysicalDeviceImageFormatProperties = nullptr; + PFN_vkGetPhysicalDeviceMemoryProperties GetPhysicalDeviceMemoryProperties = nullptr; + PFN_vkGetPhysicalDeviceProperties GetPhysicalDeviceProperties = nullptr; + PFN_vkGetPhysicalDeviceQueueFamilyProperties GetPhysicalDeviceQueueFamilyProperties = nullptr; + PFN_vkGetPhysicalDeviceSparseImageFormatProperties GetPhysicalDeviceSparseImageFormatProperties = nullptr; + // Not technically an instance proc but we want to be able to use it as soon as the + // device is created. + PFN_vkDestroyDevice DestroyDevice = nullptr; + + // VK_EXT_debug_report + PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT = nullptr; + PFN_vkDebugReportMessageEXT DebugReportMessageEXT = nullptr; + PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT = nullptr; }; + + } } diff --git a/src/backend/vulkan/VulkanInfo.cpp b/src/backend/vulkan/VulkanInfo.cpp index f3869bf091..14ca081427 100644 --- a/src/backend/vulkan/VulkanInfo.cpp +++ b/src/backend/vulkan/VulkanInfo.cpp @@ -16,9 +16,25 @@ #include "backend/vulkan/VulkanBackend.h" +#include + +namespace { + bool IsLayerName(const VkLayerProperties& layer, const char* name) { + return strncmp(layer.layerName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0; + } + + bool IsExtensionName(const VkExtensionProperties& extension, const char* name) { + return strncmp(extension.extensionName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0; + } +} + namespace backend { namespace vulkan { + const char kLayerNameLunargStandardValidation[] = "VK_LAYER_LUNARG_standard_validation"; + + const char kExtensionNameExtDebugReport[] = "VK_EXT_debug_report"; + bool VulkanInfo::GatherGlobalInfo(const Device& device) { // Gather the info about the instance layers { @@ -36,6 +52,12 @@ namespace vulkan { if (result != VK_SUCCESS) { return false; } + + for (const auto& layer : global.layers) { + if (IsLayerName(layer, kLayerNameLunargStandardValidation)) { + global.standardValidation = true; + } + } } // Gather the info about the instance extensions @@ -51,11 +73,21 @@ namespace vulkan { if (result != VK_SUCCESS) { return false; } + + for (const auto& extension : global.extensions) { + if (IsExtensionName(extension, kExtensionNameExtDebugReport)) { + global.debugReport = true; + } + } } // TODO(cwallez@chromium:org): Each layer can expose additional extensions, query them? return true; } + + void VulkanInfo::SetUsedGlobals(const KnownGlobalVulkanExtensions& usedGlobals) { + *static_cast(&global) = usedGlobals; + } } } diff --git a/src/backend/vulkan/VulkanInfo.h b/src/backend/vulkan/VulkanInfo.h index bf41728531..f15bf72b21 100644 --- a/src/backend/vulkan/VulkanInfo.h +++ b/src/backend/vulkan/VulkanInfo.h @@ -24,18 +24,30 @@ namespace vulkan { class Device; + extern const char kLayerNameLunargStandardValidation[]; + + extern const char kExtensionNameExtDebugReport[]; + + struct KnownGlobalVulkanExtensions { + // Layers + bool standardValidation = false; + + // Extensions + bool debugReport = false; + }; + // Stores the information about the Vulkan system that are required to use Vulkan. // Also does the querying of the information. struct VulkanInfo { - // Global information - gathered before the instance is created - struct { + struct : KnownGlobalVulkanExtensions { std::vector layers; std::vector extensions; // TODO(cwallez@chromium.org): layer instance extensions } global; bool GatherGlobalInfo(const Device& device); + void SetUsedGlobals(const KnownGlobalVulkanExtensions& usedGlobals); }; }