text: #include "vk_factory.h"
  
  #include <vector>
  
  #include <vulkan/vulkan.h>
  #include <GLFW/glfw3.h>
  
  #include "carbon/debugging/cr_log.h"
  #include "carbon/platform/vulkan/vk_assert.h"
  
  #pragma warning(push)
  #pragma warning(disable: 26812)
  
  namespace CR {
  
  #ifdef CR_BUILD_DEBUG
  static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(
      VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
      VkDebugUtilsMessageTypeFlagsEXT messageType,
      const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
      void* pUserData) {
  
      CR_LOG("[Vulkan] %s\n", pCallbackData->pMessage);
  
      return VK_FALSE;
  }
  #endif // CR_BUILD_DEBUG
  
  VkInstance VulkanFactory::CreateInstance(VkAllocationCallbacks* allocator) {
    uint32_t glfw_extension_count = 0;
    const char** glfw_extensions = glfwGetRequiredInstanceExtensions(&glfw_extension_count);
    std::vector<const char*> extensions(glfw_extension_count);
    for (int i = 0; i < glfw_extension_count; ++i) {
      extensions[i] = glfw_extensions[i];
    }
  
    VkApplicationInfo app_info{ VK_STRUCTURE_TYPE_APPLICATION_INFO };
    app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    app_info.pApplicationName = "Carbon Forge";
    app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
    app_info.pEngineName = "Carbon";
    app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0);
    app_info.apiVersion = VK_API_VERSION_1_0;
  
    VkInstanceCreateInfo create_info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
    create_info.enabledExtensionCount = extensions.size();
    create_info.ppEnabledExtensionNames = extensions.data();
  
  #ifdef CR_BUILD_DEBUG
  
    const char* layers[] = { "VK_LAYER_KHRONOS_validation" };
    create_info.enabledLayerCount = 1;
    create_info.ppEnabledLayerNames = layers;
    
    extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
    create_info.enabledExtensionCount = extensions.size();
    create_info.ppEnabledExtensionNames = extensions.data();
    
    VkInstance instance = VK_NULL_HANDLE;
    CR_VK_CALL(vkCreateInstance(&create_info, allocator, &instance));
  
  #else
  
    VkInstance instance = VK_NULL_HANDLE;
    vkCreateInstance(&create_info, allocator, &instance);
  
  #endif
    
    return instance;
  }
  
  #ifdef CR_BUILD_DEBUG
  VkDebugUtilsMessengerEXT VulkanFactory::CreateDebugMessenger(VkInstance instance, VkAllocationCallbacks* allocator) {
    auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
    CR_ASSERT((func != nullptr), "Failed to get vkCreateDebugUtilsMessengerEXT function pointer");
    
    // Setup the debug report messenger
    VkDebugUtilsMessengerCreateInfoEXT debug_report_ci = { VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT };
    debug_report_ci.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT 
                                    | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
                                    | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
    debug_report_ci.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
                                | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
                                | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
    debug_report_ci.pfnUserCallback = DebugCallback;
  
    VkDebugUtilsMessengerEXT debug_messenger = VK_NULL_HANDLE;
    func(instance, &debug_report_ci, allocator, &debug_messenger);
    return debug_messenger;
  }
  #endif
  
  VkPhysicalDevice VulkanFactory::ChoosePhysicalDevice(VkInstance instance) {
    uint32_t gpu_count;
    CR_VK_CALL(vkEnumeratePhysicalDevices(instance, &gpu_count, nullptr));
    CR_ASSERT((gpu_count > 0), "Failed to find a GPU that supports Vulkan");
    
    std::vector<VkPhysicalDevice> gpus(gpu_count);
    CR_VK_CALL(vkEnumeratePhysicalDevices(instance, &gpu_count, gpus.data()));
    
    for (int i = 0; i < gpu_count; ++i) {
      VkPhysicalDeviceProperties properties;
      vkGetPhysicalDeviceProperties(gpus[i], &properties);
      if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
        CR_LOG("Picking discrete GPU: %s\n", properties.deviceName);
        return gpus[i];
      }
    }
  
    if (gpu_count > 0) {
      VkPhysicalDeviceProperties properties;
      vkGetPhysicalDeviceProperties(gpus[0], &properties);
      CR_LOG("Picking fallback GPU: %s\n", properties.deviceName);
      return gpus[0];
    }
  
    CR_ASSERT(false, "No physical devices available");
    return VK_NULL_HANDLE;
  }
  
  uint32_t VulkanFactory::ChooseQueueFamily(VkPhysicalDevice physical_device) {
    uint32_t count;
    vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &count, nullptr);
    std::vector<VkQueueFamilyProperties> queues(count);
    vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &count, queues.data());
    uint32_t queue_family = -1;
    for (uint32_t i = 0; i < count; ++i) {
      if (queues[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
        queue_family = i;
        break;
      }
    }
    CR_ASSERT((queue_family != -1), "No queues found that are capable of graphics. Is this a compute-only GPU?");
    return queue_family;
  }
  
  VkDevice VulkanFactory::CreateDevice(VkPhysicalDevice physical_device, uint32_t queue_family, 
                                       VkAllocationCallbacks* allocator) {
    const float queue_priority[] = { 1.0f };
    std::vector<VkDeviceQueueCreateInfo> queue_infos = { { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO } };
    queue_infos[0].queueFamilyIndex = queue_family;
    queue_infos[0].queueCount = 1;
    queue_infos[0].pQueuePriorities = queue_priority;
    VkDeviceCreateInfo create_info = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
    create_info.pQueueCreateInfos = queue_infos.data();
    create_info.queueCreateInfoCount = queue_infos.size();
    std::vector<const char*> device_extensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
    create_info.enabledExtensionCount = device_extensions.size();
    create_info.ppEnabledExtensionNames = device_extensions.data();
    VkDevice device = VK_NULL_HANDLE;
    CR_VK_CALL(vkCreateDevice(physical_device, &create_info, allocator, &device));
    return device;
  }
  
  VkDescriptorPool VulkanFactory::CreateDescriptorPool(VkDevice device, VkAllocationCallbacks* allocator) {
    const std::vector<VkDescriptorPoolSize> pool_sizes = {
      { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
      { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
      { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
      { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
      { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
      { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
      { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
      { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
      { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
      { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
      { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 }
    };
    VkDescriptorPoolCreateInfo pool_info = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
    pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
    pool_info.maxSets = 1000 * pool_sizes.size();
    pool_info.poolSizeCount = (uint32_t)pool_sizes.size();
    pool_info.pPoolSizes = pool_sizes.data();
    VkDescriptorPool descriptor_pool = VK_NULL_HANDLE;
    CR_VK_CALL(vkCreateDescriptorPool(device, &pool_info, allocator, &descriptor_pool));
    return descriptor_pool;
  }
  
  void DestroyFrame(VkDevice device, VulkanContext::Frame* fd, const VkAllocationCallbacks* allocator) {
    vkDestroyFence(device, fd->fence, allocator);
    vkFreeCommandBuffers(device, fd->command_pool, 1, &fd->command_buffer);
    vkDestroyCommandPool(device, fd->command_pool, allocator);
    fd->fence = VK_NULL_HANDLE;
    fd->command_buffer = VK_NULL_HANDLE;
    fd->command_pool = VK_NULL_HANDLE;
    vkDestroyImageView(device, fd->backbuffer_view, allocator);
    vkDestroyFramebuffer(device, fd->framebuffer, allocator);
  }
  
  void DestroyFrameSemaphores(VkDevice device, VulkanContext::FrameSemaphores* fsd, 
                              const VkAllocationCallbacks* allocator) {
    vkDestroySemaphore(device, fsd->image_acquired_semaphore, allocator);
    vkDestroySemaphore(device, fsd->render_complete_semaphore, allocator);
    fsd->image_acquired_semaphore = fsd->render_complete_semaphore = VK_NULL_HANDLE;
  }
  
  constexpr int GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode) {
    if (present_mode == VK_PRESENT_MODE_MAILBOX_KHR) return 3;
    if (present_mode == VK_PRESENT_MODE_FIFO_KHR || present_mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR) return 2;
    if (present_mode == VK_PRESENT_MODE_IMMEDIATE_KHR) return 1;
    CR_ASSERT(false, "Undefined/unsupported present mode - unable to get min image count");
    return 1;
  }
  
  void CreateWindowSwapchain(VkPhysicalDevice physical_device, VkDevice device,
      VulkanContext::WindowData* window_data, const VkAllocationCallbacks* allocator, int w, int h, 
      uint32_t min_image_count) {
    VkSwapchainKHR old_swapchain = window_data->swapchain;
    window_data->swapchain = nullptr;
    CR_VK_CALL(vkDeviceWaitIdle(device));
    
    // Destroy old Framebuffer
    for (uint32_t i = 0; i < window_data->image_count; ++i) {
      DestroyFrame(device, &window_data->frames[i], allocator);
      DestroyFrameSemaphores(device, &window_data->frame_semaphores[i], allocator);
    }
    if (window_data->frames) {
      delete[] window_data->frames;
      window_data->frames = nullptr;
    }
    if (window_data->frame_semaphores) {
      delete[] window_data->frame_semaphores;
      window_data->frame_semaphores = nullptr;
    }
  
    window_data->image_count = 0;
    if (window_data->render_pass) {
      vkDestroyRenderPass(device, window_data->render_pass, allocator);
    }
    // TODO: ImGui destroys the pipeline here?
    
    // If min image count was not specified, request different count of images dependent on selected present mode
    if (min_image_count == 0) {
      min_image_count = GetMinImageCountFromPresentMode(window_data->present_mode);
    }
    
    // Create Swapchain
    {
      VkSwapchainCreateInfoKHR info = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR };
      info.surface = window_data->surface;
      info.minImageCount = min_image_count;
      info.imageFormat = window_data->surface_format.format;
      info.imageColorSpace = window_data->surface_format.colorSpace;
      info.imageArrayLayers = 1;
      info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
      info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; // Assume that graphics family == present family
      info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
      info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
      info.presentMode = window_data->present_mode;
      info.clipped = VK_TRUE;
      info.oldSwapchain = old_swapchain;
      VkSurfaceCapabilitiesKHR cap;
      CR_VK_CALL(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, window_data->surface, &cap));
      if (info.minImageCount < cap.minImageCount) {
        info.minImageCount = cap.minImageCount;
      }
      else if (cap.maxImageCount != 0 && info.minImageCount > cap.maxImageCount) { 
        info.minImageCount = cap.maxImageCount;
      }
  
      if (cap.currentExtent.width == 0xffffffff) {
        info.imageExtent.width = window_data->width = w;
        info.imageExtent.height = window_data->height = h;
      } 
      else {
        info.imageExtent.width = window_data->width = cap.currentExtent.width;
        info.imageExtent.height = window_data->height = cap.currentExtent.height;
      }
      CR_VK_CALL(vkCreateSwapchainKHR(device, &info, allocator, &window_data->swapchain));
      CR_VK_CALL(vkGetSwapchainImagesKHR(device, window_data->swapchain, &window_data->image_count, nullptr));
      std::vector<VkImage> backbuffers(window_data->image_count);
      CR_VK_CALL(vkGetSwapchainImagesKHR(device, window_data->swapchain, &window_data->image_count, backbuffers.data()));
      
      window_data->frames = new VulkanContext::Frame[window_data->image_count];
      window_data->frame_semaphores = new VulkanContext::FrameSemaphores[window_data->image_count];
      memset(window_data->frames, 0, sizeof(window_data->frames[0]) * window_data->image_count);
      memset(window_data->frame_semaphores, 0, sizeof(window_data->frame_semaphores[0]) * window_data->image_count);
      for (uint32_t i = 0; i < window_data->image_count; ++i) {
        window_data->frames[i].backbuffer = backbuffers[i];
      }
    }
    if (old_swapchain) {
      vkDestroySwapchainKHR(device, old_swapchain, allocator);
    }
  
    // Create the Render Pass
    {
      VkAttachmentDescription attachments[1] = {};
      // Attachment 0 = Colour attachment
      attachments[0].format = window_data->surface_format.format;
      attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
      attachments[0].loadOp = window_data->clear_enable ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
      attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
      attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
      attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
      attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
      attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
  
      VkAttachmentReference colour_attachment = {};
      // This 0 refers to the index into the attachments array above
      colour_attachment.attachment = 0;
      colour_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
  
      VkSubpassDescription subpass = {};
      subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
      subpass.colorAttachmentCount = 1;
      subpass.pColorAttachments = &colour_attachment;
  
      VkSubpassDependency dependency = {};
      dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
      dependency.dstSubpass = 0;
      dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
      dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
      dependency.srcAccessMask = 0;
      dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
  
      VkRenderPassCreateInfo info = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
      info.attachmentCount = 1;
      info.pAttachments = attachments;
      info.subpassCount = 1;
      info.pSubpasses = &subpass;
      info.dependencyCount = 1;
      info.pDependencies = &dependency;
      CR_VK_CALL(vkCreateRenderPass(device, &info, allocator, &window_data->render_pass));
    }
  
    // Create The Image Views
    {
      VkImageViewCreateInfo info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
      info.viewType = VK_IMAGE_VIEW_TYPE_2D;
      info.format = window_data->surface_format.format;
      info.components.r = VK_COMPONENT_SWIZZLE_R;
      info.components.g = VK_COMPONENT_SWIZZLE_G;
      info.components.b = VK_COMPONENT_SWIZZLE_B;
      info.components.a = VK_COMPONENT_SWIZZLE_A;
      VkImageSubresourceRange image_range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
      info.subresourceRange = image_range;
      for (uint32_t i = 0; i < window_data->image_count; ++i) {
        VulkanContext::Frame* fd = &window_data->frames[i];
        info.image = fd->backbuffer;
        CR_VK_CALL(vkCreateImageView(device, &info, allocator, &fd->backbuffer_view));
      }
    }
  
    // Create Framebuffer
    {
      VkImageView attachments[1] = {};
      VkFramebufferCreateInfo info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
      info.renderPass = window_data->render_pass;
      info.attachmentCount = 1;
      info.pAttachments = attachments;
      info.width = window_data->width;
      info.height = window_data->height;
      info.layers = 1;
      for (uint32_t i = 0; i < window_data->image_count; ++i) {
        VulkanContext::Frame* fd = &window_data->frames[i];
        attachments[0] = fd->backbuffer_view;
        CR_VK_CALL(vkCreateFramebuffer(device, &info, allocator, &fd->framebuffer));
      }
    }
  }
  
  void VulkanFactory::DestroyWindowData(VulkanContext::WindowData* window_data, VkDevice device, 
                                        VkAllocationCallbacks* allocator) {
  for (uint32_t i = 0; i < window_data->image_count; ++i) {
      DestroyFrame(device, &window_data->frames[i], allocator);
      DestroyFrameSemaphores(device, &window_data->frame_semaphores[i], allocator);
    }
  }
  
  void CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device,
      VulkanContext::WindowData* wd, uint32_t queue_family, VkAllocationCallbacks* allocator) {
    CR_ASSERT((physical_device != VK_NULL_HANDLE && device != VK_NULL_HANDLE), "Device or physical device doesn't exist");
    (void)physical_device;
    (void)allocator;
    
    // Create Command Buffers
    for (uint32_t i = 0; i < wd->image_count; ++i) {
      VulkanContext::Frame* fd = &wd->frames[i];
      VulkanContext::FrameSemaphores* fsd = &wd->frame_semaphores[i];
      {
        VkCommandPoolCreateInfo info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
        info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
        info.queueFamilyIndex = queue_family;
        CR_VK_CALL(vkCreateCommandPool(device, &info, allocator, &fd->command_pool));
      }
      {
        VkCommandBufferAllocateInfo info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
        info.commandPool = fd->command_pool;
        info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
        info.commandBufferCount = 1;
        CR_VK_CALL(vkAllocateCommandBuffers(device, &info, &fd->command_buffer));
      }
      {
        VkFenceCreateInfo info = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO };
        info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
        CR_VK_CALL(vkCreateFence(device, &info, allocator, &fd->fence));
      }
      {
        VkSemaphoreCreateInfo info = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
        CR_VK_CALL(vkCreateSemaphore(device, &info, allocator, &fsd->image_acquired_semaphore));
        CR_VK_CALL(vkCreateSemaphore(device, &info, allocator, &fsd->render_complete_semaphore));
      }
    }
  }
  
  VkSurfaceFormatKHR SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface,
                                         const std::vector<VkFormat>& request_formats, 
                                         VkColorSpaceKHR request_color_space) {
    uint32_t avail_count;
    vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &avail_count, nullptr);
    std::vector<VkSurfaceFormatKHR> avail_format;
    avail_format.resize((int)avail_count);
    vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &avail_count, avail_format.data());
    
    // First check if only one format, VK_FORMAT_UNDEFINED, is available, which would imply that any format is available
    if (avail_count == 1) {
      if (avail_format[0].format == VK_FORMAT_UNDEFINED) {
          VkSurfaceFormatKHR ret;
          ret.format = request_formats[0];
          ret.colorSpace = request_color_space;
          return ret;
      }
      else {
        // No point in searching for another format
        return avail_format[0];
      }
    }
    else {
      // Request several formats, the first found will be used
      for (int request_i = 0; request_i < request_formats.size(); ++request_i) {
        for (uint32_t avail_i = 0; avail_i < avail_count; ++avail_i) {
          if (avail_format[avail_i].format == request_formats[request_i] && 
                avail_format[avail_i].colorSpace == request_color_space) {
              return avail_format[avail_i];
          }
        }
      }
      // If none of the requested image formats could be found, use the first available
      return avail_format[0];
    }
  }
  
  VkPresentModeKHR SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface,
                                     const std::vector<VkPresentModeKHR>& request_modes) {
    // Request a certain mode and confirm that it is available. If not use VK_PRESENT_MODE_FIFO_KHR which is mandatory
    uint32_t avail_count = 0;
    vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &avail_count, nullptr);
    std::vector<VkPresentModeKHR> avail_modes;
    avail_modes.resize((int)avail_count);
    vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &avail_count, avail_modes.data());
    
    for (int request_i = 0; request_i < request_modes.size(); ++request_i) {
      for (uint32_t avail_i = 0; avail_i < avail_count; ++avail_i) {
        if (request_modes[request_i] == avail_modes[avail_i]) {
          return request_modes[request_i];
        }
      }
    }
    
    return VK_PRESENT_MODE_FIFO_KHR; // Always available
  }
  
  void VulkanFactory::SetupWindow(int width, int height, VkPhysicalDevice physical_device, uint32_t queue_family, 
      VulkanContext::WindowData* window_data, bool use_vsync, VkDevice device, VkInstance instance, 
      uint32_t min_image_count, VkAllocationCallbacks* allocator) {
    VkBool32 res;
    vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, queue_family, window_data->surface, &res);
    CR_VK_ASSERT(res, "No WSI support");
  
    const std::vector<VkFormat> requestSurfaceImageFormat = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, 
                                                              VK_FORMAT_B8G8R8_UNORM,   VK_FORMAT_R8G8B8_UNORM };
    const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
    window_data->surface_format = SelectSurfaceFormat(physical_device, window_data->surface, 
                                                      requestSurfaceImageFormat, requestSurfaceColorSpace);
  
    if (!use_vsync) {
      const std::vector<VkPresentModeKHR> present_modes = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, 
                                                            VK_PRESENT_MODE_FIFO_KHR };
      window_data->present_mode = SelectPresentMode(physical_device, window_data->surface, present_modes);
    }
    else {
      const std::vector<VkPresentModeKHR> present_modes = { VK_PRESENT_MODE_FIFO_KHR };
      window_data->present_mode = SelectPresentMode(physical_device, window_data->surface, present_modes);
    }
  
    // Create SwapChain, RenderPass, Framebuffer, etc.
    CR_ASSERT((min_image_count >= 2), "At least 2 images are needed");
    CreateOrResizeWindow(instance, physical_device, device, window_data, 
                         queue_family, allocator, width, height, min_image_count);
  }
  
  void VulkanFactory::CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device,
      VulkanContext::WindowData* wd, uint32_t queue_family, VkAllocationCallbacks* allocator, int width, int height,
      uint32_t min_image_count) {
    (void)instance;
    CreateWindowSwapchain(physical_device, device, wd, allocator, width, height, min_image_count);
    CreateWindowCommandBuffers(physical_device, device, wd, queue_family, allocator);
  }
  
  } // CR
  
  #pragma warning(pop)
  