diff --git a/.editorconfig b/.editorconfig index ca1b9dc..15ebbc3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -89,7 +89,6 @@ cpp_includes_use_forward_slash = true [*.{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 @@ -115,7 +114,6 @@ 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 @@ -137,6 +135,15 @@ cpp_naming_symbols.structs.applicable_kinds = struct cpp_naming_symbols.all_symbols.applicable_kinds = * # Naming convention styles +cpp_naming_style.member_variables_style.capitalization = camel_case +cpp_naming_style.member_variables_style.required_prefix = m_ +cpp_naming_style.member_variables_style.required_suffix = +cpp_naming_style.member_variables_style.word_separator = + +cpp_naming_style.parameters_in_style.capitalization = camel_case +cpp_naming_style.parameters_in_style.required_prefix = in_ +cpp_naming_style.parameters_in_style.required_suffix = +cpp_naming_style.parameters_in_style.word_separator = cpp_naming_style.unreal_engine_default.capitalization = pascal_case cpp_naming_style.unreal_engine_default.required_prefix = diff --git a/GlfwWindowManager.cpp b/GlfwWindowManager.cpp index cc7348d..d4c1a26 100644 --- a/GlfwWindowManager.cpp +++ b/GlfwWindowManager.cpp @@ -1,39 +1,40 @@ #include "GlfwWindowManager.h" #include + #include "Logger.h" -// bool WindowManager::bGlfwInitialized = false; +VkSurfaceKHR GlfwWindowManager::Surface = VK_NULL_HANDLE; +GLFWwindow* GlfwWindowManager::Window = nullptr; GlfwWindowManager::GlfwWindowManager() = default; GlfwWindowManager::~GlfwWindowManager() { - Cleanup(); + // 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; -} +// GlfwWindowManager::GlfwWindowManager(GlfwWindowManager&& Other) noexcept +// : Window(Other.Window), Config(Other.Config), Surface(Other.Surface) +//{ +// Other.Window = nullptr; +// } +// +// GlfwWindowManager& GlfwWindowManager::operator=(GlfwWindowManager&& Other) noexcept +//{ +// if (this != &Other) +// { +// Cleanup(); +// +// Window = Other.Window; +// Config = Other.Config; +// Surface = Other.Surface; +// +// Other.Window = nullptr; +// } +// +// return *this; +// } void GlfwWindowManager::Initialize(const FWindowConfig& Config) { @@ -42,29 +43,12 @@ void GlfwWindowManager::Initialize(const FWindowConfig& Config) return; } + this->Config = Config; + 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() +void GlfwWindowManager::Cleanup(VkInstance Instance) { if (!IsInitialized()) { @@ -100,7 +84,7 @@ void GlfwWindowManager::WaitEvents() const void GlfwWindowManager::SetTitle(const std::string& Title) { - this->Title = Title; + // this->Title = Title; if (Window) { @@ -108,20 +92,44 @@ void GlfwWindowManager::SetTitle(const std::string& Title) } } -VkSurfaceKHR GlfwWindowManager::CreateSurface(VkInstance Instance) const +void GlfwWindowManager::CreateSurface(VkInstance Instance) { - if (!Window || !Instance) + if (!Window) { - return VK_NULL_HANDLE; + Log::Error("Window not initialized."); } - VkSurfaceKHR Surface = VK_NULL_HANDLE; - if (glfwCreateWindowSurface(Instance, Window, nullptr, &Surface) != VK_SUCCESS) + if (!Instance) { - Log::Error("Failed to create window surface"); + Log::Error("Instance is null."); } - return Surface; + VkResult result = glfwCreateWindowSurface(Instance, Window, nullptr, &Surface); + if (result != VK_SUCCESS) + { + std::string errorMsg; + switch (result) + { + case VK_ERROR_EXTENSION_NOT_PRESENT: + errorMsg = "VK_ERROR_EXTENSION_NOT_PRESENT - Required extension not present"; + break; + case VK_ERROR_INITIALIZATION_FAILED: + errorMsg = "VK_ERROR_INITIALIZATION_FAILED - Initialization failed"; + break; + case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: + errorMsg = "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR - Native window already in use"; + break; + default: + errorMsg = "Unknown error code: " + std::to_string(result); + break; + } + + Log::Error("Failed to create window surface: " + errorMsg); + } + else + { + Log::Info("Window surface created successfully."); + } } void GlfwWindowManager::SetResizeCallback(GLFWwindowsizefun Callback) @@ -182,4 +190,23 @@ void GlfwWindowManager::InitializeGlfw() Log::Error("Failed to initialize GLFW"); } } + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, Config.bResizable ? GLFW_TRUE : GLFW_FALSE); + + Window = glfwCreateWindow( + Config.Width, + Config.Height, + Config.Title.c_str(), + Config.bFullscreen ? glfwGetPrimaryMonitor() : nullptr, + nullptr); + + if (!Window) + { + Log::Error("Failed to create GLFW window"); + } + else + { + Log::Info("Created GLFW window successfully."); + } } diff --git a/GlfwWindowManager.h b/GlfwWindowManager.h index 299024f..732716f 100644 --- a/GlfwWindowManager.h +++ b/GlfwWindowManager.h @@ -2,8 +2,11 @@ #include +#define VK_USE_PLATFORM_WIN32_KHR #define GLFW_INCLUDE_VULKAN #include +#define GLFW_EXPOSE_NATIVE_WIN32 +#include struct FWindowConfig { @@ -20,14 +23,14 @@ public: GlfwWindowManager(); ~GlfwWindowManager(); - GlfwWindowManager(const GlfwWindowManager&) = delete; - GlfwWindowManager& operator=(const GlfwWindowManager&) = delete; + // GlfwWindowManager(const GlfwWindowManager&) = delete; + // GlfwWindowManager& operator=(const GlfwWindowManager&) = delete; - GlfwWindowManager(GlfwWindowManager&& Other) noexcept; - GlfwWindowManager& operator=(GlfwWindowManager&& Other) noexcept; + // GlfwWindowManager(GlfwWindowManager&& Other) noexcept; + // GlfwWindowManager& operator=(GlfwWindowManager&& Other) noexcept; void Initialize(const FWindowConfig& Config); - void Cleanup(); + void Cleanup(VkInstance Instance); bool ShouldClose() const; void PollEvents(); @@ -35,12 +38,11 @@ public: 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; } + uint32_t GetWidth() const { return Config.Width; } + uint32_t GetHeight() const { return Config.Height; } + const std::string& GetTitle() const { return Config.Title; } - VkSurfaceKHR CreateSurface(VkInstance Instance) const; + bool IsInitialized() const { return Window && Surface; } void SetResizeCallback(GLFWwindowsizefun Callback); void SetKeyCallback(GLFWkeyfun Callback); @@ -48,14 +50,18 @@ public: void SetCursorPositionCallback(GLFWcursorposfun Callback); void SetScrollCallback(GLFWscrollfun Callback); -private: - // static bool bGlfwInitialized; - void InitializeGlfw(); + void CreateSurface(VkInstance Instance); - GLFWwindow* Window = nullptr; - std::string Title; - uint32_t Width = 0; - uint32_t Height = 0; + static VkSurfaceKHR Surface; + + static GLFWwindow* Window; + +private: + FWindowConfig Config; + + // VkSurfaceKHR Surface = VK_NULL_HANDLE; + + void InitializeGlfw(); void DestroyWindow(); }; diff --git a/Learning Vulkan.vcxproj b/Learning Vulkan.vcxproj index 709b1e1..0a4340b 100644 --- a/Learning Vulkan.vcxproj +++ b/Learning Vulkan.vcxproj @@ -131,6 +131,7 @@ + @@ -139,6 +140,9 @@ + + + @@ -147,6 +151,8 @@ + + diff --git a/Learning Vulkan.vcxproj.filters b/Learning Vulkan.vcxproj.filters index 6b60c61..888d47a 100644 --- a/Learning Vulkan.vcxproj.filters +++ b/Learning Vulkan.vcxproj.filters @@ -13,6 +13,9 @@ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + {699a4173-18fe-4109-979d-1612d035b2d6} + @@ -33,6 +36,9 @@ Source Files + + Source Files + @@ -50,8 +56,23 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Shaders + + + Shaders + \ No newline at end of file diff --git a/Shaders/compile.bat b/Shaders/compile.bat new file mode 100644 index 0000000..5e0ee8c --- /dev/null +++ b/Shaders/compile.bat @@ -0,0 +1,3 @@ +C:/Users/jorda/Development/Libraries/Vulkan/1.4.335.0/Bin/glslc.exe shader.vert -o vert.spv +C:/Users/jorda/Development/Libraries/Vulkan/1.4.335.0/Bin/glslc.exe shader.frag -o frag.spv +pause \ No newline at end of file diff --git a/Shaders/frag.spv b/Shaders/frag.spv new file mode 100644 index 0000000..da37f7e Binary files /dev/null and b/Shaders/frag.spv differ diff --git a/Shaders/shader.frag b/Shaders/shader.frag new file mode 100644 index 0000000..13009da --- /dev/null +++ b/Shaders/shader.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) in vec3 fragColor; + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(fragColor, 1.0); +} \ No newline at end of file diff --git a/Shaders/shader.vert b/Shaders/shader.vert new file mode 100644 index 0000000..66d6766 --- /dev/null +++ b/Shaders/shader.vert @@ -0,0 +1,20 @@ +#version 450 + +layout(location = 0) out vec3 fragColor; + +vec2 positions[3] = vec2[]( + vec2(0.0, -0.5), + vec2(0.5, 0.5), + vec2(-0.5, 0.5) +); + +vec3 colors[3] = vec3[]( + vec3(1.0, 0.0, 0.0), + vec3(0.0, 1.0, 0.0), + vec3(0.0, 0.0, 1.0) +); + +void main() { + gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); + fragColor = colors[gl_VertexIndex]; +} \ No newline at end of file diff --git a/Shaders/vert.spv b/Shaders/vert.spv new file mode 100644 index 0000000..cf7123f Binary files /dev/null and b/Shaders/vert.spv differ diff --git a/Utilities/FileReader.h b/Utilities/FileReader.h new file mode 100644 index 0000000..6dd476e --- /dev/null +++ b/Utilities/FileReader.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#include "Logger.h" + +static std::vector ReadFile(const std::string& FileName) +{ + std::ifstream File(FileName, std::ios::ate | std::ios::binary); + + if (!File.is_open()) + { + Log::Error("Failed to open file: " + FileName); + } + + size_t FileSize = (size_t)File.tellg(); + std::vector Buffer(FileSize); + + File.seekg(0); + File.read(Buffer.data(), FileSize); + + File.close(); + + return Buffer; +} \ No newline at end of file diff --git a/VkDebug.h b/VkDebug.h deleted file mode 100644 index 8e46127..0000000 --- a/VkDebug.h +++ /dev/null @@ -1,77 +0,0 @@ -#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 deleted file mode 100644 index 3a4e4f9..0000000 --- a/VkSetup.h +++ /dev/null @@ -1,65 +0,0 @@ -#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/VulkanContext.h b/VulkanContext.h index 31e9915..de77be7 100644 --- a/VulkanContext.h +++ b/VulkanContext.h @@ -1,11 +1,12 @@ #pragma once -#include -#include - +// #include +// #include +// #include "Logger.h" #include "VulkanInstanceManager.h" #include "VulkanDeviceManager.h" #include "VulkanDebugManager.h" +#include "VulkanGraphicsPipeline.h" #include "GlfwWindowManager.h" #define GLFW_INCLUDE_VULKAN @@ -27,7 +28,7 @@ public: VulkanContext(); ~VulkanContext(); - void Initialize(FVulkanConfig& inConfig); + void Initialize(FVulkanConfig& Config); void Cleanup(); private: @@ -40,6 +41,12 @@ private: VkSurfaceKHR Surface = VK_NULL_HANDLE; VkDebugUtilsMessengerEXT debugMessenger = VK_NULL_HANDLE; +public: + static VulkanDebugManager DebugManager; + +private: + VulkanInstanceManager InstanceManager; + public: VkInstance GetInstance() const { return Instance; } VkPhysicalDevice GetPhysicalDevice() const { return PhysicalDevice; } diff --git a/VulkanDebugManager.cpp b/VulkanDebugManager.cpp index b37ff3f..4ccd94c 100644 --- a/VulkanDebugManager.cpp +++ b/VulkanDebugManager.cpp @@ -7,7 +7,7 @@ VulkanDebugManager::VulkanDebugManager() VulkanDebugManager::~VulkanDebugManager() { - Cleanup(); + // Cleanup(); } VulkanDebugManager::VulkanDebugManager(VulkanDebugManager&& Other) noexcept @@ -30,6 +30,7 @@ VulkanDebugManager& VulkanDebugManager::operator=(VulkanDebugManager&& Other) no return *this; } + void VulkanDebugManager::Initialize(VkInstance Instance) { if (IsInitialized()) diff --git a/VulkanDeviceManager.cpp b/VulkanDeviceManager.cpp index 9fd45ea..467bb1b 100644 --- a/VulkanDeviceManager.cpp +++ b/VulkanDeviceManager.cpp @@ -1,9 +1,16 @@ #include "VulkanDeviceManager.h" #include +#include +#include // Necessary for uint32_t +#include // Necessary for std::numeric_limits +#include // Necessary for std::clamp #include "Logger.h" -#include +#include "GlfwWindowManager.h" +#include + +std::vector VulkanDeviceManager::SwapChainImages = {}; VulkanDeviceManager::VulkanDeviceManager() { @@ -11,10 +18,16 @@ VulkanDeviceManager::VulkanDeviceManager() VulkanDeviceManager::~VulkanDeviceManager() { - Cleanup(); + // Cleanup(); } VulkanDeviceManager::VulkanDeviceManager(VulkanDeviceManager&& Other) noexcept + : PhysicalDevice(std::exchange(Other.PhysicalDevice, VK_NULL_HANDLE)) + , Instance(std::exchange(Other.Instance, VK_NULL_HANDLE)) + , Device(std::exchange(Other.Device, VK_NULL_HANDLE)) + , GraphicsQueue(std::exchange(Other.GraphicsQueue, VK_NULL_HANDLE)) + , bEnableValidationLayers(std::exchange(Other.bEnableValidationLayers, false)) + , ValidationLayers(std::move(Other.ValidationLayers)) { } @@ -22,25 +35,55 @@ VulkanDeviceManager& VulkanDeviceManager::operator=(VulkanDeviceManager&& Other) { if (this != &Other) { - Cleanup(); - } + Cleanup(); // Clean up current resources + // Transfer resources from Other + PhysicalDevice = std::exchange(Other.PhysicalDevice, VK_NULL_HANDLE); + Instance = std::exchange(Other.Instance, VK_NULL_HANDLE); + Device = std::exchange(Other.Device, VK_NULL_HANDLE); + GraphicsQueue = std::exchange(Other.GraphicsQueue, VK_NULL_HANDLE); + bEnableValidationLayers = std::exchange(Other.bEnableValidationLayers, false); + ValidationLayers = std::move(Other.ValidationLayers); + } return *this; } -void VulkanDeviceManager::Initialize(VkInstance Instance) + +void VulkanDeviceManager::Initialize( + VkInstance Instance, + bool bEnableValidationLayers, + const std::vector& ValidationLayers) { if (IsInitialized()) { + Log::Warning("Already Initialized."); return; } + + this->Instance = Instance; + this->bEnableValidationLayers = bEnableValidationLayers; + this->ValidationLayers = &ValidationLayers; + + PickPhysicalDevice(); + CreateLogicalDevice(); + CreateSwapChain(); + CreateImageViews(); } void VulkanDeviceManager::Cleanup() { if (!IsInitialized()) { + Log::Warning("Not Initialized."); return; } + for (auto ImageView : SwapChainImageViews) + { + vkDestroyImageView(Device, ImageView, nullptr); + } + vkDestroySwapchainKHR(Device, SwapChain, nullptr); + + vkDestroySurfaceKHR(Instance, GlfwWindowManager::Surface, nullptr); + vkDestroyDevice(Device, nullptr); } void VulkanDeviceManager::PickPhysicalDevice() @@ -50,7 +93,7 @@ void VulkanDeviceManager::PickPhysicalDevice() if (DeviceCount == 0) { - Log::Error("Failed to find GPU with Vulkan Support"); + Log::Error("Failed to find GPU with Vulkan Support."); } std::vector Devices(DeviceCount); @@ -60,30 +103,39 @@ void VulkanDeviceManager::PickPhysicalDevice() for (const auto& Device : Devices) { - int Score = RateDeviceSuitability(Device); - Candidates.insert(std::make_pair(Score, Device)); + if (IsDeviceSuitable(Device)) + { + int Score = RateDeviceSuitability(Device); + Candidates.insert(std::make_pair(Score, Device)); + } } if (Candidates.rbegin()->first > 0) { PhysicalDevice = Candidates.rbegin()->second; + Log::Info("Suitable GPU found."); } else { - Log::Error("Failed to find a suitable GPU"); + 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; -// } +bool VulkanDeviceManager::IsDeviceSuitable(VkPhysicalDevice Device) +{ + QueueFamilyIndices Indices = FindQueueFamilies(Device); + + bool bExtensionsSupported = CheckDeviceExtensionSupport(Device); + + bool bSwapChainAdequate = false; + if (bExtensionsSupported) + { + SwapChainSupportDetails SwapChainSupport = QuerySwapChainSupport(Device); + bSwapChainAdequate = !SwapChainSupport.Formats.empty() && !SwapChainSupport.PresentModes.empty(); + } + + return Indices.IsComplete() && bExtensionsSupported && bSwapChainAdequate; +} int VulkanDeviceManager::RateDeviceSuitability(VkPhysicalDevice Device) { @@ -109,3 +161,282 @@ int VulkanDeviceManager::RateDeviceSuitability(VkPhysicalDevice Device) return Score; } + +bool VulkanDeviceManager::CheckDeviceExtensionSupport(VkPhysicalDevice Device) +{ + uint32_t ExtensionCount; + vkEnumerateDeviceExtensionProperties(Device, nullptr, &ExtensionCount, nullptr); + + std::vector AvailableExtensions(ExtensionCount); + vkEnumerateDeviceExtensionProperties(Device, nullptr, &ExtensionCount, AvailableExtensions.data()); + + std::set RequiredExtensions(DeviceExtensions.begin(), DeviceExtensions.end()); + + for (const auto& Extension : AvailableExtensions) + { + RequiredExtensions.erase(Extension.extensionName); + } + + return RequiredExtensions.empty(); +} + +QueueFamilyIndices VulkanDeviceManager::FindQueueFamilies(VkPhysicalDevice Device) +{ + QueueFamilyIndices Indices; + + uint32_t QueueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(Device, &QueueFamilyCount, nullptr); + + std::vector QueueFamilies(QueueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(Device, &QueueFamilyCount, QueueFamilies.data()); + + int i = 0; + for (const auto& QueueFamily : QueueFamilies) + { + if (QueueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) + { + Indices.GraphicsFamily = i; + } + + VkBool32 PresentSupport = false; + vkGetPhysicalDeviceSurfaceSupportKHR(Device, i, GlfwWindowManager::Surface, &PresentSupport); + + if (PresentSupport) + { + Indices.PresentFamily = i; + } + if (Indices.IsComplete()) + { + break; + } + + i++; + } + + return Indices; +} + +void VulkanDeviceManager::CreateLogicalDevice() +{ + QueueFamilyIndices Indices = FindQueueFamilies(PhysicalDevice); + + std::vector QueueCreateInfos; + std::set UniqueQueueFamilies = { Indices.GraphicsFamily.value(), Indices.PresentFamily.value() }; + + float QueuePriority = 1.0f; + for (uint32_t QueueFamily : UniqueQueueFamilies) + { + VkDeviceQueueCreateInfo QueueCreateInfo{}; + QueueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + QueueCreateInfo.queueFamilyIndex = Indices.GraphicsFamily.value(); + QueueCreateInfo.queueCount = 1; + QueueCreateInfo.pQueuePriorities = &QueuePriority; + QueueCreateInfos.push_back(QueueCreateInfo); + } + + VkPhysicalDeviceFeatures DeviceFeatures{}; + + VkDeviceCreateInfo CreateInfo{}; + CreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + CreateInfo.queueCreateInfoCount = static_cast(QueueCreateInfos.size()); + CreateInfo.pQueueCreateInfos = QueueCreateInfos.data(); + CreateInfo.pEnabledFeatures = &DeviceFeatures; + + CreateInfo.enabledExtensionCount = static_cast(DeviceExtensions.size()); + CreateInfo.ppEnabledExtensionNames = DeviceExtensions.data(); + + if (bEnableValidationLayers) + { + CreateInfo.enabledLayerCount = static_cast(ValidationLayers->size()); + CreateInfo.ppEnabledLayerNames = ValidationLayers->data(); + } + else + { + CreateInfo.enabledLayerCount = 0; + } + + if (vkCreateDevice(PhysicalDevice, &CreateInfo, nullptr, &Device) != VK_SUCCESS) + { + Log::Error("Failed to create logical device!"); + } + + vkGetDeviceQueue(Device, Indices.GraphicsFamily.value(), 0, &GraphicsQueue); + vkGetDeviceQueue(Device, Indices.PresentFamily.value(), 0, &PresentQueue); +} + +SwapChainSupportDetails VulkanDeviceManager::QuerySwapChainSupport(VkPhysicalDevice Device) +{ + SwapChainSupportDetails Details; + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(Device, GlfwWindowManager::Surface, &Details.Capabilities); + + uint32_t FormatCount; + vkGetPhysicalDeviceSurfaceFormatsKHR(Device, GlfwWindowManager::Surface, &FormatCount, nullptr); + + if (FormatCount != 0) + { + Details.Formats.resize(FormatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(Device, GlfwWindowManager::Surface, &FormatCount, Details.Formats.data()); + } + + uint32_t PresentModeCount; + vkGetPhysicalDeviceSurfacePresentModesKHR(Device, GlfwWindowManager::Surface, &PresentModeCount, nullptr); + + if (PresentModeCount != 0) + { + Details.PresentModes.resize(PresentModeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR(Device, GlfwWindowManager::Surface, &PresentModeCount, Details.PresentModes.data()); + } + + return Details; +} + +VkSurfaceFormatKHR VulkanDeviceManager::ChooseSwapSurfaceFormat(const std::vector& AvailableFormats) +{ + for (const auto& AvailableFormat : AvailableFormats) + { + if (AvailableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && AvailableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) + { + return AvailableFormat; + } + } + return AvailableFormats[0]; +} + +VkPresentModeKHR VulkanDeviceManager::ChooseSwapPresentMode(const std::vector& AvailablePresentModes) +{ + for (const auto& AvailablePresentMode : AvailablePresentModes) + { + if (AvailablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) + { + return AvailablePresentMode; + } + } + return VK_PRESENT_MODE_FIFO_KHR; +} + +VkExtent2D VulkanDeviceManager::ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& Capabilities) +{ + if (Capabilities.currentExtent.width != (std::numeric_limits::max)()) + { + return Capabilities.currentExtent; + } + else + { + int Width, Height; + glfwGetFramebufferSize(GlfwWindowManager::Window, &Width, &Height); + + VkExtent2D ActualExtent = { + static_cast(Width), + static_cast(Height) + }; + + ActualExtent.width = std::clamp(ActualExtent.width, Capabilities.minImageExtent.width, Capabilities.maxImageExtent.width); + ActualExtent.height = std::clamp(ActualExtent.height, Capabilities.minImageExtent.height, Capabilities.maxImageExtent.height); + + return ActualExtent; + } +} + +void VulkanDeviceManager::CreateSwapChain() +{ + SwapChainSupportDetails SwapChainSupport = QuerySwapChainSupport(PhysicalDevice); + + VkSurfaceFormatKHR SurfaceFormat = ChooseSwapSurfaceFormat(SwapChainSupport.Formats); + VkPresentModeKHR PresentMode = ChooseSwapPresentMode(SwapChainSupport.PresentModes); + VkExtent2D Extent = ChooseSwapExtent(SwapChainSupport.Capabilities); + + uint32_t ImageCount = SwapChainSupport.Capabilities.minImageCount + 1; + + if (SwapChainSupport.Capabilities.maxImageCount > 0 && ImageCount > SwapChainSupport.Capabilities.maxImageCount) + { + ImageCount = SwapChainSupport.Capabilities.maxImageCount; + } + + VkSwapchainCreateInfoKHR CreateInfo{}; + CreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + CreateInfo.surface = GlfwWindowManager::Surface; + + CreateInfo.minImageCount = ImageCount; + CreateInfo.imageFormat = SurfaceFormat.format; + CreateInfo.imageColorSpace = SurfaceFormat.colorSpace; + CreateInfo.imageExtent = Extent; + CreateInfo.imageArrayLayers = 1; + CreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; // may need VK_IMAGE_USAGE_TRANSFER_DST_BIT for post processing https://vulkan-tutorial.com/Drawing_a_triangle/Presentation/Swap_chain#:~:text=VK%5FIMAGE%5FUSAGE%5FTRANSFER%5FDST%5FBIT + + QueueFamilyIndices Indices = FindQueueFamilies(PhysicalDevice); + uint32_t QueueFamilyIndices[] = { Indices.GraphicsFamily.value(), + Indices.PresentFamily.value() }; + + if (Indices.GraphicsFamily != Indices.PresentFamily) + { + CreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + CreateInfo.queueFamilyIndexCount = 2; + CreateInfo.pQueueFamilyIndices = QueueFamilyIndices; + } + else + { + CreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + CreateInfo.queueFamilyIndexCount = 0; + CreateInfo.pQueueFamilyIndices = nullptr; + } + + CreateInfo.preTransform = SwapChainSupport.Capabilities.currentTransform; + CreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + CreateInfo.presentMode = PresentMode; + CreateInfo.clipped = VK_TRUE; + CreateInfo.oldSwapchain = VK_NULL_HANDLE; + + if (vkCreateSwapchainKHR(Device, &CreateInfo, nullptr, &SwapChain) != VK_SUCCESS) + { + Log::Error("Failed to create swap chain."); + } + else + { + Log::Info("Successfully created swap chain."); + } + + vkGetSwapchainImagesKHR(Device, SwapChain, &ImageCount, nullptr); + SwapChainImages.resize(ImageCount); + vkGetSwapchainImagesKHR(Device, SwapChain, &ImageCount, SwapChainImages.data()); + + SwapChainImageFormat = SurfaceFormat.format; + SwapChainExtent = Extent; +} + +void VulkanDeviceManager::CreateImageViews() +{ + SwapChainImageViews.resize(SwapChainImages.size()); + + int CreatedViews = 0; + for (size_t i = 0; i < SwapChainImages.size(); i++) + { + VkImageViewCreateInfo CreateInfo{}; + CreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + CreateInfo.image = SwapChainImages[i]; + + CreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + CreateInfo.format = SwapChainImageFormat; + CreateInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + CreateInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + CreateInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + CreateInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + + CreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + CreateInfo.subresourceRange.baseMipLevel = 0; + CreateInfo.subresourceRange.levelCount = 1; + CreateInfo.subresourceRange.baseArrayLayer = 0; + CreateInfo.subresourceRange.layerCount = 1; + + if (vkCreateImageView(Device, &CreateInfo, nullptr, &SwapChainImageViews[i]) != VK_SUCCESS) + { + Log::Error("Failed to create image views."); + } + else + { + CreatedViews++; + } + } + + Log::Info("Successfully created " + std::to_string(CreatedViews) + " image views."); +} \ No newline at end of file diff --git a/VulkanDeviceManager.h b/VulkanDeviceManager.h index 2f66ba5..c47ac5a 100644 --- a/VulkanDeviceManager.h +++ b/VulkanDeviceManager.h @@ -1,22 +1,35 @@ #pragma once +#include +#include +#include + +#include "Logger.h" + #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; -// }; +const std::vector DeviceExtensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME +}; + +struct QueueFamilyIndices +{ + std::optional GraphicsFamily; + std::optional PresentFamily; + + bool IsComplete() + { + return GraphicsFamily.has_value() && PresentFamily.has_value(); + } +}; + +struct SwapChainSupportDetails +{ + VkSurfaceCapabilitiesKHR Capabilities; + std::vector Formats; + std::vector PresentModes; +}; class VulkanDeviceManager { @@ -30,18 +43,59 @@ public: VulkanDeviceManager(VulkanDeviceManager&& Other) noexcept; VulkanDeviceManager& operator=(VulkanDeviceManager&& Other) noexcept; - void Initialize(VkInstance Instance); + void Initialize( + VkInstance Instance, + bool EnableValidationLayers, + const std::vector& ValidationLayers); + void Cleanup(); - bool IsInitialized() const { return PhysicalDevice != VK_NULL_HANDLE; } + bool IsInitialized() const + { + bool bInitialized = PhysicalDevice && Device && Instance && GraphicsQueue; + return bInitialized; + } + + static std::vector SwapChainImages; private: VkPhysicalDevice PhysicalDevice = VK_NULL_HANDLE; VkInstance Instance = VK_NULL_HANDLE; + VkDevice Device = VK_NULL_HANDLE; + VkQueue GraphicsQueue = VK_NULL_HANDLE; + VkQueue PresentQueue = VK_NULL_HANDLE; + + VkSwapchainKHR SwapChain = VK_NULL_HANDLE; + VkFormat SwapChainImageFormat; + VkExtent2D SwapChainExtent; + + std::vector SwapChainImageViews; + + bool bEnableValidationLayers = false; + + const std::vector* ValidationLayers = nullptr; void PickPhysicalDevice(); - // bool IsDeviceSuitable(VkPhysicalDevice Device); + bool IsDeviceSuitable(VkPhysicalDevice Device); int RateDeviceSuitability(VkPhysicalDevice Device); + + bool CheckDeviceExtensionSupport(VkPhysicalDevice Device); + + QueueFamilyIndices FindQueueFamilies(VkPhysicalDevice Device); + + void CreateLogicalDevice(); + + SwapChainSupportDetails QuerySwapChainSupport(VkPhysicalDevice Device); + + VkSurfaceFormatKHR ChooseSwapSurfaceFormat(const std::vector& AvailableFormats); + + VkPresentModeKHR ChooseSwapPresentMode(const std::vector& AvailablePresentModes); + + VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& Capabilities); + + void CreateSwapChain(); + + void CreateImageViews(); }; diff --git a/VulkanGraphicsPipeline.h b/VulkanGraphicsPipeline.h new file mode 100644 index 0000000..6d000cd --- /dev/null +++ b/VulkanGraphicsPipeline.h @@ -0,0 +1,27 @@ +#pragma once + +#include "Utilities/FileReader.h" + +class VulkanGraphicsPipeline +{ +public: + void CreateGraphicsPipeline() + { + auto VertShaderCode = ReadFile("Shaders/vert.spv"); + auto FragShaderCode = ReadFile("Shaders/frag.spv"); + + Log::Info("Vert buffer size: " + std::to_string(VertShaderCode.size())); + Log::Info("Frag buffer size: " + std::to_string(FragShaderCode.size())); + } + + VkShaderModule CreateShaderModule(const std::vector& Code) + { + VkShaderModuleCreateInfo CreateInfo{}; + CreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + CreateInfo.codeSize = Code.size(); + CreateInfo.pCode = reinterpret_cast(Code.data()); + + VkShaderModule ShaderModule; + if (vkCreateShaderModule(Device)) + } +}; \ No newline at end of file diff --git a/VulkanInstanceManager.cpp b/VulkanInstanceManager.cpp index 2a3f371..1413094 100644 --- a/VulkanInstanceManager.cpp +++ b/VulkanInstanceManager.cpp @@ -8,11 +8,11 @@ VulkanInstanceManager::VulkanInstanceManager() VulkanInstanceManager::~VulkanInstanceManager() { - Cleanup(); + // Cleanup(); } VulkanInstanceManager::VulkanInstanceManager(VulkanInstanceManager&& Other) noexcept - : Instance(Other.Instance), bValidationEnabled(Other.bValidationEnabled), bVerboseLogging(Other.bVerboseLogging), VkDebugManager(std::move(Other.VkDebugManager)) + : Instance(Other.Instance) /*, bValidationEnabled(Other.bValidationEnabled), bVerboseLogging(Other.bVerboseLogging), VkDebugManager(std::move(Other.VkDebugManager))*/ { Other.Instance = VK_NULL_HANDLE; Other.bValidationEnabled = false; @@ -34,7 +34,28 @@ VulkanInstanceManager& VulkanInstanceManager::operator=(VulkanInstanceManager&& return *this; } -void VulkanInstanceManager::Initialize(const FVulkanConfig& Config) + +// 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(); +// } + +void VulkanInstanceManager::Initialize(bool bEnableValidationLayers) { if (IsInitialized()) { @@ -42,24 +63,27 @@ void VulkanInstanceManager::Initialize(const FVulkanConfig& Config) return; } - bValidationEnabled = Config.bValidationEnabled; - bVerboseLogging = Config.bVerboseLogging; - - if (bValidationEnabled) + if (bEnableValidationLayers) { Log::Info("DebugManager created with validation enabled."); VkDebugManager = std::make_unique(); } CreateInstance(); +} - if (VkDebugManager) +void VulkanInstanceManager::SetupDebug() +{ + if (bValidationEnabled) { VkDebugManager->Initialize(Instance); } +} +void VulkanInstanceManager::SetupDevice() +{ VkDeviceManager = std::make_unique(); - VkDeviceManager->Initialize(Instance); + VkDeviceManager->Initialize(Instance, bValidationEnabled, ValidationLayers); } void VulkanInstanceManager::Cleanup() @@ -70,16 +94,16 @@ void VulkanInstanceManager::Cleanup() return; } - if (VkDebugManager) - { - VkDebugManager->Cleanup(); - } - if (VkDeviceManager) { VkDeviceManager->Cleanup(); } + if (VkDebugManager) + { + VkDebugManager->Cleanup(); + } + vkDestroyInstance(Instance, nullptr); Instance = VK_NULL_HANDLE; } @@ -174,4 +198,4 @@ void VulkanInstanceManager::CreateInstance() { Log::Error("Failed to create instance!"); } -} \ No newline at end of file +} diff --git a/VulkanInstanceManager.h b/VulkanInstanceManager.h index 065cd0a..5aa9cc4 100644 --- a/VulkanInstanceManager.h +++ b/VulkanInstanceManager.h @@ -3,11 +3,13 @@ #include #include -#define GLFW_INCLUDE_VULKAN -#include - +#include "Logger.h" #include "VulkanDebugManager.h" #include "VulkanDeviceManager.h" +#include "GlfwWindowManager.h" + +#define GLFW_INCLUDE_VULKAN +#include const std::vector ValidationLayers = { "VK_LAYER_KHRONOS_validation" @@ -31,11 +33,23 @@ public: VulkanInstanceManager(VulkanInstanceManager&& Other) noexcept; VulkanInstanceManager& operator=(VulkanInstanceManager&& Other) noexcept; - void Initialize(const FVulkanConfig& Config); + // void Initialize(const FVulkanConfig& Config); + void Initialize(bool bEnableValidationLayers); + void SetupDebug(); + void SetupDevice(); + + // void Initialize(VulkanDebugManager& inDebugManager, bool inValidationEnabled); void Cleanup(); bool IsInitialized() const { return Instance != VK_NULL_HANDLE; } + const std::vector& GetValidationLayers() const + { + return ValidationLayers; + } + + const VkInstance GetInstance() const { return Instance; } + private: std::unique_ptr VkDebugManager = nullptr; std::unique_ptr VkDeviceManager = nullptr; diff --git a/main.cpp b/main.cpp index 7a3b7af..3707164 100644 --- a/main.cpp +++ b/main.cpp @@ -1,7 +1,11 @@ #include "Logger.h" #include "VulkanInstanceManager.h" +#include "VulkanDeviceManager.h" +#include "VulkanDebugManager.h" #include "GlfwWindowManager.h" +#include "VulkanGraphicsPipeline.h" +// #include "VulkanContext.h" struct AppConfig { @@ -19,41 +23,59 @@ class HelloTriangleApplication public: void Run() { - InitGlfw(); - InitVulkan(); + Initialization(); MainLoop(); Cleanup(); } private: - VulkanInstanceManager VkInstanceManager; - GlfwWindowManager GlfwWindowManager; - AppConfig Settings = {}; + AppConfig Settings = {}; + + VulkanInstanceManager InstanceManager; + GlfwWindowManager WindowManager; + + void Initialization() + { + InitGlfw(); + InitVulkan(); + InstanceManager.SetupDebug(); + WindowManager.CreateSurface(InstanceManager.GetInstance()); + InstanceManager.SetupDevice(); + } void InitVulkan() { - FVulkanConfig Config = { Settings.bValidationEnabled, Settings.bVerboseLogging }; - VkInstanceManager.Initialize(Config); + FVulkanConfig Config = { + Settings.bValidationEnabled, + Settings.bVerboseLogging + }; + InstanceManager.Initialize(Config); } void InitGlfw() { - FWindowConfig Config = { Settings.Title, Settings.Width, Settings.Height, Settings.bResizable, Settings.bFullscreen }; - GlfwWindowManager.Initialize(Config); + FWindowConfig Config = { + Settings.Title, + Settings.Width, + Settings.Height, + Settings.bResizable, + Settings.bFullscreen + }; + WindowManager.Initialize(Config); } void MainLoop() { - while (!GlfwWindowManager.ShouldClose()) + while (!WindowManager.ShouldClose()) { - GlfwWindowManager.PollEvents(); + WindowManager.PollEvents(); } } void Cleanup() { - // VkInstanceManager.Cleanup(); - // GlfwWindowManager.Cleanup(); + WindowManager.Cleanup(InstanceManager.GetInstance()); + InstanceManager.Cleanup(); } };