From be8bcb0aaf1ee386be24fa0ceabf21fee8bda734 Mon Sep 17 00:00:00 2001 From: Thraix Date: Sat, 14 Jan 2023 18:15:33 +0100 Subject: [PATCH] Add Buffer abstractions --- Vulkan/Vulkan.vcxproj | 13 +- Vulkan/Vulkan.vcxproj.filters | 27 +++ Vulkan/src/Buffer.h | 68 ++++-- Vulkan/src/Common.h | 30 ++- Vulkan/src/DebugMessenger.h | 8 +- Vulkan/src/FileSystem.h | 2 +- Vulkan/src/IndexBuffer.h | 24 ++ Vulkan/src/Instance.h | 132 ++++++++--- Vulkan/src/Pipeline.h | 283 ++++++++++++++++++++++++ Vulkan/src/PipelineCreator.h | 62 ++++++ Vulkan/src/QueueFamilies.h | 4 +- Vulkan/src/SwapChain.cpp | 305 ++++++++++++++++++++++++++ Vulkan/src/SwapChain.h | 400 +++------------------------------- Vulkan/src/Timer.h | 23 ++ Vulkan/src/UniformBuffer.h | 93 ++++++++ Vulkan/src/Vertex.h | 47 ++++ Vulkan/src/VertexBuffer.h | 30 +++ Vulkan/src/VertexDescriptor.h | 49 +++++ Vulkan/src/VulkanException.h | 4 +- Vulkan/src/main.cpp | 374 +++++-------------------------- 20 files changed, 1221 insertions(+), 757 deletions(-) create mode 100644 Vulkan/src/IndexBuffer.h create mode 100644 Vulkan/src/Pipeline.h create mode 100644 Vulkan/src/PipelineCreator.h create mode 100644 Vulkan/src/SwapChain.cpp create mode 100644 Vulkan/src/Timer.h create mode 100644 Vulkan/src/UniformBuffer.h create mode 100644 Vulkan/src/Vertex.h create mode 100644 Vulkan/src/VertexBuffer.h create mode 100644 Vulkan/src/VertexDescriptor.h diff --git a/Vulkan/Vulkan.vcxproj b/Vulkan/Vulkan.vcxproj index ca5e350..bdb1254 100644 --- a/Vulkan/Vulkan.vcxproj +++ b/Vulkan/Vulkan.vcxproj @@ -114,7 +114,7 @@ Level3 true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + _DEBUG;_CONSOLE;%(PreprocessorDefinitions);GLM_FORCE_LEFT_HANDED;GLFW_INCLUDE_VULKAN;GLM_FORCE_RADIANS;GLM_FORCE_DEPTH_ZERO_TO_ONE true $(ProjectDir)ext/include/;C:/VulkanSDK/1.3.236.0/Include;%(AdditionalIncludeDirectories) stdcpp17 @@ -132,7 +132,7 @@ true true true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + NDEBUG;_CONSOLE;%(PreprocessorDefinitions);GLM_FORCE_LEFT_HANDED;GLFW_INCLUDE_VULKAN;GLM_FORCE_RADIANS;GLM_FORCE_DEPTH_ZERO_TO_ONE true $(ProjectDir)ext/include/;C:/VulkanSDK/1.3.236.0/Include;%(AdditionalIncludeDirectories) stdcpp17 @@ -148,15 +148,24 @@ + + + + + + + + + diff --git a/Vulkan/Vulkan.vcxproj.filters b/Vulkan/Vulkan.vcxproj.filters index 9f4a713..39c3ae0 100644 --- a/Vulkan/Vulkan.vcxproj.filters +++ b/Vulkan/Vulkan.vcxproj.filters @@ -18,6 +18,9 @@ Source Files + + Source Files + @@ -47,6 +50,30 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + diff --git a/Vulkan/src/Buffer.h b/Vulkan/src/Buffer.h index 4fffca2..a39c304 100644 --- a/Vulkan/src/Buffer.h +++ b/Vulkan/src/Buffer.h @@ -7,7 +7,8 @@ class Buffer { -private: + CP_DELETE_COPY_AND_MOVE_CTOR(Buffer); +protected: Instance& instance; VkDeviceMemory memory; @@ -27,7 +28,7 @@ public: createInfo.usage = usage; createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - VK_ASSERT(vkCreateBuffer(instance.GetDevice(), &createInfo, nullptr, &handle), "Failed to initialize buffer"); + CP_VK_ASSERT(vkCreateBuffer(instance.GetDevice(), &createInfo, nullptr, &handle), "Failed to initialize buffer"); VkMemoryRequirements memoryRequirements; vkGetBufferMemoryRequirements(instance.GetDevice(), handle, &memoryRequirements); @@ -37,12 +38,12 @@ public: allocateInfo.allocationSize = memoryRequirements.size; allocateInfo.memoryTypeIndex = FindMemoryType(instance, memoryRequirements.memoryTypeBits, properties); - VK_ASSERT(vkAllocateMemory(instance.GetDevice(), &allocateInfo, nullptr, &memory), "Failed to allocate buffer memory"); + CP_VK_ASSERT(vkAllocateMemory(instance.GetDevice(), &allocateInfo, nullptr, &memory), "Failed to allocate buffer memory"); vkBindBufferMemory(instance.GetDevice(), handle, memory, 0); } - ~Buffer() + virtual ~Buffer() { vkFreeMemory(instance.GetDevice(), memory, nullptr); vkDestroyBuffer(instance.GetDevice(), handle, nullptr); @@ -50,7 +51,7 @@ public: void Update(void* indexData, int index) { - ASSERT(index >= 0 && index < count, "instance is outside of the buffer"); + CP_ASSERT(index >= 0 && index < count, "index is outside of the buffer"); if (mappedData == nullptr) { @@ -65,34 +66,58 @@ public: } } + void UpdateStaging(void* data) + { + VkDeviceSize bufferSize = size * count; + Buffer stagingBuffer{instance, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, bufferSize, 1}; + + stagingBuffer.Update(data, 0); + + CopyBuffer(instance, stagingBuffer, *this, 0, bufferSize); + } + + void UpdateStaging(void* data, VkDeviceSize offset, VkDeviceSize size) + { + Buffer stagingBuffer{instance, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, size, 1}; + + stagingBuffer.Update(data, 0); + + CopyBuffer(instance, stagingBuffer, *this, offset, size); + } + void Map() { - ASSERT(mappedData == nullptr, "Mapping an already mapped buffer") + CP_ASSERT(mappedData == nullptr, "Mapping an already mapped buffer"); vkMapMemory(instance.GetDevice(), memory, 0, size * count, 0, &mappedData); } void Unmap() { - ASSERT(mappedData != nullptr, "Unmapping an already unmapped buffer") + CP_ASSERT(mappedData != nullptr, "Unmapping an already unmapped buffer"); vkUnmapMemory(instance.GetDevice(), memory); mappedData = nullptr; } + virtual void Bind(VkCommandBuffer commandBuffer) { CP_UNIMPLEMENTED(); }; + + void BindAsVertexBuffer(VkCommandBuffer commandBuffer) + { + VkDeviceSize offset = 0; + vkCmdBindVertexBuffers(commandBuffer, 0, 1, &handle, &offset); + } + + void BindAsIndexBuffer(VkCommandBuffer commandBuffer) + { + // TODO: Maybe don't assume that indices are uint16? + vkCmdBindIndexBuffer(commandBuffer, handle, 0, VK_INDEX_TYPE_UINT16); + } + VkBuffer GetHandle() const { return handle; } - VkDescriptorBufferInfo GetDescriptorBufferInfo(int instance) - { - VkDescriptorBufferInfo bufferInfo{}; - bufferInfo.buffer = handle; - bufferInfo.offset = (VkDeviceSize)instance * size; - bufferInfo.range = size; - return bufferInfo; - } - VkDeviceSize GetSize() const { return size; @@ -100,13 +125,12 @@ public: VkDeviceSize GetPosition(int index) const { - ASSERT(index >= 0 && index < count, "Instance is outside of the buffer"); + CP_ASSERT(index >= 0 && index < count, "index is outside of the buffer"); return size * (VkDeviceSize)index; } - static void CopyBuffer(Instance& instance, const Buffer& srcBuffer, const Buffer& dstBuffer) + static void CopyBuffer(Instance& instance, const Buffer& srcBuffer, const Buffer& dstBuffer, VkDeviceSize offset, VkDeviceSize size) { - ASSERT(srcBuffer.size == dstBuffer.size && srcBuffer.count == dstBuffer.count, "Buffers have different sizes"); VkCommandBufferAllocateInfo allocateInfo{}; allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; @@ -115,7 +139,7 @@ public: allocateInfo.commandBufferCount = 1; VkCommandBuffer commandBuffer; - VK_ASSERT(vkAllocateCommandBuffers(instance.GetDevice(), &allocateInfo, &commandBuffer), "Failed to initialize command buffer"); + CP_VK_ASSERT(vkAllocateCommandBuffers(instance.GetDevice(), &allocateInfo, &commandBuffer), "Failed to initialize command buffer"); VkCommandBufferBeginInfo beginInfo{}; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; @@ -124,9 +148,9 @@ public: vkBeginCommandBuffer(commandBuffer, &beginInfo); VkBufferCopy bufferCopy{}; - bufferCopy.dstOffset = 0; + bufferCopy.dstOffset = offset; bufferCopy.srcOffset = 0; - bufferCopy.size = srcBuffer.size * (VkDeviceSize)srcBuffer.count; + bufferCopy.size = size; vkCmdCopyBuffer(commandBuffer, srcBuffer.GetHandle(), dstBuffer.GetHandle(), 1, &bufferCopy); diff --git a/Vulkan/src/Common.h b/Vulkan/src/Common.h index 20fe90a..f8ad6f4 100644 --- a/Vulkan/src/Common.h +++ b/Vulkan/src/Common.h @@ -4,10 +4,21 @@ #include #include -#define ASSERT(Function, message) if(!(Function)) { throw std::runtime_error(message); } while(false) -#define VK_ASSERT(Function, message) if(Function != VK_SUCCESS) { throw VulkanException(message); } while(false) +#define CP_DEBUG(format, ...) std::cout << "[DBG] " << StringFormat(format, __VA_ARGS__) << std::endl +#define CP_INFO(format, ...) std::cout << "[INF] " << StringFormat(format, __VA_ARGS__) << std::endl +#define CP_WARN(format, ...) std::cout << "[WRN] " << StringFormat(format, __VA_ARGS__) << std::endl +#define CP_ERR(format, ...) std::cout << "[ERR] " << StringFormat(format, __VA_ARGS__) << std::endl -VkResult vkCreateDebugUtilsMessengerEXT(VkInstance instance, +#define CP_UNIMPLEMENTED() CP_WARN("%s is unimplemented", __FUNCTION__) +#define CP_ASSERT(Function, format, ...) if(!(Function)) { throw std::runtime_error(StringFormat(format, __VA_ARGS__)); } while(false) +#define CP_VK_ASSERT(Function, format, ...) if(Function != VK_SUCCESS) { throw VulkanException(StringFormat(format, __VA_ARGS__)); } while(false) +#define CP_DELETE_COPY_AND_MOVE_CTOR(ClassName) \ + ClassName(ClassName&&) = delete; \ + ClassName(const ClassName&) = delete; \ + ClassName& operator=(ClassName&&) = delete; \ + ClassName& operator=(const ClassName&) = delete + +static VkResult vkCreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) @@ -18,7 +29,7 @@ VkResult vkCreateDebugUtilsMessengerEXT(VkInstance instance, return VK_ERROR_EXTENSION_NOT_PRESENT; } -void vkDestroyDebugUtilsMessengerEXT(VkInstance instance, +static void vkDestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) { auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); @@ -26,3 +37,14 @@ void vkDestroyDebugUtilsMessengerEXT(VkInstance instance, func(instance, debugMessenger, pAllocator); } } + +template +std::string StringFormat(const std::string& format, Args... args) +{ + int size = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; + CP_ASSERT(size > 0, "Error during formatting"); + std::unique_ptr buf(new char[size]); + std::snprintf(buf.get(), size, format.c_str(), args...); + return std::string(buf.get(), buf.get() + size - 1); +} + diff --git a/Vulkan/src/DebugMessenger.h b/Vulkan/src/DebugMessenger.h index 383f6d8..cb6402c 100644 --- a/Vulkan/src/DebugMessenger.h +++ b/Vulkan/src/DebugMessenger.h @@ -4,6 +4,7 @@ class DebugMessenger { + CP_DELETE_COPY_AND_MOVE_CTOR(DebugMessenger); public: VkInstance instance; VkDebugUtilsMessengerEXT debugMessenger; @@ -22,7 +23,7 @@ public: VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; createInfo.pfnUserCallback = DebugCallback; createInfo.pUserData = nullptr; - VK_ASSERT(vkCreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger), "Failed to initialze debug messenger"); + CP_VK_ASSERT(vkCreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger), "Failed to initialze debug messenger"); #endif } @@ -33,11 +34,6 @@ public: #endif } - DebugMessenger(DebugMessenger&&) = delete; - DebugMessenger(const DebugMessenger&) = delete; - DebugMessenger& operator=(DebugMessenger&&) = delete; - DebugMessenger& operator=(const DebugMessenger&) = delete; - static void AddRequiredExtensions(std::vector* extensions) { #ifndef NDEBUG diff --git a/Vulkan/src/FileSystem.h b/Vulkan/src/FileSystem.h index 61643c9..ca2c92f 100644 --- a/Vulkan/src/FileSystem.h +++ b/Vulkan/src/FileSystem.h @@ -9,7 +9,7 @@ namespace FileSystem static std::vector ReadFile(const std::string& filename) { std::ifstream file(filename, std::ios::ate | std::ios::binary); - ASSERT(file.is_open(), "Failed to open file"); + CP_ASSERT(file.is_open(), "Failed to open file"); size_t fileSize = (size_t) file.tellg(); std::vector buffer(fileSize); diff --git a/Vulkan/src/IndexBuffer.h b/Vulkan/src/IndexBuffer.h new file mode 100644 index 0000000..590e883 --- /dev/null +++ b/Vulkan/src/IndexBuffer.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Buffer.h" + +class IndexBuffer : public Buffer +{ + CP_DELETE_COPY_AND_MOVE_CTOR(IndexBuffer); +private: + int indexCount; +public: + IndexBuffer(Instance& instance, int indexCount) + : Buffer{instance, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexCount * sizeof(uint16_t), 1}, indexCount{indexCount} + {} + + void Bind(VkCommandBuffer commandBuffer) override + { + vkCmdBindIndexBuffer(commandBuffer, handle, 0, VK_INDEX_TYPE_UINT16); + } + + void Draw(VkCommandBuffer commandBuffer) + { + vkCmdDrawIndexed(commandBuffer, indexCount, 1, 0, 0, 0); + } +}; \ No newline at end of file diff --git a/Vulkan/src/Instance.h b/Vulkan/src/Instance.h index 24a6fe9..b2847c0 100644 --- a/Vulkan/src/Instance.h +++ b/Vulkan/src/Instance.h @@ -1,35 +1,47 @@ #pragma once #include +#include #include #include "DebugMessenger.h" #include "QueueFamilies.h" #include "SwapChain.h" +#include "Timer.h" class Instance final { + CP_DELETE_COPY_AND_MOVE_CTOR(Instance); private: static const int MAX_FRAMES_IN_FLIGHT = 2; static const int WINDOW_WIDTH = 1920; static const int WINDOW_HEIGHT = 1080; - VkInstance instance; GLFWwindow* window; VkSurfaceKHR surface; std::unique_ptr debugMessenger; VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; VkDevice device; + uint32_t graphicsQueueIndex; + uint32_t presentQueueIndex; VkQueue graphicsQueue; VkQueue presentQueue; std::unique_ptr swapChain; + int flightIndex; + std::vector imageAvailableSemaphores; + std::vector renderFinishedSemaphores; + std::vector inFlightFences; VkCommandPool commandPool; bool framebufferResized = false; + int frameCount = 0; + Timer timer; + public: Instance(const std::string& applicationName) { + timer.Start(); InitializeWindow(applicationName); InitializeInstance(applicationName); InitializeDebugMessenger(); @@ -38,10 +50,18 @@ public: InitializeLogicalDevice(); InitializeSwapChain(); InitializeCommandPool(); + InitializeSyncObjects(); + std::cout << "Initialized Vulkan in " << timer.Elapsed() << " seconds" << std::endl; } ~Instance() { + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) + { + vkDestroyFence(device, inFlightFences[i], nullptr); + vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); + vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); + } vkDestroyCommandPool(device, commandPool, nullptr); swapChain.reset(); vkDestroyDevice(device, nullptr); @@ -53,32 +73,38 @@ public: bool BeginPresent() { - if (!swapChain->BeginPresent()) - return true; + vkWaitForFences(device, 1, &inFlightFences[flightIndex], VK_TRUE, UINT64_MAX); + + if (!swapChain->BeginPresent(imageAvailableSemaphores[flightIndex])) + return false; + + vkResetFences(device, 1, &inFlightFences[flightIndex]); + return true; } bool EndPresent() { - swapChain->EndPresent(presentQueue, framebufferResized); + swapChain->EndPresent(presentQueue, &renderFinishedSemaphores[flightIndex], framebufferResized); + + framebufferResized = false; + flightIndex = (flightIndex + 1) % MAX_FRAMES_IN_FLIGHT; return !glfwWindowShouldClose(window); } void SubmitGraphicsQueue(const std::vector& commandBuffers) { - VkSemaphore waitSemaphores[] = {swapChain->GetAvailableImageSemaphore()}; - VkSemaphore signalSemaphores[] = {swapChain->GetRenderFinishedSemaphore()}; VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; VkSubmitInfo submitInfo{}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = waitSemaphores; + submitInfo.pWaitSemaphores = &imageAvailableSemaphores[flightIndex]; submitInfo.pWaitDstStageMask = waitStages; submitInfo.commandBufferCount = commandBuffers.size(); submitInfo.pCommandBuffers = commandBuffers.data(); submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = signalSemaphores; + submitInfo.pSignalSemaphores = &renderFinishedSemaphores[flightIndex]; - VK_ASSERT(vkQueueSubmit(graphicsQueue, 1, &submitInfo, swapChain->GetInFlightFence()), "Failed to submit command buffer"); + CP_VK_ASSERT(vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[flightIndex]), "Failed to submit command buffer"); } VkInstance GetInstance() const @@ -86,6 +112,16 @@ public: return instance; } + GLFWwindow* GetWindow() const + { + return window; + } + + VkSurfaceKHR GetSurface() const + { + return surface; + } + VkPhysicalDevice GetPhysicalDevice() const { return physicalDevice; @@ -106,7 +142,12 @@ public: return graphicsQueue; } - int GetMaxFramesInFlight() + int GetFlightIndex() const + { + return flightIndex; + } + + int GetMaxFramesInFlight() const { return MAX_FRAMES_IN_FLIGHT; } @@ -120,7 +161,21 @@ private: void InitializeWindow(const std::string& applicationName) { glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + + +#if defined(FULLSCREEN) + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + window = glfwCreateWindow(mode->width, mode->height, applicationName.c_str(), glfwGetPrimaryMonitor(), nullptr); +#elif defined(BORDERLESS_WINDOWED) + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + window = glfwCreateWindow(mode->width, mode->height, applicationName.c_str(), nullptr, nullptr); + glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); +#else window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, applicationName.c_str(), nullptr, nullptr); +#endif + CP_ASSERT(window, "Failed to initialize glfw window"); glfwSetWindowUserPointer(window, this); glfwSetFramebufferSizeCallback(window, FramebufferResizeCallback); @@ -132,7 +187,7 @@ private: appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.pApplicationName = applicationName.c_str(); appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); - appInfo.pEngineName = "Greet Engine"; + appInfo.pEngineName = "Copium Engine"; appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); appInfo.apiVersion = VK_API_VERSION_1_1; @@ -151,7 +206,7 @@ private: std::vector layers{}; DebugMessenger::AddRequiredLayers(&layers); - ASSERT(CheckLayerSupport(layers), "Some required layers are not supported"); + CP_ASSERT(CheckLayerSupport(layers), "Some required layers are not supported"); VkInstanceCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; @@ -160,7 +215,7 @@ private: createInfo.ppEnabledExtensionNames = requiredExtensions.data(); createInfo.enabledLayerCount = layers.size(); createInfo.ppEnabledLayerNames = layers.data(); - VK_ASSERT(vkCreateInstance(&createInfo, nullptr, &instance), "Failed to create instance"); + CP_VK_ASSERT(vkCreateInstance(&createInfo, nullptr, &instance), "Failed to create instance"); } void InitializeDebugMessenger() @@ -170,14 +225,14 @@ private: void InitializeSurface() { - VK_ASSERT(glfwCreateWindowSurface(instance, window, nullptr, &surface), "Failed to create Vulkan surface"); + CP_VK_ASSERT(glfwCreateWindowSurface(instance, window, nullptr, &surface), "Failed to create Vulkan surface"); } void SelectPhysicalDevice() { uint32_t deviceCount; vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); - ASSERT(deviceCount != 0, "No available devices support Vulkan"); + CP_ASSERT(deviceCount != 0, "No available devices support Vulkan"); std::vector devices(deviceCount); vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); @@ -199,17 +254,17 @@ private: break; } } - ASSERT(physicalDevice != VK_NULL_HANDLE, "Failed to find suitable GPU"); + CP_ASSERT(physicalDevice != VK_NULL_HANDLE, "Failed to find suitable GPU"); } void InitializeLogicalDevice() { - QueueFamilies queueFamilies{surface, physicalDevice}; + QueueFamiliesQuery query{surface, physicalDevice}; float queuePriority = 1.0f; std::vector queueCreateInfos{}; - std::set uniqueQueueFamilies{queueFamilies.graphicsFamily.value(), queueFamilies.presentFamily.value()}; + std::set uniqueQueueFamilies{query.graphicsFamily.value(), query.presentFamily.value()}; for(auto&& queueFamily : uniqueQueueFamilies) { VkDeviceQueueCreateInfo queueCreateInfo{}; @@ -230,28 +285,48 @@ private: createInfo.ppEnabledExtensionNames = deviceExtensions.data(); createInfo.enabledExtensionCount = deviceExtensions.size(); - VK_ASSERT(vkCreateDevice(physicalDevice, &createInfo, nullptr, &device), "Failed to initialize logical device"); + CP_VK_ASSERT(vkCreateDevice(physicalDevice, &createInfo, nullptr, &device), "Failed to initialize logical device"); - vkGetDeviceQueue(device, queueFamilies.graphicsFamily.value(), 0, &graphicsQueue); - vkGetDeviceQueue(device, queueFamilies.presentFamily.value(), 0, &presentQueue); + graphicsQueueIndex = query.graphicsFamily.value(); + presentQueueIndex = query.presentFamily.value(); + vkGetDeviceQueue(device, graphicsQueueIndex, 0, &graphicsQueue); + vkGetDeviceQueue(device, presentQueueIndex , 0, &presentQueue); } void InitializeSwapChain() { - swapChain = std::make_unique(window, surface, device, physicalDevice); + swapChain = std::make_unique(*this); } void InitializeCommandPool() { - QueueFamilies queueFamilies{surface, physicalDevice}; - VkCommandPoolCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; createInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - createInfo.queueFamilyIndex = queueFamilies.graphicsFamily.value(); - VK_ASSERT(vkCreateCommandPool(device, &createInfo, nullptr, &commandPool), "Failed to initialize command pool"); + createInfo.queueFamilyIndex = graphicsQueueIndex; + CP_VK_ASSERT(vkCreateCommandPool(device, &createInfo, nullptr, &commandPool), "Failed to initialize command pool"); } + void InitializeSyncObjects() + { + imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); + inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); + VkSemaphoreCreateInfo semaphoreCreateInfo{}; + semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) + { + CP_VK_ASSERT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &imageAvailableSemaphores[i]), "Failed to initialize available image semaphore"); + CP_VK_ASSERT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &renderFinishedSemaphores[i]), "Failed to initialize render finished semaphore"); + + VkFenceCreateInfo fenceCreateInfo{}; + fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + CP_VK_ASSERT(vkCreateFence(device, &fenceCreateInfo, nullptr, &inFlightFences[i]), "Failed to initialize in flight fence"); + } + } + std::vector GetRequiredExtensions() { uint32_t glfwExtensionCount; @@ -308,8 +383,8 @@ private: if (!deviceFeatures.fillModeNonSolid) return false; - QueueFamilies queueFamilies{surface, device}; - if (!queueFamilies.AllRequiredFamiliesSupported()) + QueueFamiliesQuery query{surface, device}; + if (!query.AllRequiredFamiliesSupported()) return false; if (!CheckDeviceExtensionSupport(device)) @@ -355,5 +430,4 @@ private: Instance* instance = static_cast(glfwGetWindowUserPointer(window)); instance->framebufferResized = true; } - }; diff --git a/Vulkan/src/Pipeline.h b/Vulkan/src/Pipeline.h new file mode 100644 index 0000000..a5b0bc1 --- /dev/null +++ b/Vulkan/src/Pipeline.h @@ -0,0 +1,283 @@ +#pragma once + +#include "Common.h" +#include "Instance.h" +#include "FileSystem.h" +#include "PipelineCreator.h" + +#include +#include + + +class Pipeline +{ + CP_DELETE_COPY_AND_MOVE_CTOR(Pipeline); +private: + Instance& instance; + + std::map vertexDescriptorSetLayouts; + std::map fragmentDescriptorSetLayouts; + VkPipelineLayout pipelineLayout; + VkPipeline graphicsPipeline; + +public: + Pipeline(Instance& instance, PipelineCreator creator) + : instance{instance} + { + InitializeDescriptorSetLayouts(creator); + InitializePipeline(creator); + } + + ~Pipeline() + { + vkDestroyPipeline(instance.GetDevice(), graphicsPipeline, nullptr); + vkDestroyPipelineLayout(instance.GetDevice(), pipelineLayout, nullptr); + for (auto&& descriptorSetLayout : vertexDescriptorSetLayouts) + { + vkDestroyDescriptorSetLayout(instance.GetDevice(), descriptorSetLayout.second, nullptr); + } + for (auto&& descriptorSetLayout : fragmentDescriptorSetLayouts) + { + vkDestroyDescriptorSetLayout(instance.GetDevice(), descriptorSetLayout.second, nullptr); + } + } + + void Bind(VkCommandBuffer commandBuffer) + { + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); + + VkViewport viewport{}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = instance.GetSwapChain().GetExtent().width; + viewport.height = instance.GetSwapChain().GetExtent().height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(commandBuffer, 0, 1, &viewport); + VkRect2D scissor{}; + scissor.offset = {0, 0}; + scissor.extent = instance.GetSwapChain().GetExtent(); + vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + } + + VkPipelineLayout GetPipelineLayout() const + { + return pipelineLayout; + } + + VkDescriptorSetLayout GetVertexDescriptorSetLayout(uint32_t binding) + { + return vertexDescriptorSetLayouts.at(binding); + } + + VkDescriptorSetLayout GetFragmentDescriptorSetLayout(uint32_t binding) + { + return fragmentDescriptorSetLayouts.at(binding); + } + +private: + void InitializeDescriptorSetLayouts(const PipelineCreator& creator) + { + { + int i = 0; + for (auto& binding : creator.vertexDescriptorSetLayouts) + { + vertexDescriptorSetLayouts.emplace(binding, InitializeDescriptorSetLayout(binding, VK_SHADER_STAGE_VERTEX_BIT)); + i++; + } + } + { + int i = 0; + for (auto& binding : creator.fragmentDescriptorSetLayouts) + { + fragmentDescriptorSetLayouts.emplace(binding, InitializeDescriptorSetLayout(binding, VK_SHADER_STAGE_FRAGMENT_BIT)); + i++; + } + } + } + + void InitializePipeline(const PipelineCreator& creator) + { + std::vector vertShaderCode = FileSystem::ReadFile(creator.vertexShader); + std::vector fragShaderCode = FileSystem::ReadFile(creator.fragmentShader); + + VkShaderModule vertShaderModule = InitializeShaderModule(vertShaderCode); + VkShaderModule fragShaderModule = InitializeShaderModule(fragShaderCode); + + VkPipelineShaderStageCreateInfo shaderStages[2]; + shaderStages[0] = {}; + shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + shaderStages[0].module = vertShaderModule; + shaderStages[0].pName = "main"; + + shaderStages[1] = {}; + shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + shaderStages[1].module = fragShaderModule; + shaderStages[1].pName = "main"; + + VkPipelineVertexInputStateCreateInfo vertexInputCreateInfo{}; + vertexInputCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputCreateInfo.vertexBindingDescriptionCount = 1; + vertexInputCreateInfo.pVertexBindingDescriptions = &creator.vertexInputBindingDescription; + vertexInputCreateInfo.vertexAttributeDescriptionCount = creator.vertexInputAttributeDescriptions.size(); + vertexInputCreateInfo.pVertexAttributeDescriptions = creator.vertexInputAttributeDescriptions.data(); + + VkPipelineInputAssemblyStateCreateInfo inputAssemblyCreateInfo{}; + inputAssemblyCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssemblyCreateInfo.topology = creator.topology; + inputAssemblyCreateInfo.primitiveRestartEnable = VK_FALSE; + + VkViewport viewport{}; + viewport.x = 0; + viewport.y = 0; + viewport.width = instance.GetSwapChain().GetExtent().width; + viewport.height = instance.GetSwapChain().GetExtent().height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + + VkRect2D scissor{}; + scissor.offset = {0, 0}; + scissor.extent = instance.GetSwapChain().GetExtent(); + + std::vector dynamicStates = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR + }; + + VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo{}; + dynamicStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicStateCreateInfo.dynamicStateCount = dynamicStates.size(); + dynamicStateCreateInfo.pDynamicStates = dynamicStates.data(); + + VkPipelineViewportStateCreateInfo viewportStateCreateInfo{}; + viewportStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportStateCreateInfo.viewportCount = 1; + viewportStateCreateInfo.pViewports = &viewport; + viewportStateCreateInfo.scissorCount = 1; + viewportStateCreateInfo.pScissors = &scissor; + + VkPipelineRasterizationStateCreateInfo rasterizerCreateInfo{}; + rasterizerCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizerCreateInfo.depthClampEnable = VK_FALSE; + rasterizerCreateInfo.rasterizerDiscardEnable = VK_FALSE; + rasterizerCreateInfo.polygonMode = VK_POLYGON_MODE_FILL; + rasterizerCreateInfo.lineWidth = 1.0f; + rasterizerCreateInfo.cullMode = creator.cullMode; + rasterizerCreateInfo.frontFace = creator.frontFace; + + rasterizerCreateInfo.depthBiasEnable = VK_FALSE; + rasterizerCreateInfo.depthBiasConstantFactor = 0.0f; + rasterizerCreateInfo.depthBiasClamp = 0.0f; + rasterizerCreateInfo.depthBiasSlopeFactor = 0.0f; + + VkPipelineMultisampleStateCreateInfo multisampleCreateInfo{}; + multisampleCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampleCreateInfo.sampleShadingEnable = VK_FALSE; + multisampleCreateInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisampleCreateInfo.minSampleShading = 1.0f; + multisampleCreateInfo.pSampleMask = nullptr; + multisampleCreateInfo.alphaToCoverageEnable = VK_FALSE; + multisampleCreateInfo.alphaToOneEnable = VK_FALSE; + + VkPipelineColorBlendAttachmentState colorBlendAttachment{}; // TODO: Add to PipelineCreator + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | + VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | + VK_COLOR_COMPONENT_A_BIT; + colorBlendAttachment.blendEnable = VK_FALSE; + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + + VkPipelineColorBlendStateCreateInfo colorBlendCreateInfo{}; + colorBlendCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlendCreateInfo.logicOpEnable = VK_FALSE; + colorBlendCreateInfo.logicOp = VK_LOGIC_OP_COPY; + colorBlendCreateInfo.attachmentCount = 1; + colorBlendCreateInfo.pAttachments = &colorBlendAttachment; + colorBlendCreateInfo.blendConstants[0] = 0.0f; + colorBlendCreateInfo.blendConstants[1] = 0.0f; + colorBlendCreateInfo.blendConstants[2] = 0.0f; + colorBlendCreateInfo.blendConstants[3] = 0.0f; + + std::vector layouts{vertexDescriptorSetLayouts.size() + fragmentDescriptorSetLayouts.size()}; + int i = 0; + for (auto&& descriptorSetLayout : vertexDescriptorSetLayouts) + { + layouts[i++] = descriptorSetLayout.second; + } + for (auto&& descriptorSetLayout : fragmentDescriptorSetLayouts) + { + layouts[i++] = descriptorSetLayout.second; + } + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{}; + pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutCreateInfo.setLayoutCount = layouts.size(); + pipelineLayoutCreateInfo.pSetLayouts = layouts.data(); + pipelineLayoutCreateInfo.pushConstantRangeCount = 0; + pipelineLayoutCreateInfo.pPushConstantRanges = nullptr; + + CP_VK_ASSERT(vkCreatePipelineLayout(instance.GetDevice(), &pipelineLayoutCreateInfo, nullptr, &pipelineLayout), "Failed to initialize pipeline layout"); + + VkGraphicsPipelineCreateInfo graphicsPipelineCreateInfo{}; + graphicsPipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + graphicsPipelineCreateInfo.stageCount = 2; + graphicsPipelineCreateInfo.pStages = shaderStages; + graphicsPipelineCreateInfo.pVertexInputState = &vertexInputCreateInfo; + graphicsPipelineCreateInfo.pInputAssemblyState = &inputAssemblyCreateInfo; + graphicsPipelineCreateInfo.pViewportState = &viewportStateCreateInfo; + graphicsPipelineCreateInfo.pRasterizationState = &rasterizerCreateInfo; + graphicsPipelineCreateInfo.pMultisampleState = &multisampleCreateInfo; + graphicsPipelineCreateInfo.pDepthStencilState = nullptr; + graphicsPipelineCreateInfo.pColorBlendState = &colorBlendCreateInfo; + graphicsPipelineCreateInfo.pDynamicState = &dynamicStateCreateInfo; + graphicsPipelineCreateInfo.layout = pipelineLayout; + graphicsPipelineCreateInfo.renderPass = instance.GetSwapChain().GetRenderPass(); + graphicsPipelineCreateInfo.subpass = 0; + graphicsPipelineCreateInfo.basePipelineHandle = VK_NULL_HANDLE; + graphicsPipelineCreateInfo.basePipelineIndex = -1; + + CP_VK_ASSERT(vkCreateGraphicsPipelines(instance.GetDevice(), VK_NULL_HANDLE, 1, &graphicsPipelineCreateInfo, nullptr, &graphicsPipeline), "Failed to initialize graphics pipeline"); + + vkDestroyShaderModule(instance.GetDevice(), vertShaderModule, nullptr); + vkDestroyShaderModule(instance.GetDevice(), fragShaderModule, nullptr); + } + + VkShaderModule InitializeShaderModule(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; + CP_VK_ASSERT(vkCreateShaderModule(instance.GetDevice(), &createInfo, nullptr, &shaderModule), "Failed to initialize shader module"); + + return shaderModule; + } + + VkDescriptorSetLayout InitializeDescriptorSetLayout(uint32_t binding, VkShaderStageFlags flags) + { + VkDescriptorSetLayout descriptorSetLayout; + VkDescriptorSetLayoutBinding layoutBinding{}; + layoutBinding.binding = binding; + layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + layoutBinding.descriptorCount = 1; + layoutBinding.stageFlags = flags; + layoutBinding.pImmutableSamplers = nullptr; + + VkDescriptorSetLayoutCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + createInfo.bindingCount = 1; + createInfo.pBindings = &layoutBinding; + + CP_VK_ASSERT(vkCreateDescriptorSetLayout(instance.GetDevice(), &createInfo, nullptr, &descriptorSetLayout), "Failed to initialize descriptor set layout"); + + return descriptorSetLayout; + } +}; \ No newline at end of file diff --git a/Vulkan/src/PipelineCreator.h b/Vulkan/src/PipelineCreator.h new file mode 100644 index 0000000..d037a62 --- /dev/null +++ b/Vulkan/src/PipelineCreator.h @@ -0,0 +1,62 @@ +#pragma once + +#include "Common.h" +#include +#include + +class PipelineCreator +{ + friend class Pipeline; +private: + std::set vertexDescriptorSetLayouts{}; + std::set fragmentDescriptorSetLayouts{}; + + std::string vertexShader; + std::string fragmentShader; + VkVertexInputBindingDescription vertexInputBindingDescription; + std::vector vertexInputAttributeDescriptions{}; + VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + VkCullModeFlags cullMode = VK_CULL_MODE_BACK_BIT; + VkFrontFace frontFace = VK_FRONT_FACE_CLOCKWISE; + + +public: + PipelineCreator(const std::string& vertexShader, const std::string& fragmentShader) + : vertexShader{vertexShader}, fragmentShader{fragmentShader} + {} + + void SetVertexInputBindingDescription(VkVertexInputBindingDescription description) + { + vertexInputBindingDescription = description; + } + + void SetVertexInputAttributeDescription(const std::vector& descriptions) + { + vertexInputAttributeDescriptions = descriptions; + } + + void AddVertexDescriptorSetLayoutBinding(uint32_t binding) + { + vertexDescriptorSetLayouts.emplace(binding); + } + + void AddFragmentDescriptorSetLayoutBinding(uint32_t binding) + { + fragmentDescriptorSetLayouts.emplace(binding); + } + + void SetPrimitiveTopology(VkPrimitiveTopology primitiveTopology) + { + topology = primitiveTopology; + } + + void SetCullMode(VkCullModeFlags flags) + { + cullMode = flags; + } + + void SetCullFrontFace(VkFrontFace cullFrontFace) + { + frontFace = cullFrontFace; + } +}; diff --git a/Vulkan/src/QueueFamilies.h b/Vulkan/src/QueueFamilies.h index 6a61f6c..9c35953 100644 --- a/Vulkan/src/QueueFamilies.h +++ b/Vulkan/src/QueueFamilies.h @@ -4,12 +4,12 @@ #include #include -struct QueueFamilies +struct QueueFamiliesQuery { std::optional graphicsFamily; std::optional presentFamily; - QueueFamilies(VkSurfaceKHR surface, VkPhysicalDevice device) + QueueFamiliesQuery(VkSurfaceKHR surface, VkPhysicalDevice device) { uint32_t queueFamilyCount = 0; vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); diff --git a/Vulkan/src/SwapChain.cpp b/Vulkan/src/SwapChain.cpp new file mode 100644 index 0000000..44c4631 --- /dev/null +++ b/Vulkan/src/SwapChain.cpp @@ -0,0 +1,305 @@ +#include "QueueFamilies.h" +#include "Instance.h" +#include +#include +#include + +SwapChainSupportDetails::SwapChainSupportDetails(VkSurfaceKHR surface, VkPhysicalDevice physicalDevice) +{ + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &capabilities); + + uint32_t formatCount; + vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, nullptr); + if (formatCount != 0) + { + formats.resize(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, formats.data()); + } + + uint32_t presentModeCount; + vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, nullptr); + if (presentModeCount != 0) + { + presentModes.resize(presentModeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, presentModes.data()); + } +} + +bool SwapChainSupportDetails::Valid() +{ + return !formats.empty() && !presentModes.empty(); +} + +SwapChain::SwapChain(Instance& instance) + : instance{instance} +{ + Initialize(); + InitializeImageViews(); + InitializeRenderPass(); + InitializeFramebuffers(); +} + +SwapChain::~SwapChain() +{ + Destroy(); + vkDestroyRenderPass(instance.GetDevice(), renderPass, nullptr); +} + +VkSwapchainKHR SwapChain::GetHandle() const +{ + return handle; +} + +VkRenderPass SwapChain::GetRenderPass() const +{ + return renderPass; +} + +VkExtent2D SwapChain::GetExtent() const +{ + return extent; +} + +VkFramebuffer SwapChain::GetFramebuffer() const +{ + return framebuffers[imageIndex]; +} + +bool SwapChain::BeginPresent(VkSemaphore signalSemaphore) +{ + VkResult result = vkAcquireNextImageKHR(instance.GetDevice(), handle, UINT64_MAX, signalSemaphore, VK_NULL_HANDLE, &imageIndex); + if (result == VK_ERROR_OUT_OF_DATE_KHR) + { + Recreate(); + return false; + } + return true; +} + +void SwapChain::EndPresent(VkQueue presentQueue, VkSemaphore* waitSemaphore, bool framebufferResized) +{ + VkPresentInfoKHR presentInfo{}; + presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + presentInfo.waitSemaphoreCount = 1; + presentInfo.pWaitSemaphores = waitSemaphore; + presentInfo.swapchainCount = 1; + presentInfo.pSwapchains = &handle; + presentInfo.pImageIndices = &imageIndex; + presentInfo.pResults = nullptr; + + VkResult result = vkQueuePresentKHR(presentQueue, &presentInfo); + if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) + { + Recreate(); + } +} + +void SwapChain::Recreate() +{ + int width = 0; + int height = 0; + glfwGetFramebufferSize(instance.GetWindow(), &width, &height); + while (width == 0 || height == 0) + { + glfwGetFramebufferSize(instance.GetWindow(), &width, &height); + glfwWaitEvents(); + } + + vkDeviceWaitIdle(instance.GetDevice()); + + Destroy(); + + Initialize(); + InitializeImageViews(); + InitializeFramebuffers(); +} + +void SwapChain::Initialize() +{ + SwapChainSupportDetails swapChainSupport{instance.GetSurface(), instance.GetPhysicalDevice()}; + + VkSurfaceFormatKHR format = SelectSwapSurfaceFormat(swapChainSupport.formats); + VkPresentModeKHR presentMode = SelectSwapPresentMode(swapChainSupport.presentModes); + extent = SelectSwapExtent(instance.GetWindow(), swapChainSupport.capabilities); + imageFormat = format.format; + uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; + if (swapChainSupport.capabilities.maxImageCount != 0) + { + imageCount = std::min(imageCount, swapChainSupport.capabilities.maxImageCount); + } + + QueueFamiliesQuery queueFamilies{instance.GetSurface(), instance.GetPhysicalDevice()}; + std::vector queueFamilyIndices{queueFamilies.graphicsFamily.value(), queueFamilies.presentFamily.value()}; + + VkSwapchainCreateInfoKHR createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + createInfo.surface = instance.GetSurface(); + createInfo.minImageCount = imageCount; + createInfo.imageFormat = format.format; + createInfo.imageColorSpace = format.colorSpace; + createInfo.imageExtent = extent; + createInfo.imageArrayLayers = 1; + createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + 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 (queueFamilies.graphicsFamily != queueFamilies.presentFamily) + { + createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createInfo.queueFamilyIndexCount = 2; + createInfo.pQueueFamilyIndices = queueFamilyIndices.data(); + } + else + { + createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + createInfo.queueFamilyIndexCount = 0; + createInfo.pQueueFamilyIndices = nullptr; + } + + CP_VK_ASSERT(vkCreateSwapchainKHR(instance.GetDevice(), &createInfo, nullptr, &handle), "Failed to initialize the swapchain"); + + vkGetSwapchainImagesKHR(instance.GetDevice(), handle, &imageCount, nullptr); + images.resize(imageCount); + vkGetSwapchainImagesKHR(instance.GetDevice(), handle, &imageCount, images.data()); +} + +void SwapChain::InitializeImageViews() +{ + imageViews.resize(images.size()); + for (size_t i = 0; i < images.size(); i++) + { + VkImageViewCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.image = images[i]; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = imageFormat; + 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; + CP_VK_ASSERT(vkCreateImageView(instance.GetDevice(), &createInfo, nullptr, &imageViews[i]), "Failed to initialize swapchain image view"); + } +} + +void SwapChain::InitializeRenderPass() +{ + VkAttachmentDescription colorAttachment{}; + colorAttachment.format = imageFormat; + colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; + colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference colorAttachmentRef{}; + colorAttachmentRef.attachment = 0; + colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass{}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &colorAttachmentRef; + + VkSubpassDependency dependency{}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + VkRenderPassCreateInfo renderPassCreateInfo{}; + renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassCreateInfo.attachmentCount = 1; + renderPassCreateInfo.pAttachments = &colorAttachment; + renderPassCreateInfo.subpassCount = 1; + renderPassCreateInfo.pSubpasses = &subpass; + renderPassCreateInfo.dependencyCount = 1; + renderPassCreateInfo.pDependencies = &dependency; + + CP_VK_ASSERT(vkCreateRenderPass(instance.GetDevice(), &renderPassCreateInfo, nullptr, &renderPass), "Failed to initialze render pass"); +} + +void SwapChain::InitializeFramebuffers() +{ + framebuffers.resize(images.size()); + + for (size_t i = 0; i < imageViews.size(); ++i) + { + VkImageView attachments[] = {imageViews[i]}; + + VkFramebufferCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + createInfo.renderPass = renderPass; + createInfo.attachmentCount = 1; + createInfo.pAttachments = attachments; + createInfo.width = extent.width; + createInfo.height = extent.height; + createInfo.layers = 1; + + CP_VK_ASSERT(vkCreateFramebuffer(instance.GetDevice(), &createInfo, nullptr, &framebuffers[i]), "Failed to initialize swap chain framebuffer"); + } +} + +void SwapChain::Destroy() +{ + for (auto&& framebuffer : framebuffers) + { + vkDestroyFramebuffer(instance.GetDevice(), framebuffer, nullptr); + } + for (auto&& swapChainImageView : imageViews) + { + vkDestroyImageView(instance.GetDevice(), swapChainImageView, nullptr); + } + vkDestroySwapchainKHR(instance.GetDevice(), handle, nullptr); +} + +VkSurfaceFormatKHR SwapChain::SelectSwapSurfaceFormat(const std::vector& availableFormats) +{ + for (auto&& availableFormat : availableFormats) + { + if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) + { + return availableFormat; + } + } + return availableFormats[0]; +} + +VkPresentModeKHR SwapChain::SelectSwapPresentMode(const std::vector& availablePresentModes) +{ + for (auto&& availablePresentMode : availablePresentModes) + { + if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) + { + return availablePresentMode; + } + } + + // VK_PRESENT_MODE_FIFO_KHR is guaranteed to be present + return VK_PRESENT_MODE_FIFO_KHR; +} + +VkExtent2D SwapChain::SelectSwapExtent(GLFWwindow* window, const VkSurfaceCapabilitiesKHR& capabilities) +{ + if (capabilities.currentExtent.width != std::numeric_limits::max()) + return capabilities.currentExtent; + + int width, height; + glfwGetFramebufferSize(window, &width, &height); + + VkExtent2D extent{width, height}; + extent.width = std::clamp(extent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); + extent.height = std::clamp(extent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); + return extent; +} diff --git a/Vulkan/src/SwapChain.h b/Vulkan/src/SwapChain.h index 6b342bd..80a82b5 100644 --- a/Vulkan/src/SwapChain.h +++ b/Vulkan/src/SwapChain.h @@ -1,53 +1,25 @@ #pragma once -#include "QueueFamilies.h" -#include #include #include +class Instance; + struct SwapChainSupportDetails { VkSurfaceCapabilitiesKHR capabilities; std::vector formats; std::vector presentModes; - SwapChainSupportDetails(VkSurfaceKHR surface, VkPhysicalDevice device) - { - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &capabilities); - - uint32_t formatCount; - vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); - if (formatCount != 0) - { - formats.resize(formatCount); - vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, formats.data()); - } - - uint32_t presentModeCount; - vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); - if (presentModeCount != 0) - { - presentModes.resize(presentModeCount); - vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, presentModes.data()); - } - } - - bool Valid() - { - return !formats.empty() && !presentModes.empty(); - } + SwapChainSupportDetails(VkSurfaceKHR surface, VkPhysicalDevice physicalDevice); + bool Valid(); }; -class SwapChain +class SwapChain final { - // TODO: Remove, replaced by Instance::MAX_FRAMES_IN_FLIGHT - static const int MAX_FRAMES_IN_FLIGHT = 2; - - // Needed for recreation and destruction - GLFWwindow* window; - VkSurfaceKHR surface; - VkPhysicalDevice physicalDevice; - VkDevice device; + CP_DELETE_COPY_AND_MOVE_CTOR(SwapChain); +private: + Instance& instance; // Created by the class VkSwapchainKHR handle; @@ -58,349 +30,29 @@ class SwapChain std::vector images; std::vector framebuffers; - int flightIndex; uint32_t imageIndex; - std::vector imageAvailableSemaphores; - std::vector renderFinishedSemaphores; - std::vector inFlightFences; public: - SwapChain(GLFWwindow* window, VkSurfaceKHR surface, VkDevice device, VkPhysicalDevice physicalDevice) - : window{window}, surface{surface}, physicalDevice{physicalDevice}, device{device} - { - Initialize(); - InitializeImageViews(); - InitializeRenderPass(); - InitializeFramebuffers(); - InitializeSyncObjects(); - } + SwapChain(Instance& instance); + ~SwapChain(); - ~SwapChain() - { - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) - { - vkDestroyFence(device, inFlightFences[i], nullptr); - vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); - vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); - } - Destroy(); - vkDestroyRenderPass(device, renderPass, nullptr); - } - - VkSwapchainKHR GetHandle() const - { - return handle; - } - - VkRenderPass GetRenderPass() const - { - return renderPass; - } - - VkExtent2D GetExtent() const - { - return extent; - } - - VkFramebuffer GetFramebuffer() const - { - return framebuffers[imageIndex]; - } - - bool BeginPresent() - { - vkWaitForFences(device, 1, &inFlightFences[flightIndex], VK_TRUE, UINT64_MAX); - - VkResult result = vkAcquireNextImageKHR(device, handle, UINT64_MAX, imageAvailableSemaphores[flightIndex], VK_NULL_HANDLE, &imageIndex); - if (result == VK_ERROR_OUT_OF_DATE_KHR) - { - Recreate(); - return false; - } - - vkResetFences(device, 1, &inFlightFences[flightIndex]); - return true; - } - - void EndPresent(VkQueue presentQueue, bool framebufferResized) - { - VkSwapchainKHR swapChains[] = {handle}; - VkPresentInfoKHR presentInfo{}; - presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - presentInfo.waitSemaphoreCount = 1; - presentInfo.pWaitSemaphores = &renderFinishedSemaphores[flightIndex]; - presentInfo.swapchainCount = 1; - presentInfo.pSwapchains = &handle; - presentInfo.pImageIndices = &imageIndex; - presentInfo.pResults = nullptr; - - VkResult result = vkQueuePresentKHR(presentQueue, &presentInfo); - if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) - { - framebufferResized = false; - Recreate(); - } - - flightIndex = (flightIndex + 1) % MAX_FRAMES_IN_FLIGHT; - } - - int GetFlightIndex() const - { - return flightIndex; - } - - int GetMaxFramesInFlight() const - { - return MAX_FRAMES_IN_FLIGHT; - } - - VkFence GetInFlightFence() const - { - return inFlightFences[flightIndex]; - } - - VkSemaphore GetAvailableImageSemaphore() const - { - return imageAvailableSemaphores[flightIndex]; - } - - VkSemaphore GetRenderFinishedSemaphore() const - { - return renderFinishedSemaphores[flightIndex]; - } - - - void Recreate() - { - int width = 0; - int height = 0; - glfwGetFramebufferSize(window, &width, &height); - while (width == 0 || height == 0) - { - glfwGetFramebufferSize(window, &width, &height); - glfwWaitEvents(); - } - - vkDeviceWaitIdle(device); - - Destroy(); - - Initialize(); - InitializeImageViews(); - InitializeFramebuffers(); - } + VkSwapchainKHR GetHandle() const; + VkRenderPass GetRenderPass() const; + VkExtent2D GetExtent() const; + VkFramebuffer GetFramebuffer() const; + bool BeginPresent(VkSemaphore signalSemaphore); + void EndPresent(VkQueue presentQueue, VkSemaphore* waitSemaphore, bool framebufferResized); + void Recreate(); + private: + void Initialize(); + void InitializeImageViews(); + void InitializeRenderPass(); + void InitializeFramebuffers(); + void Destroy(); - void Initialize() - { - SwapChainSupportDetails swapChainSupport{surface, physicalDevice}; - - VkSurfaceFormatKHR format = SelectSwapSurfaceFormat(swapChainSupport.formats); - VkPresentModeKHR presentMode = SelectSwapPresentMode(swapChainSupport.presentModes); - extent = SelectSwapExtent(window, swapChainSupport.capabilities); - imageFormat = format.format; - uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; - if (swapChainSupport.capabilities.maxImageCount != 0) - { - imageCount = std::min(imageCount, swapChainSupport.capabilities.maxImageCount); - } - - QueueFamilies queueFamilies{surface, physicalDevice}; - std::vector queueFamilyIndices{queueFamilies.graphicsFamily.value(), queueFamilies.presentFamily.value()}; - - VkSwapchainCreateInfoKHR createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; - createInfo.surface = surface; - createInfo.minImageCount = imageCount; - createInfo.imageFormat = format.format; - createInfo.imageColorSpace = format.colorSpace; - createInfo.imageExtent = extent; - createInfo.imageArrayLayers = 1; - createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - 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 (queueFamilies.graphicsFamily != queueFamilies.presentFamily) - { - createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; - createInfo.queueFamilyIndexCount = 2; - createInfo.pQueueFamilyIndices = queueFamilyIndices.data(); - } - else - { - createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; - createInfo.queueFamilyIndexCount = 0; - createInfo.pQueueFamilyIndices = nullptr; - } - - VK_ASSERT(vkCreateSwapchainKHR(device, &createInfo, nullptr, &handle), "Failed to initialize the swapchain"); - - vkGetSwapchainImagesKHR(device, handle, &imageCount, nullptr); - images.resize(imageCount); - vkGetSwapchainImagesKHR(device, handle, &imageCount, images.data()); - } - - void InitializeImageViews() - { - imageViews.resize(images.size()); - for (size_t i = 0; i < images.size(); i++) - { - VkImageViewCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - createInfo.image = images[i]; - createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - createInfo.format = imageFormat; - 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; - VK_ASSERT(vkCreateImageView(device, &createInfo, nullptr, &imageViews[i]), "Failed to initialize swapchain image view"); - } - } - - void InitializeRenderPass() - { - VkAttachmentDescription colorAttachment{}; - colorAttachment.format = imageFormat; - colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; - colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - - VkAttachmentReference colorAttachmentRef{}; - colorAttachmentRef.attachment = 0; - colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - - VkSubpassDescription subpass{}; - subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpass.colorAttachmentCount = 1; - subpass.pColorAttachments = &colorAttachmentRef; - - VkSubpassDependency dependency{}; - dependency.srcSubpass = VK_SUBPASS_EXTERNAL; - dependency.dstSubpass = 0; - dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dependency.srcAccessMask = 0; - dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - - VkRenderPassCreateInfo renderPassCreateInfo{}; - renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - renderPassCreateInfo.attachmentCount = 1; - renderPassCreateInfo.pAttachments = &colorAttachment; - renderPassCreateInfo.subpassCount = 1; - renderPassCreateInfo.pSubpasses = &subpass; - renderPassCreateInfo.dependencyCount = 1; - renderPassCreateInfo.pDependencies = &dependency; - - VK_ASSERT(vkCreateRenderPass(device, &renderPassCreateInfo, nullptr, &renderPass), "Failed to initialze render pass"); - } - - void InitializeFramebuffers() - { - framebuffers.resize(images.size()); - - for (size_t i = 0; i < imageViews.size(); ++i) - { - VkImageView attachments[] = {imageViews[i]}; - - VkFramebufferCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - createInfo.renderPass = renderPass; - createInfo.attachmentCount = 1; - createInfo.pAttachments = attachments; - createInfo.width = extent.width; - createInfo.height = extent.height; - createInfo.layers = 1; - - VK_ASSERT(vkCreateFramebuffer(device, &createInfo, nullptr, &framebuffers[i]), "Failed to initialize swap chain framebuffer"); - } - } - - void InitializeSyncObjects() - { - imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); - renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); - inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); - VkSemaphoreCreateInfo semaphoreCreateInfo{}; - semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) - { - VK_ASSERT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &imageAvailableSemaphores[i]), "Failed to initialize available image semaphore"); - VK_ASSERT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &renderFinishedSemaphores[i]), "Failed to initialize render finished semaphore"); - - VkFenceCreateInfo fenceCreateInfo{}; - fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - fenceCreateInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; - - VK_ASSERT(vkCreateFence(device, &fenceCreateInfo, nullptr, &inFlightFences[i]), "Failed to initialize in flight fence"); - } - } - - void Destroy() - { - for (auto&& framebuffer : framebuffers) - { - vkDestroyFramebuffer(device, framebuffer, nullptr); - } - for (auto&& swapChainImageView : imageViews) - { - vkDestroyImageView(device, swapChainImageView, nullptr); - } - vkDestroySwapchainKHR(device, handle, nullptr); - } - - VkSurfaceFormatKHR SelectSwapSurfaceFormat(const std::vector& availableFormats) - { - for (auto&& availableFormat : availableFormats) - { - if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) - { - return availableFormat; - } - } - return availableFormats[0]; - } - - VkPresentModeKHR SelectSwapPresentMode(const std::vector& availablePresentModes) - { - for (auto&& availablePresentMode : availablePresentModes) - { - if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) - { - return availablePresentMode; - } - } - - // VK_PRESENT_MODE_FIFO_KHR is guaranteed to be present - return VK_PRESENT_MODE_FIFO_KHR; - } - - VkExtent2D SelectSwapExtent(GLFWwindow* window, const VkSurfaceCapabilitiesKHR& capabilities) - { - if (capabilities.currentExtent.width != std::numeric_limits::max()) - return capabilities.currentExtent; - - int width, height; - glfwGetFramebufferSize(window, &width, &height); - - VkExtent2D extent{width, height}; - extent.width = std::clamp(extent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); - extent.height = std::clamp(extent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); - return extent; - } - - + VkSurfaceFormatKHR SelectSwapSurfaceFormat(const std::vector& availableFormats); + VkPresentModeKHR SelectSwapPresentMode(const std::vector& availablePresentModes); + VkExtent2D SelectSwapExtent(GLFWwindow* window, const VkSurfaceCapabilitiesKHR& capabilities); }; diff --git a/Vulkan/src/Timer.h b/Vulkan/src/Timer.h new file mode 100644 index 0000000..b1d0e11 --- /dev/null +++ b/Vulkan/src/Timer.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +class Timer +{ +private: + std::chrono::time_point startTime; +public: + Timer() + : startTime{std::chrono::steady_clock::now()} + {} + + void Start() + { + startTime = std::chrono::steady_clock::now(); + } + + double Elapsed() + { + return std::chrono::duration(std::chrono::high_resolution_clock::now() - startTime).count(); + } +}; diff --git a/Vulkan/src/UniformBuffer.h b/Vulkan/src/UniformBuffer.h new file mode 100644 index 0000000..bd75947 --- /dev/null +++ b/Vulkan/src/UniformBuffer.h @@ -0,0 +1,93 @@ +#pragma once + +#include "Common.h" +#include "Buffer.h" +#include "Pipeline.h" +#include + +template +class UniformBuffer : public Buffer +{ + CP_DELETE_COPY_AND_MOVE_CTOR(UniformBuffer); +private: + Pipeline& pipeline; + + VkDescriptorPool descriptorPool; + std::vector descriptorSets; + +public: + UniformBuffer(Instance& instance, Pipeline& pipeline, uint32_t binding, VkDescriptorSetLayout layout) + : Buffer{instance, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(T), instance.GetMaxFramesInFlight()}, pipeline{pipeline} + { + InitializeDescriptorPool(); + InitializeDescriptorSet(binding, layout); + } + + ~UniformBuffer() override + { + vkDestroyDescriptorPool(instance.GetDevice(), descriptorPool, nullptr); + } + + void Update(const T& t) + { + Buffer::Update((void*)&t, instance.GetFlightIndex()); + } + + void Bind(VkCommandBuffer commandBuffer) const + { + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.GetPipelineLayout(), 0, 1, &descriptorSets[instance.GetFlightIndex()], 0, nullptr); + } + +private: + void InitializeDescriptorPool() + { + VkDescriptorPoolSize poolSize{}; + poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + poolSize.descriptorCount = instance.GetMaxFramesInFlight(); + + VkDescriptorPoolCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + createInfo.poolSizeCount = 1; + createInfo.pPoolSizes = &poolSize; + createInfo.maxSets = instance.GetMaxFramesInFlight(); + + CP_VK_ASSERT(vkCreateDescriptorPool(instance.GetDevice(), &createInfo, nullptr, &descriptorPool), "Failed to initialize descriptor pool"); + } + + void InitializeDescriptorSet(uint32_t binding, VkDescriptorSetLayout layout) + { + std::vector layouts{static_cast(instance.GetMaxFramesInFlight()), layout}; + VkDescriptorSetAllocateInfo allocateInfo{}; + allocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocateInfo.descriptorPool = descriptorPool; + allocateInfo.descriptorSetCount = instance.GetMaxFramesInFlight(); + allocateInfo.pSetLayouts = layouts.data(); + + descriptorSets.resize(instance.GetMaxFramesInFlight()); + CP_VK_ASSERT(vkAllocateDescriptorSets(instance.GetDevice(), &allocateInfo, descriptorSets.data()), "Failed to allocate descriptor sets"); + for (size_t i = 0; i < instance.GetMaxFramesInFlight(); ++i) { + VkDescriptorBufferInfo bufferInfo = GetDescriptorBufferInfo(i); + + VkWriteDescriptorSet descriptorWrite{}; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.dstSet = descriptorSets[i]; + descriptorWrite.dstBinding = binding; + descriptorWrite.dstArrayElement = 0; + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrite.descriptorCount = 1; + descriptorWrite.pBufferInfo = &bufferInfo; + descriptorWrite.pImageInfo = nullptr; + descriptorWrite.pTexelBufferView = nullptr; + vkUpdateDescriptorSets(instance.GetDevice(), 1, &descriptorWrite, 0, nullptr); + } + } + + VkDescriptorBufferInfo GetDescriptorBufferInfo(int index) + { + VkDescriptorBufferInfo bufferInfo{}; + bufferInfo.buffer = handle; + bufferInfo.offset = (VkDeviceSize)index * size; + bufferInfo.range = size; + return bufferInfo; + } +}; diff --git a/Vulkan/src/Vertex.h b/Vulkan/src/Vertex.h new file mode 100644 index 0000000..852b609 --- /dev/null +++ b/Vulkan/src/Vertex.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include "VertexDescriptor.h" + +struct Vertex { + glm::vec2 pos; + glm::vec3 color; + + static VertexDescriptor GetDescriptor() + { + VertexDescriptor descriptor{}; + descriptor.AddAttribute(0, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, pos)); + descriptor.AddAttribute(0, 1, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, color)); + return descriptor; + } + + static VkVertexInputBindingDescription GetBindingDescription() + { + VkVertexInputBindingDescription description{}; + + description.binding = 0; + description.stride = sizeof(Vertex); + description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + return description; + } + + static std::vector GetAttributeDescriptions() + { + std::vector descriptions{2}; + + descriptions[0].binding = 0; + descriptions[0].location = 0; + descriptions[0].format = VK_FORMAT_R32G32_SFLOAT; + descriptions[0].offset = offsetof(Vertex, pos); + + descriptions[1].binding = 0; + descriptions[1].location = 1; + descriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; + descriptions[1].offset = offsetof(Vertex, color); + + return descriptions; + } +}; + diff --git a/Vulkan/src/VertexBuffer.h b/Vulkan/src/VertexBuffer.h new file mode 100644 index 0000000..9dd99ec --- /dev/null +++ b/Vulkan/src/VertexBuffer.h @@ -0,0 +1,30 @@ +#pragma once + +#include "Buffer.h" +#include "VertexDescriptor.h" + +class VertexBuffer : public Buffer +{ + CP_DELETE_COPY_AND_MOVE_CTOR(VertexBuffer); + std::map bindingOffsets; + std::map bindingSizes; +public: + VertexBuffer(Instance& instance, VkDeviceSize size) + : Buffer{instance, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, size, 1} + {} + + VertexBuffer(Instance& instance, const VertexDescriptor& descriptor, int vertexCount) + : Buffer{instance, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, descriptor.GetVertexSize() * vertexCount, 1} + {} + + void Bind(VkCommandBuffer commandBuffer) override + { + VkDeviceSize offset = 0; + vkCmdBindVertexBuffers(commandBuffer, 0, 1, &handle, &offset); + } + + void Update(uint32_t binding, void* data) + { + UpdateStaging(data, bindingOffsets[binding], bindingSizes[binding]); + } +}; diff --git a/Vulkan/src/VertexDescriptor.h b/Vulkan/src/VertexDescriptor.h new file mode 100644 index 0000000..106b730 --- /dev/null +++ b/Vulkan/src/VertexDescriptor.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +class VertexDescriptor +{ +private: + uint32_t bindingIndex = 0; + std::vector bindings; + std::vector attributes; + +public: + template + void AddAttribute(uint32_t binding, uint32_t location, VkFormat format, uint32_t offset) + { + auto it = std::find_if(bindings.begin(), bindings.end(), [&binding](const VkVertexInputBindingDescription& description) { return description.binding == binding; }); + if (it == bindings.end()) + AddLayout(binding, sizeof(T)); + + VkVertexInputAttributeDescription description{}; + description.binding = binding; + description.location = location; + description.format = format; + description.offset = offset; + attributes.emplace_back(description); + } + + VkDeviceSize GetVertexSize() const + { + VkDeviceSize bufferSize = 0; + for (auto&& binding : bindings) + { + bufferSize += binding.stride; + } + return bufferSize; + } + +private: + uint32_t AddLayout(uint32_t binding, uint32_t size) + { + VkVertexInputBindingDescription description{}; + description.binding = binding; + description.stride = size; + description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + bindings.emplace_back(description); + return description.binding; + } +}; diff --git a/Vulkan/src/VulkanException.h b/Vulkan/src/VulkanException.h index 1007e85..1e32957 100644 --- a/Vulkan/src/VulkanException.h +++ b/Vulkan/src/VulkanException.h @@ -5,7 +5,7 @@ class VulkanException : public std::runtime_error { public: - VulkanException(const char* str) - : runtime_error{str} + VulkanException(const std::string& str) + : runtime_error{str.c_str()} {} }; diff --git a/Vulkan/src/main.cpp b/Vulkan/src/main.cpp index 515b819..95348e0 100644 --- a/Vulkan/src/main.cpp +++ b/Vulkan/src/main.cpp @@ -1,13 +1,14 @@ #include "FileSystem.h" #include "Buffer.h" +#include "IndexBuffer.h" +#include "VertexBuffer.h" #include "Instance.h" -#define GLM_FORCE_LEFT_HANDED -#define GLFW_INCLUDE_VULKAN +#include "Timer.h" +#include "UniformBuffer.h" +#include "Vertex.h" +#include "Pipeline.h" #include -#define GLM_FORCE_RADIANS -#define GLM_FORCE_DEPTH_ZERO_TO_ONE - #include #include #include @@ -16,39 +17,6 @@ #include #include -struct Vertex { - glm::vec2 pos; - glm::vec3 color; - - static VkVertexInputBindingDescription getBindingDescription() - { - VkVertexInputBindingDescription description{}; - - description.binding = 0; - description.stride = sizeof(Vertex); - description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - - return description; - } - - static std::vector getAttributeDescriptions() - { - std::vector descriptions{2}; - - descriptions[0].binding = 0; - descriptions[0].location = 0; - descriptions[0].format = VK_FORMAT_R32G32_SFLOAT; - descriptions[0].offset = offsetof(Vertex, pos); - - descriptions[1].binding = 0; - descriptions[1].location = 1; - descriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; - descriptions[1].offset = offsetof(Vertex, color); - - return descriptions; - } -}; - const std::vector vertices = { Vertex{{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}}, Vertex{{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}}, @@ -60,7 +28,7 @@ const std::vector indices = { 0, 1, 2, 2, 3, 0 }; -struct UniformBufferObject +struct ShaderUniform { glm::mat4 model; glm::mat4 view; @@ -71,42 +39,26 @@ class Application final { private: std::unique_ptr instance; - VkDescriptorSetLayout uniformBufferLayout; - VkDescriptorPool descriptorPool; - std::vector descriptorSets; - VkPipelineLayout pipelineLayout; - VkPipeline graphicsPipeline; - std::unique_ptr vertexBuffer; - std::unique_ptr indexBuffer; - std::unique_ptr uniformBuffer; + std::unique_ptr graphicsPipeline; + std::unique_ptr vertexBuffer; + std::unique_ptr indexBuffer; + std::unique_ptr> uniformBuffer; std::vector commandBuffers; - UniformBufferObject uniformBufferObject; public: Application() { - instance = std::make_unique("Vulkan Tutorial"); - InitializeUniformBufferLayout(); + InitializeInstance(); InitializeGraphicsPipeline(); + InitializeUniformBuffer(); InitializeVertexBuffer(); InitializeIndexBuffer(); - InitializeUniformBuffer(); - InitializeDescriptorPool(); - InitializeDescriptorSet(); - InitializeCommandBuffer(); + InitializeCommandBuffers(); } ~Application() { vkDeviceWaitIdle(instance->GetDevice()); - uniformBuffer.reset(); - vertexBuffer.reset(); - indexBuffer.reset(); - vkDestroyPipeline(instance->GetDevice(), graphicsPipeline, nullptr); - vkDestroyPipelineLayout(instance->GetDevice(), pipelineLayout, nullptr); - vkDestroyDescriptorPool(instance->GetDevice(), descriptorPool, nullptr); - vkDestroyDescriptorSetLayout(instance->GetDevice(), uniformBufferLayout, nullptr); - instance.reset(); } Application(Application&&) = delete; @@ -119,250 +71,48 @@ public: if (!instance->BeginPresent()) return true; - RecordCommandBuffer(commandBuffers[instance->GetSwapChain().GetFlightIndex()]); + RecordCommandBuffer(commandBuffers[instance->GetFlightIndex()]); - instance->SubmitGraphicsQueue(std::vector{commandBuffers[instance->GetSwapChain().GetFlightIndex()]}); + instance->SubmitGraphicsQueue(std::vector{commandBuffers[instance->GetFlightIndex()]}); return instance->EndPresent(); } private: - void InitializeUniformBufferLayout() + void InitializeInstance() { - VkDescriptorSetLayoutBinding layoutBinding{}; - layoutBinding.binding = 0; - layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - layoutBinding.descriptorCount = 1; - layoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - layoutBinding.pImmutableSamplers = nullptr; - - VkDescriptorSetLayoutCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - createInfo.bindingCount = 1; - createInfo.pBindings = &layoutBinding; - - VK_ASSERT(vkCreateDescriptorSetLayout(instance->GetDevice(), &createInfo, nullptr, &uniformBufferLayout), "Failed to initialize uniform buffer layout"); + instance = std::make_unique("Vulkan Tutorial"); } - void InitializeDescriptorPool() + void InitializeUniformBuffer() { - VkDescriptorPoolSize poolSize{}; - poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - poolSize.descriptorCount = instance->GetMaxFramesInFlight(); - - VkDescriptorPoolCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - createInfo.poolSizeCount = 1; - createInfo.pPoolSizes = &poolSize; - createInfo.maxSets = instance->GetMaxFramesInFlight(); - - VK_ASSERT(vkCreateDescriptorPool(instance->GetDevice(), &createInfo, nullptr, &descriptorPool), "Failed to initialize descriptor pool"); - } - - void InitializeDescriptorSet() - { - std::vector layouts{static_cast(instance->GetMaxFramesInFlight()), uniformBufferLayout}; - VkDescriptorSetAllocateInfo allocateInfo{}; - allocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - allocateInfo.descriptorPool = descriptorPool; - allocateInfo.descriptorSetCount = instance->GetMaxFramesInFlight(); - allocateInfo.pSetLayouts = layouts.data(); - - descriptorSets.resize(instance->GetMaxFramesInFlight()); - VK_ASSERT(vkAllocateDescriptorSets(instance->GetDevice(), &allocateInfo, descriptorSets.data()), "Failed to allocate descriptor sets"); - for (size_t i = 0; i < instance->GetMaxFramesInFlight(); ++i) { - VkDescriptorBufferInfo bufferInfo = uniformBuffer->GetDescriptorBufferInfo(i); - - 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(instance->GetDevice(), 1, &descriptorWrite, 0, nullptr); - } - + uniformBuffer = std::make_unique>(*instance, *graphicsPipeline, 0, graphicsPipeline->GetVertexDescriptorSetLayout(0)); } void InitializeGraphicsPipeline() { - std::vector vertShaderCode = FileSystem::ReadFile("res/shaders/vert.spv"); - std::vector fragShaderCode = FileSystem::ReadFile("res/shaders/frag.spv"); - - VkShaderModule vertShaderModule = InitializeShaderModule(vertShaderCode); - VkShaderModule fragShaderModule = InitializeShaderModule(fragShaderCode); - - VkPipelineShaderStageCreateInfo shaderStages[2]; - shaderStages[0] = {}; - shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; - shaderStages[0].module = vertShaderModule; - shaderStages[0].pName = "main"; - - shaderStages[1] = {}; - shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; - shaderStages[1].module = fragShaderModule; - shaderStages[1].pName = "main"; - - VkVertexInputBindingDescription bindingDescriptions{Vertex::getBindingDescription()}; - std::vector attributeDescriptions{Vertex::getAttributeDescriptions()}; - - VkPipelineVertexInputStateCreateInfo vertexInputCreateInfo{}; - vertexInputCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertexInputCreateInfo.vertexBindingDescriptionCount = 1; - vertexInputCreateInfo.pVertexBindingDescriptions = &bindingDescriptions; - vertexInputCreateInfo.vertexAttributeDescriptionCount = attributeDescriptions.size(); - vertexInputCreateInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); - - VkPipelineInputAssemblyStateCreateInfo inputAssemblyCreateInfo{}; - inputAssemblyCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - inputAssemblyCreateInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - inputAssemblyCreateInfo.primitiveRestartEnable = VK_FALSE; - - VkViewport viewport{}; - viewport.x = 0; - viewport.y = 0; - viewport.width = instance->GetSwapChain().GetExtent().width; - viewport.height = instance->GetSwapChain().GetExtent().height; - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - - VkRect2D scissor{}; - scissor.offset = {0, 0}; - scissor.extent = instance->GetSwapChain().GetExtent(); - - std::vector dynamicStates = { - VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR - }; - - VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo{}; - dynamicStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; - dynamicStateCreateInfo.dynamicStateCount = dynamicStates.size(); - dynamicStateCreateInfo.pDynamicStates = dynamicStates.data(); - - VkPipelineViewportStateCreateInfo viewportStateCreateInfo{}; - viewportStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; - viewportStateCreateInfo.viewportCount = 1; - viewportStateCreateInfo.pViewports = &viewport; - viewportStateCreateInfo.scissorCount = 1; - viewportStateCreateInfo.pScissors = &scissor; - - VkPipelineRasterizationStateCreateInfo rasterizerCreateInfo{}; - rasterizerCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rasterizerCreateInfo.depthClampEnable = VK_FALSE; - rasterizerCreateInfo.rasterizerDiscardEnable = VK_FALSE; - rasterizerCreateInfo.polygonMode = VK_POLYGON_MODE_FILL; - rasterizerCreateInfo.lineWidth = 1.0f; - rasterizerCreateInfo.cullMode = VK_CULL_MODE_BACK_BIT; - rasterizerCreateInfo.frontFace = VK_FRONT_FACE_CLOCKWISE; - - rasterizerCreateInfo.depthBiasEnable = VK_FALSE; - rasterizerCreateInfo.depthBiasConstantFactor = 0.0f; - rasterizerCreateInfo.depthBiasClamp = 0.0f; - rasterizerCreateInfo.depthBiasSlopeFactor = 0.0f; - - VkPipelineMultisampleStateCreateInfo multisampleCreateInfo{}; - multisampleCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; - multisampleCreateInfo.sampleShadingEnable = VK_FALSE; - multisampleCreateInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - multisampleCreateInfo.minSampleShading = 1.0f; - multisampleCreateInfo.pSampleMask = nullptr; - multisampleCreateInfo.alphaToCoverageEnable = VK_FALSE; - multisampleCreateInfo.alphaToOneEnable = VK_FALSE; - - VkPipelineColorBlendAttachmentState colorBlendAttachment{}; - colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | - VK_COLOR_COMPONENT_G_BIT | - VK_COLOR_COMPONENT_B_BIT | - VK_COLOR_COMPONENT_A_BIT; - colorBlendAttachment.blendEnable = VK_FALSE; - colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; - colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; - colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; - colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; - colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; - colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; - - VkPipelineColorBlendStateCreateInfo colorBlendCreateInfo{}; - colorBlendCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - colorBlendCreateInfo.logicOpEnable = VK_FALSE; - colorBlendCreateInfo.logicOp = VK_LOGIC_OP_COPY; - colorBlendCreateInfo.attachmentCount = 1; - colorBlendCreateInfo.pAttachments = &colorBlendAttachment; - colorBlendCreateInfo.blendConstants[0] = 0.0f; - colorBlendCreateInfo.blendConstants[1] = 0.0f; - colorBlendCreateInfo.blendConstants[2] = 0.0f; - colorBlendCreateInfo.blendConstants[3] = 0.0f; - - VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{}; - pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutCreateInfo.setLayoutCount = 1; - pipelineLayoutCreateInfo.pSetLayouts = &uniformBufferLayout; - pipelineLayoutCreateInfo.pushConstantRangeCount = 0; - pipelineLayoutCreateInfo.pPushConstantRanges = nullptr; - - VK_ASSERT(vkCreatePipelineLayout(instance->GetDevice(), &pipelineLayoutCreateInfo, nullptr, &pipelineLayout), "Failed to initialize pipeline layout"); - - VkGraphicsPipelineCreateInfo graphicsPipelineCreateInfo{}; - graphicsPipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; - graphicsPipelineCreateInfo.stageCount = 2; - graphicsPipelineCreateInfo.pStages = shaderStages; - graphicsPipelineCreateInfo.pVertexInputState = &vertexInputCreateInfo; - graphicsPipelineCreateInfo.pInputAssemblyState = &inputAssemblyCreateInfo; - graphicsPipelineCreateInfo.pViewportState = &viewportStateCreateInfo; - graphicsPipelineCreateInfo.pRasterizationState = &rasterizerCreateInfo; - graphicsPipelineCreateInfo.pMultisampleState = &multisampleCreateInfo; - graphicsPipelineCreateInfo.pDepthStencilState = nullptr; - graphicsPipelineCreateInfo.pColorBlendState = &colorBlendCreateInfo; - graphicsPipelineCreateInfo.pDynamicState = &dynamicStateCreateInfo; - graphicsPipelineCreateInfo.layout = pipelineLayout; - graphicsPipelineCreateInfo.renderPass = instance->GetSwapChain().GetRenderPass(); - graphicsPipelineCreateInfo.subpass = 0; - graphicsPipelineCreateInfo.basePipelineHandle = VK_NULL_HANDLE; - graphicsPipelineCreateInfo.basePipelineIndex = -1; - - VK_ASSERT(vkCreateGraphicsPipelines(instance->GetDevice(), VK_NULL_HANDLE, 1, &graphicsPipelineCreateInfo, nullptr, &graphicsPipeline), "Failed to initialize graphics pipeline"); - - vkDestroyShaderModule(instance->GetDevice(), vertShaderModule, nullptr); - vkDestroyShaderModule(instance->GetDevice(), fragShaderModule, nullptr); + PipelineCreator creator{"res/shaders/vert.spv", "res/shaders/frag.spv"}; + creator.AddVertexDescriptorSetLayoutBinding(0); + creator.SetVertexInputBindingDescription(Vertex::GetBindingDescription()); + creator.SetVertexInputAttributeDescription(Vertex::GetAttributeDescriptions()); + graphicsPipeline = std::make_unique(*instance, creator); } void InitializeVertexBuffer() { VkDeviceSize bufferSize = sizeof(Vertex) * vertices.size(); - Buffer stagingBuffer{*instance, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, bufferSize, 1}; - - stagingBuffer.Update((void*)vertices.data(), 0); - - vertexBuffer = std::make_unique(*instance, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, bufferSize, 1); - Buffer::CopyBuffer(*instance, stagingBuffer, *vertexBuffer); + vertexBuffer = std::make_unique(*instance, bufferSize); + vertexBuffer->UpdateStaging((void*)vertices.data()); } void InitializeIndexBuffer() { VkDeviceSize bufferSize = sizeof(uint16_t) * indices.size(); - Buffer stagingBuffer{*instance, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, bufferSize, 1}; - - stagingBuffer.Update((void*)indices.data(), 0); - - indexBuffer = std::make_unique(*instance, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, bufferSize, 1); - Buffer::CopyBuffer(*instance, stagingBuffer, *indexBuffer); - } - - void InitializeUniformBuffer() - { - VkDeviceSize bufferSize = sizeof(UniformBufferObject) * instance->GetMaxFramesInFlight(); - uniformBuffer = std::make_unique(*instance, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, bufferSize, instance->GetMaxFramesInFlight()); - uniformBuffer->Map(); + indexBuffer = std::make_unique(*instance, indices.size()); + indexBuffer->UpdateStaging((void*)indices.data()); } - void InitializeCommandBuffer() + void InitializeCommandBuffers() { commandBuffers.resize(instance->GetMaxFramesInFlight()); VkCommandBufferAllocateInfo allocateInfo{}; @@ -371,7 +121,7 @@ private: allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocateInfo.commandBufferCount = commandBuffers.size(); - VK_ASSERT(vkAllocateCommandBuffers(instance->GetDevice(), &allocateInfo, commandBuffers.data()), "Failed to initialize command buffer"); + CP_VK_ASSERT(vkAllocateCommandBuffers(instance->GetDevice(), &allocateInfo, commandBuffers.data()), "Failed to initialize command buffer"); } void RecordCommandBuffer(VkCommandBuffer commandBuffer) @@ -383,10 +133,11 @@ private: beginInfo.flags = 0; beginInfo.pInheritanceInfo = nullptr; - VK_ASSERT(vkBeginCommandBuffer(commandBuffer, &beginInfo), "Failed to begin command buffer"); + CP_VK_ASSERT(vkBeginCommandBuffer(commandBuffer, &beginInfo), "Failed to begin command buffer"); VkClearValue clearValue = {{{0.0f, 0.0f, 0.0f, 1.0f}}}; + // TODO: framebuffer->Bind(); VkRenderPassBeginInfo renderPassBeginInfo{}; renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; renderPassBeginInfo.renderPass = instance->GetSwapChain().GetRenderPass(); @@ -395,49 +146,34 @@ private: renderPassBeginInfo.renderArea.extent = instance->GetSwapChain().GetExtent(); renderPassBeginInfo.clearValueCount = 1; renderPassBeginInfo.pClearValues = &clearValue; - vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); - vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); + + graphicsPipeline->Bind(commandBuffer); UpdateUniformBuffer(); - VkBuffer vbo = vertexBuffer->GetHandle(); - VkDeviceSize offset = 0; - vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vbo, &offset); - vkCmdBindIndexBuffer(commandBuffer, indexBuffer->GetHandle(), 0, VK_INDEX_TYPE_UINT16); + vertexBuffer->Bind(commandBuffer); + indexBuffer->Bind(commandBuffer); + uniformBuffer->Bind(commandBuffer); - VkViewport viewport{}; - viewport.x = 0.0f; - viewport.y = 0.0f; - viewport.width = instance->GetSwapChain().GetExtent().width; - viewport.height = instance->GetSwapChain().GetExtent().height; - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - vkCmdSetViewport(commandBuffer, 0, 1, &viewport); - VkRect2D scissor{}; - scissor.offset = {0, 0}; - scissor.extent = instance->GetSwapChain().GetExtent(); - vkCmdSetScissor(commandBuffer, 0, 1, &scissor); - - vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[instance->GetSwapChain().GetFlightIndex()], 0, nullptr); - vkCmdDrawIndexed(commandBuffer, indices.size(), 1, 0, 0, 0); + indexBuffer->Draw(commandBuffer); vkCmdEndRenderPass(commandBuffer); - VK_ASSERT(vkEndCommandBuffer(commandBuffer), "Failed to end command buffer"); + CP_VK_ASSERT(vkEndCommandBuffer(commandBuffer), "Failed to end command buffer"); } void UpdateUniformBuffer() { - static auto startTime = std::chrono::high_resolution_clock::now(); + static Timer startTimer; - auto currentTime = std::chrono::high_resolution_clock::now(); - float time = std::chrono::duration(currentTime - startTime).count(); - uniformBufferObject.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); - uniformBufferObject.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)); - uniformBufferObject.projection = glm::perspective(glm::radians(45.0f), instance->GetSwapChain().GetExtent().width / (float) instance->GetSwapChain().GetExtent().height, 0.1f, 10.0f); - uniformBufferObject.projection[1][1] *= -1; + float time = startTimer.Elapsed(); + ShaderUniform shaderUniform; + shaderUniform.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f)); + shaderUniform.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)); + shaderUniform.projection = glm::perspective(glm::radians(45.0f), instance->GetSwapChain().GetExtent().width / (float) instance->GetSwapChain().GetExtent().height, 0.1f, 10.0f); + shaderUniform.projection[1][1] *= -1; - uniformBuffer->Update(&uniformBufferObject, instance->GetSwapChain().GetFlightIndex()); + uniformBuffer->Update(shaderUniform); } VkShaderModule InitializeShaderModule(const std::vector& code) @@ -448,7 +184,7 @@ private: createInfo.pCode = reinterpret_cast(code.data()); VkShaderModule shaderModule; - VK_ASSERT(vkCreateShaderModule(instance->GetDevice(), &createInfo, nullptr, &shaderModule), "Failed to initialize shader module"); + CP_VK_ASSERT(vkCreateShaderModule(instance->GetDevice(), &createInfo, nullptr, &shaderModule), "Failed to initialize shader module"); return shaderModule; } @@ -456,13 +192,21 @@ private: int main() { - glfwInit(); - + CP_ASSERT(glfwInit() == GLFW_TRUE, "Failed to initialize the glfw context"); { Application application; + Timer timer; + int frames = 0; while (application.Update()) { glfwPollEvents(); + if (timer.Elapsed() >= 1.0) + { + std::cout << frames << "fps" << std::endl; + frames = 0; + timer.Start(); + } + frames++; } }