#include "VulkanContext.h" #include "VulkanCommandBuffers.h" #include "VulkanDeviceManager.h" #include "VulkanFramebuffers.h" #include "VulkanSwapChain.h" #include "VulkanVertexBuffer.h" #include "utilities/Logger.h" #include "stb_image.h" #include "imgui_impl_glfw.h" #include "imgui_impl_vulkan.h" #include #include #include #include #include #include #define GLM_FORCE_RADIANS #define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES #include #include #include #include #include VulkanContext::VulkanContext() { } VulkanContext::~VulkanContext() { } void VulkanContext::Initialize(FVulkanConfig& InConfig, const std::vector& InVertices, const std::vector& InIndices) { Config = InConfig; if (Config.bValidationEnabled) { InstanceManager.CreateInstance(&DebugManager); DebugManager.Initialize(InstanceManager.GetInstance()); } else { InstanceManager.CreateInstance(); } CreateSurface(InConfig.Window); DeviceManager.Initialize(FDeviceConfig( InstanceManager.GetInstance(), Config.bValidationEnabled, Surface, InConfig.Window)); DeviceManager.PickPhysicalDevice(); DeviceManager.CreateLogicalDevice(); auto SwapChainSupport = DeviceManager.QuerySwapChainSupport(DeviceManager.GetPhysicalDevice()); SwapChain.Initialize(FSwapConfig( DeviceManager.GetDevice(), Surface, Config.Window, DeviceManager.GetPhysicalQueueFamilies().GraphicsFamily, DeviceManager.GetPhysicalQueueFamilies().PresentFamily, SwapChainSupport.Capabilities, SwapChainSupport.Formats, SwapChainSupport.PresentModes)); SwapChain.CreateSwapChain(); SwapChain.CreateImageViews(); RenderPass.Initialize(DeviceManager.GetDevice()); RenderPass.CreateRenderPass(SwapChain.GetSwapChainImageFormat()); CreateDescriptorSetLayout(); GraphicsPipeline.Initialize(DeviceManager.GetDevice()); GraphicsPipeline.CreateGraphicsPipeline(SwapChain.GetSwapChainExtent(), RenderPass.GetRenderPass(), DescriptorSetLayout); Framebuffers.Initialize(FFramebufferConfig( DeviceManager.GetDevice(), RenderPass.GetRenderPass(), SwapChain.GetSwapChainImageViews(), SwapChain.GetSwapChainExtent())); Framebuffers.CreateFramebuffers(); CommandBuffers.Initialize(DeviceManager.GetDevice(), RenderPass.GetRenderPass()); CommandBuffers.CreateCommandPool(DeviceManager.GetPhysicalQueueFamilies().GraphicsFamily); CreateTextureImage("textures/texture.jpg"); VertexBuffer.Initialize(FVertexBufferConfig( DeviceManager.GetDevice(), DeviceManager.GetPhysicalDevice(), CommandBuffers.GetCommandPool(), DeviceManager.GetGraphicsQueue())); VertexBuffer.CreateVertexBuffer(InVertices); VertexBuffer.CreateIndexBuffer(InIndices); VertexBuffer.CreateUniformBuffers(MAX_FRAMES_IN_FLIGHT); CreateDescriptorPool(); CreateDescriptorSets(); CommandBuffers.CreateCommandBuffers(MAX_FRAMES_IN_FLIGHT); CreateSyncObjects(); VkDescriptorPoolSize PoolSizes[] = { { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 }, { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 }, { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 }, { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 }, { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 }, { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 }, { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 }, { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 }, { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 }, { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 }, { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 } }; VkDescriptorPoolCreateInfo PoolInfo = {}; PoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; PoolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; PoolInfo.maxSets = 1000; PoolInfo.poolSizeCount = (uint32_t)std::size(PoolSizes); PoolInfo.pPoolSizes = PoolSizes; vkCreateDescriptorPool(DeviceManager.GetDevice(), &PoolInfo, nullptr, &ImGuiPool); } void VulkanContext::Cleanup() { if (bImGuiVulkanInitialized) { ImGui_ImplVulkan_Shutdown(); } if (bImGuiGlfwInitialized) { ImGui_ImplGlfw_Shutdown(); } CleanupSwapChain(); vkDestroyDescriptorPool(DeviceManager.GetDevice(), DescriptorPool, nullptr); vkDestroyDescriptorSetLayout(DeviceManager.GetDevice(), DescriptorSetLayout, nullptr); VertexBuffer.Cleanup(MAX_FRAMES_IN_FLIGHT); GraphicsPipeline.Cleanup(); RenderPass.Cleanup(); for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { vkDestroySemaphore(DeviceManager.GetDevice(), ImageAvailableSemaphores[i], nullptr); vkDestroySemaphore(DeviceManager.GetDevice(), RenderFinishedSemaphores[i], nullptr); vkDestroyFence(DeviceManager.GetDevice(), InFlightFences[i], nullptr); } CommandBuffers.Cleanup(); DeviceManager.Cleanup(); if (Config.bValidationEnabled) { DebugManager.Cleanup(); } vkDestroySurfaceKHR(InstanceManager.GetInstance(), Surface, nullptr); InstanceManager.Cleanup(); } void VulkanContext::CreateSurface(GLFWwindow* Window) { if (!Window) { Log::Error("Window not initialized."); } if (!InstanceManager.GetInstance()) { Log::Error("Instance is null."); } VkResult result = glfwCreateWindowSurface(InstanceManager.GetInstance(), 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 VulkanContext::CreateDescriptorSetLayout() { VkDescriptorSetLayoutBinding UboLayoutBinding{}; UboLayoutBinding.binding = 0; UboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; UboLayoutBinding.descriptorCount = 1; UboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; UboLayoutBinding.pImmutableSamplers = nullptr; VkDescriptorSetLayoutCreateInfo LayoutInfo{}; LayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; LayoutInfo.bindingCount = 1; LayoutInfo.pBindings = &UboLayoutBinding; if (vkCreateDescriptorSetLayout(DeviceManager.GetDevice(), &LayoutInfo, nullptr, &DescriptorSetLayout) != VK_SUCCESS) { Log::Error("Failed to create descriptor set layout!"); } else { Log::Info("Successfully created descriptor set layout."); } } void VulkanContext::CreateDescriptorPool() { VkDescriptorPoolSize PoolSize{}; PoolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; PoolSize.descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); VkDescriptorPoolCreateInfo PoolInfo{}; PoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; PoolInfo.poolSizeCount = 1; PoolInfo.pPoolSizes = &PoolSize; PoolInfo.maxSets = static_cast(MAX_FRAMES_IN_FLIGHT); if (vkCreateDescriptorPool(DeviceManager.GetDevice(), &PoolInfo, nullptr, &DescriptorPool) != VK_SUCCESS) { Log::Error("Failed to create descriptor pool!"); } else { Log::Info("Successfully created descriptor pool."); } } void VulkanContext::CreateDescriptorSets() { std::vector Layouts(MAX_FRAMES_IN_FLIGHT, DescriptorSetLayout); VkDescriptorSetAllocateInfo AllocateInfo{}; AllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; AllocateInfo.descriptorPool = DescriptorPool; AllocateInfo.descriptorSetCount = static_cast(MAX_FRAMES_IN_FLIGHT); AllocateInfo.pSetLayouts = Layouts.data(); DescriptorSets.resize(MAX_FRAMES_IN_FLIGHT); if (vkAllocateDescriptorSets(DeviceManager.GetDevice(), &AllocateInfo, DescriptorSets.data()) != VK_SUCCESS) { Log::Error("Failed to allocate descriptor sets!"); } else { Log::Info("Successfully allocated descriptor sets."); } for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { VkDescriptorBufferInfo BufferInfo{}; BufferInfo.buffer = VertexBuffer.GetUniformBuffers()[i]; BufferInfo.offset = 0; BufferInfo.range = sizeof(UniformBufferObject); VkWriteDescriptorSet DescriptorWrite{}; DescriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; DescriptorWrite.dstSet = DescriptorSets[i]; DescriptorWrite.dstBinding = 0; DescriptorWrite.dstArrayElement = 0; DescriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; DescriptorWrite.descriptorCount = 1; DescriptorWrite.pBufferInfo = &BufferInfo; DescriptorWrite.pImageInfo = nullptr; DescriptorWrite.pTexelBufferView = nullptr; vkUpdateDescriptorSets(DeviceManager.GetDevice(), 1, &DescriptorWrite, 0, nullptr); } } void VulkanContext::CreateImage( uint32_t Width, uint32_t Height, VkFormat Format, VkImageTiling Tiling, VkImageUsageFlags Usage, VkMemoryPropertyFlags Properties, VkImage& Image, VkDeviceMemory& ImageMemory) { VkImageCreateInfo ImageInfo{}; ImageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; ImageInfo.imageType = VK_IMAGE_TYPE_2D; ImageInfo.extent.width = Width; ImageInfo.extent.height = Height; ImageInfo.extent.depth = 1; ImageInfo.mipLevels = 1; ImageInfo.arrayLayers = 1; ImageInfo.format = Format; ImageInfo.tiling = Tiling; ImageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; ImageInfo.usage = Usage; ImageInfo.samples = VK_SAMPLE_COUNT_1_BIT; ImageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; if (vkCreateImage(DeviceManager.GetDevice(), &ImageInfo, nullptr, &Image) != VK_SUCCESS) { Log::Error("Failed to create image!"); } else { Log::Info("Successfully created image."); } VkMemoryRequirements MemoryRequirements; vkGetImageMemoryRequirements(DeviceManager.GetDevice(), Image, &MemoryRequirements); VkMemoryAllocateInfo AllocateInfo{}; AllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; AllocateInfo.allocationSize = MemoryRequirements.size; AllocateInfo.memoryTypeIndex = VertexBuffer.FindMemoryType(MemoryRequirements.memoryTypeBits, Properties); if (vkAllocateMemory(DeviceManager.GetDevice(), &AllocateInfo, nullptr, &ImageMemory) != VK_SUCCESS) { Log::Error("Failed to allocate image memory!"); } else { Log::Info("Successfully allocated imaged memory."); } vkBindImageMemory(DeviceManager.GetDevice(), Image, ImageMemory, 0); } void VulkanContext::CreateTextureImage(const char* FileName) { int TextureWidth, TextureHeight, TextureChannels; stbi_uc* Pixels = stbi_load(FileName, &TextureWidth, &TextureHeight, &TextureChannels, STBI_rgb_alpha); VkDeviceSize ImageSize = TextureWidth * TextureHeight * 4; if (!Pixels) { Log::Error("Failed to load texture image!"); } else { Log::Info("Successfully loaded texture image."); } VkBuffer StagingBuffer; VkDeviceMemory StagingBufferMemory; VertexBuffer.CreateBuffer(ImageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, StagingBuffer, StagingBufferMemory); void* Data; vkMapMemory(DeviceManager.GetDevice(), StagingBufferMemory, 0, ImageSize, 0, &Data); memcpy(Data, Pixels, static_cast(ImageSize)); vkUnmapMemory(DeviceManager.GetDevice(), StagingBufferMemory); stbi_image_free(Pixels); CreateImage( TextureWidth, TextureHeight, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, TextureImage, TextureImageMemory); } void VulkanContext::CreateSyncObjects() { ImageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); RenderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); InFlightFences.resize(MAX_FRAMES_IN_FLIGHT); VkSemaphoreCreateInfo SemaphoreInfo{}; SemaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; VkFenceCreateInfo FenceInfo{}; FenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; FenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { if (vkCreateSemaphore(DeviceManager.GetDevice(), &SemaphoreInfo, nullptr, &ImageAvailableSemaphores[i]) != VK_SUCCESS || vkCreateSemaphore(DeviceManager.GetDevice(), &SemaphoreInfo, nullptr, &RenderFinishedSemaphores[i]) != VK_SUCCESS || vkCreateFence(DeviceManager.GetDevice(), &FenceInfo, nullptr, &InFlightFences[i]) != VK_SUCCESS) { Log::Error("Failed to create semaphores!"); } else { Log::Info("Successfully created semaphores"); } } } void VulkanContext::DrawFrame(bool bDrawImGui, uint32_t InVerticesSize, uint32_t InIndexSize) { vkWaitForFences(DeviceManager.GetDevice(), 1, &InFlightFences[CurrentFrame], VK_TRUE, UINT64_MAX); uint32_t ImageIndex; VkResult result = vkAcquireNextImageKHR( DeviceManager.GetDevice(), SwapChain.GetSwapChain(), UINT64_MAX, ImageAvailableSemaphores[CurrentFrame], VK_NULL_HANDLE, &ImageIndex); if (result == VK_ERROR_OUT_OF_DATE_KHR) { RecreateSwapChain(); return; } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { Log::Error("Failed to acquire swap chain images!"); } vkResetFences(DeviceManager.GetDevice(), 1, &InFlightFences[CurrentFrame]); vkResetCommandBuffer(CommandBuffers.GetCommandBuffer(CurrentFrame), 0); FRecordCommandBuffersParams Params{ CommandBuffers.GetCommandBuffer(CurrentFrame), ImageIndex, VertexBuffer.GetVertexBuffer(), InVerticesSize, VertexBuffer.GetIndexBuffer(), InIndexSize, RenderPass.GetRenderPass(), SwapChain.GetSwapChainExtent(), GraphicsPipeline.GetGraphicsPipeline(), Framebuffers.GetSwapChainFrameBuffers(), DescriptorSets[CurrentFrame], GraphicsPipeline.GetPipelineLayout(), bDrawImGui // DrawData }; CommandBuffers.RecordCommandBuffer(Params); UpdateUniformBuffer(CurrentFrame); VkSubmitInfo SubmitInfo{}; SubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; VkSemaphore WaitSemaphores[] = { ImageAvailableSemaphores[CurrentFrame] }; VkPipelineStageFlags WaitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; SubmitInfo.waitSemaphoreCount = 1; SubmitInfo.pWaitSemaphores = WaitSemaphores; SubmitInfo.pWaitDstStageMask = WaitStages; SubmitInfo.commandBufferCount = 1; VkCommandBuffer CommandBuffer = CommandBuffers.GetCommandBuffer(CurrentFrame); SubmitInfo.pCommandBuffers = &CommandBuffer; VkSemaphore SignalSemaphores[] = { RenderFinishedSemaphores[CurrentFrame] }; SubmitInfo.signalSemaphoreCount = 1; SubmitInfo.pSignalSemaphores = SignalSemaphores; if (vkQueueSubmit(DeviceManager.GetGraphicsQueue(), 1, &SubmitInfo, InFlightFences[CurrentFrame]) != VK_SUCCESS) { Log::Error("Failed to submit draw command buffer!"); } VkPresentInfoKHR PresentInfo{}; PresentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; PresentInfo.waitSemaphoreCount = 1; PresentInfo.pWaitSemaphores = SignalSemaphores; VkSwapchainKHR SwapChains[] = { SwapChain.GetSwapChain() }; PresentInfo.swapchainCount = 1; PresentInfo.pSwapchains = SwapChains; PresentInfo.pImageIndices = &ImageIndex; PresentInfo.pResults = nullptr; result = vkQueuePresentKHR(DeviceManager.GetPresentQueue(), &PresentInfo); if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || bFramebufferResized) { bFramebufferResized = false; RecreateSwapChain(); } else if (result != VK_SUCCESS) { Log::Error("Failed to present swap chain image!"); } CurrentFrame = (CurrentFrame + 1) % MAX_FRAMES_IN_FLIGHT; } void VulkanContext::UpdateUniformBuffer(uint32_t CurrentImage) { static auto StartTime = std::chrono::high_resolution_clock::now(); auto CurrentTime = std::chrono::high_resolution_clock::now(); float Time = std::chrono::duration(CurrentTime - StartTime).count(); UniformBufferObject Ubo{}; Ubo.Model = glm::rotate(glm::mat4(1.0f), Time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); Ubo.View = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); Ubo.Projection = glm::perspective(glm::radians(45.0f), SwapChain.GetSwapChainExtent().width / (float)SwapChain.GetSwapChainExtent().height, 0.1f, 10.0f); Ubo.Projection[1][1] *= -1; memcpy(VertexBuffer.GetUniformBuffersMapped()[CurrentImage], &Ubo, sizeof(Ubo)); } void VulkanContext::RecreateSwapChain() { int Width = 0, Height = 0; glfwGetFramebufferSize(Config.Window, &Width, &Height); while (Width == 0 || Height == 0) { glfwGetFramebufferSize(Config.Window, &Width, &Height); glfwWaitEvents(); } Log::Info("Recreating SwapChain..."); vkDeviceWaitIdle(DeviceManager.GetDevice()); CleanupSwapChain(); auto SwapChainSupport = DeviceManager.QuerySwapChainSupport(DeviceManager.GetPhysicalDevice()); SwapChain.Initialize(FSwapConfig( DeviceManager.GetDevice(), Surface, Config.Window, DeviceManager.GetPhysicalQueueFamilies().GraphicsFamily, DeviceManager.GetPhysicalQueueFamilies().PresentFamily, SwapChainSupport.Capabilities, SwapChainSupport.Formats, SwapChainSupport.PresentModes)); SwapChain.CreateSwapChain(); SwapChain.CreateImageViews(); Framebuffers.Initialize(FFramebufferConfig( DeviceManager.GetDevice(), RenderPass.GetRenderPass(), SwapChain.GetSwapChainImageViews(), SwapChain.GetSwapChainExtent())); Framebuffers.CreateFramebuffers(); InitImGui(); } void VulkanContext::InitImGui() { if (!bImGuiGlfwInitialized) { if (!ImGui_ImplGlfw_InitForVulkan(Config.Window, true)) { Log::Error("Failed to initialize ImGui GLFW backend!"); return; } bImGuiGlfwInitialized = true; } if (bImGuiVulkanInitialized) { ImGui_ImplVulkan_Shutdown(); bImGuiVulkanInitialized = false; } ImGui_ImplVulkan_InitInfo ImGuiInitInfo = {}; ImGuiInitInfo.Instance = InstanceManager.GetInstance(); ImGuiInitInfo.PhysicalDevice = DeviceManager.GetPhysicalDevice(); ImGuiInitInfo.Device = DeviceManager.GetDevice(); ImGuiInitInfo.QueueFamily = DeviceManager.GetPhysicalQueueFamilies().GraphicsFamily.value_or(0); ImGuiInitInfo.Queue = DeviceManager.GetGraphicsQueue(); ImGuiInitInfo.PipelineCache = VK_NULL_HANDLE; ImGuiInitInfo.DescriptorPool = ImGuiPool; ImGuiInitInfo.MinImageCount = MAX_FRAMES_IN_FLIGHT; ImGuiInitInfo.ImageCount = SwapChain.GetSwapChainImageViews().size(); ImGuiInitInfo.Allocator = nullptr; ImGuiInitInfo.PipelineInfoMain.RenderPass = RenderPass.GetRenderPass(); ImGuiInitInfo.PipelineInfoMain.Subpass = 0; ImGuiInitInfo.PipelineInfoMain.MSAASamples = VK_SAMPLE_COUNT_1_BIT; ImGuiInitInfo.CheckVkResultFn = nullptr; if (!ImGui_ImplVulkan_Init(&ImGuiInitInfo)) { Log::Error("Failed to reinitialize ImGui Vulkan backend!"); return; } bImGuiVulkanInitialized = true; } void VulkanContext::CleanupSwapChain() { Framebuffers.Cleanup(); SwapChain.Cleanup(); }