1
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include "Application.hpp"
#include <stdexcept>
#include <vector>
#include <cstring>
#include <iostream>
#include <map>
#include <string>
#include <optional>
const std::vector<const char*> validation_layers = {
"VK_LAYER_KHRONOS_validation"
};
#ifdef DEBUG
const bool enable_validation_layers = true;
#else
const bool enable_validation_layers = false;
#endif
#ifdef VERBOSE
const bool verbose = true;
#else
const bool verbose = false;
#endif
struct Application::QueueFamilyIndices{
std::optional<uint32_t> graphics;
bool is_complete(){
return graphics.has_value();
}
};
void Application::run() {
initVulkan();
initWindow();
mainLoop();
cleanUp();
}
void Application::initWindow(){
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
window = glfwCreateWindow(WIDTH, HEIGHT, "DOMK", nullptr, nullptr);
};
void Application::initVulkan(){
createInstance();
createMessenger();
pickPhysicalDevice();
}
void Application::createInstance(){
VkApplicationInfo appInfo{};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = "Hello Triangle";
appInfo.applicationVersion = VK_MAKE_VERSION(0, 0, 0);
appInfo.pEngineName = "DOMK";
appInfo.engineVersion = VK_MAKE_VERSION(0, 0, 0);
appInfo.apiVersion = VK_API_VERSION_1_0;
VkInstanceCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{};
if(enable_validation_layers){
createInfo.ppEnabledLayerNames = validation_layers.data();
createInfo.enabledLayerCount = static_cast<uint32_t>(validation_layers.size());
populateDebugMessengerCreateInfo(debugCreateInfo);
createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*) &debugCreateInfo;
} else {
createInfo.enabledLayerCount = 0;
createInfo.pNext = nullptr;
}
std::vector<const char*> extensions = getRequiredExtensions();
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
createInfo.ppEnabledExtensionNames = extensions.data();
VkResult result = vkCreateInstance(&createInfo, nullptr, &instance);
if(result != VK_SUCCESS)
throw std::runtime_error("Could not create VkInstance.");
else if(enable_validation_layers && !checkValidationLayerSupport())
throw std::runtime_error("Validation layer requested but not supported.");
};
bool Application::checkValidationLayerSupport(){
uint32_t layerCount;
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
std::vector<VkLayerProperties> available_layers(layerCount);
vkEnumerateInstanceLayerProperties(&layerCount, available_layers.data());
for (const char* layerName : validation_layers) {
bool layerFound = false;
for (const auto& layerProperties : available_layers) {
if (strcmp(layerName, layerProperties.layerName) == 0) {
layerFound = true;
break;
}
}
if (!layerFound) {
return false;
}
}
return true;
}
std::vector<const char*> Application::getRequiredExtensions() {
uint32_t glfwExtensionCount = 0;
const char** glfwExtensions;
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
if (enable_validation_layers) {
std::cout<< "ADDED VK_EXT_debug_utils" << std::endl;
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
return extensions;
}
void Application::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
if(verbose){
createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
} else {
createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
}
createInfo.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;
createInfo.pfnUserCallback = debugCallback;
createInfo.pUserData = nullptr;
}
void Application::createMessenger(){
if(!enable_validation_layers) return;
VkDebugUtilsMessengerCreateInfoEXT createInfo{};
populateDebugMessengerCreateInfo(createInfo);
auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
if(func != nullptr){
func(instance, &createInfo, nullptr , &debug_messenger);
} else {
throw std::runtime_error("Could not create debug messenger.");
}
}
VKAPI_ATTR VkBool32 VKAPI_CALL Application::debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) {
std::map<VkDebugUtilsMessageSeverityFlagBitsEXT, std::string> severity_translation_dict = {
{VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT, "Verbose"},
{VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT, "Info"},
{VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT, "Warning"},
{VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, "Error"},
{VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT, "MAXFLAGS"}
};
std::cout << "[VL, " << severity_translation_dict[messageSeverity] << "] :" << pCallbackData->pMessage << std::endl;
return VK_FALSE;
}
void Application::pickPhysicalDevice(){
uint32_t device_count;
vkEnumeratePhysicalDevices(instance, &device_count, nullptr);
if(device_count == 0){
std::runtime_error("Could not find any GPU's with vulkan support.");
}
std::vector<VkPhysicalDevice> devices(device_count);
vkEnumeratePhysicalDevices(instance, &device_count, devices.data());
std::map<uint32_t, VkPhysicalDevice, std::greater<int>> points;
for (const VkPhysicalDevice device : devices) {
if(isDeviceSuitable(device)){
uint32_t device_points = rateSuitability(device);
points.insert(std::make_pair(device_points, device));
}
}
if(points.empty()){
throw std::runtime_error("Failed to find a suitable GPU.");
} else {
physical_device = points.begin()->second;
std::cout << physical_device << std::endl;
}
}
Application::QueueFamilyIndices Application::findQueueFamilies(VkPhysicalDevice device){
QueueFamilyIndices indices;
uint32_t queue_family_count = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, nullptr);
std::vector<VkQueueFamilyProperties> family_properties(queue_family_count);
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, family_properties.data());
uint16_t i = 0;
for(const VkQueueFamilyProperties &property : family_properties){
if(property.queueFlags & VK_QUEUE_GRAPHICS_BIT){
indices.graphics = i;
}
if(indices.is_complete()){
break;
}
i++;
}
return indices;
}
bool Application::isDeviceSuitable(VkPhysicalDevice device){
QueueFamilyIndices indices = findQueueFamilies(device);
return indices.is_complete();
}
int Application::rateSuitability(VkPhysicalDevice physical_device) {
uint32_t score = 0;
VkPhysicalDeviceFeatures device_features;
vkGetPhysicalDeviceFeatures(physical_device, &device_features);
if (!device_features.geometryShader) {
return 0;
}
VkPhysicalDeviceProperties device_properties;
vkGetPhysicalDeviceProperties(physical_device, &device_properties);
if (device_properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
score += 1;
}
score += device_properties.limits.maxGeometryShaderInvocations;
return score;
}
void Application::mainLoop(){
while(!glfwWindowShouldClose(window)){
glfwPollEvents();
};
};
void Application::cleanUp(){
if(enable_validation_layers){
auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
if (func != nullptr) {
func(instance, debug_messenger, nullptr);
}
}
vkDestroyInstance(instance, nullptr);
glfwDestroyWindow(window);
glfwTerminate();
};
For immediate assistance, please email our customer support: [email protected]