More vulkan work

This commit is contained in:
Bailey Harrison 2022-09-19 18:33:56 +01:00
parent ed5eb51535
commit 54587d3bbb
3 changed files with 203 additions and 58 deletions

View File

@ -13,6 +13,9 @@ namespace engine::gfx {
struct ENGINE_API Device { struct ENGINE_API Device {
Device(AppInfo appInfo, SDL_Window* window); Device(AppInfo appInfo, SDL_Window* window);
Device(const Device&) = delete;
Device& operator=(const Device&) = delete;
~Device(); ~Device();
private: private:

View File

@ -14,6 +14,7 @@
#include <iostream> #include <iostream>
#include <optional> #include <optional>
#include <unordered_set>
namespace engine::gfx { namespace engine::gfx {
@ -37,15 +38,151 @@ namespace engine::gfx {
Impl(AppInfo appInfo, SDL_Window* window) Impl(AppInfo appInfo, SDL_Window* window)
{ {
#ifdef NDEBUG #ifdef NDEBUG
findAvailableLayers(m_layerInfo, false); // release mode; don't use validation layer
m_layerInfo = std::make_unique<LayerInfo>(false);
#else #else
findAvailableLayers(m_layerInfo, true); // debug mode; use validation layer
m_layerInfo = std::make_unique<LayerInfo>(true);
#endif #endif
m_instance = std::make_shared<Instance>(appInfo, m_layerInfo, getRequiredVulkanExtensions(window)); m_instance = std::make_shared<Instance>(appInfo, *m_layerInfo, getRequiredVulkanExtensions(window));
m_debugMessenger = std::make_unique<DebugMessenger>(m_instance); m_debugMessenger = std::make_unique<DebugMessenger>(m_instance);
// enumerate physical devices
uint32_t physDeviceCount = 0;
VkResult res;
res = vkEnumeratePhysicalDevices(m_instance->getHandle(), &physDeviceCount, nullptr);
assert(res == VK_SUCCESS);
if (physDeviceCount == 0) {
throw std::runtime_error("No GPU found with vulkan support!");
} }
~Impl() std::vector<VkPhysicalDevice> physicalDevices(physDeviceCount);
{ res = vkEnumeratePhysicalDevices(m_instance->getHandle(), &physDeviceCount, physicalDevices.data());
assert(res == VK_SUCCESS);
// find suitable device
const std::vector<const char*> requiredDeviceExtensions{
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
};
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
for (const auto& dev : physicalDevices) {
uint32_t extensionCount;
res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, nullptr);
assert(res == VK_SUCCESS);
std::vector<VkExtensionProperties> availableExtensions(extensionCount);
res = vkEnumerateDeviceExtensionProperties(dev, nullptr, &extensionCount, availableExtensions.data());
assert(res == VK_SUCCESS);
bool suitable = true;
for (const auto& extToFind : requiredDeviceExtensions) {
bool extFound = false;
for (const auto& ext : availableExtensions) {
if (strcmp(extToFind, ext.extensionName) == 0) {
extFound = true;
}
}
if (!extFound) {
suitable = false;
}
}
if (suitable) {
physicalDevice = dev;
break;
}
}
if (physicalDevice == VK_NULL_HANDLE) {
throw std::runtime_error("No suitable Vulkan physical device found");
}
VkPhysicalDeviceProperties devProps;
vkGetPhysicalDeviceProperties(physicalDevice, &devProps);
TRACE("Physical device to use: {}", devProps.deviceName);
// queue families
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.data());
std::optional<uint32_t> graphicsFamilyIndex;
std::optional<uint32_t> transferFamilyIndex;
std::optional<uint32_t> computeFamilyIndex;
for (uint32_t i = 0; i < queueFamilyCount; i++) {
VkQueueFamilyProperties family = queueFamilies[i];
if (family.queueCount > 0) {
if (graphicsFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
TRACE("GRAPHICS:");
graphicsFamilyIndex = i;
}
if (transferFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_TRANSFER_BIT) {
TRACE("TRANSFER:");
transferFamilyIndex = i;
}
if (computeFamilyIndex.has_value() == false && family.queueFlags & VK_QUEUE_COMPUTE_BIT) {
TRACE("COMPUTE:");
computeFamilyIndex = i;
}
TRACE("\t\ti = {}\t\tcount = {}", i, family.queueCount);
}
}
if (graphicsFamilyIndex.has_value() == false || transferFamilyIndex.has_value() == false) {
throw std::runtime_error("Unable to find a queue with the GRAPHICS family flag");
}
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos{};
// use a set to filter out duplicate indices
std::unordered_set<uint32_t> uniqueQueueFamilies{ graphicsFamilyIndex.value(), transferFamilyIndex.value(), computeFamilyIndex.value() };
float queuePriority = 1.0f;
for (uint32_t family : uniqueQueueFamilies) {
// create a queue for each unique type to ensure that there are queues available for graphics, transfer, and compute
TRACE("Creating queue from family {}", family);
VkDeviceQueueCreateInfo queueCreateInfo{
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.queueFamilyIndex = family,
.queueCount = 1,
.pQueuePriorities = &queuePriority,
};
queueCreateInfos.push_back(queueCreateInfo);
}
VkDeviceCreateInfo deviceCreateInfo{
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.queueCreateInfoCount = (uint32_t)queueCreateInfos.size(),
.pQueueCreateInfos = queueCreateInfos.data(),
// IGNORED: .enabledLayerCount
// IGNORED: .ppEnabledLayerNames
.enabledExtensionCount = (uint32_t)requiredDeviceExtensions.size(),
.ppEnabledExtensionNames = requiredDeviceExtensions.data(),
.pEnabledFeatures = nullptr,
};
VkDevice device;
res = vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &device);
if (res != VK_SUCCESS) {
throw std::runtime_error("Unable to create Vulkan logical device, error code: " + std::to_string(res));
}
volkLoadDevice(device);
vkDestroyDevice(device, nullptr);
} }
private: private:
@ -54,38 +191,37 @@ namespace engine::gfx {
struct LayerInfo { struct LayerInfo {
static constexpr const char* VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation"; LayerInfo(bool useValidation)
std::vector<VkLayerProperties> layersAvailable{};
std::optional<std::vector<VkLayerProperties>::iterator> validationLayer;
} m_layerInfo;
static void findAvailableLayers(LayerInfo& layerInfo, bool useValidation)
{ {
VkResult res; VkResult res;
uint32_t layerCount; uint32_t layerCount;
res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr); res = vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
assert(res == VK_SUCCESS); assert(res == VK_SUCCESS);
layerInfo.layersAvailable.resize(layerCount); layersAvailable.resize(layerCount);
res = vkEnumerateInstanceLayerProperties(&layerCount, layerInfo.layersAvailable.data()); res = vkEnumerateInstanceLayerProperties(&layerCount, layersAvailable.data());
assert(res == VK_SUCCESS); assert(res == VK_SUCCESS);
if (useValidation == true) { if (useValidation == true) {
// find validation layer and print all layers to log // find validation layer and print all layers to log
for (auto it = layerInfo.layersAvailable.begin(); it != layerInfo.layersAvailable.end(); it++) { for (auto it = layersAvailable.begin(); it != layersAvailable.end(); it++) {
if (strncmp(it->layerName, LayerInfo::VALIDATION_LAYER_NAME, 256) == 0) { if (strncmp(it->layerName, LayerInfo::VALIDATION_LAYER_NAME, 256) == 0) {
layerInfo.validationLayer = it; validationLayer = it;
} }
} }
if (layerInfo.validationLayer.has_value() == false) { if (validationLayer.has_value() == false) {
CRITICAL("The validation layer was not found. Quitting."); throw std::runtime_error("The validation layer was not found. Quitting.");
throw std::runtime_error("Validation layer not found");
} }
} }
} }
static constexpr const char* VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation";
std::vector<VkLayerProperties> layersAvailable{};
std::optional<std::vector<VkLayerProperties>::iterator> validationLayer;
};
class Instance { class Instance {
public: public:
@ -93,9 +229,9 @@ namespace engine::gfx {
{ {
VkResult res; VkResult res;
int appVersionMajor, appVersionMinor, appVersionPatch; int appVersionMajor = 0, appVersionMinor = 0, appVersionPatch = 0;
assert(versionFromCharArray(appInfo.version, &appVersionMajor, &appVersionMinor, &appVersionPatch)); assert(versionFromCharArray(appInfo.version, &appVersionMajor, &appVersionMinor, &appVersionPatch));
int engineVersionMajor, engineVersionMinor, engineVersionPatch; int engineVersionMajor = 0, engineVersionMinor = 0, engineVersionPatch = 0;
assert(versionFromCharArray(ENGINE_VERSION, &engineVersionMajor, &engineVersionMinor, &engineVersionPatch)); assert(versionFromCharArray(ENGINE_VERSION, &engineVersionMajor, &engineVersionMinor, &engineVersionPatch));
VkApplicationInfo applicationInfo{ VkApplicationInfo applicationInfo{
@ -112,11 +248,13 @@ namespace engine::gfx {
std::vector<const char*> extensions{}; std::vector<const char*> extensions{};
extensions.insert(extensions.end(), windowExtensions.begin(), windowExtensions.end()); extensions.insert(extensions.end(), windowExtensions.begin(), windowExtensions.end());
// also use debug utils extension
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
std::vector<const char*> layers{}; std::vector<const char*> layers{};
if (layerInfo.validationLayer.has_value()) { if (layerInfo.validationLayer.has_value()) {
layers.push_back(layerInfo.validationLayer.value()->layerName); layers.push_back(layerInfo.validationLayer.value()->layerName);
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
} }
VkInstanceCreateInfo instanceInfo{ VkInstanceCreateInfo instanceInfo{
@ -144,20 +282,19 @@ namespace engine::gfx {
res = vkCreateInstance(&instanceInfo, nullptr, &m_handle); res = vkCreateInstance(&instanceInfo, nullptr, &m_handle);
if (res == VK_ERROR_INCOMPATIBLE_DRIVER) { if (res == VK_ERROR_INCOMPATIBLE_DRIVER) {
CRITICAL("The graphics driver is incompatible with vulkan"); throw std::runtime_error("The graphics driver is incompatible with vulkan");
throw std::runtime_error("Graphics driver is incompatible with Vulkan");
} }
assert(res == VK_SUCCESS); assert(res == VK_SUCCESS);
volkLoadInstance(m_handle); volkLoadInstanceOnly(m_handle);
} }
Instance(const Instance&) = delete;
Instance& operator=(const Instance&) = delete;
~Instance() ~Instance()
{ {
INFO("DESTROYING INSTANCE...");
vkDestroyInstance(m_handle, nullptr); vkDestroyInstance(m_handle, nullptr);
INFO("DESTROYED INSTANCE.");
} }
VkInstance getHandle() VkInstance getHandle()
@ -166,7 +303,6 @@ namespace engine::gfx {
} }
private: private:
VkInstance m_handle; VkInstance m_handle;
}; };
@ -182,15 +318,13 @@ namespace engine::gfx {
assert(res == VK_SUCCESS); assert(res == VK_SUCCESS);
} }
DebugMessenger(const DebugMessenger&) = delete;
DebugMessenger& operator=(const DebugMessenger&) = delete;
~DebugMessenger() ~DebugMessenger()
{ {
INFO("DESTROYING MESSENGER...");
vkDestroyDebugUtilsMessengerEXT(m_instance->getHandle(), m_messengerHandle, nullptr); vkDestroyDebugUtilsMessengerEXT(m_instance->getHandle(), m_messengerHandle, nullptr);
INFO("DESTROYED MESSENGER.");
} }
static constexpr VkDebugUtilsMessageSeverityFlagBitsEXT MESSAGE_LEVEL = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
static VkDebugUtilsMessengerCreateInfoEXT getCreateInfo() static VkDebugUtilsMessengerCreateInfoEXT getCreateInfo()
{ {
VkDebugUtilsMessengerCreateInfoEXT createInfo{ VkDebugUtilsMessengerCreateInfoEXT createInfo{
@ -207,16 +341,16 @@ namespace engine::gfx {
}; };
switch (MESSAGE_LEVEL) { switch (MESSAGE_LEVEL) {
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: case Severity::VERBOSE:
createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
// fall-through // fall-through
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: case Severity::INFO:
createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
// fall-through // fall-through
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: case Severity::WARNING:
createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
// fall-through // fall-through
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: case Severity::ERROR:
createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; createInfo.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
// fall-through // fall-through
default: default:
@ -230,6 +364,14 @@ namespace engine::gfx {
VkDebugUtilsMessengerEXT m_messengerHandle; VkDebugUtilsMessengerEXT m_messengerHandle;
std::shared_ptr<Instance> m_instance; std::shared_ptr<Instance> m_instance;
enum class Severity {
VERBOSE,
INFO,
WARNING,
ERROR
};
static constexpr Severity MESSAGE_LEVEL = Severity::WARNING;
static VkBool32 messengerCallback( static VkBool32 messengerCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageTypes, VkDebugUtilsMessageTypeFlagsEXT messageTypes,
@ -267,6 +409,7 @@ namespace engine::gfx {
}; };
std::unique_ptr<LayerInfo> m_layerInfo;
std::shared_ptr<Instance> m_instance; std::shared_ptr<Instance> m_instance;
std::unique_ptr<DebugMessenger> m_debugMessenger; std::unique_ptr<DebugMessenger> m_debugMessenger;
@ -288,7 +431,6 @@ namespace engine::gfx {
VkResult res; VkResult res;
res = volkInitialize(); res = volkInitialize();
if (res == VK_ERROR_INITIALIZATION_FAILED) { if (res == VK_ERROR_INITIALIZATION_FAILED) {
CRITICAL("Unable to load vulkan, is it installed?");
throw std::runtime_error("Unable to load vulkan, is it installed?"); throw std::runtime_error("Unable to load vulkan, is it installed?");
} }
assert(res == VK_SUCCESS); assert(res == VK_SUCCESS);

View File

@ -89,8 +89,8 @@ void Window::resetInputDeltas()
m_keyboard.deltas.fill(ButtonDelta::SAME); m_keyboard.deltas.fill(ButtonDelta::SAME);
m_mouse.deltas.fill(ButtonDelta::SAME); m_mouse.deltas.fill(ButtonDelta::SAME);
m_mouse.dx = 0.0f; m_mouse.dx = 0;
m_mouse.dy = 0.0f; m_mouse.dy = 0;
m_mouse.xscroll = 0.0f; m_mouse.xscroll = 0.0f;
m_mouse.yscroll = 0.0f; m_mouse.yscroll = 0.0f;
} }
@ -143,14 +143,14 @@ void Window::onMouseButtonEvent(SDL_MouseButtonEvent &e)
button = inputs::MouseButton::M_X2; button = inputs::MouseButton::M_X2;
break; break;
} }
int buttonIndex = static_cast<int>(button);
bool buttonWasDown = m_mouse.buttons[static_cast<int>(button)]; bool buttonWasDown = m_mouse.buttons.at(buttonIndex);
bool buttonIsDown = (e.state == SDL_PRESSED); bool buttonIsDown = (e.state == SDL_PRESSED);
m_mouse.buttons[static_cast<int>(button)] = buttonIsDown; m_mouse.buttons.at(buttonIndex) = buttonIsDown;
if (buttonIsDown != buttonWasDown) { // (if button was pressed or released) if (buttonIsDown != buttonWasDown) { // (if button was pressed or released)
// only sets delta if it hasn't already been set this frame (to detect very fast presses) // only sets delta if it hasn't already been set this frame (to detect very fast presses)
if (m_mouse.deltas[static_cast<int>(button)] == ButtonDelta::SAME) { if (m_mouse.deltas[buttonIndex] == ButtonDelta::SAME) {
m_mouse.deltas[static_cast<int>(button)] = buttonIsDown ? ButtonDelta::PRESSED : ButtonDelta::RELEASED; m_mouse.deltas[buttonIndex] = buttonIsDown ? ButtonDelta::PRESSED : ButtonDelta::RELEASED;
} }
} }
} }