// Copyright (C) 2020 Jérôme Leclercq // This file is part of the "Nazara Engine - Vulkan Renderer" // For conditions of distribution and use, see copyright notice in Config.hpp #include #include #include #include #include #include #include #include namespace Nz { namespace Vk { namespace { VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* /*pUserData*/) { std::stringstream ss; ss << "Vulkan log: "; if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) ss << "[Verbose]"; if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) ss << "[Info]"; if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) ss << "[Warning]"; if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) ss << "[Error]"; if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) ss << "[General]"; if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) ss << "[Performance]"; if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) ss << "[Validation]"; ss << "[" << pCallbackData->messageIdNumber << ":" << pCallbackData->pMessageIdName << "]: " << pCallbackData->pMessage; if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) NazaraError(ss.str()); else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) NazaraWarning(ss.str()); else NazaraNotice(ss.str()); return VK_FALSE; //< Should the Vulkan call be aborted } } struct Instance::InternalData { DebugUtilsMessengerEXT debugMessenger; }; Instance::Instance() : m_instance(nullptr) { } Instance::~Instance() { if (m_instance) DestroyInstance(); } bool Instance::Create(const VkInstanceCreateInfo& createInfo, const VkAllocationCallbacks* allocator) { m_lastErrorCode = Loader::vkCreateInstance(&createInfo, allocator, &m_instance); if (m_lastErrorCode != VkResult::VK_SUCCESS) { NazaraError("Failed to create Vulkan instance: " + TranslateVulkanError(m_lastErrorCode)); return false; } m_apiVersion = createInfo.pApplicationInfo->apiVersion; // Store the allocator to access them when needed if (allocator) m_allocator = *allocator; else m_allocator.pfnAllocation = nullptr; // Parse extensions and layers for (UInt32 i = 0; i < createInfo.enabledExtensionCount; ++i) m_loadedExtensions.insert(createInfo.ppEnabledExtensionNames[i]); for (UInt32 i = 0; i < createInfo.enabledLayerCount; ++i) m_loadedLayers.insert(createInfo.ppEnabledLayerNames[i]); // And now load everything try { ErrorFlags flags(ErrorFlag_ThrowException, true); #define NAZARA_VULKANRENDERER_INSTANCE_EXT_BEGIN(ext) if (IsExtensionLoaded(#ext)) { #define NAZARA_VULKANRENDERER_INSTANCE_EXT_END() } #define NAZARA_VULKANRENDERER_INSTANCE_FUNCTION(func) func = reinterpret_cast(GetProcAddr(#func)); #define NAZARA_VULKANRENDERER_INSTANCE_CORE_EXT_FUNCTION(func, coreVersion, suffix, extName) \ if (m_apiVersion >= coreVersion) \ func = reinterpret_cast(GetProcAddr(#func)); \ else if (IsExtensionLoaded("VK_" #suffix "_" #extName)) \ func = reinterpret_cast(GetProcAddr(#func #suffix)); #include } catch (const std::exception& e) { NazaraError(std::string("Failed to query instance function: ") + e.what()); return false; } m_internalData = std::make_unique(); InstallDebugMessageCallback(); return true; } bool Instance::EnumeratePhysicalDevices(std::vector* devices) { NazaraAssert(devices, "Invalid device vector"); // First, query physical device count UInt32 deviceCount = 0; // Remember, Nz::UInt32 is a typedef on uint32_t m_lastErrorCode = vkEnumeratePhysicalDevices(m_instance, &deviceCount, nullptr); if (m_lastErrorCode != VkResult::VK_SUCCESS || deviceCount == 0) { NazaraError("Failed to query physical device count: " + TranslateVulkanError(m_lastErrorCode)); return false; } // Now we can get the list of the available physical device devices->resize(deviceCount); m_lastErrorCode = vkEnumeratePhysicalDevices(m_instance, &deviceCount, devices->data()); if (m_lastErrorCode != VkResult::VK_SUCCESS) { NazaraError("Failed to query physical devices: " + TranslateVulkanError(m_lastErrorCode)); return false; } return true; } bool Instance::GetPhysicalDeviceExtensions(VkPhysicalDevice device, std::vector* extensionProperties) { NazaraAssert(extensionProperties, "Invalid extension properties vector"); // First, query extension count UInt32 extensionPropertyCount = 0; m_lastErrorCode = vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionPropertyCount, nullptr); if (m_lastErrorCode != VkResult::VK_SUCCESS) { NazaraError("Failed to query extension properties count: " + TranslateVulkanError(m_lastErrorCode)); return false; } if (extensionPropertyCount == 0) return true; //< No extension available // Now we can get the list of the available extensions extensionProperties->resize(extensionPropertyCount); m_lastErrorCode = vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionPropertyCount, extensionProperties->data()); if (m_lastErrorCode != VkResult::VK_SUCCESS) { NazaraError("Failed to query extension properties count: " + TranslateVulkanError(m_lastErrorCode)); return false; } return true; } bool Instance::GetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice device, std::vector* queueFamilyProperties) { NazaraAssert(queueFamilyProperties, "Invalid family properties vector"); // First, query physical device count UInt32 queueFamiliesCount = 0; // Remember, Nz::UInt32 is a typedef on uint32_t vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamiliesCount, nullptr); if (queueFamiliesCount == 0) { NazaraError("Failed to query physical device count"); return false; } // Now we can get the list of the available physical device queueFamilyProperties->resize(queueFamiliesCount); vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamiliesCount, queueFamilyProperties->data()); return true; } void Instance::InstallDebugMessageCallback() { NazaraAssert(m_internalData, "Instance must be created before callbacks are installed"); if (!Vk::DebugUtilsMessengerEXT::IsSupported(*this)) { NazaraWarning(VK_EXT_DEBUG_UTILS_EXTENSION_NAME " is not supported, cannot install debug message callback"); return; } VkDebugUtilsMessengerCreateInfoEXT callbackCreateInfo = { VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT }; callbackCreateInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; callbackCreateInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; callbackCreateInfo.pfnUserCallback = &DebugCallback; if (!m_internalData->debugMessenger.Create(*this, callbackCreateInfo)) { NazaraWarning("failed to install debug message callback"); return; } } void Instance::DestroyInstance() { assert(m_instance != VK_NULL_HANDLE); m_internalData.reset(); if (vkDestroyInstance) vkDestroyInstance(m_instance, (m_allocator.pfnAllocation) ? &m_allocator : nullptr); } void Instance::ResetPointers() { assert(m_instance != VK_NULL_HANDLE); m_instance = VK_NULL_HANDLE; #define NAZARA_VULKANRENDERER_INSTANCE_FUNCTION(func) func = nullptr; #define NAZARA_VULKANRENDERER_INSTANCE_CORE_EXT_FUNCTION(func, ...) NAZARA_VULKANRENDERER_INSTANCE_FUNCTION(func) #include } } }