diff --git a/Vulkan/Vulkan.vcxproj b/Vulkan/Vulkan.vcxproj index 1b57a78..ca5e350 100644 --- a/Vulkan/Vulkan.vcxproj +++ b/Vulkan/Vulkan.vcxproj @@ -150,10 +150,15 @@ + + + + + diff --git a/Vulkan/Vulkan.vcxproj.filters b/Vulkan/Vulkan.vcxproj.filters index 7bdbeb6..9f4a713 100644 --- a/Vulkan/Vulkan.vcxproj.filters +++ b/Vulkan/Vulkan.vcxproj.filters @@ -32,6 +32,21 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + diff --git a/Vulkan/res/shaders/shader.vert b/Vulkan/res/shaders/shader.vert index 66d6766..e368b85 100644 --- a/Vulkan/res/shaders/shader.vert +++ b/Vulkan/res/shaders/shader.vert @@ -1,20 +1,18 @@ #version 450 +layout(binding = 0) uniform UniformBufferObject +{ + mat4 model; + mat4 view; + mat4 projection; +} ubo; + +layout(location = 0) in vec2 inPosition; +layout(location = 1) in vec3 inColor; + layout(location = 0) out vec3 fragColor; -vec2 positions[3] = vec2[]( - vec2(0.0, -0.5), - vec2(0.5, 0.5), - vec2(-0.5, 0.5) -); - -vec3 colors[3] = vec3[]( - vec3(1.0, 0.0, 0.0), - vec3(0.0, 1.0, 0.0), - vec3(0.0, 0.0, 1.0) -); - void main() { - gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); - fragColor = colors[gl_VertexIndex]; + gl_Position = ubo.projection * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0); + fragColor = inColor; } \ No newline at end of file diff --git a/Vulkan/res/shaders/vert.spv b/Vulkan/res/shaders/vert.spv index a41dd2c..4092755 100644 Binary files a/Vulkan/res/shaders/vert.spv and b/Vulkan/res/shaders/vert.spv differ diff --git a/Vulkan/src/Buffer.h b/Vulkan/src/Buffer.h new file mode 100644 index 0000000..4fffca2 --- /dev/null +++ b/Vulkan/src/Buffer.h @@ -0,0 +1,158 @@ +#pragma once + +#include "Common.h" +#include "Instance.h" +#include +#include + +class Buffer +{ +private: + Instance& instance; + + VkDeviceMemory memory; + VkBuffer handle; + VkDeviceSize size; + int count; + + void* mappedData = nullptr; + +public: + Buffer(Instance& instance, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkDeviceSize size, int count) + : instance{instance}, size{size}, count{count} + { + VkBufferCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + createInfo.size = size * (VkDeviceSize)count; + createInfo.usage = usage; + createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VK_ASSERT(vkCreateBuffer(instance.GetDevice(), &createInfo, nullptr, &handle), "Failed to initialize buffer"); + + VkMemoryRequirements memoryRequirements; + vkGetBufferMemoryRequirements(instance.GetDevice(), handle, &memoryRequirements); + + VkMemoryAllocateInfo allocateInfo{}; + allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocateInfo.allocationSize = memoryRequirements.size; + allocateInfo.memoryTypeIndex = FindMemoryType(instance, memoryRequirements.memoryTypeBits, properties); + + VK_ASSERT(vkAllocateMemory(instance.GetDevice(), &allocateInfo, nullptr, &memory), "Failed to allocate buffer memory"); + + vkBindBufferMemory(instance.GetDevice(), handle, memory, 0); + } + + ~Buffer() + { + vkFreeMemory(instance.GetDevice(), memory, nullptr); + vkDestroyBuffer(instance.GetDevice(), handle, nullptr); + } + + void Update(void* indexData, int index) + { + ASSERT(index >= 0 && index < count, "instance is outside of the buffer"); + + if (mappedData == nullptr) + { + void* data; + vkMapMemory(instance.GetDevice(), memory, index * size, size, 0, &data); + memcpy(data, indexData, size); + vkUnmapMemory(instance.GetDevice(), memory); + } + else + { + memcpy((char*)mappedData + index * size, indexData, size); + } + } + + void Map() + { + 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") + + vkUnmapMemory(instance.GetDevice(), memory); + mappedData = nullptr; + } + + 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; + } + + VkDeviceSize GetPosition(int index) const + { + ASSERT(index >= 0 && index < count, "Instance is outside of the buffer"); + return size * (VkDeviceSize)index; + } + + static void CopyBuffer(Instance& instance, const Buffer& srcBuffer, const Buffer& dstBuffer) + { + ASSERT(srcBuffer.size == dstBuffer.size && srcBuffer.count == dstBuffer.count, "Buffers have different sizes"); + VkCommandBufferAllocateInfo allocateInfo{}; + + allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocateInfo.commandPool = instance.GetCommandPool(); + allocateInfo.commandBufferCount = 1; + + VkCommandBuffer commandBuffer; + VK_ASSERT(vkAllocateCommandBuffers(instance.GetDevice(), &allocateInfo, &commandBuffer), "Failed to initialize command buffer"); + + VkCommandBufferBeginInfo beginInfo{}; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + vkBeginCommandBuffer(commandBuffer, &beginInfo); + + VkBufferCopy bufferCopy{}; + bufferCopy.dstOffset = 0; + bufferCopy.srcOffset = 0; + bufferCopy.size = srcBuffer.size * (VkDeviceSize)srcBuffer.count; + + vkCmdCopyBuffer(commandBuffer, srcBuffer.GetHandle(), dstBuffer.GetHandle(), 1, &bufferCopy); + + vkEndCommandBuffer(commandBuffer); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + vkQueueSubmit(instance.GetGraphicsQueue(), 1, &submitInfo, VK_NULL_HANDLE); + vkQueueWaitIdle(instance.GetGraphicsQueue()); + + vkFreeCommandBuffers(instance.GetDevice(), instance.GetCommandPool(), 1, &commandBuffer); + } + +private: + static uint32_t FindMemoryType(Instance& instance, uint32_t typeFilter, VkMemoryPropertyFlags properties) + { + VkPhysicalDeviceMemoryProperties memoryProperties; + vkGetPhysicalDeviceMemoryProperties(instance.GetPhysicalDevice(), &memoryProperties); + for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; ++i) + { + if ((typeFilter & (1 << i)) && (memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) + return i; + } + throw std::runtime_error("Failed to find suitable memory type"); + } +}; diff --git a/Vulkan/src/Common.h b/Vulkan/src/Common.h index 986455e..20fe90a 100644 --- a/Vulkan/src/Common.h +++ b/Vulkan/src/Common.h @@ -1,10 +1,11 @@ #pragma once +#include "VulkanException.h" #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 std::runtime_error(message); } while(false) +#define VK_ASSERT(Function, message) if(Function != VK_SUCCESS) { throw VulkanException(message); } while(false) VkResult vkCreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, diff --git a/Vulkan/src/DebugMessenger.h b/Vulkan/src/DebugMessenger.h index 8c621fc..383f6d8 100644 --- a/Vulkan/src/DebugMessenger.h +++ b/Vulkan/src/DebugMessenger.h @@ -38,14 +38,14 @@ public: DebugMessenger& operator=(DebugMessenger&&) = delete; DebugMessenger& operator=(const DebugMessenger&) = delete; - void AddRequiredExtensions(std::vector* extensions) + static void AddRequiredExtensions(std::vector* extensions) { #ifndef NDEBUG extensions->emplace_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); #endif } - void AddRequiredLayers(std::vector* layers) + static void AddRequiredLayers(std::vector* layers) { #ifndef NDEBUG layers->emplace_back("VK_LAYER_KHRONOS_validation"); @@ -61,6 +61,8 @@ private: if (messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { std::cerr << pCallbackData->pMessage << std::endl; + if (messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) + throw VulkanException(pCallbackData->pMessage); } return VK_FALSE; } diff --git a/Vulkan/src/Instance.h b/Vulkan/src/Instance.h new file mode 100644 index 0000000..24a6fe9 --- /dev/null +++ b/Vulkan/src/Instance.h @@ -0,0 +1,359 @@ +#pragma once + +#include +#include + +#include "DebugMessenger.h" +#include "QueueFamilies.h" +#include "SwapChain.h" + +class Instance final +{ +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; + VkQueue graphicsQueue; + VkQueue presentQueue; + std::unique_ptr swapChain; + VkCommandPool commandPool; + bool framebufferResized = false; + +public: + Instance(const std::string& applicationName) + { + InitializeWindow(applicationName); + InitializeInstance(applicationName); + InitializeDebugMessenger(); + InitializeSurface(); + SelectPhysicalDevice(); + InitializeLogicalDevice(); + InitializeSwapChain(); + InitializeCommandPool(); + } + + ~Instance() + { + vkDestroyCommandPool(device, commandPool, nullptr); + swapChain.reset(); + vkDestroyDevice(device, nullptr); + vkDestroySurfaceKHR(instance, surface, nullptr); + debugMessenger.reset(); + vkDestroyInstance(instance, nullptr); + glfwDestroyWindow(window); + } + + bool BeginPresent() + { + if (!swapChain->BeginPresent()) + return true; + } + + bool EndPresent() + { + swapChain->EndPresent(presentQueue, framebufferResized); + 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.pWaitDstStageMask = waitStages; + submitInfo.commandBufferCount = commandBuffers.size(); + submitInfo.pCommandBuffers = commandBuffers.data(); + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = signalSemaphores; + + VK_ASSERT(vkQueueSubmit(graphicsQueue, 1, &submitInfo, swapChain->GetInFlightFence()), "Failed to submit command buffer"); + } + + VkInstance GetInstance() const + { + return instance; + } + + VkPhysicalDevice GetPhysicalDevice() const + { + return physicalDevice; + } + + VkDevice GetDevice() const + { + return device; + } + + VkCommandPool GetCommandPool() const + { + return commandPool; + } + + VkQueue GetGraphicsQueue() const + { + return graphicsQueue; + } + + int GetMaxFramesInFlight() + { + return MAX_FRAMES_IN_FLIGHT; + } + + const SwapChain& GetSwapChain() const + { + return *swapChain; + } + +private: + void InitializeWindow(const std::string& applicationName) + { + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, applicationName.c_str(), nullptr, nullptr); + + glfwSetWindowUserPointer(window, this); + glfwSetFramebufferSizeCallback(window, FramebufferResizeCallback); + } + + void InitializeInstance(const std::string& applicationName) + { + VkApplicationInfo appInfo{}; + 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.engineVersion = VK_MAKE_VERSION(1, 0, 0); + appInfo.apiVersion = VK_API_VERSION_1_1; + + std::vector requiredExtensions = GetRequiredExtensions(); + + uint32_t extensionCount; + vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); + std::vector extensions{extensionCount}; + vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data()); + + std::cout << "Supported Extensions: " << std::endl; + for (auto&& extension : extensions) + { + std::cout << "\t" << extension.extensionName << std::endl; + } + + std::vector layers{}; + DebugMessenger::AddRequiredLayers(&layers); + ASSERT(CheckLayerSupport(layers), "Some required layers are not supported"); + + VkInstanceCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + createInfo.pApplicationInfo = &appInfo; + createInfo.enabledExtensionCount = requiredExtensions.size(); + createInfo.ppEnabledExtensionNames = requiredExtensions.data(); + createInfo.enabledLayerCount = layers.size(); + createInfo.ppEnabledLayerNames = layers.data(); + VK_ASSERT(vkCreateInstance(&createInfo, nullptr, &instance), "Failed to create instance"); + } + + void InitializeDebugMessenger() + { + debugMessenger = std::make_unique(instance); + } + + void InitializeSurface() + { + 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"); + + std::vector devices(deviceCount); + vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); + std::cout << "Available devices: " << std::endl; + for (auto&& device : devices) + { + VkPhysicalDeviceProperties deviceProperties; + vkGetPhysicalDeviceProperties(device, &deviceProperties); + std::cout << "\t" << deviceProperties.deviceName << std::endl; + } + for (auto&& device : devices) + { + if (IsPhysicalDeviceSuitable(device)) + { + VkPhysicalDeviceProperties deviceProperties; + vkGetPhysicalDeviceProperties(device, &deviceProperties); + physicalDevice = device; + std::cout << "Selecting device: " << deviceProperties.deviceName << std::endl; + break; + } + } + ASSERT(physicalDevice != VK_NULL_HANDLE, "Failed to find suitable GPU"); + } + + void InitializeLogicalDevice() + { + QueueFamilies queueFamilies{surface, physicalDevice}; + + float queuePriority = 1.0f; + + std::vector queueCreateInfos{}; + std::set uniqueQueueFamilies{queueFamilies.graphicsFamily.value(), queueFamilies.presentFamily.value()}; + for(auto&& queueFamily : uniqueQueueFamilies) + { + VkDeviceQueueCreateInfo queueCreateInfo{}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = queueFamily; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &queuePriority; + queueCreateInfos.emplace_back(queueCreateInfo); + } + std::vector deviceExtensions = GetRequiredDeviceExtensions(); + VkPhysicalDeviceFeatures deviceFeatures{}; + deviceFeatures.fillModeNonSolid = VK_TRUE; + VkDeviceCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + createInfo.pQueueCreateInfos = queueCreateInfos.data(); + createInfo.queueCreateInfoCount = queueCreateInfos.size(); + createInfo.pEnabledFeatures = &deviceFeatures; + createInfo.ppEnabledExtensionNames = deviceExtensions.data(); + createInfo.enabledExtensionCount = deviceExtensions.size(); + + 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); + } + + void InitializeSwapChain() + { + swapChain = std::make_unique(window, surface, device, physicalDevice); + } + + 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"); + } + + std::vector GetRequiredExtensions() + { + uint32_t glfwExtensionCount; + const char** glfwExtensions; + glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + + std::vector extensions{glfwExtensions, glfwExtensions + glfwExtensionCount}; + + debugMessenger->AddRequiredExtensions(&extensions); + + return extensions; + } + + bool CheckLayerSupport(const std::vector& layers) + { + uint32_t layerCount; + vkEnumerateInstanceLayerProperties(&layerCount, nullptr); + + std::vector availableLayers(layerCount); + vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); + + std::cout << "Supported Layers: " << std::endl; + for (auto&& availableLayer : availableLayers) + { + std::cout << "\t" << availableLayer.layerName << std::endl; + } + + for (auto&& layer : layers) + { + bool layerFound = false; + for (auto&& availableLayer : availableLayers) + { + if (std::strcmp(layer, availableLayer.layerName) == 0) + { + layerFound = true; + break; + } + } + if (!layerFound) + return false; + } + return true; + } + + bool IsPhysicalDeviceSuitable(VkPhysicalDevice device) + { + VkPhysicalDeviceProperties deviceProperties; + vkGetPhysicalDeviceProperties(device, &deviceProperties); + if (deviceProperties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) + return false; + + VkPhysicalDeviceFeatures deviceFeatures; + vkGetPhysicalDeviceFeatures(device, &deviceFeatures); + if (!deviceFeatures.fillModeNonSolid) + return false; + + QueueFamilies queueFamilies{surface, device}; + if (!queueFamilies.AllRequiredFamiliesSupported()) + return false; + + if (!CheckDeviceExtensionSupport(device)) + return false; + SwapChainSupportDetails details{surface, device}; + if (!details.Valid()) + return false; + + return true; + } + + bool CheckDeviceExtensionSupport(VkPhysicalDevice device) + { + uint32_t extensionCount; + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); + std::vector extensions{extensionCount}; + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, extensions.data()); + + for (auto&& requiredExtension : GetRequiredDeviceExtensions()) + { + bool found = false; + for (auto&& extension : extensions) + { + if (std::strcmp(requiredExtension, extension.extensionName) == 0) + { + found = true; + break; + } + } + if (!found) + return false; + } + return true; + } + + std::vector GetRequiredDeviceExtensions() + { + return {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; + } + + static void FramebufferResizeCallback(GLFWwindow* window, int width, int height) + { + Instance* instance = static_cast(glfwGetWindowUserPointer(window)); + instance->framebufferResized = true; + } + +}; diff --git a/Vulkan/src/QueueFamilies.h b/Vulkan/src/QueueFamilies.h new file mode 100644 index 0000000..6a61f6c --- /dev/null +++ b/Vulkan/src/QueueFamilies.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include + +struct QueueFamilies +{ + std::optional graphicsFamily; + std::optional presentFamily; + + QueueFamilies(VkSurfaceKHR surface, VkPhysicalDevice device) + { + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); + + int i = 0; + for (auto&& queueFamily : queueFamilies) + { + if(queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) + { + graphicsFamily = i; + } + VkBool32 presentSupport = false; + vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); + if (presentSupport) + { + presentFamily = i; + } + i++; + } + + } + + bool AllRequiredFamiliesSupported() + { + return graphicsFamily.has_value() && presentFamily.has_value(); + } +}; diff --git a/Vulkan/src/SwapChain.h b/Vulkan/src/SwapChain.h index 9d865c0..6b342bd 100644 --- a/Vulkan/src/SwapChain.h +++ b/Vulkan/src/SwapChain.h @@ -1,20 +1,406 @@ #pragma once +#include "QueueFamilies.h" +#include #include #include +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(); + } +}; + class SwapChain { - VkSwapchainKHR swapChain; - VkFormat swapChainImageFormat; - VkExtent2D swapChainExtent; - std::vector swapChainImageViews; - std::vector swapChainImages; - std::vector swapChainFramebuffers; + // TODO: Remove, replaced by Instance::MAX_FRAMES_IN_FLIGHT + static const int MAX_FRAMES_IN_FLIGHT = 2; - SwapChain() + // Needed for recreation and destruction + GLFWwindow* window; + VkSurfaceKHR surface; + VkPhysicalDevice physicalDevice; + VkDevice device; + + // Created by the class + VkSwapchainKHR handle; + VkRenderPass renderPass; + VkFormat imageFormat; + VkExtent2D extent; + std::vector imageViews; + 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() + { + 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(); + } + +private: + + 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; + } + + }; diff --git a/Vulkan/src/VulkanException.h b/Vulkan/src/VulkanException.h new file mode 100644 index 0000000..1007e85 --- /dev/null +++ b/Vulkan/src/VulkanException.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +class VulkanException : public std::runtime_error +{ +public: + VulkanException(const char* str) + : runtime_error{str} + {} +}; diff --git a/Vulkan/src/Window.h b/Vulkan/src/Window.h new file mode 100644 index 0000000..a27107a --- /dev/null +++ b/Vulkan/src/Window.h @@ -0,0 +1,6 @@ +#pragma once + +class Window +{ + +}; diff --git a/Vulkan/src/main.cpp b/Vulkan/src/main.cpp index 303c287..515b819 100644 --- a/Vulkan/src/main.cpp +++ b/Vulkan/src/main.cpp @@ -1,6 +1,7 @@ -#include "DebugMessenger.h" #include "FileSystem.h" - +#include "Buffer.h" +#include "Instance.h" +#define GLM_FORCE_LEFT_HANDED #define GLFW_INCLUDE_VULKAN #include @@ -11,73 +12,101 @@ #include #include #include +#include +#include +#include -static const int WINDOW_WIDTH = 1920; -static const int WINDOW_HEIGHT = 1080; -static const int MAX_FRAMES_IN_FLIGHT = 2; - -struct QueueFamilyIndices -{ - std::optional graphicsFamily; - std::optional presentFamily; - - bool AllRequiredFamiliesSupported() +struct Vertex { + glm::vec2 pos; + glm::vec3 color; + + static VkVertexInputBindingDescription getBindingDescription() { - return graphicsFamily.has_value() && presentFamily.has_value(); + 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; } }; -struct SwapChainSupportDetails -{ - VkSurfaceCapabilitiesKHR capabilities; - std::vector formats; - std::vector presentModes; +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}}, + Vertex{{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}}, + Vertex{{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}} +}; - bool Valid() - { - return !formats.empty() && !presentModes.empty(); - } +const std::vector indices = { + 0, 1, 2, 2, 3, 0 +}; + +struct UniformBufferObject +{ + glm::mat4 model; + glm::mat4 view; + glm::mat4 projection; }; class Application final { private: - GLFWwindow* window; - VkInstance instance; - VkSurfaceKHR surface; - DebugMessenger* debugMessenger; - VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; - VkDevice device; - VkQueue graphicsQueue; - VkQueue presentQueue; - VkSwapchainKHR swapChain; - VkFormat swapChainImageFormat; - VkExtent2D swapChainExtent; - std::vector swapChainImageViews; - std::vector swapChainImages; - std::vector swapChainFramebuffers; + std::unique_ptr instance; + VkDescriptorSetLayout uniformBufferLayout; + VkDescriptorPool descriptorPool; + std::vector descriptorSets; VkPipelineLayout pipelineLayout; - VkRenderPass renderPass; VkPipeline graphicsPipeline; - VkCommandPool commandPool; + std::unique_ptr vertexBuffer; + std::unique_ptr indexBuffer; + std::unique_ptr uniformBuffer; std::vector commandBuffers; - std::vector imageAvailableSemaphores; - std::vector renderFinishedSemaphores; - std::vector inFlightFences; - int currentFrame = 0; - bool framebufferResized = false; + UniformBufferObject uniformBufferObject; public: Application() { - InitializeWindow(); - InitializeVulkan(); + instance = std::make_unique("Vulkan Tutorial"); + InitializeUniformBufferLayout(); + InitializeGraphicsPipeline(); + InitializeVertexBuffer(); + InitializeIndexBuffer(); + InitializeUniformBuffer(); + InitializeDescriptorPool(); + InitializeDescriptorSet(); + InitializeCommandBuffer(); } ~Application() { - DestroyVulkan(); - DestroyWindow(); + 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; @@ -87,306 +116,76 @@ public: bool Update() { - vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); - - uint32_t imageIndex; - VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); - if (result == VK_ERROR_OUT_OF_DATE_KHR) - { - RecreateSwapChain(); + if (!instance->BeginPresent()) return true; - } - vkResetFences(device, 1, &inFlightFences[currentFrame]); + RecordCommandBuffer(commandBuffers[instance->GetSwapChain().GetFlightIndex()]); - RecordCommandBuffer(commandBuffers[currentFrame], imageIndex); - VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]}; - VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]}; - 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.pWaitDstStageMask = waitStages; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandBuffers[currentFrame]; - submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = signalSemaphores; - - VK_ASSERT(vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]), "Failed to submit command buffer"); - - VkSwapchainKHR swapChains[] = {swapChain}; - VkPresentInfoKHR presentInfo{}; - presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - presentInfo.waitSemaphoreCount = 1; - presentInfo.pWaitSemaphores = signalSemaphores; - presentInfo.swapchainCount = 1; - presentInfo.pSwapchains = swapChains; - presentInfo.pImageIndices = &imageIndex; - presentInfo.pResults = nullptr; - - result = vkQueuePresentKHR(presentQueue, &presentInfo); - if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) - { - framebufferResized = false; - RecreateSwapChain(); - } - - currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; - return !glfwWindowShouldClose(window); + instance->SubmitGraphicsQueue(std::vector{commandBuffers[instance->GetSwapChain().GetFlightIndex()]}); + return instance->EndPresent(); } private: - void InitializeWindow() - { - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Vulkan Window", nullptr, nullptr); - glfwSetWindowUserPointer(window, this); - glfwSetFramebufferSizeCallback(window, FramebufferResizeCallback); + void InitializeUniformBufferLayout() + { + 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"); } - void InitializeVulkan() + void InitializeDescriptorPool() { - InitializeInstance(); - InitializeDebugMessenger(); - InitializeSurface(); - SelectPhysicalDevice(); - InitializeLogicalDevice(); - InitializeSwapChain(); - InitializeSwapChainImageViews(); - InitializeRenderPass(); - InitializeGraphicsPipeline(); - InitializeSwapChainFramebuffers(); - InitializeCommandPool(); - InitializeCommandBuffer(); - InitializeSyncObjects(); + 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 InitializeInstance() + void InitializeDescriptorSet() { - VkApplicationInfo appInfo{}; - appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - appInfo.pApplicationName = "Vulkan Tutorial"; - appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); - appInfo.pEngineName = "Greet Engine"; - appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); - appInfo.apiVersion = VK_API_VERSION_1_1; + 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(); - std::vector requiredExtensions = GetRequiredExtensions(); + 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); - uint32_t extensionCount; - vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); - std::vector extensions{extensionCount}; - vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data()); + 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); + } - std::cout << "Supported Extensions: " << std::endl; - for (auto&& extension : extensions) - { - std::cout << "\t" << extension.extensionName << std::endl; - } - - std::vector layers{}; - debugMessenger->AddRequiredLayers(&layers); - ASSERT(CheckLayerSupport(layers), "Some required layers are not supported"); - - VkInstanceCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - createInfo.pApplicationInfo = &appInfo; - createInfo.enabledExtensionCount = requiredExtensions.size(); - createInfo.ppEnabledExtensionNames = requiredExtensions.data(); - createInfo.enabledLayerCount = layers.size(); - createInfo.ppEnabledLayerNames = layers.data(); - VK_ASSERT(vkCreateInstance(&createInfo, nullptr, &instance), "Failed to create instance"); - } - - void InitializeDebugMessenger() - { - debugMessenger = new DebugMessenger(instance); - } - - void InitializeSurface() - { - 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"); - - std::vector devices(deviceCount); - vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); - std::cout << "Available devices: " << std::endl; - for (auto&& device : devices) - { - VkPhysicalDeviceProperties deviceProperties; - vkGetPhysicalDeviceProperties(device, &deviceProperties); - std::cout << "\t" << deviceProperties.deviceName << std::endl; - } - for (auto&& device : devices) - { - if (IsPhysicalDeviceSuitable(device)) - { - VkPhysicalDeviceProperties deviceProperties; - vkGetPhysicalDeviceProperties(device, &deviceProperties); - physicalDevice = device; - std::cout << "Selecting device: " << deviceProperties.deviceName << std::endl; - break; - } - } - ASSERT(physicalDevice != VK_NULL_HANDLE, "Failed to find suitable GPU"); - } - - void InitializeLogicalDevice() - { - QueueFamilyIndices indices = FindQueueFamilies(physicalDevice); - - float queuePriority = 1.0f; - - std::vector queueCreateInfos{}; - std::set uniqueQueueFamilies{indices.graphicsFamily.value(), indices.presentFamily.value()}; - for(auto&& queueFamily : uniqueQueueFamilies) - { - VkDeviceQueueCreateInfo queueCreateInfo{}; - queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueCreateInfo.queueFamilyIndex = queueFamily; - queueCreateInfo.queueCount = 1; - queueCreateInfo.pQueuePriorities = &queuePriority; - queueCreateInfos.emplace_back(queueCreateInfo); - } - std::vector deviceExtensions = GetRequiredDeviceExtensions(); - VkPhysicalDeviceFeatures deviceFeatures{}; - deviceFeatures.fillModeNonSolid = VK_TRUE; - VkDeviceCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - createInfo.pQueueCreateInfos = queueCreateInfos.data(); - createInfo.queueCreateInfoCount = queueCreateInfos.size(); - createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.ppEnabledExtensionNames = deviceExtensions.data(); - createInfo.enabledExtensionCount = deviceExtensions.size(); - - VK_ASSERT(vkCreateDevice(physicalDevice, &createInfo, nullptr, &device), "Failed to initialize logical device"); - - vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); - vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); - } - - void InitializeSwapChain() - { - SwapChainSupportDetails swapChainSupport = FindSwapChainSupport(physicalDevice); - - VkSurfaceFormatKHR format = SelectSwapSurfaceFormat(swapChainSupport.formats); - VkPresentModeKHR presentMode = SelectSwapPresentMode(swapChainSupport.presentModes); - swapChainExtent = SelectSwapExtent(swapChainSupport.capabilities); - swapChainImageFormat = format.format; - uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; - if (swapChainSupport.capabilities.maxImageCount != 0) - { - imageCount = std::min(imageCount, swapChainSupport.capabilities.maxImageCount); - } - - QueueFamilyIndices indices = FindQueueFamilies(physicalDevice); - std::vector queueFamilyIndices{indices.graphicsFamily.value(), indices.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 = swapChainExtent; - 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 (indices.graphicsFamily != indices.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, &swapChain), "Failed to initialize the swapchain"); - - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); - swapChainImages.resize(imageCount); - vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data()); - } - - void InitializeSwapChainImageViews() - { - swapChainImageViews.resize(swapChainImages.size()); - for (size_t i = 0; i < swapChainImages.size(); i++) - { - VkImageViewCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - createInfo.image = swapChainImages[i]; - createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - createInfo.format = swapChainImageFormat; - createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - createInfo.subresourceRange.baseMipLevel = 0; - createInfo.subresourceRange.levelCount = 1; - createInfo.subresourceRange.baseArrayLayer = 0; - createInfo.subresourceRange.layerCount = 1; - VK_ASSERT(vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]), "Failed to initialize swapchain image view"); - } - } - - void InitializeRenderPass() - { - VkAttachmentDescription colorAttachment{}; - colorAttachment.format = swapChainImageFormat; - 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 InitializeGraphicsPipeline() @@ -410,12 +209,15 @@ private: 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 = 0; - vertexInputCreateInfo.pVertexBindingDescriptions = nullptr; - vertexInputCreateInfo.vertexAttributeDescriptionCount = 0; - vertexInputCreateInfo.pVertexAttributeDescriptions = nullptr; + 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; @@ -425,14 +227,14 @@ private: VkViewport viewport{}; viewport.x = 0; viewport.y = 0; - viewport.width = swapChainExtent.width; - viewport.height = swapChainExtent.height; + 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 = swapChainExtent; + scissor.extent = instance->GetSwapChain().GetExtent(); std::vector dynamicStates = { VK_DYNAMIC_STATE_VIEWPORT, @@ -500,12 +302,12 @@ private: VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{}; pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutCreateInfo.setLayoutCount = 0; - pipelineLayoutCreateInfo.pSetLayouts = nullptr; + pipelineLayoutCreateInfo.setLayoutCount = 1; + pipelineLayoutCreateInfo.pSetLayouts = &uniformBufferLayout; pipelineLayoutCreateInfo.pushConstantRangeCount = 0; pipelineLayoutCreateInfo.pPushConstantRanges = nullptr; - VK_ASSERT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout), "Failed to initialize pipeline layout"); + VK_ASSERT(vkCreatePipelineLayout(instance->GetDevice(), &pipelineLayoutCreateInfo, nullptr, &pipelineLayout), "Failed to initialize pipeline layout"); VkGraphicsPipelineCreateInfo graphicsPipelineCreateInfo{}; graphicsPipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; @@ -520,83 +322,59 @@ private: graphicsPipelineCreateInfo.pColorBlendState = &colorBlendCreateInfo; graphicsPipelineCreateInfo.pDynamicState = &dynamicStateCreateInfo; graphicsPipelineCreateInfo.layout = pipelineLayout; - graphicsPipelineCreateInfo.renderPass = renderPass; + graphicsPipelineCreateInfo.renderPass = instance->GetSwapChain().GetRenderPass(); graphicsPipelineCreateInfo.subpass = 0; graphicsPipelineCreateInfo.basePipelineHandle = VK_NULL_HANDLE; graphicsPipelineCreateInfo.basePipelineIndex = -1; - VK_ASSERT(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &graphicsPipelineCreateInfo, nullptr, &graphicsPipeline), "Failed to initialize graphics pipeline"); + VK_ASSERT(vkCreateGraphicsPipelines(instance->GetDevice(), VK_NULL_HANDLE, 1, &graphicsPipelineCreateInfo, nullptr, &graphicsPipeline), "Failed to initialize graphics pipeline"); - vkDestroyShaderModule(device, vertShaderModule, nullptr); - vkDestroyShaderModule(device, fragShaderModule, nullptr); + vkDestroyShaderModule(instance->GetDevice(), vertShaderModule, nullptr); + vkDestroyShaderModule(instance->GetDevice(), fragShaderModule, nullptr); } - void InitializeSwapChainFramebuffers() + void InitializeVertexBuffer() { - swapChainFramebuffers.resize(swapChainImages.size()); + 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}; - for (size_t i = 0; i < swapChainImageViews.size(); ++i) - { - VkImageView attachments[] = {swapChainImageViews[i]}; + stagingBuffer.Update((void*)vertices.data(), 0); - VkFramebufferCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; - createInfo.renderPass = renderPass; - createInfo.attachmentCount = 1; - createInfo.pAttachments = attachments; - createInfo.width = swapChainExtent.width; - createInfo.height = swapChainExtent.height; - createInfo.layers = 1; + 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); + } - VK_ASSERT(vkCreateFramebuffer(device, &createInfo, nullptr, &swapChainFramebuffers[i]), "Failed to initialize swap chain framebuffer"); - } - } - - void InitializeCommandPool() + void InitializeIndexBuffer() { - QueueFamilyIndices indices = FindQueueFamilies(physicalDevice); + 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}; - VkCommandPoolCreateInfo createInfo{}; - createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - createInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - createInfo.queueFamilyIndex = indices.graphicsFamily.value(); - VK_ASSERT(vkCreateCommandPool(device, &createInfo, nullptr, &commandPool), "Failed to initialize command pool"); + 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(); } void InitializeCommandBuffer() { - commandBuffers.resize(MAX_FRAMES_IN_FLIGHT); + commandBuffers.resize(instance->GetMaxFramesInFlight()); VkCommandBufferAllocateInfo allocateInfo{}; allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocateInfo.commandPool = commandPool; + allocateInfo.commandPool = instance->GetCommandPool(); allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocateInfo.commandBufferCount = commandBuffers.size(); - VK_ASSERT(vkAllocateCommandBuffers(device, &allocateInfo, commandBuffers.data()), "Failed to initialize command buffer"); + VK_ASSERT(vkAllocateCommandBuffers(instance->GetDevice(), &allocateInfo, commandBuffers.data()), "Failed to initialize command buffer"); } - 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 RecordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) + void RecordCommandBuffer(VkCommandBuffer commandBuffer) { vkResetCommandBuffer(commandBuffer, 0); @@ -611,35 +389,57 @@ private: VkRenderPassBeginInfo renderPassBeginInfo{}; renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - renderPassBeginInfo.renderPass = renderPass; - renderPassBeginInfo.framebuffer = swapChainFramebuffers[imageIndex]; + renderPassBeginInfo.renderPass = instance->GetSwapChain().GetRenderPass(); + renderPassBeginInfo.framebuffer = instance->GetSwapChain().GetFramebuffer(); renderPassBeginInfo.renderArea.offset = {0, 0}; - renderPassBeginInfo.renderArea.extent = swapChainExtent; + 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); + UpdateUniformBuffer(); + + VkBuffer vbo = vertexBuffer->GetHandle(); + VkDeviceSize offset = 0; + vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vbo, &offset); + vkCmdBindIndexBuffer(commandBuffer, indexBuffer->GetHandle(), 0, VK_INDEX_TYPE_UINT16); + VkViewport viewport{}; viewport.x = 0.0f; viewport.y = 0.0f; - viewport.width = swapChainExtent.width; - viewport.height = swapChainExtent.height; + 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 = swapChainExtent; + scissor.extent = instance->GetSwapChain().GetExtent(); vkCmdSetScissor(commandBuffer, 0, 1, &scissor); - vkCmdDraw(commandBuffer, 3, 1, 0, 0); + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[instance->GetSwapChain().GetFlightIndex()], 0, nullptr); + vkCmdDrawIndexed(commandBuffer, indices.size(), 1, 0, 0, 0); vkCmdEndRenderPass(commandBuffer); VK_ASSERT(vkEndCommandBuffer(commandBuffer), "Failed to end command buffer"); } + void UpdateUniformBuffer() + { + 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.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; + + uniformBuffer->Update(&uniformBufferObject, instance->GetSwapChain().GetFlightIndex()); + } + VkShaderModule InitializeShaderModule(const std::vector& code) { VkShaderModuleCreateInfo createInfo{}; @@ -648,283 +448,22 @@ private: createInfo.pCode = reinterpret_cast(code.data()); VkShaderModule shaderModule; - VK_ASSERT(vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule), "Failed to initialize shader module"); + VK_ASSERT(vkCreateShaderModule(instance->GetDevice(), &createInfo, nullptr, &shaderModule), "Failed to initialize shader module"); return shaderModule; } - - QueueFamilyIndices FindQueueFamilies(VkPhysicalDevice device) - { - QueueFamilyIndices indices; - - uint32_t queueFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); - std::vector queueFamilies(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); - - int i = 0; - for (auto&& queueFamily : queueFamilies) - { - if(queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) - { - indices.graphicsFamily = i; - } - VkBool32 presentSupport = false; - vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); - if (presentSupport) - { - indices.presentFamily = i; - } - i++; - } - - return indices; - } - - SwapChainSupportDetails FindSwapChainSupport(VkPhysicalDevice device) - { - SwapChainSupportDetails details; - - vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); - - uint32_t formatCount; - vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr); - if (formatCount != 0) - { - details.formats.resize(formatCount); - vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data()); - } - - uint32_t presentModeCount; - vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr); - if (presentModeCount != 0) - { - details.presentModes.resize(presentModeCount); - vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data()); - } - - return details; - } - - bool CheckLayerSupport(const std::vector& layers) - { - uint32_t layerCount; - vkEnumerateInstanceLayerProperties(&layerCount, nullptr); - - std::vector availableLayers(layerCount); - vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); - - std::cout << "Supported Layers: " << std::endl; - for (auto&& availableLayer : availableLayers) - { - std::cout << "\t" << availableLayer.layerName << std::endl; - } - - for (auto&& layer : layers) - { - bool layerFound = false; - for (auto&& availableLayer : availableLayers) - { - if (std::strcmp(layer, availableLayer.layerName) == 0) - { - layerFound = true; - break; - } - } - if (!layerFound) - return false; - } - return true; - } - - bool IsPhysicalDeviceSuitable(VkPhysicalDevice device) - { - VkPhysicalDeviceProperties deviceProperties; - vkGetPhysicalDeviceProperties(device, &deviceProperties); - if (deviceProperties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) - return false; - - VkPhysicalDeviceFeatures deviceFeatures; - vkGetPhysicalDeviceFeatures(device, &deviceFeatures); - if (!deviceFeatures.fillModeNonSolid) - return false; - - if (!FindQueueFamilies(device).AllRequiredFamiliesSupported()) - return false; - - if (!CheckDeviceExtensionSupport(device)) - return false; - - if (!FindSwapChainSupport(device).Valid()) - return false; - - return true; - } - - std::vector GetRequiredExtensions() - { - uint32_t glfwExtensionCount; - const char** glfwExtensions; - glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); - - std::vector extensions{glfwExtensions, glfwExtensions + glfwExtensionCount}; - - debugMessenger->AddRequiredExtensions(&extensions); - - return extensions; - } - - bool CheckDeviceExtensionSupport(VkPhysicalDevice device) - { - uint32_t extensionCount; - vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); - std::vector extensions{extensionCount}; - vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, extensions.data()); - - for (auto&& requiredExtension : GetRequiredDeviceExtensions()) - { - bool found = false; - for (auto&& extension : extensions) - { - if (std::strcmp(requiredExtension, extension.extensionName) == 0) - { - found = true; - break; - } - } - if (!found) - return false; - } - return true; - } - - 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(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; - } - - void RecreateSwapChain() - { - int width = 0; - int height = 0; - glfwGetFramebufferSize(window, &width, &height); - while (width == 0 || height == 0) - { - glfwGetFramebufferSize(window, &width, &height); - glfwWaitEvents(); - } - - vkDeviceWaitIdle(device); - - DestroySwapChain(); - - InitializeSwapChain(); - InitializeSwapChainImageViews(); - InitializeSwapChainFramebuffers(); - } - - void DestroySwapChain() - { - - for (auto&& framebuffer : swapChainFramebuffers) - { - vkDestroyFramebuffer(device, framebuffer, nullptr); - } - for (auto&& swapChainImageView : swapChainImageViews) - { - vkDestroyImageView(device, swapChainImageView, nullptr); - } - vkDestroySwapchainKHR(device, swapChain, nullptr); - } - - void DestroyVulkan() - { - vkDeviceWaitIdle(device); - 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); - vkDestroyPipeline(device, graphicsPipeline, nullptr); - vkDestroyPipelineLayout(device, pipelineLayout, nullptr); - vkDestroyRenderPass(device, renderPass, nullptr); - DestroySwapChain(); - vkDestroyDevice(device, nullptr); - vkDestroySurfaceKHR(instance, surface, nullptr); - delete debugMessenger; - vkDestroyInstance(instance, nullptr); - } - - void DestroyWindow() - { - glfwDestroyWindow(window); - } - - std::vector GetRequiredDeviceExtensions() - { - return {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; - } - - static void FramebufferResizeCallback(GLFWwindow* window, int width, int height) - { - Application* application = static_cast(glfwGetWindowUserPointer(window)); - application->framebufferResized = true; - } }; int main() { glfwInit(); - try { Application application; while (application.Update()) { glfwPollEvents(); } - } - catch(std::exception& e) - { - std::cerr << "Exception: " << e.what() << std::endl; - return 1; } glfwTerminate();