text: /*
  * Class wrapping access to the swap chain
  * 
  * A swap chain is a collection of framebuffers used for rendering
  * The swap chain images can then presented to the windowing system
  *
  * Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
  *
  * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
  */
  #ifndef __VULKANSWAPCHAIN_HPP__
  #define __VULKANSWAPCHAIN_HPP__
  
  #pragma once
  
  #include <stdlib.h>
  #include <string>
  #include <fstream>
  #include <assert.h>
  #include <stdio.h>
  #include <vector>
  #ifdef _WIN32
  #include <windows.h>
  #include <fcntl.h>
  #include <io.h>
  #else
  #endif
  
  #include <vulkan/vulkan.h>
  #include "vulkantools.h"
  
  #ifdef __ANDROID__
  #include "vulkanandroid.h"
  #endif
  
  // Macro to get a procedure address based on a vulkan instance
  #define GET_INSTANCE_PROC_ADDR(inst, entrypoint)                        \
  {                                                                       \
  	fp##entrypoint = (PFN_vk##entrypoint) vkGetInstanceProcAddr(inst, "vk"#entrypoint); \
  	if (fp##entrypoint == NULL)                                         \
  	{																    \
  		exit(1);                                                        \
  	}                                                                   \
  }
  
  // Macro to get a procedure address based on a vulkan device
  #define GET_DEVICE_PROC_ADDR(dev, entrypoint)                           \
  {                                                                       \
  	fp##entrypoint = (PFN_vk##entrypoint) vkGetDeviceProcAddr(dev, "vk"#entrypoint);   \
  	if (fp##entrypoint == NULL)                                         \
  	{																    \
  		exit(1);                                                        \
  	}                                                                   \
  }
  
  typedef struct _SwapChainBuffers {
  	VkImage image;
  	VkImageView view;
  } SwapChainBuffer;
  
  class VulkanSwapChain
  {
  private: 
  	VkInstance instance;
  	VkDevice device;
  	VkPhysicalDevice physicalDevice;
  	VkSurfaceKHR surface;
  	// Function pointers
  	PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR;
  	PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR; 
  	PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fpGetPhysicalDeviceSurfaceFormatsKHR;
  	PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR;
  	PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR;
  	PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR;
  	PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR;
  	PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR;
  	PFN_vkQueuePresentKHR fpQueuePresentKHR;
  public:
  	VkFormat colorFormat;
  	VkColorSpaceKHR colorSpace;
  
  	VkSwapchainKHR swapChain = VK_NULL_HANDLE;
  
  	uint32_t imageCount;
  	std::vector<VkImage> images;
  	std::vector<SwapChainBuffer> buffers;
  
  	// Index of the deteced graphics and presenting device queue
  	uint32_t queueNodeIndex = UINT32_MAX;
  
  	// Creates an os specific surface
  	// Tries to find a graphics and a present queue
  	void initSurface(
  #ifdef _WIN32
  		void* platformHandle, void* platformWindow
  #else
  #ifdef __ANDROID__
  		ANativeWindow* window
  #else
  		xcb_connection_t* connection, xcb_window_t window
  #endif
  #endif
  	)
  	{
  		VkResult err;
  
  		// Create surface depending on OS
  #ifdef _WIN32
  		VkWin32SurfaceCreateInfoKHR surfaceCreateInfo = {};
  		surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
  		surfaceCreateInfo.hinstance = (HINSTANCE)platformHandle;
  		surfaceCreateInfo.hwnd = (HWND)platformWindow;
  		err = vkCreateWin32SurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface);
  #else
  #ifdef __ANDROID__
  		VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo = {};
  		surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
  		surfaceCreateInfo.window = window;
  		err = vkCreateAndroidSurfaceKHR(instance, &surfaceCreateInfo, NULL, &surface);
  #else
  		VkXcbSurfaceCreateInfoKHR surfaceCreateInfo = {};
  		surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
  		surfaceCreateInfo.connection = connection;
  		surfaceCreateInfo.window = window;
  		err = vkCreateXcbSurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface);
  #endif
  #endif
  
  		// Get available queue family properties
  		uint32_t queueCount;
  		vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, NULL);
  		assert(queueCount >= 1);
  
  		std::vector<VkQueueFamilyProperties> queueProps(queueCount);
  		vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, queueProps.data());
  
  		// Iterate over each queue to learn whether it supports presenting:
  		// Find a queue with present support
  		// Will be used to present the swap chain images to the windowing system
  		std::vector<VkBool32> supportsPresent(queueCount);
  		for (uint32_t i = 0; i < queueCount; i++) 
  		{
  			fpGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface, &supportsPresent[i]);
  		}
  
  		// Search for a graphics and a present queue in the array of queue
  		// families, try to find one that supports both
  		uint32_t graphicsQueueNodeIndex = UINT32_MAX;
  		uint32_t presentQueueNodeIndex = UINT32_MAX;
  		for (uint32_t i = 0; i < queueCount; i++) 
  		{
  			if ((queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) 
  			{
  				if (graphicsQueueNodeIndex == UINT32_MAX) 
  				{
  					graphicsQueueNodeIndex = i;
  				}
  
  				if (supportsPresent[i] == VK_TRUE) 
  				{
  					graphicsQueueNodeIndex = i;
  					presentQueueNodeIndex = i;
  					break;
  				}
  			}
  		}
  		if (presentQueueNodeIndex == UINT32_MAX) 
  		{	
  			// If there's no queue that supports both present and graphics
  			// try to find a separate present queue
  			for (uint32_t i = 0; i < queueCount; ++i) 
  			{
  				if (supportsPresent[i] == VK_TRUE) 
  				{
  					presentQueueNodeIndex = i;
  					break;
  				}
  			}
  		}
  
  		// Exit if either a graphics or a presenting queue hasn't been found
  		if (graphicsQueueNodeIndex == UINT32_MAX || presentQueueNodeIndex == UINT32_MAX) 
  		{
  			vkTools::exitFatal("Could not find a graphics and/or presenting queue!", "Fatal error");
  		}
  
  		// todo : Add support for separate graphics and presenting queue
  		if (graphicsQueueNodeIndex != presentQueueNodeIndex) 
  		{
  			vkTools::exitFatal("Separate graphics and presenting queues are not supported yet!", "Fatal error");
  		}
  
  		queueNodeIndex = graphicsQueueNodeIndex;
  
  		// Get list of supported surface formats
  		uint32_t formatCount;
  		err = fpGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, NULL);
  		assert(!err);
  		assert(formatCount > 0);
  
  		std::vector<VkSurfaceFormatKHR> surfaceFormats(formatCount);
  		err = fpGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, surfaceFormats.data());
  		assert(!err);
  
  		// If the surface format list only includes one entry with VK_FORMAT_UNDEFINED,
  		// there is no preferered format, so we assume VK_FORMAT_B8G8R8A8_UNORM
  		if ((formatCount == 1) && (surfaceFormats[0].format == VK_FORMAT_UNDEFINED))
  		{
  			colorFormat = VK_FORMAT_B8G8R8A8_UNORM;
  		}
  		else
  		{
  			// Always select the first available color format
  			// If you need a specific format (e.g. SRGB) you'd need to
  			// iterate over the list of available surface format and
  			// check for it's presence
  			colorFormat = surfaceFormats[0].format;
  		}
  		colorSpace = surfaceFormats[0].colorSpace;
  	}
  
  	// Connect to the instance und device and get all required function pointers
  	void connect(VkInstance instance, VkPhysicalDevice physicalDevice, VkDevice device)
  	{
  		this->instance = instance;
  		this->physicalDevice = physicalDevice;
  		this->device = device;
  		GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfaceSupportKHR);
  		GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfaceCapabilitiesKHR);
  		GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfaceFormatsKHR);
  		GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfacePresentModesKHR);
  		GET_DEVICE_PROC_ADDR(device, CreateSwapchainKHR);
  		GET_DEVICE_PROC_ADDR(device, DestroySwapchainKHR);
  		GET_DEVICE_PROC_ADDR(device, GetSwapchainImagesKHR);
  		GET_DEVICE_PROC_ADDR(device, AcquireNextImageKHR);
  		GET_DEVICE_PROC_ADDR(device, QueuePresentKHR);
  	}
  
  	// Create the swap chain and get images with given width and height
  	void create(VkCommandBuffer cmdBuffer, uint32_t *width, uint32_t *height, bool vsync = false)
  	{
  		VkResult err;
  		VkSwapchainKHR oldSwapchain = swapChain;
  
  		// Get physical device surface properties and formats
  		VkSurfaceCapabilitiesKHR surfCaps;
  		err = fpGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfCaps);
  		assert(!err);
  
  		// Get available present modes
  		uint32_t presentModeCount;
  		err = fpGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, NULL);
  		assert(!err);
  		assert(presentModeCount > 0);
  
  		std::vector<VkPresentModeKHR> presentModes(presentModeCount);
  
  		err = fpGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, presentModes.data());
  		assert(!err);
  
  		VkExtent2D swapchainExtent = {};
  		// width and height are either both -1, or both not -1.
  		if (surfCaps.currentExtent.width == ~uint32_t(0)) // -1)
  		{
  			// If the surface size is undefined, the size is set to
  			// the size of the images requested.
  			swapchainExtent.width = *width;
  			swapchainExtent.height = *height;
  		}
  		else
  		{
  			// If the surface size is defined, the swap chain size must match
  			swapchainExtent = surfCaps.currentExtent;
  			*width = surfCaps.currentExtent.width;
  			*height = surfCaps.currentExtent.height;
  		}
  
  
  		// Select a present mode for the swapchain
  
  		// The VK_PRESENT_MODE_FIFO_KHR mode must always be present as per spec
  		// This mode waits for the vertical blank ("v-sync")
  		VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
  
  		// If v-sync is not requested, try to find a mailbox mode if present
  		// It's the lowest latency non-tearing present mode available
  		if (!vsync)
  		{
  			for (size_t i = 0; i < presentModeCount; i++)
  			{
  				if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR)
  				{
  					swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR;
  					break;
  				}
  				if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) && (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR))
  				{
  					swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
  				}
  			}
  		}
  
  		// Determine the number of images
  		uint32_t desiredNumberOfSwapchainImages = surfCaps.minImageCount + 1;
  		if ((surfCaps.maxImageCount > 0) && (desiredNumberOfSwapchainImages > surfCaps.maxImageCount))
  		{
  			desiredNumberOfSwapchainImages = surfCaps.maxImageCount;
  		}
  
  		VkSurfaceTransformFlagsKHR preTransform;
  		if (surfCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
  		{
  			preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
  		}
  		else 
  		{
  			preTransform = surfCaps.currentTransform;
  		}
  
  		VkSwapchainCreateInfoKHR swapchainCI = {};
  		swapchainCI.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
  		swapchainCI.pNext = NULL;
  		swapchainCI.surface = surface;
  		swapchainCI.minImageCount = desiredNumberOfSwapchainImages;
  		swapchainCI.imageFormat = colorFormat;
  		swapchainCI.imageColorSpace = colorSpace;
  		swapchainCI.imageExtent = { swapchainExtent.width, swapchainExtent.height };
  		swapchainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
  		swapchainCI.preTransform = (VkSurfaceTransformFlagBitsKHR)preTransform;
  		swapchainCI.imageArrayLayers = 1;
  		swapchainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
  		swapchainCI.queueFamilyIndexCount = 0;
  		swapchainCI.pQueueFamilyIndices = NULL;
  		swapchainCI.presentMode = swapchainPresentMode;
  		swapchainCI.oldSwapchain = oldSwapchain;
  		swapchainCI.clipped = true;
  		swapchainCI.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
  
  		err = fpCreateSwapchainKHR(device, &swapchainCI, nullptr, &swapChain);
  		assert(!err);
  
  		// If an existing sawp chain is re-created, destroy the old swap chain
  		// This also cleans up all the presentable images
  		if (oldSwapchain != VK_NULL_HANDLE) 
  		{ 
  			for (uint32_t i = 0; i < imageCount; i++)
  			{
  				vkDestroyImageView(device, buffers[i].view, nullptr);
  			}
  			fpDestroySwapchainKHR(device, oldSwapchain, nullptr);
  		}
  
  		err = fpGetSwapchainImagesKHR(device, swapChain, &imageCount, NULL);
  		assert(!err);
  
  		// Get the swap chain images
  		images.resize(imageCount);
  		err = fpGetSwapchainImagesKHR(device, swapChain, &imageCount, images.data());
  		assert(!err);
  
  		// Get the swap chain buffers containing the image and imageview
  		buffers.resize(imageCount);
  		for (uint32_t i = 0; i < imageCount; i++)
  		{
  			VkImageViewCreateInfo colorAttachmentView = {};
  			colorAttachmentView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
  			colorAttachmentView.pNext = NULL;
  			colorAttachmentView.format = colorFormat;
  			colorAttachmentView.components = {
  				VK_COMPONENT_SWIZZLE_R,
  				VK_COMPONENT_SWIZZLE_G,
  				VK_COMPONENT_SWIZZLE_B,
  				VK_COMPONENT_SWIZZLE_A
  			};
  			colorAttachmentView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
  			colorAttachmentView.subresourceRange.baseMipLevel = 0;
  			colorAttachmentView.subresourceRange.levelCount = 1;
  			colorAttachmentView.subresourceRange.baseArrayLayer = 0;
  			colorAttachmentView.subresourceRange.layerCount = 1;
  			colorAttachmentView.viewType = VK_IMAGE_VIEW_TYPE_2D;
  			colorAttachmentView.flags = 0;
  
  			buffers[i].image = images[i];
  
  			colorAttachmentView.image = buffers[i].image;
  
  			err = vkCreateImageView(device, &colorAttachmentView, nullptr, &buffers[i].view);
  			assert(!err);
  		}
  	}
  
  	// Acquires the next image in the swap chain
  	VkResult acquireNextImage(VkSemaphore presentCompleteSemaphore, uint32_t *currentBuffer)
  	{
  		return fpAcquireNextImageKHR(device, swapChain, UINT64_MAX, presentCompleteSemaphore, (VkFence)nullptr, currentBuffer);
  	}
  
  	// Present the current image to the queue
  	VkResult queuePresent(VkQueue queue, uint32_t currentBuffer)
  	{
  		VkPresentInfoKHR presentInfo = {};
  		presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
  		presentInfo.pNext = NULL;
  		presentInfo.swapchainCount = 1;
  		presentInfo.pSwapchains = &swapChain;
  		presentInfo.pImageIndices = &currentBuffer;
  		return fpQueuePresentKHR(queue, &presentInfo);
  	}
  
  	// Present the current image to the queue
  	VkResult queuePresent(VkQueue queue, uint32_t currentBuffer, VkSemaphore waitSemaphore)
  	{
  		VkPresentInfoKHR presentInfo = {};
  		presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
  		presentInfo.pNext = NULL;
  		presentInfo.swapchainCount = 1;
  		presentInfo.pSwapchains = &swapChain;
  		presentInfo.pImageIndices = &currentBuffer;
  		if (waitSemaphore != VK_NULL_HANDLE)
  		{
  			presentInfo.pWaitSemaphores = &waitSemaphore;
  			presentInfo.waitSemaphoreCount = 1;
  		}
  		return fpQueuePresentKHR(queue, &presentInfo);
  	}
  
  
  	// Free all Vulkan resources used by the swap chain
  	void cleanup()
  	{
  		for (uint32_t i = 0; i < imageCount; i++)
  		{
  			vkDestroyImageView(device, buffers[i].view, nullptr);
  		}
  		fpDestroySwapchainKHR(device, swapChain, nullptr);
  		vkDestroySurfaceKHR(instance, surface, nullptr);
  	}
  
  };
  
  #endif // __VULKANSWAPCHAIN_HPP__