Advertisement
Guest User

TestRenderer.hpp

a guest
Feb 22nd, 2018
513
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 57.54 KB | None | 0 0
  1. #pragma once
  2. //
  3.  
  4. #include <vector>
  5. #include <array>
  6. #include <vulkan/vulkan.h>
  7. #include <glm/glm.hpp>
  8. #include "glm/gtc/matrix_transform.hpp"
  9. #include "GLFW/glfw3.h"
  10. #include <iostream>
  11. #include <set>
  12. #include <string>
  13. #include <chrono>
  14. #include <string_view>
  15.  
  16.  
  17. // The validation layers that we might use.
  18. const std::vector<const char*> kValidationLayers = {
  19.     "VK_LAYER_LUNARG_standard_validation",
  20.  
  21.     // Dumps the params and return values to Vulkan API functions.
  22.     // Useful to see what we're passing to the API and what they're returning.
  23.     //"VK_LAYER_LUNARG_api_dump",
  24.  
  25.     "VK_LAYER_LUNARG_core_validation",
  26.     //"VK_LAYER_LUNARG_device_simulation",
  27.     //"VK_LAYER_LUNARG_monitor",
  28.     "VK_LAYER_LUNARG_object_tracker",
  29.     "VK_LAYER_LUNARG_parameter_validation",
  30.     //"VK_LAYER_LUNARG_screenshot",
  31.     //"VK_LAYER_LUNARG_vktrace",
  32.     //"VK_LAYER_GOOGLE_threading",
  33.     //"VK_LAYER_GOOGLE_unique_objects",
  34.     //"VK_LAYER_NV_optimus",
  35.     //"VK_LAYER_VALVE_steam_overlay",
  36.     //"VK_LAYER_RENDERDOC_Capture",
  37. };
  38.  
  39. const std::vector<const char*> kDeviceExtensions = {
  40.     VK_KHR_SWAPCHAIN_EXTENSION_NAME
  41. };
  42.  
  43. // Whether we want layer validation or not.
  44. #ifdef Ben_DEBUG
  45.     constexpr bool kEnableValidationLayers = true;
  46. #else
  47.     constexpr bool kEnableValidationLayers = false;
  48. #endif
  49.  
  50. constexpr int kWIDTH = 800;
  51. constexpr int kHEIGHT = 600;
  52.  
  53. using BinaryFileData = std::vector<char>;
  54. BinaryFileData ReadBinaryFile(std::string_view name);
  55.  
  56. VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback);
  57. void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator);
  58. void GlfwErrorCallback(int, const char*);
  59.  
  60. struct QueueFamilyIndices {
  61.     int32_t mGraphicsFamily = -1; // Default: not found
  62.     int32_t mPresentFamily = -1;
  63.  
  64.     bool IsComplete() {
  65.         return mGraphicsFamily >= 0 && mPresentFamily >= 0;
  66.     }
  67. };
  68.  
  69. struct SwapChainSupportDetails {
  70.     VkSurfaceCapabilitiesKHR mCapabilities;
  71.     std::vector<VkSurfaceFormatKHR> mFormats;
  72.     std::vector<VkPresentModeKHR> mPresentModes;
  73. };
  74.  
  75. struct Vertex {
  76.     glm::vec3 pos;
  77.     glm::vec2 coord;
  78.  
  79.     static VkVertexInputBindingDescription GetBindingDescription() {
  80.         VkVertexInputBindingDescription bindingDescription = {};
  81.         bindingDescription.binding = 0;
  82.         bindingDescription.stride = sizeof(Vertex);
  83.         bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
  84.  
  85.         return bindingDescription;
  86.     }
  87.  
  88.     static std::array<VkVertexInputAttributeDescription, 2> GetAttributeDescriptions() {
  89.         std::array<VkVertexInputAttributeDescription, 2> attributeDescriptions = {};
  90.  
  91.         attributeDescriptions[0].binding = 0;
  92.         attributeDescriptions[0].location = 0;
  93.         attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
  94.         attributeDescriptions[0].offset = offsetof(Vertex, pos);
  95.  
  96.         attributeDescriptions[1].binding = 0;
  97.         attributeDescriptions[1].location = 1;
  98.         attributeDescriptions[1].format = VK_FORMAT_R32G32_SFLOAT;
  99.         attributeDescriptions[1].offset = offsetof(Vertex, coord);
  100.  
  101.         return attributeDescriptions;
  102.     }
  103. };
  104.  
  105. struct UniformBufferObject {
  106.     glm::mat4 mModel;
  107.     glm::mat4 mView;
  108.     glm::mat4 mProj;
  109. };
  110.  
  111. const std::vector<Vertex> gVertices = {
  112.     {{-0.5f, -0.5f, 0.0f}, {0.0f, 0.0f}},   // Vert 0: Top left
  113.     {{0.5f, -0.5f, 0.0f},  {1.0f, 0.0f}},   // Vert 1: Top Right
  114.     {{0.5f, 0.5f, 0.0f},   {1.0f, 1.0f}},   // Vert 2: Bottom Right
  115.     {{-0.5f, 0.5f, 0.0f},  {0.0f, 1.0f}},   // Vert 3: Bottom Left
  116. };;
  117.  
  118. const std::vector<uint16_t> gIndices = {
  119.     0, 1, 2, 2, 3, 0
  120. };
  121.  
  122. struct TestRenderer {
  123.  
  124.     // Master init routine
  125.     void Start() {
  126.         InitWindow();
  127.         InitVulkan();
  128.     }
  129.  
  130.     // Main game loop.
  131.     bool Update() {
  132.         if (glfwWindowShouldClose(mWindow)) {
  133.             return false;
  134.         }
  135.  
  136.         glfwPollEvents();
  137.         UpdateUniformBuffer();
  138.         DrawFrame();
  139.         return true;
  140.     }
  141.  
  142.     // Cleanup for shutdown.
  143.     void Stop() {
  144.         // Wait to finish any async operation that might still be going one.
  145.         vkDeviceWaitIdle(mLogicalDevice);
  146.  
  147.         CleanupSwapChain();
  148.  
  149.         vkDestroySampler(mLogicalDevice, mTextureSampler, nullptr);
  150.         vkDestroyImageView(mLogicalDevice, mTextureImageView, nullptr);
  151.         vkDestroyImage(mLogicalDevice, mTextureImage, nullptr);
  152.         vkFreeMemory(mLogicalDevice, mTextureImageMemory, nullptr);
  153.         vkDestroyDescriptorSetLayout(mLogicalDevice, mDescriptorSetLayout, nullptr);
  154.         vkDestroyDescriptorPool(mLogicalDevice, mDescriptorPool, nullptr);
  155.         vkDestroyBuffer(mLogicalDevice, mIndexBuffer, nullptr);
  156.         vkFreeMemory(mLogicalDevice, mIndexBufferMemory, nullptr);
  157.         vkDestroyBuffer(mLogicalDevice, mVertexBuffer, nullptr);
  158.         vkFreeMemory(mLogicalDevice, mVertexBufferMemory, nullptr);
  159.         vkDestroyBuffer(mLogicalDevice, mUniformBuffer, nullptr);
  160.         vkFreeMemory(mLogicalDevice, mUniformBufferMemory, nullptr);
  161.         vkDestroySemaphore(mLogicalDevice, mRenderFinishedSemaphore, nullptr);
  162.         vkDestroySemaphore(mLogicalDevice, mImageAvailableSemaphore, nullptr);
  163.         vkDestroyCommandPool(mLogicalDevice, mCommandPool, nullptr);
  164.         vkDestroySurfaceKHR(mInstance, mSurface, nullptr);
  165.         vkDestroyDevice(mLogicalDevice, nullptr);
  166.         DestroyDebugReportCallbackEXT(mInstance, mDebugCallback, nullptr);
  167.         vkDestroyInstance(mInstance, nullptr);
  168.         glfwDestroyWindow(mWindow);
  169.         glfwTerminate();
  170.     }
  171.  
  172. private:
  173.     // Window initialization stuff.
  174.     void InitWindow() {
  175.         // Initialize GLFW library
  176.         glfwInit();
  177.  
  178.         glfwSetErrorCallback(GlfwErrorCallback);
  179.  
  180.         // Don't create an OpenGL context.
  181.         glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
  182.  
  183.         // Don't allow resizing of the window.
  184.         glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
  185.  
  186.         // Create the window handle.
  187.         mWindow = glfwCreateWindow(kWIDTH, kHEIGHT, "TestRenderer", nullptr, nullptr);
  188.  
  189.         glfwSetWindowUserPointer(mWindow, this);
  190.         glfwSetWindowSizeCallback(mWindow, TestRenderer::OnWindowResized);
  191.     }
  192.  
  193.     static void OnWindowResized(GLFWwindow* window, int width, int height) {
  194.         if (width == 0 || height == 0) return;
  195.  
  196.         TestRenderer* app = reinterpret_cast<TestRenderer*>(glfwGetWindowUserPointer(window));
  197.         app->RecreateSwapChain();
  198.     }
  199.  
  200.     // Show the Vulkan extensions that are supported by the GPU
  201.     void ShowSupportedExtensions() {
  202.         uint32_t extensionCount = 0;
  203.         vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
  204.         std::vector<VkExtensionProperties> extensions(extensionCount);
  205.         vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());
  206.  
  207.         std::cout << "Available Vulkan Extensions:" << std::endl;
  208.         for (const auto& extension : extensions) {
  209.             std::cout << "\t" << extension.extensionName << "\n";
  210.         }
  211.     }
  212.  
  213.     // Create an instance of the Vulkan library.
  214.     void CreateVulkanInstance() {
  215.         if (kEnableValidationLayers && CheckValidationLayerSupport() == false) {
  216.             throw std::runtime_error("One or more validation-layers are not available.");
  217.         }
  218.  
  219.         // Some info about our application and also the general Vulkan API we're using.
  220.         // §3.2. Instances
  221.         VkApplicationInfo appInfo = {};
  222.         appInfo.sType               = VK_STRUCTURE_TYPE_APPLICATION_INFO;
  223.         appInfo.pApplicationName    = "BattleEngine";
  224.         appInfo.applicationVersion  = VK_MAKE_VERSION(1, 0, 0);
  225.         appInfo.apiVersion          = VK_API_VERSION_1_0;
  226.  
  227.         // Information about our required Vulkan extensions.
  228.         auto extensions = GetRequiredExtensions();
  229.  
  230.         // The arg passed to the instance creation function.
  231.         // §3.2. Instances
  232.         VkInstanceCreateInfo createInfo = {};
  233.         createInfo.sType                    = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
  234.         createInfo.pApplicationInfo         = &appInfo;
  235.         createInfo.enabledExtensionCount    = static_cast<uint32_t>(extensions.size());
  236.         createInfo.ppEnabledExtensionNames  = extensions.data();
  237.  
  238.         // Validation-layers
  239.         if (kEnableValidationLayers) {
  240.             createInfo.enabledLayerCount = static_cast<uint32_t>(kValidationLayers.size());
  241.             createInfo.ppEnabledLayerNames = kValidationLayers.data();
  242.         }
  243.         else {
  244.             createInfo.enabledLayerCount = 0;
  245.         }
  246.  
  247.         // Actually create the Vulkan instance.
  248.         // §3.2. Instances
  249.         if (vkCreateInstance(&createInfo, nullptr, &mInstance) != VK_SUCCESS) {
  250.             throw std::runtime_error("Could not create Vulkan Instance.");
  251.         }
  252.     }
  253.  
  254.     // Choose the pixed depth and format.
  255.     VkSurfaceFormatKHR
  256.     ChooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) {
  257.         if (availableFormats.size() == 1 && availableFormats[0].format == VK_FORMAT_UNDEFINED) {
  258.             return { VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
  259.         }
  260.  
  261.         for (const auto& availableFormat : availableFormats) {
  262.             if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
  263.                 return availableFormat;
  264.             }
  265.         }
  266.  
  267.         // Just choose the first format, since we didn't find a preffered one.
  268.         return availableFormats[0];
  269.     }
  270.  
  271.     // Choose the mode that we're going to present the swap-chain (double-buffer/triple-buffer)
  272.     VkPresentModeKHR
  273.     ChooseSwapPresentMode(const std::vector<VkPresentModeKHR> availablePresentModes) {
  274.         VkPresentModeKHR bestMode = VK_PRESENT_MODE_FIFO_KHR;
  275.  
  276.         for (const auto& availablePresentMode : availablePresentModes) {
  277.             if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
  278.                 return availablePresentMode;
  279.             }
  280.             else if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) {
  281.                 bestMode = availablePresentMode;
  282.             }
  283.         }
  284.  
  285.         return bestMode;
  286.     }
  287.  
  288.     // Choose the resolution of the swap-chain images.
  289.     VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) {
  290.         if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
  291.             return capabilities.currentExtent;
  292.         }
  293.        
  294.         int width, height;
  295.         glfwGetWindowSize(mWindow, &width, &height);
  296.         VkExtent2D actualExtent = {(uint32_t)width, (uint32_t)height};
  297.         actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width));
  298.         actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height));
  299.  
  300.         return actualExtent;       
  301.     }
  302.  
  303.     // Check whether the validation-layers that we want enabled are suppored.
  304.     bool CheckValidationLayerSupport() {
  305.         uint32_t layerCount;
  306.         vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
  307.  
  308.         std::vector<VkLayerProperties> availableLayers(layerCount);
  309.         vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
  310.  
  311.         std::cout << "Available validation-layers: \n";
  312.         for (const auto& layerProperties : availableLayers) {
  313.             std::cout << "\t" << layerProperties.layerName << "\n";
  314.         }
  315.  
  316.         for (auto layerName : kValidationLayers) {
  317.             bool layerFound = false;
  318.             for (const auto& layerProperties : availableLayers) {
  319.                 if (strcmp(layerName, layerProperties.layerName) == 0) {
  320.                     layerFound = true;
  321.                     break;
  322.                 }
  323.             }
  324.  
  325.             if (!layerFound) {
  326.                 std::cerr << "Validation layer not available: " << layerName << "\n";
  327.                 return false;
  328.             }
  329.         }
  330.  
  331.         return true;
  332.     }
  333.  
  334.     // Get a list of Vulkan extensions that we required for the app.
  335.     auto GetRequiredExtensions() -> std::vector<const char*> {
  336.         std::vector<const char*> extensions;
  337.  
  338.         unsigned int glfwExtensionCount = 0;
  339.         const char** glfwExtensions;
  340.         glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
  341.  
  342.         for (unsigned int i = 0; i < glfwExtensionCount; i++) {
  343.             extensions.push_back(glfwExtensions[i]);
  344.         }
  345.  
  346.         if (kEnableValidationLayers) {
  347.             extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
  348.         }
  349.  
  350.         return extensions;
  351.     }
  352.  
  353.     // The debug function that will be called by the validation layers if problems are encountered.
  354.     static VKAPI_ATTR VkBool32 VKAPI_CALL
  355.     LayerDebugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t obj, size_t location, int32_t code, const char* layerPrefix, const char* msg, void* userData) {
  356.         std::cerr << "Validation layer: " << msg << std::endl;
  357.         return VK_FALSE;
  358.     }
  359.  
  360.     // Setup the function that we'll use to get messages from validation layers.
  361.     void SetupDebugCallback() {
  362.         if (kEnableValidationLayers == false) {
  363.             return;
  364.         }
  365.  
  366.         VkDebugReportCallbackCreateInfoEXT createInfo = {};
  367.         createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT;
  368.         createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT;
  369.         createInfo.pfnCallback = LayerDebugCallback;
  370.  
  371.         if (CreateDebugReportCallbackEXT(mInstance, &createInfo, nullptr, &mDebugCallback) != VK_SUCCESS) {
  372.             throw std::runtime_error("Failed to set up Vulkan debug callback.");
  373.         }
  374.     }
  375.  
  376.     // Select a graphics card to use.
  377.     void PickPhysicalDevice() {
  378.         uint32_t deviceCount = 0;
  379.  
  380.         // Get the count of how many graphic cards we have.
  381.         vkEnumeratePhysicalDevices(mInstance, &deviceCount, nullptr);
  382.  
  383.         if (deviceCount == 0) {
  384.             throw std::runtime_error("Failed to find a GPU with Vulkan support.");
  385.         }
  386.  
  387.         // Grab handles to the physical cards found that support Vulkan.
  388.         std::vector<VkPhysicalDevice> devices(deviceCount);
  389.         vkEnumeratePhysicalDevices(mInstance, &deviceCount, devices.data());
  390.  
  391.         // Choose the first physical device that suits our needs.
  392.         // Note: It would be best to give each device a score and
  393.         //       pick the best, based on that score.
  394.         for (const auto& device : devices) {
  395.             if (IsDeviceSuitable(device)) {
  396.                 mPhysicalDevice = device;
  397.                 break;
  398.             }
  399.         }
  400.  
  401.         if (mPhysicalDevice == VK_NULL_HANDLE) {
  402.             throw std::runtime_error("Failed to find a suitable GPU.");
  403.         }
  404.     }
  405.  
  406.     // Check that a GPU has the capabilities that we need for our app.
  407.     bool IsDeviceSuitable(VkPhysicalDevice device) {
  408.         QueueFamilyIndices indices = FindQueueFamilies(device);
  409.         bool extensionsSupported = CheckDeviceExtensionSupport(device);
  410.         bool swapChainAdequate = false;
  411.         if (extensionsSupported) {
  412.             SwapChainSupportDetails swapChainSupport = QuerySwapChainSupport(device);
  413.             swapChainAdequate = !swapChainSupport.mFormats.empty() && !swapChainSupport.mPresentModes.empty();
  414.         }
  415.  
  416.         VkPhysicalDeviceFeatures supportedFeatures;
  417.         vkGetPhysicalDeviceFeatures(device, &supportedFeatures);
  418.  
  419.         return indices.IsComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
  420.     };
  421.  
  422.     // Check that the GPU supports some extensions that we require.
  423.     bool CheckDeviceExtensionSupport(VkPhysicalDevice device) {
  424.         uint32_t extensionCount;
  425.         vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
  426.  
  427.         std::vector<VkExtensionProperties> availableExtensions(extensionCount);
  428.         vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());
  429.         std::set<std::string> requiredExtensions(kDeviceExtensions.begin(), kDeviceExtensions.end());
  430.  
  431.         for (const auto& extension : availableExtensions) {
  432.             requiredExtensions.erase(extension.extensionName);
  433.         }
  434.  
  435.         return requiredExtensions.empty();
  436.     }
  437.  
  438.     // Find out the types of queue families supported by the graphics card.
  439.     auto FindQueueFamilies(VkPhysicalDevice device) -> QueueFamilyIndices {
  440.         QueueFamilyIndices indices;
  441.  
  442.         uint32_t queueFamilyCount = 0;
  443.  
  444.         // §4.1. Physical Devices
  445.         // Get the amount of queue families supported by the device.
  446.         vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
  447.  
  448.  
  449.         std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
  450.         vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
  451.  
  452.         uint32_t i = 0;
  453.         for (const auto& queueFamily : queueFamilies) {
  454.             // See if this Queue famility support creating graphics.
  455.             if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
  456.                 indices.mGraphicsFamily = i;
  457.             }
  458.            
  459.             // Check that the device can present surface on a window.
  460.             // Some devices are compute only.
  461.             VkBool32 presentSupport = false;
  462.             vkGetPhysicalDeviceSurfaceSupportKHR(device, i, mSurface, &presentSupport);
  463.             if (queueFamily.queueCount > 0 && presentSupport) {
  464.                 indices.mPresentFamily = i;
  465.             }
  466.  
  467.             if (indices.IsComplete()) {
  468.                 break;
  469.             }
  470.  
  471.             ++i;
  472.         }
  473.  
  474.         return indices;
  475.     }
  476.  
  477.     // Create the logical device, which represents the physical GPU.
  478.     void CreateLogicalDevice() {       
  479.         // Find out the queue families that this GPU supports.
  480.         QueueFamilyIndices indices = FindQueueFamilies(mPhysicalDevice);
  481.  
  482.         float queuePriority = 1.0f;
  483.  
  484.         // Fill in one 'VkDeviceQueueCreateInfo' for each queue family that we want to use.
  485.         // This allows us to specify how many queues within each family to create.
  486.         std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
  487.         std::set<int32_t> uniqueQueueFamilies = { indices.mGraphicsFamily, indices.mPresentFamily };
  488.         for (int queueFamily : uniqueQueueFamilies) {
  489.             VkDeviceQueueCreateInfo queueCreateInfo = {};
  490.             queueCreateInfo.sType                   = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
  491.             queueCreateInfo.queueFamilyIndex        = queueFamily;
  492.             queueCreateInfo.queueCount              = 1;
  493.             queueCreateInfo.pQueuePriorities        = &queuePriority;
  494.             queueCreateInfos.push_back(queueCreateInfo);
  495.         }
  496.  
  497.         // Specify a set of device features that we'll be using.
  498.         // This includes the queue families that we want to use.
  499.         VkPhysicalDeviceFeatures deviceFeatures = {};
  500.         deviceFeatures.samplerAnisotropy        = VK_TRUE;
  501.         VkDeviceCreateInfo createInfo           = {};
  502.         createInfo.sType                        = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
  503.         createInfo.pEnabledFeatures             = &deviceFeatures;
  504.         createInfo.queueCreateInfoCount         = static_cast<uint32_t>(queueCreateInfos.size());
  505.         createInfo.pQueueCreateInfos            = queueCreateInfos.data();     
  506.         createInfo.enabledExtensionCount        = static_cast<uint32_t>(kDeviceExtensions.size());
  507.         createInfo.ppEnabledExtensionNames      = kDeviceExtensions.data();
  508.  
  509.         if (kEnableValidationLayers) {
  510.             createInfo.enabledLayerCount = static_cast<uint32_t>(kValidationLayers.size());
  511.             createInfo.ppEnabledLayerNames = kValidationLayers.data();
  512.         }
  513.         else {
  514.             createInfo.enabledLayerCount = 0;
  515.         }
  516.        
  517.         // §4.2.1. Device Creation
  518.         if (vkCreateDevice(mPhysicalDevice, &createInfo, nullptr, &mLogicalDevice) != VK_SUCCESS) {
  519.             throw std::runtime_error("Failed to create logical device!");
  520.         }
  521.        
  522.         // Retrieve handles to the queues (not the queue families).
  523.         constexpr uint32_t queueIndex = 0;
  524.         vkGetDeviceQueue(mLogicalDevice, indices.mGraphicsFamily, queueIndex, &mGraphicsQueue);
  525.         vkGetDeviceQueue(mLogicalDevice, indices.mPresentFamily, queueIndex, &mPresentQueue);
  526.     }
  527.  
  528.     // Query the details of the GPU's swap-chain support.
  529.     auto QuerySwapChainSupport(VkPhysicalDevice device) -> SwapChainSupportDetails {
  530.         SwapChainSupportDetails details;
  531.         vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, mSurface, &details.mCapabilities);
  532.  
  533.         uint32_t formatCount;
  534.         vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, nullptr);
  535.         if (formatCount != 0) {
  536.             details.mFormats.resize(formatCount);
  537.             vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, details.mFormats.data());
  538.         }
  539.  
  540.         uint32_t presentModeCount;
  541.         vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &presentModeCount, nullptr);
  542.         if (presentModeCount != 0) {
  543.             details.mPresentModes.resize(presentModeCount);
  544.             vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &presentModeCount, details.mPresentModes.data());
  545.         }
  546.  
  547.         return details;
  548.     }
  549.  
  550.     // Create the Vulkan Surface, based on the Windowing handle, where we'll write the graphics.
  551.     void CreateSurface() {
  552.         if (glfwCreateWindowSurface(mInstance, mWindow, nullptr, &mSurface) != VK_SUCCESS) {
  553.             throw std::runtime_error("failed to create window surface!");
  554.         }
  555.     }
  556.  
  557.     // Create the image swap chain.
  558.     void CreateSwapChain() {
  559.         SwapChainSupportDetails swapChainSupport = QuerySwapChainSupport(mPhysicalDevice);
  560.         VkSurfaceFormatKHR surfaceFormat = ChooseSwapSurfaceFormat(swapChainSupport.mFormats);
  561.         VkPresentModeKHR presentMode = ChooseSwapPresentMode(swapChainSupport.mPresentModes);
  562.         VkExtent2D extent = ChooseSwapExtent(swapChainSupport.mCapabilities);
  563.  
  564.         // The amount of images supported by the swap chain.
  565.         // We want, at least, 3 for triple buffering.
  566.         uint32_t imageCount = swapChainSupport.mCapabilities.minImageCount + 1;
  567.         if (swapChainSupport.mCapabilities.maxImageCount > 0 && imageCount > swapChainSupport.mCapabilities.maxImageCount) {
  568.             imageCount = swapChainSupport.mCapabilities.maxImageCount;
  569.         }
  570.  
  571.         VkSwapchainCreateInfoKHR createInfo = {};
  572.         createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
  573.         createInfo.surface = mSurface;
  574.         createInfo.minImageCount = imageCount;
  575.         createInfo.imageFormat = surfaceFormat.format;
  576.         createInfo.imageColorSpace = surfaceFormat.colorSpace;
  577.         createInfo.imageExtent = extent;
  578.         createInfo.imageArrayLayers = 1;
  579.         createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
  580.  
  581.         QueueFamilyIndices indices = FindQueueFamilies(mPhysicalDevice);
  582.         uint32_t queueFamilyIndices[] = { (uint32_t)indices.mGraphicsFamily, (uint32_t)indices.mPresentFamily };
  583.  
  584.         // NOTE: Performance:
  585.         // VK_SHARING_MODE_EXCLUSIVE is faster, but requires to careful handling.
  586.         // In the future, if needed for speed, maybe always use VK_SHARING_MODE_EXCLUSIVE.
  587.         // https://vulkan-tutorial.com/Drawing_a_triangle/Presentation/Swap_chain
  588.         if (indices.mGraphicsFamily != indices.mPresentFamily) {
  589.             createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
  590.             createInfo.queueFamilyIndexCount = 2;
  591.             createInfo.pQueueFamilyIndices = queueFamilyIndices;
  592.         }
  593.         else {
  594.             createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
  595.         }
  596.  
  597.         createInfo.preTransform = swapChainSupport.mCapabilities.currentTransform;
  598.         createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
  599.         createInfo.presentMode = presentMode;
  600.         createInfo.clipped = VK_TRUE;
  601.         createInfo.oldSwapchain = VK_NULL_HANDLE;
  602.  
  603.         if (vkCreateSwapchainKHR(mLogicalDevice, &createInfo, nullptr, &mSwapChain) != VK_SUCCESS) {
  604.             throw std::runtime_error("Failed to create swap chain.");
  605.         }
  606.  
  607.         vkGetSwapchainImagesKHR(mLogicalDevice, mSwapChain, &imageCount, nullptr);
  608.         mSwapChainImages.resize(imageCount);
  609.         vkGetSwapchainImagesKHR(mLogicalDevice, mSwapChain, &imageCount, mSwapChainImages.data());
  610.  
  611.         mSwapChainImageFormat = surfaceFormat.format;
  612.         mSwapChainExtent = extent;
  613.     }
  614.  
  615.     // Create the ImageViews that give us access to image buffers.
  616.     void CreateImageViews() {
  617.         mSwapChainImageViews.resize(mSwapChainImages.size());
  618.  
  619.         for (uint32_t i = 0; i < mSwapChainImages.size(); i++) {
  620.             mSwapChainImageViews[i] = CreateImageView(mSwapChainImages[i], mSwapChainImageFormat);
  621.         }
  622.     }
  623.  
  624.     VkShaderModule CreateShaderModule(const std::vector<char>& code) {     
  625.         VkShaderModuleCreateInfo createInfo = {};
  626.         createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
  627.         createInfo.codeSize = code.size();
  628.         createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());
  629.  
  630.         VkShaderModule shaderModule;
  631.         if (vkCreateShaderModule(mLogicalDevice, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
  632.             throw std::runtime_error("failed to create shader module!");
  633.         }
  634.  
  635.         return shaderModule;
  636.     }
  637.  
  638.     void CreateGraphicsPipeline() {
  639.         auto vertShaderCode = ReadBinaryFile("Shaders/Vert.spv");
  640.         auto fragShaderCode = ReadBinaryFile("Shaders/Frag.spv");
  641.  
  642.         VkShaderModule vertShaderModule = CreateShaderModule(vertShaderCode);
  643.         VkShaderModule fragShaderModule = CreateShaderModule(fragShaderCode);
  644.  
  645.         VkPipelineShaderStageCreateInfo vertShaderStageInfo = {};
  646.         vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
  647.         vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
  648.         vertShaderStageInfo.module = vertShaderModule;
  649.         vertShaderStageInfo.pName = "main";
  650.  
  651.         VkPipelineShaderStageCreateInfo fragShaderStageInfo = {};
  652.         fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
  653.         fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
  654.         fragShaderStageInfo.module = fragShaderModule;
  655.         fragShaderStageInfo.pName = "main";
  656.  
  657.         auto bindingDescription = Vertex::GetBindingDescription();
  658.         auto attributeDescriptions = Vertex::GetAttributeDescriptions();
  659.  
  660.         VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo };
  661.         VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
  662.         vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
  663.         vertexInputInfo.vertexBindingDescriptionCount = 1;
  664.         vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());;
  665.         vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
  666.         vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
  667.    
  668.         VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
  669.         inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
  670.         inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
  671.         inputAssembly.primitiveRestartEnable = VK_FALSE;
  672.  
  673.         VkViewport viewport = {};
  674.         viewport.x = 0.0f;
  675.         viewport.y = 0.0f;
  676.         viewport.width = (float)mSwapChainExtent.width;
  677.         viewport.height = (float)mSwapChainExtent.height;
  678.         viewport.minDepth = 0.0f;
  679.         viewport.maxDepth = 1.0f;
  680.  
  681.         VkRect2D scissor = {};
  682.         scissor.offset = { 0, 0 };
  683.         scissor.extent = mSwapChainExtent;
  684.  
  685.         VkPipelineViewportStateCreateInfo viewportState = {};
  686.         viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
  687.         viewportState.viewportCount = 1;
  688.         viewportState.pViewports = &viewport;
  689.         viewportState.scissorCount = 1;
  690.         viewportState.pScissors = &scissor;
  691.  
  692.         VkPipelineRasterizationStateCreateInfo rasterizer = {};
  693.         rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
  694.         rasterizer.depthClampEnable = VK_FALSE;
  695.         rasterizer.rasterizerDiscardEnable = VK_FALSE;
  696.         rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
  697.         rasterizer.lineWidth = 1.0f;
  698.         rasterizer.cullMode = VK_CULL_MODE_NONE;
  699.         rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
  700.         rasterizer.depthBiasEnable = VK_FALSE;
  701.  
  702.         VkPipelineMultisampleStateCreateInfo multisampling = {};
  703.         multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
  704.         multisampling.sampleShadingEnable = VK_FALSE;
  705.         multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
  706.  
  707.         VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
  708.         colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
  709.         colorBlendAttachment.blendEnable = VK_FALSE;
  710.  
  711.         VkPipelineColorBlendStateCreateInfo colorBlending = {};
  712.         colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
  713.         colorBlending.logicOpEnable = VK_FALSE;
  714.         colorBlending.logicOp = VK_LOGIC_OP_COPY;
  715.         colorBlending.attachmentCount = 1;
  716.         colorBlending.pAttachments = &colorBlendAttachment;
  717.         colorBlending.blendConstants[0] = 0.0f;
  718.         colorBlending.blendConstants[1] = 0.0f;
  719.         colorBlending.blendConstants[2] = 0.0f;
  720.         colorBlending.blendConstants[3] = 0.0f;
  721.  
  722.         VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
  723.         pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
  724.         pipelineLayoutInfo.setLayoutCount = 1;     
  725.         pipelineLayoutInfo.pSetLayouts = &mDescriptorSetLayout;
  726.         pipelineLayoutInfo.pushConstantRangeCount = 0;
  727.         if (vkCreatePipelineLayout(mLogicalDevice, &pipelineLayoutInfo, nullptr, &mPipelineLayout) != VK_SUCCESS) {
  728.             throw std::runtime_error("failed to create pipeline layout!");
  729.         }
  730.  
  731.         VkGraphicsPipelineCreateInfo pipelineInfo = {};
  732.         pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
  733.         pipelineInfo.stageCount = 2;
  734.         pipelineInfo.pStages = shaderStages;
  735.         pipelineInfo.pVertexInputState = &vertexInputInfo;
  736.         pipelineInfo.pInputAssemblyState = &inputAssembly;
  737.         pipelineInfo.pViewportState = &viewportState;
  738.         pipelineInfo.pRasterizationState = &rasterizer;
  739.         pipelineInfo.pMultisampleState = &multisampling;
  740.         pipelineInfo.pColorBlendState = &colorBlending;
  741.         pipelineInfo.layout = mPipelineLayout;
  742.         pipelineInfo.renderPass = mRenderPass;
  743.         pipelineInfo.subpass = 0;
  744.         pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
  745.  
  746.         if (vkCreateGraphicsPipelines(mLogicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &mGraphicsPipeline) != VK_SUCCESS) {
  747.             throw std::runtime_error("failed to create graphics pipeline!");
  748.         }
  749.  
  750.         vkDestroyShaderModule(mLogicalDevice, fragShaderModule, nullptr);
  751.         vkDestroyShaderModule(mLogicalDevice, vertShaderModule, nullptr);
  752.     }
  753.  
  754.     void CreateRenderPass() {
  755.         // An attachment description describes the properties of an attachment, including its format,
  756.         // sample count, and how its contents are treated at the beginning and end of each render pass instance.
  757.         VkAttachmentDescription colorAttachment = {};
  758.         colorAttachment.format          = mSwapChainImageFormat;
  759.         colorAttachment.samples         = VK_SAMPLE_COUNT_1_BIT;
  760.         colorAttachment.loadOp          = VK_ATTACHMENT_LOAD_OP_CLEAR;
  761.         colorAttachment.storeOp         = VK_ATTACHMENT_STORE_OP_STORE;
  762.         colorAttachment.stencilLoadOp   = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
  763.         colorAttachment.stencilStoreOp  = VK_ATTACHMENT_STORE_OP_DONT_CARE;
  764.         colorAttachment.initialLayout   = VK_IMAGE_LAYOUT_UNDEFINED;
  765.         colorAttachment.finalLayout     = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
  766.  
  767.         VkAttachmentReference colorAttachmentRef = {};
  768.         colorAttachmentRef.attachment   = 0;
  769.         colorAttachmentRef.layout       = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
  770.  
  771.         // A subpass description describes the subset of attachments that are involved in the execution of a subpass.
  772.         // A subpass uses an attachment if the attachment is a color, depth/stencil, resolve, or input attachment for
  773.         // that subpass (as determined by the pColorAttachments, pDepthStencilAttachment, pResolveAttachments, and
  774.         // pInputAttachments members of VkSubpassDescription, respectively).
  775.         VkSubpassDescription subpass = {};
  776.         subpass.pipelineBindPoint       = VK_PIPELINE_BIND_POINT_GRAPHICS;
  777.         subpass.colorAttachmentCount    = 1;
  778.         subpass.pColorAttachments       = &colorAttachmentRef;     
  779.  
  780.         // Subpass dependencies describe execution and memory dependencies between subpasses.
  781.         VkSubpassDependency dependency = {};
  782.         dependency.srcSubpass       = VK_SUBPASS_EXTERNAL;
  783.         dependency.dstSubpass       = 0;
  784.         dependency.srcStageMask     = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
  785.         dependency.srcAccessMask    = 0;
  786.         dependency.dstStageMask     = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
  787.         dependency.dstAccessMask    = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
  788.  
  789.         VkRenderPassCreateInfo renderPassInfo = {};
  790.         renderPassInfo.sType            = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
  791.         renderPassInfo.attachmentCount  = 1;
  792.         renderPassInfo.pAttachments     = &colorAttachment;
  793.         renderPassInfo.subpassCount     = 1;
  794.         renderPassInfo.pSubpasses       = &subpass;
  795.         renderPassInfo.dependencyCount  = 1;
  796.         renderPassInfo.pDependencies    = &dependency;
  797.  
  798.         if (vkCreateRenderPass(mLogicalDevice, &renderPassInfo, nullptr, &mRenderPass) != VK_SUCCESS) {
  799.             throw std::runtime_error("Failed to create render pass.");
  800.         }
  801.     }
  802.  
  803.     void CreateFramebuffers() {
  804.         mSwapChainFramebuffers.resize(mSwapChainImageViews.size());
  805.  
  806.         for (size_t i = 0; i < mSwapChainImageViews.size(); i++) {
  807.             VkImageView attachments[] = {
  808.                 mSwapChainImageViews[i]
  809.             };
  810.  
  811.             VkFramebufferCreateInfo framebufferInfo = {};
  812.             framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
  813.             framebufferInfo.renderPass = mRenderPass;
  814.             framebufferInfo.attachmentCount = 1;
  815.             framebufferInfo.pAttachments = attachments;
  816.             framebufferInfo.width = mSwapChainExtent.width;
  817.             framebufferInfo.height = mSwapChainExtent.height;
  818.             framebufferInfo.layers = 1;
  819.  
  820.             if (vkCreateFramebuffer(mLogicalDevice, &framebufferInfo, nullptr, &mSwapChainFramebuffers[i]) != VK_SUCCESS) {
  821.                 throw std::runtime_error("failed to create framebuffer!");
  822.             }
  823.         }
  824.     }
  825.  
  826.     void CreateCommandPool() {
  827.         QueueFamilyIndices queueFamilyIndices = FindQueueFamilies(mPhysicalDevice);
  828.  
  829.         VkCommandPoolCreateInfo poolInfo = {};
  830.         poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
  831.         poolInfo.queueFamilyIndex = queueFamilyIndices.mGraphicsFamily;
  832.  
  833.         if (vkCreateCommandPool(mLogicalDevice, &poolInfo, nullptr, &mCommandPool) != VK_SUCCESS) {
  834.             throw std::runtime_error("failed to create command pool!");
  835.         }
  836.     }
  837.  
  838.     void CreateCommandBuffers() {
  839.         mCommandBuffers.resize(mSwapChainFramebuffers.size());
  840.  
  841.         VkCommandBufferAllocateInfo allocInfo = {};
  842.         allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
  843.         allocInfo.commandPool = mCommandPool;
  844.         allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
  845.         allocInfo.commandBufferCount = (uint32_t)mCommandBuffers.size();
  846.  
  847.         if (vkAllocateCommandBuffers(mLogicalDevice, &allocInfo, mCommandBuffers.data()) != VK_SUCCESS) {
  848.             throw std::runtime_error("failed to allocate command buffers!");
  849.         }
  850.  
  851.         for (size_t i = 0; i < mCommandBuffers.size(); i++) {
  852.             VkCommandBufferBeginInfo beginInfo = {};
  853.             beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
  854.             beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
  855.  
  856.             vkBeginCommandBuffer(mCommandBuffers[i], &beginInfo);
  857.  
  858.             VkRenderPassBeginInfo renderPassInfo = {};
  859.             renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
  860.             renderPassInfo.renderPass = mRenderPass;
  861.             renderPassInfo.framebuffer = mSwapChainFramebuffers[i];
  862.             renderPassInfo.renderArea.offset = { 0, 0 };
  863.             renderPassInfo.renderArea.extent = mSwapChainExtent;
  864.  
  865.             VkClearValue clearColor = { 0.2f, 0.2f, 0.2f, 1.0f };
  866.             renderPassInfo.clearValueCount = 1;
  867.             renderPassInfo.pClearValues = &clearColor;
  868.  
  869.             vkCmdBeginRenderPass(mCommandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
  870.             vkCmdBindPipeline(mCommandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, mGraphicsPipeline);
  871.             VkBuffer vertexBuffers[] = { mVertexBuffer };
  872.             VkDeviceSize offsets[] = { 0 };
  873.             vkCmdBindVertexBuffers(mCommandBuffers[i], 0, 1, vertexBuffers, offsets);
  874.     vkCmdBindDescriptorSets(mCommandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, mPipelineLayout, 0, 1, &mDescriptorSet, 0, nullptr);
  875.             vkCmdBindIndexBuffer(mCommandBuffers[i], mIndexBuffer, 0, VK_INDEX_TYPE_UINT16);
  876.             vkCmdDrawIndexed(mCommandBuffers[i], static_cast<uint32_t>(gIndices.size()), 1, 0, 0, 0);
  877.             vkCmdEndRenderPass(mCommandBuffers[i]);
  878.  
  879.             if (vkEndCommandBuffer(mCommandBuffers[i]) != VK_SUCCESS) {
  880.                 throw std::runtime_error("Failed to record command buffer.");
  881.             }
  882.         }
  883.     }
  884.  
  885.     void CreateSemaphores() {
  886.         VkSemaphoreCreateInfo semaphoreInfo = {};
  887.         semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
  888.  
  889.         auto res1 = vkCreateSemaphore(mLogicalDevice, &semaphoreInfo, nullptr, &mImageAvailableSemaphore);
  890.         auto res2 = vkCreateSemaphore(mLogicalDevice, &semaphoreInfo, nullptr, &mRenderFinishedSemaphore);
  891.         if (res1 != VK_SUCCESS || res2 != VK_SUCCESS) {
  892.             throw std::runtime_error("Failed to create semaphores.");
  893.         }
  894.     }
  895.  
  896.     void CleanupSwapChain() {
  897.         for (size_t i = 0; i < mSwapChainFramebuffers.size(); i++) {
  898.             vkDestroyFramebuffer(mLogicalDevice, mSwapChainFramebuffers[i], nullptr);
  899.         }
  900.  
  901.         vkFreeCommandBuffers(mLogicalDevice, mCommandPool, static_cast<uint32_t>(mCommandBuffers.size()), mCommandBuffers.data());
  902.         vkDestroyPipeline(mLogicalDevice, mGraphicsPipeline, nullptr);
  903.         vkDestroyPipelineLayout(mLogicalDevice, mPipelineLayout, nullptr);
  904.         vkDestroyRenderPass(mLogicalDevice, mRenderPass, nullptr);
  905.  
  906.         for (size_t i = 0; i < mSwapChainImageViews.size(); i++) {
  907.             vkDestroyImageView(mLogicalDevice, mSwapChainImageViews[i], nullptr);
  908.         }
  909.  
  910.         vkDestroySwapchainKHR(mLogicalDevice, mSwapChain, nullptr);
  911.     }
  912.  
  913.     void RecreateSwapChain() {
  914.         vkDeviceWaitIdle(mLogicalDevice);
  915.  
  916.         CleanupSwapChain();
  917.  
  918.         CreateSwapChain();
  919.         CreateImageViews();
  920.         CreateRenderPass();
  921.         CreateGraphicsPipeline();
  922.         CreateFramebuffers();
  923.         CreateCommandBuffers();
  924.     }
  925.  
  926.  
  927.     void DrawFrame() {
  928.         uint32_t imageIndex;
  929.         constexpr uint64_t noTimeout = std::numeric_limits<uint64_t>::max();
  930.         VkResult result = vkAcquireNextImageKHR(mLogicalDevice, mSwapChain, noTimeout, mImageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex);
  931.  
  932.         if (result == VK_ERROR_OUT_OF_DATE_KHR) {
  933.             RecreateSwapChain();
  934.             return;
  935.         }
  936.         else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
  937.             throw std::runtime_error("Failed to acquire swap chain image.");
  938.         }
  939.  
  940.         VkSemaphore waitSemaphores[] = { mImageAvailableSemaphore };
  941.         VkSemaphore signalSemaphores[] = { mRenderFinishedSemaphore };
  942.         VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
  943.         VkSubmitInfo submitInfo = {};
  944.         submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
  945.         submitInfo.waitSemaphoreCount = 1;
  946.         submitInfo.pWaitSemaphores = waitSemaphores;
  947.         submitInfo.pSignalSemaphores = signalSemaphores;
  948.         submitInfo.pWaitDstStageMask = waitStages;
  949.         submitInfo.commandBufferCount = 1;
  950.         submitInfo.pCommandBuffers = &mCommandBuffers[imageIndex];
  951.         submitInfo.signalSemaphoreCount = 1;
  952.    
  953.         if (vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE) != VK_SUCCESS) {
  954.             throw std::runtime_error("failed to submit draw command buffer!");
  955.         }
  956.  
  957.         VkPresentInfoKHR presentInfo = {};
  958.         presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
  959.         presentInfo.waitSemaphoreCount = 1;
  960.         presentInfo.pWaitSemaphores = signalSemaphores;
  961.  
  962.         VkSwapchainKHR swapChains[] = { mSwapChain };
  963.         presentInfo.swapchainCount = 1;
  964.         presentInfo.pSwapchains = swapChains;
  965.         presentInfo.pImageIndices = &imageIndex;
  966.  
  967.         result = vkQueuePresentKHR(mPresentQueue, &presentInfo);
  968.  
  969.         if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
  970.             RecreateSwapChain();
  971.         }
  972.         else if (result != VK_SUCCESS) {
  973.             throw std::runtime_error("Failed to present swap chain image.");
  974.         }
  975.  
  976.         vkQueueWaitIdle(mPresentQueue);
  977.     }
  978.  
  979.     uint32_t FindMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) {
  980.         VkPhysicalDeviceMemoryProperties memProperties;
  981.         vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &memProperties);
  982.  
  983.         for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
  984.             if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
  985.                 return i;
  986.             }
  987.         }
  988.  
  989.         throw std::runtime_error("Failed to find suitable memory type.");
  990.     }
  991.  
  992.     void CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) {
  993.         VkBufferCreateInfo bufferInfo = {};
  994.         bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
  995.         bufferInfo.size = size;
  996.         bufferInfo.usage = usage;
  997.         bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
  998.  
  999.         if (vkCreateBuffer(mLogicalDevice, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) {
  1000.             throw std::runtime_error("Failed to create vertex buffer.");
  1001.         }
  1002.  
  1003.         VkMemoryRequirements memRequirements;
  1004.         vkGetBufferMemoryRequirements(mLogicalDevice, buffer, &memRequirements);
  1005.  
  1006.         VkMemoryAllocateInfo allocInfo = {};
  1007.         allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
  1008.         allocInfo.allocationSize = memRequirements.size;
  1009.         allocInfo.memoryTypeIndex = FindMemoryType(memRequirements.memoryTypeBits, properties);
  1010.  
  1011.         // Per:
  1012.         // https://vulkan-tutorial.com/Vertex_buffers/Staging_buffer
  1013.         // We're not supposed to actually call vkAllocateMemory for every individual buffer.
  1014.         // The maximum number of simultaneous memory allocations is limited by the maxMemoryAllocationCount physical
  1015.         // device limit, which may be as low as 4096 even on high end hardware like an NVIDIA GTX 1080.
  1016.         // The right way to allocate memory for a large number of objects at the same time is to create a
  1017.         // custom allocator that splits up a single allocation among many different objects by using the
  1018.         // offset parameters that we've seen in many functions.
  1019.         // We can either implement such an allocator yourself, or use the VulkanMemoryAllocator library provided
  1020.         // by the GPUOpen initiative.
  1021.         if (vkAllocateMemory(mLogicalDevice, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) {
  1022.             throw std::runtime_error("Failed to allocate vertex buffer memory.");
  1023.         }
  1024.  
  1025.         vkBindBufferMemory(mLogicalDevice, buffer, bufferMemory, 0);
  1026.     }
  1027.  
  1028.     // Create a region of memory in the graphics card where we'll store the vertex data.
  1029.     void CreateVertexBuffer() {
  1030.         VkDeviceSize bufferSize = sizeof(gVertices[0]) * gVertices.size();     
  1031.  
  1032.         // Create staging buffer in the GPU, to which we can transfer
  1033.         // data from our application.
  1034.         VkBuffer stagingBuffer;
  1035.         VkDeviceMemory stagingBufferMemory;
  1036.         CreateBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
  1037.  
  1038.         // Copy the data to the staging buffer.
  1039.         void* data;
  1040.         vkMapMemory(mLogicalDevice, stagingBufferMemory, 0, bufferSize, 0, &data);
  1041.         memcpy(data, gVertices.data(), (size_t)bufferSize);
  1042.         vkUnmapMemory(mLogicalDevice, stagingBufferMemory);
  1043.  
  1044.         // Create the GPU logical buffer. We cannot tranfer data directly to this buffer,
  1045.         // but we can copy from the staging buffer to this buffer.
  1046.         CreateBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mVertexBuffer, mVertexBufferMemory);
  1047.         CopyBuffer(stagingBuffer, mVertexBuffer, bufferSize);
  1048.  
  1049.         vkDestroyBuffer(mLogicalDevice, stagingBuffer, nullptr);
  1050.         vkFreeMemory(mLogicalDevice, stagingBufferMemory, nullptr);
  1051.     }
  1052.  
  1053.     // NOTE: This is verify similar to the CreateVertexBuffer() function.
  1054.     //       These 2 functions can probably be combined into one.
  1055.     void CreateIndexBuffer() {
  1056.         VkDeviceSize bufferSize = sizeof(gIndices[0]) * gIndices.size();
  1057.  
  1058.         VkBuffer stagingBuffer;
  1059.         VkDeviceMemory stagingBufferMemory;
  1060.         CreateBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
  1061.  
  1062.         void* data;
  1063.         vkMapMemory(mLogicalDevice, stagingBufferMemory, 0, bufferSize, 0, &data);
  1064.         memcpy(data, gIndices.data(), (size_t)bufferSize);
  1065.         vkUnmapMemory(mLogicalDevice, stagingBufferMemory);
  1066.  
  1067.         CreateBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mIndexBuffer, mIndexBufferMemory);
  1068.  
  1069.         CopyBuffer(stagingBuffer, mIndexBuffer, bufferSize);
  1070.  
  1071.         vkDestroyBuffer(mLogicalDevice, stagingBuffer, nullptr);
  1072.         vkFreeMemory(mLogicalDevice, stagingBufferMemory, nullptr);
  1073.     }
  1074.  
  1075.     void CreateUniformBuffer() {
  1076.         VkDeviceSize bufferSize = sizeof(UniformBufferObject);
  1077.         CreateBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, mUniformBuffer, mUniformBufferMemory);
  1078.     }
  1079.  
  1080.     // NOTE: There are optimization oportunities here:
  1081.     //      1. Use a command pool for copying of buffers.
  1082.     //      2. If multiple transfers are needed, instead of waiting with vkQueueWaitIdle, we
  1083.     //         can use vkWaitForFences to schedule multiple transfers and then do one wait.
  1084.     void CopyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) {
  1085.         VkCommandBuffer commandBuffer = BeginSingleTimeCommands();
  1086.  
  1087.         VkBufferCopy copyRegion = {};
  1088.         copyRegion.size = size;
  1089.         vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);
  1090.  
  1091.         EndSingleTimeCommands(commandBuffer);
  1092.     }
  1093.  
  1094.     void CopyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) {
  1095.         VkCommandBuffer commandBuffer = BeginSingleTimeCommands();
  1096.  
  1097.         VkBufferImageCopy region = {};
  1098.         region.bufferOffset = 0;
  1099.         region.bufferRowLength = 0;
  1100.         region.bufferImageHeight = 0;
  1101.  
  1102.         region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  1103.         region.imageSubresource.mipLevel = 0;
  1104.         region.imageSubresource.baseArrayLayer = 0;
  1105.         region.imageSubresource.layerCount = 1;
  1106.  
  1107.         region.imageOffset = { 0, 0, 0 };
  1108.         region.imageExtent = {
  1109.             width,
  1110.             height,
  1111.             1
  1112.         };
  1113.  
  1114.         vkCmdCopyBufferToImage(
  1115.             commandBuffer,
  1116.             buffer,
  1117.             image,
  1118.             VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
  1119.             1,
  1120.             &region
  1121.         );
  1122.  
  1123.  
  1124.         EndSingleTimeCommands(commandBuffer);
  1125.     }
  1126.  
  1127.  
  1128.     void TransitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout) {
  1129.         VkCommandBuffer commandBuffer = BeginSingleTimeCommands();
  1130.  
  1131.         VkImageMemoryBarrier barrier = {};
  1132.         barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
  1133.         barrier.oldLayout = oldLayout;
  1134.         barrier.newLayout = newLayout;
  1135.         barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  1136.         barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  1137.         barrier.image = image;
  1138.         barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  1139.         barrier.subresourceRange.baseMipLevel = 0;
  1140.         barrier.subresourceRange.levelCount = 1;
  1141.         barrier.subresourceRange.baseArrayLayer = 0;
  1142.         barrier.subresourceRange.layerCount = 1;
  1143.         barrier.srcAccessMask = 0; // TODO
  1144.         barrier.dstAccessMask = 0; // TODO
  1145.  
  1146. /////////
  1147.         VkPipelineStageFlags sourceStage;
  1148.         VkPipelineStageFlags destinationStage;
  1149.  
  1150.         if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
  1151.             barrier.srcAccessMask = 0;
  1152.             barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
  1153.  
  1154.             sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
  1155.             destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
  1156.         }
  1157.         else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
  1158.             barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
  1159.             barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
  1160.  
  1161.             sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
  1162.             destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
  1163.         }
  1164.         else {
  1165.             throw std::invalid_argument("unsupported layout transition!");
  1166.         }
  1167. ////////////
  1168.  
  1169.         vkCmdPipelineBarrier(
  1170.             commandBuffer,
  1171.             sourceStage,
  1172.             destinationStage,
  1173.             0,
  1174.             0, nullptr,
  1175.             0, nullptr,
  1176.             1, &barrier
  1177.         );
  1178.  
  1179.         EndSingleTimeCommands(commandBuffer);
  1180.     }
  1181.  
  1182.     VkCommandBuffer BeginSingleTimeCommands() {
  1183.         VkCommandBufferAllocateInfo allocInfo = {};
  1184.         allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
  1185.         allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
  1186.         allocInfo.commandPool = mCommandPool;
  1187.         allocInfo.commandBufferCount = 1;
  1188.  
  1189.         VkCommandBuffer commandBuffer;
  1190.         vkAllocateCommandBuffers(mLogicalDevice, &allocInfo, &commandBuffer);
  1191.  
  1192.         VkCommandBufferBeginInfo beginInfo = {};
  1193.         beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
  1194.         beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
  1195.  
  1196.         vkBeginCommandBuffer(commandBuffer, &beginInfo);
  1197.  
  1198.         return commandBuffer;
  1199.     }
  1200.  
  1201.     void EndSingleTimeCommands(VkCommandBuffer commandBuffer) {
  1202.         vkEndCommandBuffer(commandBuffer);
  1203.  
  1204.         VkSubmitInfo submitInfo = {};
  1205.         submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
  1206.         submitInfo.commandBufferCount = 1;
  1207.         submitInfo.pCommandBuffers = &commandBuffer;
  1208.  
  1209.         vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
  1210.         vkQueueWaitIdle(mGraphicsQueue);
  1211.  
  1212.         vkFreeCommandBuffers(mLogicalDevice, mCommandPool, 1, &commandBuffer);
  1213.     }
  1214.  
  1215.     void CreateDescriptorSetLayout() {
  1216.         VkDescriptorSetLayoutBinding uboLayoutBinding = {};
  1217.         uboLayoutBinding.binding = 0; //  The binding used in the shader program.
  1218.         uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
  1219.         uboLayoutBinding.descriptorCount = 1;
  1220.         uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; // We're using this description only in the vertex shader.
  1221.         uboLayoutBinding.pImmutableSamplers = nullptr; // This is optional, and it's related to image sampling descriptors.
  1222.  
  1223.         VkDescriptorSetLayoutBinding samplerLayoutBinding = {};
  1224.         samplerLayoutBinding.binding = 1;
  1225.         samplerLayoutBinding.descriptorCount = 1;
  1226.         samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
  1227.         samplerLayoutBinding.pImmutableSamplers = nullptr;
  1228.         samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
  1229.  
  1230.         std::array<VkDescriptorSetLayoutBinding, 2> bindings = {uboLayoutBinding, samplerLayoutBinding };
  1231.         VkDescriptorSetLayoutCreateInfo layoutInfo = {};
  1232.         layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
  1233.         layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());;
  1234.         layoutInfo.pBindings = bindings.data();
  1235.  
  1236.         if (vkCreateDescriptorSetLayout(mLogicalDevice, &layoutInfo, nullptr, &mDescriptorSetLayout) != VK_SUCCESS) {
  1237.             throw std::runtime_error("Failed to create descriptor set layout.");
  1238.         }
  1239.     }
  1240.  
  1241.     void CreateDescriptorPool() {
  1242.         std::array<VkDescriptorPoolSize, 2> poolSizes = {};
  1243.         poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
  1244.         poolSizes[0].descriptorCount = 1;
  1245.         poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
  1246.         poolSizes[1].descriptorCount = 1;
  1247.  
  1248.         VkDescriptorPoolCreateInfo poolInfo = {};
  1249.         poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
  1250.         poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
  1251.         poolInfo.pPoolSizes = poolSizes.data();
  1252.         poolInfo.maxSets = 1;
  1253.  
  1254.         if (vkCreateDescriptorPool(mLogicalDevice, &poolInfo, nullptr, &mDescriptorPool) != VK_SUCCESS) {
  1255.             throw std::runtime_error("Failed to create descriptor pool.");
  1256.         }
  1257.     }
  1258.  
  1259.     void CreateDescriptorSet() {
  1260.         VkDescriptorSetLayout layouts[] = { mDescriptorSetLayout };
  1261.         VkDescriptorSetAllocateInfo allocInfo = {};
  1262.         allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
  1263.         allocInfo.descriptorPool = mDescriptorPool;
  1264.         allocInfo.descriptorSetCount = 1;
  1265.         allocInfo.pSetLayouts = layouts;
  1266.  
  1267.         if (vkAllocateDescriptorSets(mLogicalDevice, &allocInfo, &mDescriptorSet) != VK_SUCCESS) {
  1268.             throw std::runtime_error("Failed to allocate descriptor Set.");
  1269.         }
  1270.  
  1271.         VkDescriptorBufferInfo bufferInfo = {};
  1272.         bufferInfo.buffer = mUniformBuffer;
  1273.         bufferInfo.offset = 0;
  1274.         bufferInfo.range = sizeof(UniformBufferObject);
  1275.  
  1276.         VkDescriptorImageInfo imageInfo = {};
  1277.         imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
  1278.         imageInfo.imageView = mTextureImageView;
  1279.         imageInfo.sampler = mTextureSampler;
  1280.  
  1281.         std::array<VkWriteDescriptorSet, 2> descriptorWrites = {};
  1282.         descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
  1283.         descriptorWrites[0].dstSet = mDescriptorSet;
  1284.         descriptorWrites[0].dstBinding = 0;
  1285.         descriptorWrites[0].dstArrayElement = 0;
  1286.         descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
  1287.         descriptorWrites[0].descriptorCount = 1;
  1288.         descriptorWrites[0].pBufferInfo = &bufferInfo;
  1289.  
  1290.         descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
  1291.         descriptorWrites[1].dstSet = mDescriptorSet;
  1292.         descriptorWrites[1].dstBinding = 1;
  1293.         descriptorWrites[1].dstArrayElement = 0;
  1294.         descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
  1295.         descriptorWrites[1].descriptorCount = 1;
  1296.         descriptorWrites[1].pImageInfo = &imageInfo;
  1297.  
  1298.         vkUpdateDescriptorSets(mLogicalDevice, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
  1299.     }
  1300.  
  1301.     void CreateImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) {
  1302.         VkImageCreateInfo imageInfo = {};
  1303.         imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
  1304.         imageInfo.imageType = VK_IMAGE_TYPE_2D;
  1305.         imageInfo.extent.width = width;
  1306.         imageInfo.extent.height = height;
  1307.         imageInfo.extent.depth = 1;
  1308.         imageInfo.mipLevels = 1;
  1309.         imageInfo.arrayLayers = 1;
  1310.         imageInfo.format = format;
  1311.         imageInfo.tiling = tiling;
  1312.         imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
  1313.         imageInfo.usage = usage;
  1314.         imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
  1315.         imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
  1316.  
  1317.         if (vkCreateImage(mLogicalDevice, &imageInfo, nullptr, &image) != VK_SUCCESS) {
  1318.             throw std::runtime_error("failed to create image!");
  1319.         }
  1320.  
  1321.         VkMemoryRequirements memRequirements;
  1322.         vkGetImageMemoryRequirements(mLogicalDevice, image, &memRequirements);
  1323.  
  1324.         VkMemoryAllocateInfo allocInfo = {};
  1325.         allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
  1326.         allocInfo.allocationSize = memRequirements.size;
  1327.         allocInfo.memoryTypeIndex = FindMemoryType(memRequirements.memoryTypeBits, properties);
  1328.  
  1329.         if (vkAllocateMemory(mLogicalDevice, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) {
  1330.             throw std::runtime_error("failed to allocate image memory!");
  1331.         }
  1332.  
  1333.         vkBindImageMemory(mLogicalDevice, image, imageMemory, 0);
  1334.     }
  1335.    
  1336.     void CreateTextureImageView() {
  1337.         mTextureImageView = CreateImageView(mTextureImage, VK_FORMAT_R8G8B8A8_UNORM);
  1338.     }
  1339.  
  1340.     VkImageView CreateImageView(VkImage image, VkFormat format) {
  1341.         VkImageViewCreateInfo viewInfo = {};
  1342.         viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
  1343.         viewInfo.image = image;
  1344.         viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
  1345.         viewInfo.format = format;
  1346.         viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  1347.         viewInfo.subresourceRange.baseMipLevel = 0;
  1348.         viewInfo.subresourceRange.levelCount = 1;
  1349.         viewInfo.subresourceRange.baseArrayLayer = 0;
  1350.         viewInfo.subresourceRange.layerCount = 1;
  1351.  
  1352.         VkImageView imageView;
  1353.         if (vkCreateImageView(mLogicalDevice, &viewInfo, nullptr, &imageView) != VK_SUCCESS) {
  1354.             throw std::runtime_error("failed to create texture image view!");
  1355.         }
  1356.  
  1357.         return imageView;
  1358.     }
  1359.  
  1360.     void CreateTextureSampler() {
  1361.         VkSamplerCreateInfo samplerInfo = {};
  1362.         samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
  1363.         samplerInfo.magFilter = VK_FILTER_LINEAR;
  1364.         samplerInfo.minFilter = VK_FILTER_LINEAR;
  1365.         samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
  1366.         samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
  1367.         samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
  1368.         samplerInfo.anisotropyEnable = VK_TRUE;
  1369.         samplerInfo.maxAnisotropy = 16;
  1370.         samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
  1371.         samplerInfo.unnormalizedCoordinates = VK_FALSE;
  1372.         samplerInfo.compareEnable = VK_FALSE;
  1373.         samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
  1374.         samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
  1375.         samplerInfo.mipLodBias = 0.0f;
  1376.         samplerInfo.minLod = 0.0f;
  1377.         samplerInfo.maxLod = 0.0f;
  1378.  
  1379.         if (vkCreateSampler(mLogicalDevice, &samplerInfo, nullptr, &mTextureSampler) != VK_SUCCESS) {
  1380.             throw std::runtime_error("failed to create texture sampler!");
  1381.         }
  1382.     }
  1383.  
  1384.     void CreateTextureImage();
  1385.  
  1386.  
  1387.  
  1388.     void UpdateUniformBuffer() {
  1389.         static auto startTime = std::chrono::high_resolution_clock::now();
  1390.  
  1391.         // Time, in seconds.
  1392.         auto currentTime = std::chrono::high_resolution_clock::now();
  1393.         float time = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - startTime).count() / 1000.0f;
  1394.  
  1395.         UniformBufferObject ubo = {};
  1396.  
  1397.         auto identMat = glm::mat4(1.0f);
  1398.         auto rotAmount = 0.0f;
  1399.         auto rotAxis = glm::vec3(0.0f, 0.0f, 1.0f);
  1400.         ubo.mModel = glm::rotate(identMat, rotAmount, rotAxis);
  1401.  
  1402.         float eyeXPos = 0.0f;// time * 1.0f;
  1403.         float eyeYPos = 0.0f; // time * 1.0f;
  1404.         float eyeZPos = time * 1.0f;
  1405.  
  1406.         auto eyePosition = glm::vec3(eyeXPos, eyeYPos, eyeZPos);
  1407.         auto centerPosition = glm::vec3(0.0f, 0.0f, 0.0f);
  1408.         auto upAxis = glm::vec3(0.0f, 1.0f, 0.0f);
  1409.         ubo.mView = glm::lookAt(eyePosition, centerPosition, upAxis);
  1410.  
  1411.         auto verticalFOV = glm::radians(45.0f);
  1412.         auto aspectRatio = mSwapChainExtent.width / (float)mSwapChainExtent.height;
  1413.         auto nearPlane = 0.1f;
  1414.         auto farPlane = 100.0f;
  1415.         ubo.mProj = glm::perspective(verticalFOV, aspectRatio, nearPlane, farPlane);
  1416.  
  1417.         // We're not OpenGL, to flip the Y coordinate.
  1418.         //ubo.mProj[1][1] *= -1;
  1419.  
  1420.         // NOTE: This is not the most efficient way to pass frequently changing data.
  1421.         //       A more efficient way to pass a small buffer of data to shaders are *push constants*.
  1422.         //       https://vulkan-tutorial.com/Uniform_buffers/Descriptor_layout_and_buffer
  1423.         void* data;
  1424.         vkMapMemory(mLogicalDevice, mUniformBufferMemory, 0, sizeof(ubo), 0, &data);
  1425.         memcpy(data, &ubo, sizeof(ubo));
  1426.         vkUnmapMemory(mLogicalDevice, mUniformBufferMemory);
  1427.     }
  1428.  
  1429.     // Init Vulkan specific stuff.
  1430.     void InitVulkan() {
  1431.         //ShowSupportedExtensions();
  1432.         CreateVulkanInstance();     // Ok
  1433.         SetupDebugCallback(); // Ok
  1434.         CreateSurface();        // Ok
  1435.         PickPhysicalDevice();   // Ok
  1436.         CreateLogicalDevice(); // Ok
  1437.         CreateSwapChain();      // Ok
  1438.         CreateImageViews();     // Ok
  1439.         CreateRenderPass();     // Ok
  1440.         CreateDescriptorSetLayout(); // Ok
  1441.         CreateGraphicsPipeline(); // Ok
  1442.         CreateFramebuffers();       // Ok
  1443.         CreateCommandPool();       // Ok
  1444.         CreateTextureImage();       // Ok
  1445.         CreateTextureImageView();   // Ok
  1446.         CreateTextureSampler();     // Ok
  1447.         CreateVertexBuffer(); // Ok
  1448.         CreateIndexBuffer();  // Ok
  1449.         CreateUniformBuffer(); // Ok
  1450.         CreateDescriptorPool(); // Ok
  1451.         CreateDescriptorSet(); // Partial
  1452.         CreateCommandBuffers();
  1453.         CreateSemaphores(); // Ok
  1454.     }
  1455.  
  1456. private:
  1457.     GLFWwindow* mWindow;
  1458.     VkInstance mInstance; // Represents the main connection to the Vulkan library.
  1459.     VkDebugReportCallbackEXT mDebugCallback;
  1460.     VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE; // Represents the actual graphics card that we end up using. It is implicitly destroyed when VkInstance is destroyed.
  1461.     VkDevice mLogicalDevice; // We interact with a physical device via a logical device.
  1462.     VkQueue mGraphicsQueue;
  1463.     VkQueue mPresentQueue;
  1464.     VkSurfaceKHR mSurface;
  1465.     VkSwapchainKHR mSwapChain; 
  1466.     VkFormat mSwapChainImageFormat;
  1467.     VkExtent2D mSwapChainExtent;
  1468.     VkDescriptorPool mDescriptorPool;
  1469.     VkDescriptorSet mDescriptorSet;
  1470.     VkDescriptorSetLayout mDescriptorSetLayout;
  1471.     VkPipelineLayout mPipelineLayout;
  1472.     VkRenderPass mRenderPass;
  1473.     VkPipeline mGraphicsPipeline;
  1474.     VkCommandPool mCommandPool;
  1475.     VkBuffer mVertexBuffer;
  1476.     VkDeviceMemory mVertexBufferMemory;
  1477.     VkBuffer mIndexBuffer;
  1478.     VkDeviceMemory mIndexBufferMemory;
  1479.     VkBuffer mUniformBuffer;
  1480.     VkDeviceMemory mUniformBufferMemory;
  1481.     std::vector<VkImage> mSwapChainImages;
  1482.     std::vector<VkImageView> mSwapChainImageViews;
  1483.     std::vector<VkFramebuffer> mSwapChainFramebuffers;
  1484.     std::vector<VkCommandBuffer> mCommandBuffers;
  1485.     VkSemaphore mImageAvailableSemaphore;
  1486.     VkSemaphore mRenderFinishedSemaphore;
  1487.  
  1488.     VkSampler mTextureSampler;
  1489.     VkImageView mTextureImageView;
  1490.     VkImage mTextureImage;
  1491.     VkDeviceMemory mTextureImageMemory;
  1492. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement