From fa7ae9ac1c999c1160ad87c02e80bfdca2c2a991 Mon Sep 17 00:00:00 2001 From: onTheZero <114521696+onTheZero@users.noreply.github.com> Date: Mon, 5 Jan 2026 10:36:27 -0500 Subject: [PATCH] Initial Gitea Commit --- .clang-format | 1 + .editorconfig | 179 ++++++++++++++++++++++ GlfwWindowManager.cpp | 185 ++++++++++++++++++++++ GlfwWindowManager.h | 61 ++++++++ Learning Vulkan.cpp | 261 -------------------------------- Learning Vulkan.vcxproj | 15 +- Learning Vulkan.vcxproj.filters | 29 +++- Logger.cpp | 64 ++++++++ Logger.h | 76 +++++++--- VkDebug.h | 77 ++++++++++ VkSetup.h | 65 ++++++++ Vulkan notes/Basics.md | 7 + VulkanDebugManager.cpp | 119 +++++++++++++++ VulkanDebugManager.h | 58 +++++++ VulkanDeviceManager.cpp | 111 ++++++++++++++ VulkanDeviceManager.h | 47 ++++++ VulkanInstanceManager.cpp | 177 ++++++++++++++++++++++ VulkanInstanceManager.h | 53 +++++++ main.cpp | 75 +++++++++ 19 files changed, 1372 insertions(+), 288 deletions(-) create mode 100644 .editorconfig create mode 100644 GlfwWindowManager.cpp create mode 100644 GlfwWindowManager.h delete mode 100644 Learning Vulkan.cpp create mode 100644 VkDebug.h create mode 100644 VkSetup.h create mode 100644 VulkanDebugManager.cpp create mode 100644 VulkanDebugManager.h create mode 100644 VulkanDeviceManager.cpp create mode 100644 VulkanDeviceManager.h create mode 100644 VulkanInstanceManager.cpp create mode 100644 VulkanInstanceManager.h create mode 100644 main.cpp diff --git a/.clang-format b/.clang-format index f243ded..b03980c 100644 --- a/.clang-format +++ b/.clang-format @@ -12,6 +12,7 @@ AllowShortBlocksOnASingleLine: Empty AllowShortEnumsOnASingleLine: false AllowShortFunctionsOnASingleLine: Inline AllowShortLambdasOnASingleLine: All +InsertBraces: true BraceWrapping: AfterCaseLabel: true AfterClass: true diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..ca1b9dc --- /dev/null +++ b/.editorconfig @@ -0,0 +1,179 @@ +# Visual Studio generated .editorconfig file with C++ settings. +root = true + +[*.{c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}] + +# Visual C++ Code Style settings + +cpp_generate_documentation_comments = xml + +# Visual C++ Formatting settings + +indent_style = tab +indent_size = 4 +tab_width= 4 +cpp_indent_braces = false +cpp_indent_multi_line_relative_to = innermost_parenthesis +cpp_indent_within_parentheses = indent +cpp_indent_preserve_within_parentheses = false +cpp_indent_case_contents = true +cpp_indent_case_labels = false +cpp_indent_case_contents_when_block = false +cpp_indent_lambda_braces_when_parameter = true +cpp_indent_goto_labels = one_left +cpp_indent_preprocessor = leftmost_column +cpp_indent_access_specifiers = false +cpp_indent_namespace_contents = true +cpp_indent_preserve_comments = false +cpp_new_line_before_open_brace_namespace = ignore +cpp_new_line_before_open_brace_type = ignore +cpp_new_line_before_open_brace_function = ignore +cpp_new_line_before_open_brace_block = ignore +cpp_new_line_before_open_brace_lambda = ignore +cpp_new_line_scope_braces_on_separate_lines = false +cpp_new_line_close_brace_same_line_empty_type = false +cpp_new_line_close_brace_same_line_empty_function = false +cpp_new_line_before_catch = true +cpp_new_line_before_else = true +cpp_new_line_before_while_in_do_while = false +cpp_space_before_function_open_parenthesis = remove +cpp_space_within_parameter_list_parentheses = false +cpp_space_between_empty_parameter_list_parentheses = false +cpp_space_after_keywords_in_control_flow_statements = true +cpp_space_within_control_flow_statement_parentheses = false +cpp_space_before_lambda_open_parenthesis = false +cpp_space_within_cast_parentheses = false +cpp_space_after_cast_close_parenthesis = false +cpp_space_within_expression_parentheses = false +cpp_space_before_block_open_brace = true +cpp_space_between_empty_braces = false +cpp_space_before_initializer_list_open_brace = false +cpp_space_within_initializer_list_braces = true +cpp_space_preserve_in_initializer_list = true +cpp_space_before_open_square_bracket = false +cpp_space_within_square_brackets = false +cpp_space_before_empty_square_brackets = false +cpp_space_between_empty_square_brackets = false +cpp_space_group_square_brackets = true +cpp_space_within_lambda_brackets = false +cpp_space_between_empty_lambda_brackets = false +cpp_space_before_comma = false +cpp_space_after_comma = true +cpp_space_remove_around_member_operators = true +cpp_space_before_inheritance_colon = true +cpp_space_before_constructor_colon = true +cpp_space_remove_before_semicolon = true +cpp_space_after_semicolon = true +cpp_space_remove_around_unary_operator = true +cpp_space_around_binary_operator = insert +cpp_space_around_assignment_operator = insert +cpp_space_pointer_reference_alignment = left +cpp_space_around_ternary_operator = insert +cpp_use_unreal_engine_macro_formatting = true +cpp_wrap_preserve_blocks = one_liners + +# Visual C++ Include Cleanup settings + +cpp_include_cleanup_add_missing_error_tag_type = suggestion +cpp_include_cleanup_remove_unused_error_tag_type = dimmed +cpp_include_cleanup_optimize_unused_error_tag_type = suggestion +cpp_include_cleanup_sort_after_edits = false +cpp_sort_includes_error_tag_type = none +cpp_sort_includes_priority_case_sensitive = false +cpp_sort_includes_priority_style = quoted +cpp_includes_style = default +cpp_includes_use_forward_slash = true + +# Unreal naming conventions + +[*.{cpp,h}] + +# Naming convention rules (note: currently need to be ordered from more to less specific) + +cpp_naming_rule.aactor_prefixed.symbols = aactor_class +cpp_naming_rule.aactor_prefixed.style = aactor_style + +cpp_naming_rule.swidget_prefixed.symbols = swidget_class +cpp_naming_rule.swidget_prefixed.style = swidget_style + +cpp_naming_rule.uobject_prefixed.symbols = uobject_class +cpp_naming_rule.uobject_prefixed.style = uobject_style + +cpp_naming_rule.booleans_prefixed.symbols = boolean_vars +cpp_naming_rule.booleans_prefixed.style = boolean_style + +cpp_naming_rule.structs_prefixed.symbols = structs +cpp_naming_rule.structs_prefixed.style = unreal_engine_structs + +cpp_naming_rule.enums_prefixed.symbols = enums +cpp_naming_rule.enums_prefixed.style = unreal_engine_enums + +cpp_naming_rule.templates_prefixed.symbols = templates +cpp_naming_rule.templates_prefixed.style = unreal_engine_templates + +cpp_naming_rule.general_names.symbols = all_symbols +cpp_naming_rule.general_names.style = unreal_engine_default + +# Naming convention symbols + +cpp_naming_symbols.aactor_class.applicable_kinds = class +cpp_naming_symbols.aactor_class.applicable_type = AActor + +cpp_naming_symbols.swidget_class.applicable_kinds = class +cpp_naming_symbols.swidget_class.applicable_type = SWidget + +cpp_naming_symbols.uobject_class.applicable_kinds = class +cpp_naming_symbols.uobject_class.applicable_type = UObject + +cpp_naming_symbols.boolean_vars.applicable_kinds = local,parameter,field +cpp_naming_symbols.boolean_vars.applicable_type = bool + +cpp_naming_symbols.enums.applicable_kinds = enum + +cpp_naming_symbols.templates.applicable_kinds = template_class + +cpp_naming_symbols.structs.applicable_kinds = struct + +cpp_naming_symbols.all_symbols.applicable_kinds = * + +# Naming convention styles + +cpp_naming_style.unreal_engine_default.capitalization = pascal_case +cpp_naming_style.unreal_engine_default.required_prefix = +cpp_naming_style.unreal_engine_default.required_suffix = +cpp_naming_style.unreal_engine_default.word_separator = + +cpp_naming_style.unreal_engine_enums.capitalization = pascal_case +cpp_naming_style.unreal_engine_enums.required_prefix = E +cpp_naming_style.unreal_engine_enums.required_suffix = +cpp_naming_style.unreal_engine_enums.word_separator = + +cpp_naming_style.unreal_engine_templates.capitalization = pascal_case +cpp_naming_style.unreal_engine_templates.required_prefix = T +cpp_naming_style.unreal_engine_templates.required_suffix = +cpp_naming_style.unreal_engine_templates.word_separator = + +cpp_naming_style.unreal_engine_structs.capitalization = pascal_case +cpp_naming_style.unreal_engine_structs.required_prefix = F +cpp_naming_style.unreal_engine_structs.required_suffix = +cpp_naming_style.unreal_engine_structs.word_separator = + +cpp_naming_style.uobject_style.capitalization = pascal_case +cpp_naming_style.uobject_style.required_prefix = U +cpp_naming_style.uobject_style.required_suffix = +cpp_naming_style.uobject_style.word_separator = + +cpp_naming_style.aactor_style.capitalization = pascal_case +cpp_naming_style.aactor_style.required_prefix = A +cpp_naming_style.aactor_style.required_suffix = +cpp_naming_style.aactor_style.word_separator = + +cpp_naming_style.swidget_style.capitalization = pascal_case +cpp_naming_style.swidget_style.required_prefix = S +cpp_naming_style.swidget_style.required_suffix = +cpp_naming_style.swidget_style.word_separator = + +cpp_naming_style.boolean_style.capitalization = pascal_case +cpp_naming_style.boolean_style.required_prefix = b +cpp_naming_style.boolean_style.required_suffix = +cpp_naming_style.boolean_style.word_separator = diff --git a/GlfwWindowManager.cpp b/GlfwWindowManager.cpp new file mode 100644 index 0000000..cc7348d --- /dev/null +++ b/GlfwWindowManager.cpp @@ -0,0 +1,185 @@ +#include "GlfwWindowManager.h" + +#include +#include "Logger.h" + +// bool WindowManager::bGlfwInitialized = false; + +GlfwWindowManager::GlfwWindowManager() = default; + +GlfwWindowManager::~GlfwWindowManager() +{ + Cleanup(); +} + +GlfwWindowManager::GlfwWindowManager(GlfwWindowManager&& Other) noexcept + : Window(Other.Window), Title(std::move(Other.Title)), Width(Other.Width), Height(Other.Height) +{ + Other.Window = nullptr; +} + +GlfwWindowManager& GlfwWindowManager::operator=(GlfwWindowManager&& Other) noexcept +{ + if (this != &Other) + { + Cleanup(); + + Window = Other.Window; + Title = std::move(Other.Title); + Width = Other.Width; + Height = Other.Height; + + Other.Window = nullptr; + } + + return *this; +} + +void GlfwWindowManager::Initialize(const FWindowConfig& Config) +{ + if (IsInitialized()) + { + return; + } + + InitializeGlfw(); + + Title = Config.Title; + Width = Config.Width; + Height = Config.Height; + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, Config.bResizable ? GLFW_TRUE : GLFW_FALSE); + + Window = glfwCreateWindow( + Width, + Height, + Title.c_str(), + Config.bFullscreen ? glfwGetPrimaryMonitor() : nullptr, + nullptr); + + if (!Window) + { + Log::Error("Failed to create GLFW window"); + } +} + +void GlfwWindowManager::Cleanup() +{ + if (!IsInitialized()) + { + Log::Warning("Not initialized"); + return; + } + + DestroyWindow(); + + glfwTerminate(); +} + +bool GlfwWindowManager::ShouldClose() const +{ + return Window && glfwWindowShouldClose(Window); +} + +void GlfwWindowManager::PollEvents() +{ + if (Window) + { + glfwPollEvents(); + } +} + +void GlfwWindowManager::WaitEvents() const +{ + if (Window) + { + glfwWaitEvents(); + } +} + +void GlfwWindowManager::SetTitle(const std::string& Title) +{ + this->Title = Title; + + if (Window) + { + glfwSetWindowTitle(Window, Title.c_str()); + } +} + +VkSurfaceKHR GlfwWindowManager::CreateSurface(VkInstance Instance) const +{ + if (!Window || !Instance) + { + return VK_NULL_HANDLE; + } + + VkSurfaceKHR Surface = VK_NULL_HANDLE; + if (glfwCreateWindowSurface(Instance, Window, nullptr, &Surface) != VK_SUCCESS) + { + Log::Error("Failed to create window surface"); + } + + return Surface; +} + +void GlfwWindowManager::SetResizeCallback(GLFWwindowsizefun Callback) +{ + if (Window) + { + glfwSetWindowSizeCallback(Window, Callback); + } +} + +void GlfwWindowManager::SetKeyCallback(GLFWkeyfun Callback) +{ + if (Window) + { + glfwSetKeyCallback(Window, Callback); + } +} + +void GlfwWindowManager::SetMouseButtonCallback(GLFWmousebuttonfun Callback) +{ + if (Window) + { + glfwSetMouseButtonCallback(Window, Callback); + } +} + +void GlfwWindowManager::SetCursorPositionCallback(GLFWcursorposfun Callback) +{ + if (Window) + { + glfwSetCursorPosCallback(Window, Callback); + } +} + +void GlfwWindowManager::SetScrollCallback(GLFWscrollfun Callback) +{ + if (Window) + { + glfwSetScrollCallback(Window, Callback); + } +} + +void GlfwWindowManager::DestroyWindow() +{ + if (Window) + { + glfwDestroyWindow(Window); + Window = nullptr; + } +} + +void GlfwWindowManager::InitializeGlfw() +{ + if (!IsInitialized()) + { + if (!glfwInit()) + { + Log::Error("Failed to initialize GLFW"); + } + } +} diff --git a/GlfwWindowManager.h b/GlfwWindowManager.h new file mode 100644 index 0000000..299024f --- /dev/null +++ b/GlfwWindowManager.h @@ -0,0 +1,61 @@ +#pragma once + +#include + +#define GLFW_INCLUDE_VULKAN +#include + +struct FWindowConfig +{ + std::string Title = "Learning Vulkan"; + uint32_t Width = 800; + uint32_t Height = 600; + bool bResizable = false; + bool bFullscreen = false; +}; + +class GlfwWindowManager +{ +public: + GlfwWindowManager(); + ~GlfwWindowManager(); + + GlfwWindowManager(const GlfwWindowManager&) = delete; + GlfwWindowManager& operator=(const GlfwWindowManager&) = delete; + + GlfwWindowManager(GlfwWindowManager&& Other) noexcept; + GlfwWindowManager& operator=(GlfwWindowManager&& Other) noexcept; + + void Initialize(const FWindowConfig& Config); + void Cleanup(); + + bool ShouldClose() const; + void PollEvents(); + void WaitEvents() const; + void SetTitle(const std::string& Title); + + GLFWwindow* GetHandle() const { return Window; } + uint32_t GetWidth() const { return Width; } + uint32_t GetHeight() const { return Height; } + const std::string& GetTitle() const { return Title; } + bool IsInitialized() const { return Window != nullptr; } + + VkSurfaceKHR CreateSurface(VkInstance Instance) const; + + void SetResizeCallback(GLFWwindowsizefun Callback); + void SetKeyCallback(GLFWkeyfun Callback); + void SetMouseButtonCallback(GLFWmousebuttonfun Callback); + void SetCursorPositionCallback(GLFWcursorposfun Callback); + void SetScrollCallback(GLFWscrollfun Callback); + +private: + // static bool bGlfwInitialized; + void InitializeGlfw(); + + GLFWwindow* Window = nullptr; + std::string Title; + uint32_t Width = 0; + uint32_t Height = 0; + + void DestroyWindow(); +}; diff --git a/Learning Vulkan.cpp b/Learning Vulkan.cpp deleted file mode 100644 index 2ee0a45..0000000 --- a/Learning Vulkan.cpp +++ /dev/null @@ -1,261 +0,0 @@ -#define GLFW_INCLUDE_VULKAN -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "Logger.h" - -const uint32_t WIDTH = 800; -const uint32_t HEIGHT = 600; - -const std::vector validationLayers = { - "VK_LAYER_KHRONOS_validation" -}; - -#ifdef NDEBUG -const bool enableValidationLayers = false; -#else -const bool enableValidationLayers = true; -#endif - -class HelloTriangleApplication -{ - -private: - static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback( - VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, - VkDebugUtilsMessageTypeFlagsEXT messageType, - const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, - void* pUserData) - { - std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl; - - return VK_FALSE; - } - -private: - GLFWwindow* window; - - VkInstance instance; - - std::vector GetRequiredExtensions() - { - uint32_t glfwExtensionCount = 0; - const char** glfwExtensions; - glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); - - std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); - - if (enableValidationLayers) - { - extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - } - - return extensions; - } - - bool CheckValidationLayerSupport() - { - uint32_t layerCount; - vkEnumerateInstanceLayerProperties(&layerCount, nullptr); - - std::vector availableLayers(layerCount); - vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); - - for (const char* layerName : validationLayers) - { - bool layerFound = false; - - for (const auto& layerProperties : availableLayers) - { - if (strcmp(layerName, layerProperties.layerName) == 0) - { - layerFound = true; - break; - } - } - - if (!layerFound) - { - return false; - } - } - - return true; - } - - // VkResult vkCreateInstance( - // const VkInstanceCreateInfo* pCreateInfo, - // const VkAllocationCallbacks* pAllocator, - // VkInstance* instance) - //{ - // if (pCreateInfo == nullptr || instance == nullptr) { - // log("Null pointer passed to required parameter!"); - // return VK_ERROR_INITIALIZATION_FAILED; - // } - - // return vkCreateInstance(pCreateInfo, pAllocator, instance); - //} - -public: - void Run() - { - InitWindow(); - InitVulkan(); - MainLoop(); - Cleanup(); - } - -private: - void CheckExtensions(const char* requiredExtensions[], uint32_t requiredExtensionsCount) - { - uint32_t extensionCount = 0; - vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); - - std::vector extensions(extensionCount); - vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data()); - - std::string required; - std::string available; - - required += "Required extensions:\n"; - - bool found = false; - for (uint32_t i = 0; i < requiredExtensionsCount; i++) - { - std::string requiredExtension = requiredExtensions[i]; - - for (const auto& extension : extensions) - { - found = strcmp(extension.extensionName, requiredExtensions[i]); - if (found) - { - break; - } - } - - std::string status = found ? "FOUND" : "NOT FOUND"; - required += "\t" + requiredExtension + " : " + status + "\n"; - } - - available += "available extensions:\n"; - - for (const auto& extension : extensions) - { - std::string extensionName = extension.extensionName; - available += "\t" + extensionName + "\n"; - } - - log(INFO, required); - log(INFO, available); - } - - void CreateInstance() - { - log(INFO, "Creating Vulkan instance"); - - if (enableValidationLayers && !CheckValidationLayerSupport()) - { - throw std::runtime_error("validation layers requested, but not available!"); - } - - VkApplicationInfo appInfo{}; - appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - appInfo.pApplicationName = "Hello Triangle"; - appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); - appInfo.pEngineName = "No Engine"; - appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); - appInfo.apiVersion = VK_API_VERSION_1_0; - - VkInstanceCreateInfo createInfo{}; - - createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - createInfo.pApplicationInfo = &appInfo; - - uint32_t glfwExtensionCount = 0; - const char** glfwExtensions; - glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); - - createInfo.enabledExtensionCount = glfwExtensionCount; - createInfo.ppEnabledExtensionNames = glfwExtensions; - - if (enableValidationLayers) - { - createInfo.enabledLayerCount = static_cast(validationLayers.size()); - createInfo.ppEnabledLayerNames = validationLayers.data(); - } - else - { - createInfo.enabledLayerCount = 0; - } - - if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) - { - throw std::runtime_error("failed to create instance!"); - } - - CheckExtensions(glfwExtensions, glfwExtensionCount); - - auto extensions = GetRequiredExtensions(); - createInfo.enabledExtensionCount = static_cast(extensions.size()); - createInfo.ppEnabledExtensionNames = extensions.data(); - } - - void InitWindow() - { - log(INFO, "Initializing GLFW window..."); - - glfwInit(); - - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); - - window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); - } - - void InitVulkan() - { - log(INFO, "Initializing Vulkan..."); - CreateInstance(); - } - - void MainLoop() - { - while (!glfwWindowShouldClose(window)) - { - glfwPollEvents(); - } - } - - void Cleanup() - { - vkDestroyInstance(instance, nullptr); - - glfwDestroyWindow(window); - - glfwTerminate(); - } -}; - -int main() -{ - HelloTriangleApplication app; - - try - { - app.Run(); - } - catch (const std::exception& e) - { - std::cerr << e.what() << std::endl; - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} \ No newline at end of file diff --git a/Learning Vulkan.vcxproj b/Learning Vulkan.vcxproj index e1255ec..709b1e1 100644 --- a/Learning Vulkan.vcxproj +++ b/Learning Vulkan.vcxproj @@ -103,7 +103,7 @@ _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true C:\Users\jorda\Development\Libraries\OpenGL\glfw-3.4.bin.WIN64\glfw-3.4.bin.WIN64\include;C:\Users\jorda\Development\Libraries\Vulkan\1.4.335.0\Include;%(AdditionalIncludeDirectories) - stdcpp17 + stdcpp20 Console @@ -121,7 +121,7 @@ NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true C:\Users\jorda\Development\Libraries\OpenGL\glfw-3.4.bin.WIN64\glfw-3.4.bin.WIN64\include;C:\Users\jorda\Development\Libraries\Vulkan\1.4.335.0\Include;%(AdditionalIncludeDirectories) - stdcpp17 + stdcpp20 Console @@ -131,10 +131,19 @@ - + + + + + + + + + + diff --git a/Learning Vulkan.vcxproj.filters b/Learning Vulkan.vcxproj.filters index 7727e76..6b60c61 100644 --- a/Learning Vulkan.vcxproj.filters +++ b/Learning Vulkan.vcxproj.filters @@ -15,7 +15,22 @@ - + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + Source Files @@ -23,6 +38,18 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + diff --git a/Logger.cpp b/Logger.cpp index e69de29..0b8f110 100644 --- a/Logger.cpp +++ b/Logger.cpp @@ -0,0 +1,64 @@ +#include "Logger.h" +#include + +void Log::Message(Level Level, const std::string& Message, const std::source_location& Location) +{ + std::string FullFunc = Location.function_name(); + std::string DisplayName = FullFunc; // Default to full function name + + // Simple cleanup: try to extract just class::method + size_t LastColon = FullFunc.rfind("::"); + if (LastColon != std::string::npos) + { + // Find the class name start + size_t ClassStart = FullFunc.rfind(' ', LastColon); + if (ClassStart != std::string::npos) + { + std::string ClassName = FullFunc.substr(ClassStart + 1, LastColon - ClassStart - 1); + + // Find method name end (first parenthesis after last ::) + size_t MethodEnd = FullFunc.find('(', LastColon); + if (MethodEnd != std::string::npos) + { + std::string MethodName = FullFunc.substr(LastColon + 2, MethodEnd - LastColon - 2); + DisplayName = ClassName + "::" + MethodName + "()"; + } + } + } + + std::string LevelString; + switch (Level) + { + case Level::Info: + LevelString = "INFO"; + break; + case Level::Warning: + LevelString = "WARNING"; + break; + case Level::Error: + LevelString = "ERROR"; + break; + case Level::Debug: + LevelString = "DEBUG"; + break; + case Level::VkValidation: + LevelString = "VALIDATION LAYER"; + break; + case Level::DeviceSetup: + LevelString = "DEVICE SETUP"; + break; + default: + LevelString = "UNKNOWN"; + break; + } + + std::string LogMessage = std::format("[{}] [{}] {}", + LevelString, DisplayName, Message); + + std::cout << LogMessage << std::endl; + + if (Level == Level::Error) + { + throw std::runtime_error(LogMessage); + } +} \ No newline at end of file diff --git a/Logger.h b/Logger.h index 076a529..3812144 100644 --- a/Logger.h +++ b/Logger.h @@ -1,29 +1,59 @@ #pragma once -#include +#include +#include +#include +#include -enum LogLevel { - INFO, - WARNING, - ERROR -}; +namespace Log +{ -void log(LogLevel level, const std::string& message) { - std::string levelStr; + enum class Level + { + Info, + Warning, + Error, + Debug, + VkValidation, + DeviceSetup + }; - switch(level) { - case INFO: - levelStr = "[INFO]"; - break; - case WARNING: - levelStr = "[WARNING]"; - break; - case ERROR: - levelStr = "[ERROR]"; - break; - default: - levelStr = "[UNKNOWN]"; - break; + // void Message(Level Level, const std::source_location& Location = std::source_location::current(), const std::string& Message); + void Message(Level Level, const std::string& Message, const std::source_location& Location); + + template + void Info(const std::string& Format, Args&&... Arguments, const std::source_location& Location = std::source_location::current()) + { + Message(Level::Info, std::vformat(Format, std::make_format_args(Arguments...)), Location); } - std::cout << levelStr << " : " << message << std::endl; -} + + template + void Warning(const std::string& Format, Args&&... Arguments, const std::source_location& Location = std::source_location::current()) + { + Message(Level::Warning, std::vformat(Format, std::make_format_args(Arguments...)), Location); + } + + template + void Error(const std::string& Format, Args&&... Arguments, const std::source_location& Location = std::source_location::current()) + { + Message(Level::Error, std::vformat(Format, std::make_format_args(Arguments...)), Location); + } + + template + void Debug(const std::string& Format, Args&&... Arguments, const std::source_location& Location = std::source_location::current()) + { + Message(Level::Warning, std::vformat(Format, std::make_format_args(Arguments...)), Location); + } + + template + void Validation(const std::string& Format, Args&&... Arguments, const std::source_location& Location = std::source_location::current()) + { + Message(Level::VkValidation, std::vformat(Format, std::make_format_args(Arguments...)), Location); + } + + template + void DeviceSetup(const std::string& Format, Args&&... Arguments, const std::source_location& Location = std::source_location::current()) + { + Message(Level::DeviceSetup, std::vformat(Format, std::make_format_args(Arguments...)), Location); + } +} // namespace Log diff --git a/VkDebug.h b/VkDebug.h new file mode 100644 index 0000000..8e46127 --- /dev/null +++ b/VkDebug.h @@ -0,0 +1,77 @@ +#pragma once + +#define GLFW_INCLUDE_VULKAN +#include + +#include +#include + +#ifdef NDEBUG +const bool enableValidationLayers = false; +#else +const bool enableValidationLayers = true; +#endif + +VkDebugUtilsMessengerEXT debugMessenger; + +VkResult CreateDebugUtilsMessengerEXT( + VkInstance instance, + const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDebugUtilsMessengerEXT* pDebugMessenger) +{ + auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); + if (func != nullptr) + { + return func(instance, pCreateInfo, pAllocator, pDebugMessenger); + } + else + { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } +} + +static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData) +{ + std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl; + + if (messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) + { + } + + return VK_FALSE; +} + +bool CheckValidationLayerSupport() +{ + uint32_t layerCount; + vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + + std::vector availableLayers(layerCount); + vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); + + for (const char* layerName : validationLayers) + { + bool layerFound = false; + + for (const auto& layerProperties : availableLayers) + { + if (strcmp(layerName, layerProperties.layerName) == 0) + { + layerFound = true; + break; + } + } + + if (!layerFound) + { + return false; + } + } + + return true; +} \ No newline at end of file diff --git a/VkSetup.h b/VkSetup.h new file mode 100644 index 0000000..3a4e4f9 --- /dev/null +++ b/VkSetup.h @@ -0,0 +1,65 @@ +#pragma once + +#define GLFW_INCLUDE_VULKAN +#include + +#include + +#ifdef NDEBUG +const bool enableValidationLayers = false; +#else +const bool enableValidationLayers = true; +#endif + +class VkSetup +{ + GLFWwindow* window; + + VkInstance instance; + + std::vector GetRequiredExtensions() + { + uint32_t glfwExtensionCount = 0; + const char** glfwExtensions; + glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + + std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); + + if (enableValidationLayers) + { + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } + + return extensions; + } + + bool CheckValidationLayerSupport() + { + uint32_t layerCount; + vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + + std::vector availableLayers(layerCount); + vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); + + for (const char* layerName : validationLayers) + { + bool layerFound = false; + + for (const auto& layerProperties : availableLayers) + { + if (strcmp(layerName, layerProperties.layerName) == 0) + { + layerFound = true; + break; + } + } + + if (!layerFound) + { + return false; + } + } + + return true; + } +}; \ No newline at end of file diff --git a/Vulkan notes/Basics.md b/Vulkan notes/Basics.md index d944403..1891ff7 100644 --- a/Vulkan notes/Basics.md +++ b/Vulkan notes/Basics.md @@ -4,3 +4,10 @@ - Vulkan objects are allocated with `vkAllocateXXX` - `vkDestroyXXX` and `vkFreeXXX` to destroy objects - + +# Validation Layers +There are a lot more settings for the behavior of validation layers than just the flags specified in the `VkDebugUtilsMessengerCreateInfoEXT` struct. Browse to the Vulkan SDK and go to the `Config` directory. There you will find a `vk_layer_settings.txt` file that explains how to configure the layers. + +To configure the layer settings for your own application, copy the file to the `Debug` and `Release` directories of your project and follow the instructions to set the desired behavior. However, for the remainder of this tutorial I'll assume that you're using the default settings. + +Throughout this tutorial I'll be making a couple of intentional mistakes to show you how helpful the validation layers are with catching them and to teach you how important it is to know exactly what you're doing with Vulkan. Now it's time to look at [Vulkan devices in the system](https://vulkan-tutorial.com/en/Drawing_a_triangle/Setup/Physical_devices_and_queue_families). \ No newline at end of file diff --git a/VulkanDebugManager.cpp b/VulkanDebugManager.cpp new file mode 100644 index 0000000..b37ff3f --- /dev/null +++ b/VulkanDebugManager.cpp @@ -0,0 +1,119 @@ +#include "VulkanDebugManager.h" +#include "Logger.h" + +VulkanDebugManager::VulkanDebugManager() +{ +} + +VulkanDebugManager::~VulkanDebugManager() +{ + Cleanup(); +} + +VulkanDebugManager::VulkanDebugManager(VulkanDebugManager&& Other) noexcept + : DebugMessenger(Other.DebugMessenger), Instance(Other.Instance) +{ + Other.DebugMessenger = VK_NULL_HANDLE; + Other.Instance = VK_NULL_HANDLE; +} + +VulkanDebugManager& VulkanDebugManager::operator=(VulkanDebugManager&& Other) noexcept +{ + if (this != &Other) + { + Cleanup(); + DebugMessenger = Other.DebugMessenger; + Instance = Other.Instance; + Other.DebugMessenger = VK_NULL_HANDLE; + Other.Instance = VK_NULL_HANDLE; + } + + return *this; +} +void VulkanDebugManager::Initialize(VkInstance Instance) +{ + if (IsInitialized()) + { + return; + } + + this->Instance = Instance; + SetupDebugMessanger(); +} + +void VulkanDebugManager::Cleanup() +{ + if (!IsInitialized()) + { + return; + } + + FDestroyDebugUtilsMessengerExtParams Params = { Instance, DebugMessenger, nullptr }; + DestroyDebugUtilsMessengerExt(Params); + DebugMessenger = VK_NULL_HANDLE; +} + +VkResult VulkanDebugManager::CreateDebugUtilsMessengerExt(const FCreateDebugUtilsMessengerExtParams& Params) +{ + auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(Params.Instance, "vkCreateDebugUtilsMessengerEXT"); + if (func != nullptr) + { + return func(Params.Instance, Params.pCreateInfo, Params.pAllocator, Params.pDebugMessenger); + } + else + { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } +} + +void VulkanDebugManager::DestroyDebugUtilsMessengerExt(const FDestroyDebugUtilsMessengerExtParams& Params) +{ + auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr( + Params.Instance, + "vkDestroyDebugUtilsMessengerEXT"); + + if (func != nullptr) + { + func(Params.Instance, Params.DebugMessenger, Params.pAllocator); + } +} + +VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugManager::DebugCallback( + VkDebugUtilsMessageSeverityFlagBitsEXT MessageSeverity, + VkDebugUtilsMessageTypeFlagsEXT MessageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData) +{ + Log::Validation(pCallbackData->pMessage); + // std::cerr << "[Validation layer] : " << pCallbackData->pMessage << std::endl; + + if (MessageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) + { + } + + return VK_FALSE; +} + +void VulkanDebugManager::PopulateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) +{ + createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + 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; + 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 = VulkanDebugManager::DebugCallback; +} + +void VulkanDebugManager::SetupDebugMessanger() +{ + VkDebugUtilsMessengerCreateInfoEXT CreateInfo; + + PopulateDebugMessengerCreateInfo(CreateInfo); + + FCreateDebugUtilsMessengerExtParams Params = { Instance, &CreateInfo, nullptr, &DebugMessenger }; + + if (CreateDebugUtilsMessengerExt(Params) != VK_SUCCESS) + { + Log::Error("Failed to set up debug messenger!"); + // throw std::runtime_error("failed to set up debug messenger!"); + } +} diff --git a/VulkanDebugManager.h b/VulkanDebugManager.h new file mode 100644 index 0000000..fa50127 --- /dev/null +++ b/VulkanDebugManager.h @@ -0,0 +1,58 @@ +#pragma once + +#define GLFW_INCLUDE_VULKAN +#include + +struct FCreateDebugUtilsMessengerExtParams +{ + VkInstance Instance; + const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo; + const VkAllocationCallbacks* pAllocator; + VkDebugUtilsMessengerEXT* pDebugMessenger; +}; + +struct FDestroyDebugUtilsMessengerExtParams +{ + VkInstance Instance; + VkDebugUtilsMessengerEXT DebugMessenger; + const VkAllocationCallbacks* pAllocator; +}; + +class VulkanDebugManager +{ +public: + VulkanDebugManager(); + ~VulkanDebugManager(); + + VulkanDebugManager(const VulkanDebugManager&) = delete; + VulkanDebugManager& operator=(const VulkanDebugManager&) = delete; + + VulkanDebugManager(VulkanDebugManager&& Other) noexcept; + VulkanDebugManager& operator=(VulkanDebugManager&& Other) noexcept; + + void Initialize(VkInstance Instance); + void Cleanup(); + + bool IsInitialized() const { return DebugMessenger != VK_NULL_HANDLE; } + + void PopulateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo); + +private: + bool bVerboseLogging = false; + + VkInstance Instance = VK_NULL_HANDLE; + + VkDebugUtilsMessengerEXT DebugMessenger = VK_NULL_HANDLE; + + VkResult CreateDebugUtilsMessengerExt(const FCreateDebugUtilsMessengerExtParams& Params); + + void DestroyDebugUtilsMessengerExt(const FDestroyDebugUtilsMessengerExtParams& Params); + + static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback( + VkDebugUtilsMessageSeverityFlagBitsEXT MessageSeverity, + VkDebugUtilsMessageTypeFlagsEXT MessageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData); + + void SetupDebugMessanger(); +}; diff --git a/VulkanDeviceManager.cpp b/VulkanDeviceManager.cpp new file mode 100644 index 0000000..9fd45ea --- /dev/null +++ b/VulkanDeviceManager.cpp @@ -0,0 +1,111 @@ +#include "VulkanDeviceManager.h" + +#include + +#include "Logger.h" +#include + +VulkanDeviceManager::VulkanDeviceManager() +{ +} + +VulkanDeviceManager::~VulkanDeviceManager() +{ + Cleanup(); +} + +VulkanDeviceManager::VulkanDeviceManager(VulkanDeviceManager&& Other) noexcept +{ +} + +VulkanDeviceManager& VulkanDeviceManager::operator=(VulkanDeviceManager&& Other) noexcept +{ + if (this != &Other) + { + Cleanup(); + } + + return *this; +} +void VulkanDeviceManager::Initialize(VkInstance Instance) +{ + if (IsInitialized()) + { + return; + } +} + +void VulkanDeviceManager::Cleanup() +{ + if (!IsInitialized()) + { + return; + } +} + +void VulkanDeviceManager::PickPhysicalDevice() +{ + uint32_t DeviceCount = 0; + vkEnumeratePhysicalDevices(Instance, &DeviceCount, nullptr); + + if (DeviceCount == 0) + { + Log::Error("Failed to find GPU with Vulkan Support"); + } + + std::vector Devices(DeviceCount); + vkEnumeratePhysicalDevices(Instance, &DeviceCount, Devices.data()); + + std::multimap Candidates; + + for (const auto& Device : Devices) + { + int Score = RateDeviceSuitability(Device); + Candidates.insert(std::make_pair(Score, Device)); + } + + if (Candidates.rbegin()->first > 0) + { + PhysicalDevice = Candidates.rbegin()->second; + } + else + { + Log::Error("Failed to find a suitable GPU"); + } +} + +// bool VulkanDeviceManager::IsDeviceSuitable(VkPhysicalDevice Device) +//{ +// VkPhysicalDeviceProperties DeviceProperties; +// vkGetPhysicalDeviceProperties(Device, &DeviceProperties); +// +// VkPhysicalDeviceFeatures DeviceFeatures; +// vkGetPhysicalDeviceFeatures(Device, &DeviceFeatures); +// +// return DeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU && DeviceFeatures.geometryShader; +// } + +int VulkanDeviceManager::RateDeviceSuitability(VkPhysicalDevice Device) +{ + VkPhysicalDeviceProperties DeviceProperties; + vkGetPhysicalDeviceProperties(Device, &DeviceProperties); + + VkPhysicalDeviceFeatures DeviceFeatures; + vkGetPhysicalDeviceFeatures(Device, &DeviceFeatures); + + int Score = 0; + + if (DeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) + { + Score += 100; + } + + Score += DeviceProperties.limits.maxImageDimension2D; + + if (!DeviceFeatures.geometryShader) + { + return 0; + } + + return Score; +} diff --git a/VulkanDeviceManager.h b/VulkanDeviceManager.h new file mode 100644 index 0000000..d45f06a --- /dev/null +++ b/VulkanDeviceManager.h @@ -0,0 +1,47 @@ +#pragma once + +#define GLFW_INCLUDE_VULKAN +#include + +struct FCreateDebugUtilsMessengerExtParams +{ + VkInstance Instance; + const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo; + const VkAllocationCallbacks* pAllocator; + VkDebugUtilsMessengerEXT* pDebugMessenger; +}; + +struct FDestroyDebugUtilsMessengerExtParams +{ + VkInstance Instance; + VkDebugUtilsMessengerEXT DebugMessenger; + const VkAllocationCallbacks* pAllocator; +}; + +class VulkanDeviceManager +{ +public: + VulkanDeviceManager(); + ~VulkanDeviceManager(); + + VulkanDeviceManager(const VulkanDeviceManager&) = delete; + VulkanDeviceManager& operator=(const VulkanDeviceManager&) = delete; + + VulkanDeviceManager(VulkanDeviceManager&& Other) noexcept; + VulkanDeviceManager& operator=(VulkanDeviceManager&& Other) noexcept; + + void Initialize(VkInstance Instance); + void Cleanup(); + + bool IsInitialized() const { return PhysicalDevice != VK_NULL_HANDLE; } + +private: + VkPhysicalDevice PhysicalDevice = VK_NULL_HANDLE; + VkInstance Instance = VK_NULL_HANDLE; + + void PickPhysicalDevice(); + + // bool IsDeviceSuitable(VkPhysicalDevice Device); + + int RateDeviceSuitability(VkPhysicalDevice Device); +}; diff --git a/VulkanInstanceManager.cpp b/VulkanInstanceManager.cpp new file mode 100644 index 0000000..2a3f371 --- /dev/null +++ b/VulkanInstanceManager.cpp @@ -0,0 +1,177 @@ +#include "VulkanInstanceManager.h" + +#include "Logger.h" + +VulkanInstanceManager::VulkanInstanceManager() +{ +} + +VulkanInstanceManager::~VulkanInstanceManager() +{ + Cleanup(); +} + +VulkanInstanceManager::VulkanInstanceManager(VulkanInstanceManager&& Other) noexcept + : Instance(Other.Instance), bValidationEnabled(Other.bValidationEnabled), bVerboseLogging(Other.bVerboseLogging), VkDebugManager(std::move(Other.VkDebugManager)) +{ + Other.Instance = VK_NULL_HANDLE; + Other.bValidationEnabled = false; + Other.bVerboseLogging = false; +} + +VulkanInstanceManager& VulkanInstanceManager::operator=(VulkanInstanceManager&& Other) noexcept +{ + if (this != &Other) + { + Cleanup(); + Instance = Other.Instance; + bValidationEnabled = Other.bValidationEnabled; + bVerboseLogging = Other.bVerboseLogging; + VkDebugManager = std::move(Other.VkDebugManager); + + Other.Instance = VK_NULL_HANDLE; + } + + return *this; +} +void VulkanInstanceManager::Initialize(const FVulkanConfig& Config) +{ + if (IsInitialized()) + { + Log::Warning("Already Initialized."); + return; + } + + bValidationEnabled = Config.bValidationEnabled; + bVerboseLogging = Config.bVerboseLogging; + + if (bValidationEnabled) + { + Log::Info("DebugManager created with validation enabled."); + VkDebugManager = std::make_unique(); + } + + CreateInstance(); + + if (VkDebugManager) + { + VkDebugManager->Initialize(Instance); + } + + VkDeviceManager = std::make_unique(); + VkDeviceManager->Initialize(Instance); +} + +void VulkanInstanceManager::Cleanup() +{ + if (!IsInitialized()) + { + Log::Warning("Not initialized."); + return; + } + + if (VkDebugManager) + { + VkDebugManager->Cleanup(); + } + + if (VkDeviceManager) + { + VkDeviceManager->Cleanup(); + } + + vkDestroyInstance(Instance, nullptr); + Instance = VK_NULL_HANDLE; +} + +std::vector VulkanInstanceManager::GetRequiredExtensions() +{ + uint32_t GlfwExtensionCount = 0; + const char** GlfwExtensions; + GlfwExtensions = glfwGetRequiredInstanceExtensions(&GlfwExtensionCount); + + std::vector Extensions(GlfwExtensions, GlfwExtensions + GlfwExtensionCount); + + if (VkDebugManager) + { + Extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } + + return Extensions; +} + +bool VulkanInstanceManager::CheckValidationLayerSupport() +{ + uint32_t LayerCount; + vkEnumerateInstanceLayerProperties(&LayerCount, nullptr); + + std::vector AvailableLayers(LayerCount); + vkEnumerateInstanceLayerProperties(&LayerCount, AvailableLayers.data()); + + for (const char* LayerName : ValidationLayers) + { + bool bLayerFound = false; + + for (const auto& LayerProperties : AvailableLayers) + { + if (strcmp(LayerName, LayerProperties.layerName) == 0) + { + bLayerFound = true; + break; + } + } + + if (!bLayerFound) + { + return false; + } + } + + return true; +} + +void VulkanInstanceManager::CreateInstance() +{ + Log::Info("Creating Vulkan instance."); + + if (bValidationEnabled && !CheckValidationLayerSupport()) + { + Log::Error("Validation layers requested, but not available!"); + } + + VkApplicationInfo AppInfo{}; + AppInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + AppInfo.pApplicationName = "Hello Triangle"; + AppInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + AppInfo.pEngineName = "No Engine"; + AppInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); + AppInfo.apiVersion = VK_API_VERSION_1_0; + + VkInstanceCreateInfo CreateInfo{}; + CreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + CreateInfo.pApplicationInfo = &AppInfo; + + auto Extensions = GetRequiredExtensions(); + CreateInfo.enabledExtensionCount = static_cast(Extensions.size()); + CreateInfo.ppEnabledExtensionNames = Extensions.data(); + + if (VkDebugManager) + { + VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{}; + CreateInfo.enabledLayerCount = static_cast(ValidationLayers.size()); + CreateInfo.ppEnabledLayerNames = ValidationLayers.data(); + + VkDebugManager->PopulateDebugMessengerCreateInfo(debugCreateInfo); + CreateInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*)&debugCreateInfo; + } + else + { + CreateInfo.enabledLayerCount = 0; + CreateInfo.pNext = nullptr; + } + + if (vkCreateInstance(&CreateInfo, nullptr, &Instance) != VK_SUCCESS) + { + Log::Error("Failed to create instance!"); + } +} \ No newline at end of file diff --git a/VulkanInstanceManager.h b/VulkanInstanceManager.h new file mode 100644 index 0000000..065cd0a --- /dev/null +++ b/VulkanInstanceManager.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include + +#define GLFW_INCLUDE_VULKAN +#include + +#include "VulkanDebugManager.h" +#include "VulkanDeviceManager.h" + +const std::vector ValidationLayers = { + "VK_LAYER_KHRONOS_validation" +}; + +struct FVulkanConfig +{ + bool bValidationEnabled = true; + bool bVerboseLogging = false; +}; + +class VulkanInstanceManager +{ +public: + VulkanInstanceManager(); + ~VulkanInstanceManager(); + + VulkanInstanceManager(const VulkanInstanceManager&) = delete; + VulkanInstanceManager& operator=(const VulkanInstanceManager&) = delete; + + VulkanInstanceManager(VulkanInstanceManager&& Other) noexcept; + VulkanInstanceManager& operator=(VulkanInstanceManager&& Other) noexcept; + + void Initialize(const FVulkanConfig& Config); + void Cleanup(); + + bool IsInitialized() const { return Instance != VK_NULL_HANDLE; } + +private: + std::unique_ptr VkDebugManager = nullptr; + std::unique_ptr VkDeviceManager = nullptr; + + bool bValidationEnabled = false; + bool bVerboseLogging = false; + + VkInstance Instance = VK_NULL_HANDLE; + + std::vector GetRequiredExtensions(); + + bool CheckValidationLayerSupport(); + + void CreateInstance(); +}; diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..7a3b7af --- /dev/null +++ b/main.cpp @@ -0,0 +1,75 @@ + +#include "Logger.h" +#include "VulkanInstanceManager.h" +#include "GlfwWindowManager.h" + +struct AppConfig +{ + std::string Title = "Learning Vulkan"; + uint32_t Width = 800; + uint32_t Height = 600; + bool bResizable = false; + bool bFullscreen = false; + bool bValidationEnabled = true; + bool bVerboseLogging = false; +}; + +class HelloTriangleApplication +{ +public: + void Run() + { + InitGlfw(); + InitVulkan(); + MainLoop(); + Cleanup(); + } + +private: + VulkanInstanceManager VkInstanceManager; + GlfwWindowManager GlfwWindowManager; + AppConfig Settings = {}; + + void InitVulkan() + { + FVulkanConfig Config = { Settings.bValidationEnabled, Settings.bVerboseLogging }; + VkInstanceManager.Initialize(Config); + } + + void InitGlfw() + { + FWindowConfig Config = { Settings.Title, Settings.Width, Settings.Height, Settings.bResizable, Settings.bFullscreen }; + GlfwWindowManager.Initialize(Config); + } + + void MainLoop() + { + while (!GlfwWindowManager.ShouldClose()) + { + GlfwWindowManager.PollEvents(); + } + } + + void Cleanup() + { + // VkInstanceManager.Cleanup(); + // GlfwWindowManager.Cleanup(); + } +}; + +int main() +{ + HelloTriangleApplication app; + + try + { + app.Run(); + } + catch (const std::exception& e) + { + Log::Error(e.what()); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} \ No newline at end of file