From 870b6a21bf8bf411b8d628ca70ff3f2839256cc2 Mon Sep 17 00:00:00 2001 From: Wunkolo Date: Tue, 18 Jul 2023 21:44:59 -0700 Subject: [PATCH] Add initial vulkan instance creation Headlessly creates a new vulkan instance, with conditional MacOS support, and enables the `VK_EXT_debug_utils` instance-extension with a debug-messenger to hook onto validation layer messages. --- CMakeLists.txt | 4 +- include/renderer_vk/renderer_vk.hpp | 6 ++ include/renderer_vk/vk_debug.hpp | 48 +++++++++ src/core/renderer_vk/renderer_vk.cpp | 123 ++++++++++++++++++++- src/core/renderer_vk/vk_debug.cpp | 156 +++++++++++++++++++++++++++ src/emulator.cpp | 4 +- 6 files changed, 336 insertions(+), 5 deletions(-) create mode 100644 include/renderer_vk/vk_debug.hpp create mode 100644 src/core/renderer_vk/vk_debug.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ae4b8c00..a174a1ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -214,11 +214,11 @@ if(ENABLE_VULKAN) COMPONENTS glslangValidator ) set(RENDERER_VK_INCLUDE_FILES include/renderer_vk/renderer_vk.hpp - include/renderer_vk/vulkan_api.hpp + include/renderer_vk/vulkan_api.hpp include/renderer_vk/vk_debug.hpp ) set(RENDERER_VK_SOURCE_FILES src/core/renderer_vk/renderer_vk.cpp - src/core/renderer_vk/vulkan_api.cpp + src/core/renderer_vk/vulkan_api.cpp src/core/renderer_vk/vk_debug.cpp ) set(HEADER_FILES ${HEADER_FILES} ${RENDERER_VK_INCLUDE_FILES}) diff --git a/include/renderer_vk/renderer_vk.hpp b/include/renderer_vk/renderer_vk.hpp index b825692b..7a2f9a55 100644 --- a/include/renderer_vk/renderer_vk.hpp +++ b/include/renderer_vk/renderer_vk.hpp @@ -4,6 +4,12 @@ class GPU; class RendererVK final : public Renderer { + vk::UniqueInstance instance = {}; + vk::PhysicalDevice physicalDevice = {}; + vk::UniqueDevice device = {}; + + vk::UniqueDebugUtilsMessengerEXT debugMessenger; + public: RendererVK(GPU& gpu, const std::array& internalRegs); ~RendererVK() override; diff --git a/include/renderer_vk/vk_debug.hpp b/include/renderer_vk/vk_debug.hpp new file mode 100644 index 00000000..afc367dc --- /dev/null +++ b/include/renderer_vk/vk_debug.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include + +#include "vulkan_api.hpp" + +namespace Vulkan { + + VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* callbackData, void* userData + ); + + void setObjectName(vk::Device device, vk::ObjectType objectType, const void* objectHandle, const char* format, ...); + + template ::value == true>, typename... ArgsT> + inline void setObjectName(vk::Device device, const T objectHandle, const char* format, ArgsT&&... args) { + setObjectName(device, T::objectType, objectHandle, format, std::forward(args)...); + } + + void beginDebugLabel(vk::CommandBuffer commandBuffer, std::span color, const char* format, ...); + + void insertDebugLabel(vk::CommandBuffer commandBuffer, std::span color, const char* format, ...); + + void endDebugLabel(vk::CommandBuffer commandBuffer); + + class DebugLabelScope { + private: + const vk::CommandBuffer commandBuffer; + + public: + template + DebugLabelScope(vk::CommandBuffer targetCommandBuffer, std::span color, const char* format, ArgsT&&... args) + : commandBuffer(targetCommandBuffer) { + beginDebugLabel(commandBuffer, color, format, std::forward(args)...); + } + + template + void operator()(std::span color, const char* format, ArgsT&&... args) const { + insertDebugLabel(commandBuffer, color, format, std::forward(args)...); + } + + ~DebugLabelScope() { endDebugLabel(commandBuffer); } + }; + +} // namespace Vulkan \ No newline at end of file diff --git a/src/core/renderer_vk/renderer_vk.cpp b/src/core/renderer_vk/renderer_vk.cpp index 22922347..6c5f3362 100644 --- a/src/core/renderer_vk/renderer_vk.cpp +++ b/src/core/renderer_vk/renderer_vk.cpp @@ -1,5 +1,8 @@ #include "renderer_vk/renderer_vk.hpp" +#include "helpers.hpp" +#include "renderer_vk/vk_debug.hpp" + RendererVK::RendererVK(GPU& gpu, const std::array& internalRegs) : Renderer(gpu, internalRegs) {} RendererVK::~RendererVK() {} @@ -8,7 +11,125 @@ void RendererVK::reset() {} void RendererVK::display() {} -void RendererVK::initGraphicsContext() { static vk::DynamicLoader dl; } +void RendererVK::initGraphicsContext() { + // Resolve all function pointers + static vk::DynamicLoader dl; + VULKAN_HPP_DEFAULT_DISPATCHER.init(dl.getProcAddress("vkGetInstanceProcAddr")); + + // Create Instance + vk::ApplicationInfo applicationInfo = {}; + applicationInfo.apiVersion = VK_API_VERSION_1_1; + + applicationInfo.pEngineName = "Alber"; + applicationInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); + + applicationInfo.pApplicationName = "Alber"; + applicationInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + + vk::InstanceCreateInfo instanceInfo = {}; + + instanceInfo.pApplicationInfo = &applicationInfo; + + static const std::array instanceExtensions = std::to_array({ +#if defined(__APPLE__) + VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME +#endif + VK_EXT_DEBUG_UTILS_EXTENSION_NAME, + }); + +#if defined(__APPLE__) + InstanceInfo.flags |= vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR; +#endif + + instanceInfo.ppEnabledExtensionNames = instanceExtensions.data(); + instanceInfo.enabledExtensionCount = instanceExtensions.size(); + + if (auto createResult = vk::createInstanceUnique(instanceInfo); createResult.result == vk::Result::eSuccess) { + instance = std::move(createResult.value); + } else { + Helpers::panic("Error creating Vulkan instance: %s\n", vk::to_string(createResult.result).c_str()); + } + // Initialize instance-specific function pointers + VULKAN_HPP_DEFAULT_DISPATCHER.init(instance.get()); + + // Enable debug messenger if the instance was able to be created with debug_utils + if (std::find( + instanceExtensions.begin(), instanceExtensions.end(), + // std::string_view has a way to compare itself to `const char*` + // so by casting it, we get the actual string comparisons + // and not pointer-comparisons + std::string_view(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) + ) != instanceExtensions.end()) { + vk::DebugUtilsMessengerCreateInfoEXT debugCreateInfo{}; + debugCreateInfo.messageSeverity = vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo | + vk::DebugUtilsMessageSeverityFlagBitsEXT::eError | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning; + debugCreateInfo.messageType = vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | + vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral; + debugCreateInfo.pfnUserCallback = &Vulkan::debugMessageCallback; + if (auto createResult = instance->createDebugUtilsMessengerEXTUnique(debugCreateInfo); createResult.result == vk::Result::eSuccess) { + debugMessenger = std::move(createResult.value); + } else { + Helpers::warn("Error registering debug messenger: %s", vk::to_string(createResult.result).c_str()); + } + } + + // Pick physical device + if (auto EnumerateResult = instance->enumeratePhysicalDevices(); EnumerateResult.result == vk::Result::eSuccess) { + std::vector PhysicalDevices = std::move(EnumerateResult.value); + + // Prefer Discrete GPUs + const auto IsDiscrete = [](const vk::PhysicalDevice& PhysicalDevice) -> bool { + return PhysicalDevice.getProperties().deviceType == vk::PhysicalDeviceType::eDiscreteGpu; + }; + + std::partition(PhysicalDevices.begin(), PhysicalDevices.end(), IsDiscrete); + + // Pick the "best" out of all of the previous criteria + physicalDevice = PhysicalDevices.front(); + } else { + Helpers::panic("Error enumerating physical devices: %s\n", vk::to_string(EnumerateResult.result).c_str()); + } + + // Create Device + vk::DeviceCreateInfo deviceInfo = {}; + + static const char* deviceExtensions[] = { +#if defined(__APPLE__) + "VK_KHR_portability_subset", +#endif + VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME + }; + deviceInfo.ppEnabledExtensionNames = deviceExtensions; + deviceInfo.enabledExtensionCount = std::size(deviceExtensions); + + vk::StructureChain deviceFeatureChain = {}; + + auto& deviceFeatures = deviceFeatureChain.get().features; + + auto& deviceTimelineFeatures = deviceFeatureChain.get(); + deviceTimelineFeatures.timelineSemaphore = true; + + deviceInfo.pNext = &deviceFeatureChain.get(); + + static const float queuePriority = 1.0f; + + vk::DeviceQueueCreateInfo queueInfo = {}; + queueInfo.queueFamilyIndex = 0; + queueInfo.queueCount = 1; + queueInfo.pQueuePriorities = &queuePriority; + + deviceInfo.queueCreateInfoCount = 1; + deviceInfo.pQueueCreateInfos = &queueInfo; + + if (auto createResult = physicalDevice.createDeviceUnique(deviceInfo); createResult.result == vk::Result::eSuccess) { + device = std::move(createResult.value); + } else { + Helpers::panic("Error creating logical device: %s\n", vk::to_string(createResult.result).c_str()); + } + + // Initialize device-specific function pointers + VULKAN_HPP_DEFAULT_DISPATCHER.init(device.get()); +} void RendererVK::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {} diff --git a/src/core/renderer_vk/vk_debug.cpp b/src/core/renderer_vk/vk_debug.cpp new file mode 100644 index 00000000..973d2a09 --- /dev/null +++ b/src/core/renderer_vk/vk_debug.cpp @@ -0,0 +1,156 @@ +#include "renderer_vk/vk_debug.hpp" + +#include +#include +#include +#include +#include + +#include "helpers.hpp" + +static std::uint8_t severityColor(vk::DebugUtilsMessageSeverityFlagBitsEXT Severity) { + switch (Severity) { + case vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose: { + // Dark Gray + return 90u; + } + case vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo: { + // Light Gray + return 90u; + } + case vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning: { + // Light Magenta + return 95u; + } + case vk::DebugUtilsMessageSeverityFlagBitsEXT::eError: { + // Light red + return 91u; + } + } + // Default Foreground Color + return 39u; +} + +static std::uint8_t messageTypeColor(vk::DebugUtilsMessageTypeFlagsEXT MessageType) { + if (MessageType & vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral) { + // Dim + return 2u; + } + if (MessageType & vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance) { + // Bold/Bright + return 1u; + } + if (MessageType & vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation) { + // Light Gray + return 90u; + } + // Default Foreground Color + return 39u; +} + +namespace Vulkan { + + static void debugMessageCallback( + vk::DebugUtilsMessageSeverityFlagBitsEXT MessageSeverity, vk::DebugUtilsMessageTypeFlagsEXT MessageType, + const vk::DebugUtilsMessengerCallbackDataEXT& CallbackData + ) { + Helpers::debug_printf( + "\033[%um[vk][%s]: \033[%um%s\033[0m\n", severityColor(MessageSeverity), CallbackData.pMessageIdName, messageTypeColor(MessageType), + CallbackData.pMessage + ); + } + + VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* callbackData, void* userData + ) { + debugMessageCallback( + vk::DebugUtilsMessageSeverityFlagBitsEXT(messageSeverity), vk::DebugUtilsMessageTypeFlagsEXT(messageType), *callbackData + ); + return VK_FALSE; + } + + void setObjectName(vk::Device device, vk::ObjectType objectType, const void* objectHandle, const char* format, ...) { + va_list args; + va_start(args, format); + const auto nameLength = std::vsnprintf(nullptr, 0, format, args); + va_end(args); + if (nameLength < 0) { + // Invalid vsnprintf + return; + } + + std::unique_ptr objectName = std::make_unique(std::size_t(nameLength) + 1u); + + // Write formatted object name + va_start(args, format); + std::vsnprintf(objectName.get(), std::size_t(nameLength) + 1u, format, args); + va_end(args); + + vk::DebugUtilsObjectNameInfoEXT nameInfo = {}; + nameInfo.objectType = objectType; + nameInfo.objectHandle = reinterpret_cast(objectHandle); + nameInfo.pObjectName = objectName.get(); + + if (device.setDebugUtilsObjectNameEXT(nameInfo) != vk::Result::eSuccess) { + // Failed to set object name + } + } + + void beginDebugLabel(vk::CommandBuffer commandBuffer, std::span color, const char* format, ...) { + va_list args; + va_start(args, format); + const auto nameLength = std::vsnprintf(nullptr, 0, format, args); + va_end(args); + if (nameLength < 0) { + // Invalid vsnprintf + return; + } + + std::unique_ptr objectName = std::make_unique(std::size_t(nameLength) + 1u); + + // Write formatted object name + va_start(args, format); + std::vsnprintf(objectName.get(), std::size_t(nameLength) + 1u, format, args); + va_end(args); + + vk::DebugUtilsLabelEXT labelInfo = {}; + labelInfo.pLabelName = objectName.get(); + labelInfo.color[0] = color[0]; + labelInfo.color[1] = color[1]; + labelInfo.color[2] = color[2]; + labelInfo.color[3] = color[3]; + + commandBuffer.beginDebugUtilsLabelEXT(labelInfo); + } + + void insertDebugLabel(vk::CommandBuffer commandBuffer, std::span color, const char* format, ...) { + va_list args; + va_start(args, format); + const auto nameLength = std::vsnprintf(nullptr, 0, format, args); + va_end(args); + if (nameLength < 0) { + // Invalid vsnprintf + return; + } + + std::unique_ptr objectName = std::make_unique(std::size_t(nameLength) + 1u); + + // Write formatted object name + va_start(args, format); + std::vsnprintf(objectName.get(), std::size_t(nameLength) + 1u, format, args); + va_end(args); + + vk::DebugUtilsLabelEXT labelInfo = {}; + labelInfo.pLabelName = objectName.get(); + labelInfo.color[0] = color[0]; + labelInfo.color[1] = color[1]; + labelInfo.color[2] = color[2]; + labelInfo.color[3] = color[3]; + + commandBuffer.insertDebugUtilsLabelEXT(labelInfo); + } + + void endDebugLabel(vk::CommandBuffer commandBuffer) { commandBuffer.endDebugUtilsLabelEXT(); } + +} // namespace Vulkan \ No newline at end of file diff --git a/src/emulator.cpp b/src/emulator.cpp index 8ad81eb6..7355c2e0 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -54,10 +54,10 @@ Emulator::Emulator() #ifdef PANDA3DS_ENABLE_VULKAN if (config.rendererType == RendererType::Vulkan) { - window = SDL_CreateWindow("Alber", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_VULKAN); + // window = SDL_CreateWindow("Alber", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_VULKAN); if (window == nullptr) { - Helpers::panic("Window creation failed: %s", SDL_GetError()); + // Helpers::panic("Window creation failed: %s", SDL_GetError()); } } #endif