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.
This commit is contained in:
Wunkolo 2023-07-18 21:44:59 -07:00
parent d2241a25bc
commit 870b6a21bf
6 changed files with 336 additions and 5 deletions

View file

@ -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})

View file

@ -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<u32, regNum>& internalRegs);
~RendererVK() override;

View file

@ -0,0 +1,48 @@
#pragma once
#include <span>
#include <type_traits>
#include <utility>
#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 <typename T, typename = std::enable_if_t<vk::isVulkanHandleType<T>::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<ArgsT>(args)...);
}
void beginDebugLabel(vk::CommandBuffer commandBuffer, std::span<const float, 4> color, const char* format, ...);
void insertDebugLabel(vk::CommandBuffer commandBuffer, std::span<const float, 4> color, const char* format, ...);
void endDebugLabel(vk::CommandBuffer commandBuffer);
class DebugLabelScope {
private:
const vk::CommandBuffer commandBuffer;
public:
template <typename... ArgsT>
DebugLabelScope(vk::CommandBuffer targetCommandBuffer, std::span<const float, 4> color, const char* format, ArgsT&&... args)
: commandBuffer(targetCommandBuffer) {
beginDebugLabel(commandBuffer, color, format, std::forward<ArgsT>(args)...);
}
template <typename... ArgsT>
void operator()(std::span<const float, 4> color, const char* format, ArgsT&&... args) const {
insertDebugLabel(commandBuffer, color, format, std::forward<ArgsT>(args)...);
}
~DebugLabelScope() { endDebugLabel(commandBuffer); }
};
} // namespace Vulkan

View file

@ -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<u32, regNum>& 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<PFN_vkGetInstanceProcAddr>("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<vk::PhysicalDevice> 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<vk::PhysicalDeviceFeatures2, vk::PhysicalDeviceTimelineSemaphoreFeatures> deviceFeatureChain = {};
auto& deviceFeatures = deviceFeatureChain.get<vk::PhysicalDeviceFeatures2>().features;
auto& deviceTimelineFeatures = deviceFeatureChain.get<vk::PhysicalDeviceTimelineSemaphoreFeatures>();
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) {}

View file

@ -0,0 +1,156 @@
#include "renderer_vk/vk_debug.hpp"
#include <cstdarg>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <memory>
#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<char[]> objectName = std::make_unique<char[]>(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<std::uintptr_t>(objectHandle);
nameInfo.pObjectName = objectName.get();
if (device.setDebugUtilsObjectNameEXT(nameInfo) != vk::Result::eSuccess) {
// Failed to set object name
}
}
void beginDebugLabel(vk::CommandBuffer commandBuffer, std::span<const float, 4> 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<char[]> objectName = std::make_unique<char[]>(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<const float, 4> 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<char[]> objectName = std::make_unique<char[]>(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

View file

@ -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