Explorar o código

More Vulkan WIP:
- Added Vulkan Framebuffer object
- Added acquire and present methods to swap chain
- Added helper methods for allocating memory and converting pixel formats

BearishSun %!s(int64=9) %!d(string=hai) anos
pai
achega
d8c6db7a32

+ 25 - 0
Source/BansheeUtility/Include/BsNonCopyable.h

@@ -0,0 +1,25 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+namespace BansheeEngine
+{
+	/** @addtogroup General
+	 *  @{
+	 */
+
+	/** Interface that prevents copies be made of any type that implements it. */
+	class INonCopyable
+	{
+	protected:
+		INonCopyable() = default;
+		~INonCopyable() = default;
+
+	private:
+		INonCopyable(const INonCopyable&) = delete;
+		INonCopyable& operator=(const INonCopyable&) = delete;
+
+	};
+
+	/** @} */
+}

+ 2 - 1
Source/BansheeUtility/Include/BsPrerequisitesUtil.h

@@ -237,4 +237,5 @@
 #include "BsStringID.h"
 #include "BsStringID.h"
 #include "BsEvent.h"
 #include "BsEvent.h"
 #include "BsPlatformUtility.h"
 #include "BsPlatformUtility.h"
-#include "BsCrashHandler.h"
+#include "BsCrashHandler.h"
+#include "BsNonCopyable.h"

+ 2 - 0
Source/BansheeVulkanRenderAPI/CMakeSources.cmake

@@ -21,6 +21,7 @@ set(BS_BANSHEEVULKANRENDERAPI_INC_NOFILTER
 	"Include/BsVulkanGpuPipelineState.h"
 	"Include/BsVulkanGpuPipelineState.h"
 	"Include/BsVulkanSwapChain.h"
 	"Include/BsVulkanSwapChain.h"
 	"Include/BsVulkanFramebuffer.h"
 	"Include/BsVulkanFramebuffer.h"
+	"Include/BsVulkanUtility.h"
 )
 )
 
 
 set(BS_BANSHEEVULKANRENDERAPI_INC_MANAGERS
 set(BS_BANSHEEVULKANRENDERAPI_INC_MANAGERS
@@ -56,6 +57,7 @@ set(BS_BANSHEEVULKANRENDERAPI_SRC_NOFILTER
 	"Source/BsVulkanGpuPipelineState.cpp"
 	"Source/BsVulkanGpuPipelineState.cpp"
 	"Source/BsVulkanSwapChain.cpp"
 	"Source/BsVulkanSwapChain.cpp"
 	"Source/BsVulkanFramebuffer.cpp"
 	"Source/BsVulkanFramebuffer.cpp"
+	"Source/BsVulkanUtility.cpp"
 )
 )
 
 
 set(BS_BANSHEEVULKANRENDERAPI_SRC_MANAGERS
 set(BS_BANSHEEVULKANRENDERAPI_SRC_MANAGERS

+ 16 - 0
Source/BansheeVulkanRenderAPI/Include/BsVulkanDevice.h

@@ -58,7 +58,23 @@ namespace BansheeEngine
 
 
 		/** Returns index of the queue family for the specified queue type. */
 		/** Returns index of the queue family for the specified queue type. */
 		UINT32 getQueueFamily(VulkanQueueType type) const { return mQueueInfos[(int)type].familyIdx; }
 		UINT32 getQueueFamily(VulkanQueueType type) const { return mQueueInfos[(int)type].familyIdx; }
+
+		/** Allocates memory for the provided image, and binds it to the image. */
+		VkDeviceMemory allocateMemory(VkImage image, VkMemoryPropertyFlags flags);
+
+		/** Allocates memory for the provided buffer, and binds it to the buffer. */
+		VkDeviceMemory allocateMemory(VkBuffer buffer, VkMemoryPropertyFlags flags);
+
+		/** Allocates a block of memory according to the provided memory requirements. */
+		VkDeviceMemory allocateMemory(const VkMemoryRequirements& reqs, VkMemoryPropertyFlags flags);
+
+		/** Frees a previously allocated block of memory. */
+		void freeMemory(VkDeviceMemory memory);
+
 	private:
 	private:
+		/** Attempts to find a memory type that matches the requirements bits and the requested flags. */
+		uint32_t findMemoryType(uint32_t requirementBits, VkMemoryPropertyFlags wantedFlags);
+
 		VkPhysicalDevice mPhysicalDevice;
 		VkPhysicalDevice mPhysicalDevice;
 		VkDevice mLogicalDevice;
 		VkDevice mLogicalDevice;
 
 

+ 74 - 0
Source/BansheeVulkanRenderAPI/Include/BsVulkanFramebuffer.h

@@ -1 +1,75 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #pragma once
 #pragma once
+
+#include "BsVulkanPrerequisites.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup Vulkan
+	 *  @{
+	 */
+
+	/** Represents a single attachment in a Vulkan frame-buffer. */
+	struct VULKAN_ATTACHMENT_DESC
+	{
+		/** View of the image to attach or VK_NULL_HANDLE if none. */
+		VkImageView view;
+
+		/** Format of the attached image. */
+		VkFormat format;
+	};
+
+	/** Contains parameters used for initializing VulkanFrameBuffer object. */
+	struct VULKAN_FRAMEBUFFER_DESC
+	{
+		/** Images describing the color attachments. */
+		VULKAN_ATTACHMENT_DESC color[BS_MAX_MULTIPLE_RENDER_TARGETS];
+
+		/** Image describing the depth attachment. */
+		VULKAN_ATTACHMENT_DESC depth;
+
+		/** Width of the images, in pixels. All images must be the same size. */
+		UINT32 width;
+
+		/** Height of the images, in pixels. All images must be the same size. */
+		UINT32 height;
+
+		/** Number of image layers to render to. */
+		UINT32 layers;
+
+		/** Number of samples in the attached images. All attachments must have the same number of samples. */
+		UINT32 numSamples;
+
+		/** Set to true if framebuffer represents an offscreen surface that will not be presented. */
+		bool offscreen;
+	};
+
+	/** Vulkan frame buffer containing one or multiple color surfaces, and an optional depth surface. */
+	class VulkanFramebuffer : INonCopyable
+	{
+	public:
+		/** Creates a new frame buffer with the specified image views attached. 
+		 *
+		 * @param[in]	device	Device to create the frame buffer on. All attachment images provided in the @p desc
+		 *						parameter must also belong to this device.
+		 * @param[in]	desc	Description of the frame buffer.
+		 */
+		VulkanFramebuffer(const SPtr<VulkanDevice>& device, const VULKAN_FRAMEBUFFER_DESC& desc);
+		~VulkanFramebuffer();
+
+		/** Gets internal Vulkan render pass object. */
+		VkRenderPass getRenderPass() const { return mRenderPass; }
+
+		/** Gets internal Vulkan framebuffer object. */
+		VkFramebuffer getFramebuffer() const { return mFramebuffer; }
+
+	private:
+		VkRenderPass mRenderPass;
+		VkFramebuffer mFramebuffer;
+
+		SPtr<VulkanDevice> mDevice;
+	};
+
+	/** @} */
+}

+ 1 - 0
Source/BansheeVulkanRenderAPI/Include/BsVulkanPrerequisites.h

@@ -36,6 +36,7 @@ namespace BansheeEngine
 	class VulkanHardwareBuffer;
 	class VulkanHardwareBuffer;
 	class VulkanDevice;
 	class VulkanDevice;
 	class VulkanGLSLProgramFactory;
 	class VulkanGLSLProgramFactory;
+	class VulkanSwapChain;
 
 
 	VkAllocationCallbacks* gVulkanAllocator = nullptr;
 	VkAllocationCallbacks* gVulkanAllocator = nullptr;
 
 

+ 29 - 11
Source/BansheeVulkanRenderAPI/Include/BsVulkanSwapChain.h

@@ -3,7 +3,6 @@
 #pragma once
 #pragma once
 
 
 #include "BsVulkanPrerequisites.h"
 #include "BsVulkanPrerequisites.h"
-#include "BsEventQuery.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
@@ -11,8 +10,19 @@ namespace BansheeEngine
 	 *  @{
 	 *  @{
 	 */
 	 */
 
 
+#define BS_NUM_BACK_BUFFERS 1
+
+	/** Description of a single swap chain surface. */
+	struct SwapChainSurface
+	{
+		VkImage image;
+		VkImageView view;
+		VkSemaphore sync;
+		bool acquired;
+	};
+
 	/** Vulkan swap chain containing two or more buffers for rendering and presenting onto the screen. */
 	/** Vulkan swap chain containing two or more buffers for rendering and presenting onto the screen. */
-	class VulkanSwapChain
+	class VulkanSwapChain : INonCopyable
 	{
 	{
 	public:
 	public:
 		~VulkanSwapChain();
 		~VulkanSwapChain();
@@ -21,8 +31,6 @@ namespace BansheeEngine
 		void rebuild(const SPtr<VulkanDevice>& device, VkSurfaceKHR surface, UINT32 width, UINT32 height, bool vsync, 
 		void rebuild(const SPtr<VulkanDevice>& device, VkSurfaceKHR surface, UINT32 width, UINT32 height, bool vsync, 
 			VkFormat colorFormat, VkColorSpaceKHR colorSpace);
 			VkFormat colorFormat, VkColorSpaceKHR colorSpace);
 
 
-		// TODO - swapBuffers(), getBackBuffer()
-
 		/**
 		/**
 		 * Returns the actual width of the swap chain, in pixels. This might differ from the requested size in case it
 		 * Returns the actual width of the swap chain, in pixels. This might differ from the requested size in case it
 		 * wasn't supported.
 		 * wasn't supported.
@@ -34,20 +42,30 @@ namespace BansheeEngine
 		 * wasn't supported.
 		 * wasn't supported.
 		 */
 		 */
 		UINT32 getHeight() const { return mHeight; }
 		UINT32 getHeight() const { return mHeight; }
-	private:
-		/** Description of a single swap chain surface. */
-		struct SwapChainSurface
-		{
-			VkImage image;
-			VkImageView view;
-		};
 
 
+		/** 
+		 * Presents the back buffer to the output device, swapping the buffers. 
+		 *
+		 * @param[in]	queue		Queue on which to queue the present operation. Must support present operations.
+		 * @param[in]	semaphore	Optional semaphore to wait on before presenting the queue.
+		 */
+		void present(VkQueue queue, VkSemaphore semaphore);
+
+		/**
+		 * Returns the current back buffer image. 
+		 * 
+		 * @note Must only be called once in-between present() calls, or before the first present() call.
+		 */
+		SwapChainSurface acquireBackBuffer();
+	private:
 		SPtr<VulkanDevice> mDevice;
 		SPtr<VulkanDevice> mDevice;
 		VkSwapchainKHR mSwapChain = VK_NULL_HANDLE;
 		VkSwapchainKHR mSwapChain = VK_NULL_HANDLE;
 
 
 		UINT32 mWidth = 0;
 		UINT32 mWidth = 0;
 		UINT32 mHeight = 0;
 		UINT32 mHeight = 0;
 		Vector<SwapChainSurface> mSurfaces;
 		Vector<SwapChainSurface> mSurfaces;
+		UINT32 mCurrentSemaphoreIdx = 0;
+		UINT32 mCurrentBackBufferIdx = 0;
 	};
 	};
 
 
 	/** @} */
 	/** @} */

+ 26 - 0
Source/BansheeVulkanRenderAPI/Include/BsVulkanUtility.h

@@ -0,0 +1,26 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsVulkanPrerequisites.h"
+#include "BsPixelUtil.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup Vulkan
+	 *  @{
+	 */
+
+	/** Contains various helper methods for dealing with Vulkan. */
+	class VulkanUtility
+	{
+	public:
+		/** Converts between Banshee and Vulkan pixel format. */
+		static VkFormat getPixelFormat(PixelFormat format, bool sRGB = false);
+
+		/** Gets Vulkan flags representing the number of samples in an image. Sample count must be a power of 2. */
+		static VkSampleCountFlagBits getSampleFlags(UINT32 numSamples);
+	};
+
+	/** @} */
+}

+ 1 - 1
Source/BansheeVulkanRenderAPI/Include/Win32/BsWin32RenderWindow.h

@@ -4,7 +4,6 @@
 
 
 #include "BsVulkanPrerequisites.h"
 #include "BsVulkanPrerequisites.h"
 #include "BsRenderWindow.h"
 #include "BsRenderWindow.h"
-#include <windows.h>
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
@@ -111,6 +110,7 @@ namespace BansheeEngine
 		VkSurfaceKHR mSurface;
 		VkSurfaceKHR mSurface;
 		VkColorSpaceKHR mColorSpace;
 		VkColorSpaceKHR mColorSpace;
 		VkFormat mColorFormat;
 		VkFormat mColorFormat;
+		SPtr<VulkanSwapChain> mSwapChain;
 
 
 		Win32RenderWindowProperties mProperties;
 		Win32RenderWindowProperties mProperties;
 		Win32RenderWindowProperties mSyncedProperties;
 		Win32RenderWindowProperties mSyncedProperties;

+ 63 - 0
Source/BansheeVulkanRenderAPI/Source/BsVulkanDevice.cpp

@@ -102,4 +102,67 @@ namespace BansheeEngine
 		vkDeviceWaitIdle(mLogicalDevice);
 		vkDeviceWaitIdle(mLogicalDevice);
 		vkDestroyDevice(mLogicalDevice, gVulkanAllocator);
 		vkDestroyDevice(mLogicalDevice, gVulkanAllocator);
 	}
 	}
+
+	VkDeviceMemory VulkanDevice::allocateMemory(VkImage image, VkMemoryPropertyFlags flags)
+	{
+		VkMemoryRequirements memReq;
+		vkGetImageMemoryRequirements(mLogicalDevice, image, &memReq);
+
+		VkDeviceMemory memory = allocateMemory(memReq, flags);
+
+		VkResult result = vkBindImageMemory(mLogicalDevice, image, memory, 0);
+		assert(result == VK_SUCCESS);
+
+		return memory;
+	}
+
+	VkDeviceMemory VulkanDevice::allocateMemory(VkBuffer buffer, VkMemoryPropertyFlags flags)
+	{
+		VkMemoryRequirements memReq;
+		vkGetBufferMemoryRequirements(mLogicalDevice, buffer, &memReq);
+
+		VkDeviceMemory memory = allocateMemory(memReq, flags);
+
+		VkResult result = vkBindBufferMemory(mLogicalDevice, buffer, memory, 0);
+		assert(result == VK_SUCCESS);
+
+		return memory;
+	}
+
+	VkDeviceMemory VulkanDevice::allocateMemory(const VkMemoryRequirements& reqs, VkMemoryPropertyFlags flags)
+	{
+		VkMemoryAllocateInfo allocateInfo;
+		allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+		allocateInfo.pNext = nullptr;
+		allocateInfo.memoryTypeIndex = findMemoryType(reqs.memoryTypeBits, flags);
+		allocateInfo.allocationSize = reqs.size;
+
+		VkDeviceMemory memory;
+
+		VkResult result = vkAllocateMemory(mLogicalDevice, &allocateInfo, gVulkanAllocator, &memory);
+		assert(result == VK_SUCCESS);
+
+		return memory;
+	}
+
+	void VulkanDevice::freeMemory(VkDeviceMemory memory)
+	{
+		vkFreeMemory(mLogicalDevice, memory, gVulkanAllocator);
+	}
+
+	uint32_t VulkanDevice::findMemoryType(uint32_t requirementBits, VkMemoryPropertyFlags wantedFlags)
+	{
+		for (uint32_t i = 0; i < mMemoryProperties.memoryTypeCount; i++)
+		{
+			if (requirementBits & (1 << i))
+			{
+				if ((mMemoryProperties.memoryTypes[i].propertyFlags & wantedFlags) == wantedFlags)
+					return i;
+			}
+
+			requirementBits >>= 1;
+		}
+
+		return -1;
+	}
 }
 }

+ 148 - 0
Source/BansheeVulkanRenderAPI/Source/BsVulkanFramebuffer.cpp

@@ -0,0 +1,148 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsVulkanFramebuffer.h"
+#include "BsVulkanUtility.h"
+#include "BsVulkanDevice.h"
+
+namespace BansheeEngine
+{
+	VulkanFramebuffer::VulkanFramebuffer(const SPtr<VulkanDevice>& device, const VULKAN_FRAMEBUFFER_DESC& desc)
+		:mDevice(device)
+	{
+		// Create render state
+		VkAttachmentDescription attachments[BS_MAX_MULTIPLE_RENDER_TARGETS + 1];
+		VkImageView attachmentViews[BS_MAX_MULTIPLE_RENDER_TARGETS + 1];
+		VkAttachmentReference colorReferences[BS_MAX_MULTIPLE_RENDER_TARGETS];
+		VkAttachmentReference depthReference;
+
+		VkSampleCountFlagBits sampleFlags = VulkanUtility::getSampleFlags(desc.numSamples);
+
+		UINT32 attachmentIdx = 0;
+		for(UINT32 i = 0; i < BS_MAX_MULTIPLE_RENDER_TARGETS; i++)
+		{
+			if (desc.color[i].view == VK_NULL_HANDLE)
+				continue;
+
+			VkAttachmentDescription& attachmentDesc = attachments[attachmentIdx];
+			attachmentDesc.flags = 0;
+			attachmentDesc.format = desc.color[i].format;
+			attachmentDesc.samples = sampleFlags;
+			attachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+			attachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+			attachmentDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+			attachmentDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+			attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+
+			// If offscreen we make the assumption the surface will be read by a shader
+			if(desc.offscreen)
+				attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+			else
+				attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+
+			VkAttachmentReference& ref = colorReferences[attachmentIdx];
+			ref.attachment = attachmentIdx;
+			ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+
+			attachmentViews[attachmentIdx] = desc.color[i].view;
+;			attachmentIdx++;
+		}
+
+		UINT32 numColorAttachments = attachmentIdx;
+		bool hasDepthAttachment = desc.depth.view != VK_NULL_HANDLE;
+
+		if (hasDepthAttachment)
+		{
+			VkAttachmentDescription& attachmentDesc = attachments[attachmentIdx];
+			attachmentDesc.flags = 0;
+			attachmentDesc.format = desc.depth.format;
+			attachmentDesc.samples = sampleFlags;
+			attachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+			attachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+			attachmentDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+			attachmentDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
+			attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+			attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+
+			VkAttachmentReference& ref = depthReference;
+			ref.attachment = attachmentIdx;
+			ref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+
+			attachmentViews[attachmentIdx] = desc.depth.view;
+			attachmentIdx++;
+		}
+
+		VkSubpassDescription subpassDesc;
+		subpassDesc.flags = 0;
+		subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+		subpassDesc.colorAttachmentCount = numColorAttachments;
+		subpassDesc.inputAttachmentCount = 0;
+		subpassDesc.pInputAttachments = nullptr;
+		subpassDesc.preserveAttachmentCount = 0;
+		subpassDesc.pPreserveAttachments = nullptr;
+		subpassDesc.pResolveAttachments = nullptr;
+
+		if (numColorAttachments > 0)
+			subpassDesc.pColorAttachments = colorReferences;
+		else
+			subpassDesc.pColorAttachments = nullptr;
+
+		if (hasDepthAttachment)
+			subpassDesc.pDepthStencilAttachment = &depthReference;
+		else
+			subpassDesc.pDepthStencilAttachment = nullptr;
+
+		// Subpass dependencies for layout transitions
+		VkSubpassDependency dependencies[2];
+
+		dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
+		dependencies[0].dstSubpass = 0;
+		dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
+		dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+		dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
+		dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; // Note: Do we really need read access?
+		dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; // Note: Is this really required?
+
+		dependencies[1].srcSubpass = 0;
+		dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
+		dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+		dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
+		dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+		dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
+		dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;// Note: Is this really required?
+
+		VkRenderPassCreateInfo renderPassCI;
+		renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+		renderPassCI.pNext = nullptr;
+		renderPassCI.flags = 0;
+		renderPassCI.attachmentCount = attachmentIdx;
+		renderPassCI.pAttachments = attachments;
+		renderPassCI.subpassCount = 1;
+		renderPassCI.pSubpasses = &subpassDesc;
+		renderPassCI.dependencyCount = 2;
+		renderPassCI.pDependencies = dependencies;
+
+		VkResult result = vkCreateRenderPass(device->getLogical(), &renderPassCI, gVulkanAllocator, &mRenderPass);
+		assert(result == VK_SUCCESS);
+
+		// Create frame buffer
+		VkFramebufferCreateInfo framebufferCI;
+		framebufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+		framebufferCI.pNext = nullptr;
+		framebufferCI.flags = 0;
+		framebufferCI.renderPass = mRenderPass;
+		framebufferCI.attachmentCount = attachmentIdx;
+		framebufferCI.pAttachments = attachmentViews;
+		framebufferCI.width = desc.width;
+		framebufferCI.height = desc.height;
+		framebufferCI.layers = desc.layers;
+
+		result = vkCreateFramebuffer(device->getLogical(), &framebufferCI, gVulkanAllocator, &mFramebuffer);
+		assert(result == VK_SUCCESS);
+	}
+
+	VulkanFramebuffer::~VulkanFramebuffer()
+	{
+		vkDestroyFramebuffer(mDevice->getLogical(), mFramebuffer, gVulkanAllocator);
+		vkDestroyRenderPass(mDevice->getLogical(), mRenderPass, gVulkanAllocator);
+	}
+}

+ 99 - 36
Source/BansheeVulkanRenderAPI/Source/BsVulkanSwapChain.cpp

@@ -12,7 +12,10 @@ namespace BansheeEngine
 		{
 		{
 			VkDevice logicalDevice = mDevice->getLogical();
 			VkDevice logicalDevice = mDevice->getLogical();
 			for (auto& surface : mSurfaces)
 			for (auto& surface : mSurfaces)
+			{
+				vkDestroySemaphore(logicalDevice, surface.sync, gVulkanAllocator);
 				vkDestroyImageView(logicalDevice, surface.view, gVulkanAllocator);
 				vkDestroyImageView(logicalDevice, surface.view, gVulkanAllocator);
+			}
 
 
 			vkDestroySwapchainKHR(logicalDevice, mSwapChain, gVulkanAllocator);
 			vkDestroySwapchainKHR(logicalDevice, mSwapChain, gVulkanAllocator);
 		}
 		}
@@ -83,7 +86,7 @@ namespace BansheeEngine
 
 
 		bs_stack_free(presentModes);
 		bs_stack_free(presentModes);
 
 
-		uint32_t numImages = std::min(surfaceCaps.minImageCount + 1, surfaceCaps.maxImageCount);
+		uint32_t numImages = std::min(surfaceCaps.minImageCount + BS_NUM_BACK_BUFFERS, surfaceCaps.maxImageCount);
 
 
 		VkSurfaceTransformFlagsKHR transform;
 		VkSurfaceTransformFlagsKHR transform;
 		if (surfaceCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
 		if (surfaceCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
@@ -91,29 +94,29 @@ namespace BansheeEngine
 		else
 		else
 			transform = surfaceCaps.currentTransform;
 			transform = surfaceCaps.currentTransform;
 
 
-		VkSwapchainCreateInfoKHR swapChainCreateInfo;
-		swapChainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
-		swapChainCreateInfo.pNext = nullptr;
-		swapChainCreateInfo.flags = 0;
-		swapChainCreateInfo.surface = surface;
-		swapChainCreateInfo.minImageCount = numImages;
-		swapChainCreateInfo.imageFormat = colorFormat;
-		swapChainCreateInfo.imageColorSpace = colorSpace;
-		swapChainCreateInfo.imageExtent = { swapchainExtent.width, swapchainExtent.height };
-		swapChainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
-		swapChainCreateInfo.preTransform = (VkSurfaceTransformFlagBitsKHR)transform;
-		swapChainCreateInfo.imageArrayLayers = 1;
-		swapChainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
-		swapChainCreateInfo.queueFamilyIndexCount = 0;
-		swapChainCreateInfo.pQueueFamilyIndices = NULL;
-		swapChainCreateInfo.presentMode = presentMode;
-		swapChainCreateInfo.oldSwapchain = mSwapChain;
-		swapChainCreateInfo.clipped = VK_TRUE;
-		swapChainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+		VkSwapchainCreateInfoKHR swapChainCI;
+		swapChainCI.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+		swapChainCI.pNext = nullptr;
+		swapChainCI.flags = 0;
+		swapChainCI.surface = surface;
+		swapChainCI.minImageCount = numImages;
+		swapChainCI.imageFormat = colorFormat;
+		swapChainCI.imageColorSpace = colorSpace;
+		swapChainCI.imageExtent = { swapchainExtent.width, swapchainExtent.height };
+		swapChainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+		swapChainCI.preTransform = (VkSurfaceTransformFlagBitsKHR)transform;
+		swapChainCI.imageArrayLayers = 1;
+		swapChainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+		swapChainCI.queueFamilyIndexCount = 0;
+		swapChainCI.pQueueFamilyIndices = NULL;
+		swapChainCI.presentMode = presentMode;
+		swapChainCI.oldSwapchain = mSwapChain;
+		swapChainCI.clipped = VK_TRUE;
+		swapChainCI.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
 
 
 		VkSwapchainKHR oldSwapChain = mSwapChain;
 		VkSwapchainKHR oldSwapChain = mSwapChain;
 		VkDevice logicalDevice = device->getLogical();
 		VkDevice logicalDevice = device->getLogical();
-		result = vkCreateSwapchainKHR(logicalDevice, &swapChainCreateInfo, gVulkanAllocator, &mSwapChain);
+		result = vkCreateSwapchainKHR(logicalDevice, &swapChainCI, gVulkanAllocator, &mSwapChain);
 		assert(result == VK_SUCCESS);
 		assert(result == VK_SUCCESS);
 
 
 		if (oldSwapChain != VK_NULL_HANDLE)
 		if (oldSwapChain != VK_NULL_HANDLE)
@@ -135,30 +138,90 @@ namespace BansheeEngine
 		mSurfaces.resize(numImages);
 		mSurfaces.resize(numImages);
 		for (UINT32 i = 0; i < numImages; i++)
 		for (UINT32 i = 0; i < numImages; i++)
 		{
 		{
-			VkImageViewCreateInfo colorAttachmentView;
-			colorAttachmentView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
-			colorAttachmentView.pNext = nullptr;
-			colorAttachmentView.flags = 0;
-			colorAttachmentView.format = colorFormat;
-			colorAttachmentView.components = {
+			VkImageViewCreateInfo colorViewCI;
+			colorViewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+			colorViewCI.pNext = nullptr;
+			colorViewCI.flags = 0;
+			colorViewCI.format = colorFormat;
+			colorViewCI.components = {
 				VK_COMPONENT_SWIZZLE_R,
 				VK_COMPONENT_SWIZZLE_R,
 				VK_COMPONENT_SWIZZLE_G,
 				VK_COMPONENT_SWIZZLE_G,
 				VK_COMPONENT_SWIZZLE_B,
 				VK_COMPONENT_SWIZZLE_B,
 				VK_COMPONENT_SWIZZLE_A
 				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.image = images[i];
-
+			colorViewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+			colorViewCI.subresourceRange.baseMipLevel = 0;
+			colorViewCI.subresourceRange.levelCount = 1;
+			colorViewCI.subresourceRange.baseArrayLayer = 0;
+			colorViewCI.subresourceRange.layerCount = 1;
+			colorViewCI.viewType = VK_IMAGE_VIEW_TYPE_2D;
+			colorViewCI.image = images[i];
+
+			mSurfaces[i].acquired = false;
 			mSurfaces[i].image = images[i];
 			mSurfaces[i].image = images[i];
-			result = vkCreateImageView(logicalDevice, &colorAttachmentView, gVulkanAllocator, &mSurfaces[i].view);
+			result = vkCreateImageView(logicalDevice, &colorViewCI, gVulkanAllocator, &mSurfaces[i].view);
+			assert(result == VK_SUCCESS);
+
+			VkSemaphoreCreateInfo semaphoreCI;
+			semaphoreCI.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+			semaphoreCI.pNext = nullptr;
+			semaphoreCI.flags = 0;
+
+			result = vkCreateSemaphore(logicalDevice, &semaphoreCI, gVulkanAllocator, &mSurfaces[i].sync);
 			assert(result == VK_SUCCESS);
 			assert(result == VK_SUCCESS);
 		}
 		}
 
 
 		bs_stack_free(images);
 		bs_stack_free(images);
 	}
 	}
+
+	void VulkanSwapChain::present(VkQueue queue, VkSemaphore semaphore)
+	{
+		assert(mSurfaces[mCurrentBackBufferIdx].acquired && "Attempting to present an unacquired back buffer.");
+		mSurfaces[mCurrentBackBufferIdx].acquired = false;
+
+		VkPresentInfoKHR presentInfo;
+		presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
+		presentInfo.pNext = nullptr;
+		presentInfo.swapchainCount = 1;
+		presentInfo.pSwapchains = &mSwapChain;
+		presentInfo.pImageIndices = &mCurrentBackBufferIdx;
+		presentInfo.pResults = nullptr;
+
+		// Wait before presenting, if required
+		if (semaphore != VK_NULL_HANDLE)
+		{
+			presentInfo.pWaitSemaphores = &semaphore;
+			presentInfo.waitSemaphoreCount = 1;
+		}
+		else
+		{
+			presentInfo.pWaitSemaphores = nullptr;
+			presentInfo.waitSemaphoreCount = 0;
+		}
+
+		VkResult result = vkQueuePresentKHR(queue, &presentInfo);
+		assert(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR);
+	}
+
+	SwapChainSurface VulkanSwapChain::acquireBackBuffer()
+	{
+		uint32_t imageIndex;
+
+		VkResult result = vkAcquireNextImageKHR(mDevice->getLogical(), mSwapChain, UINT64_MAX,
+			mSurfaces[mCurrentSemaphoreIdx].sync, VK_NULL_HANDLE, &imageIndex);
+		assert(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR);
+
+		// In case surfaces aren't being distributed in round-robin fashion the image and semaphore indices might not match,
+		// in which case just move the semaphores
+		if(imageIndex != mCurrentSemaphoreIdx)
+			std::swap(mSurfaces[mCurrentSemaphoreIdx].sync, mSurfaces[imageIndex].sync);
+
+		mCurrentSemaphoreIdx = (mCurrentSemaphoreIdx + 1) % mSurfaces.size();
+
+		assert(!mSurfaces[imageIndex].acquired && "Same swap chain surface being acquired twice in a row without present().");
+		mSurfaces[imageIndex].acquired = true;
+
+		mCurrentBackBufferIdx = imageIndex;
+		return mSurfaces[imageIndex];
+	}
 }
 }

+ 130 - 0
Source/BansheeVulkanRenderAPI/Source/BsVulkanUtility.cpp

@@ -0,0 +1,130 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsVulkanUtility.h"
+#include "BsException.h"
+
+namespace BansheeEngine
+{
+	VkFormat VulkanUtility::getPixelFormat(PixelFormat format, bool sRGB)
+	{
+		switch (format)
+		{
+		case PF_R8:
+			if(sRGB)
+				return VK_FORMAT_R8_SRGB;
+
+			return VK_FORMAT_R8_UNORM;
+		case PF_R8G8:
+			if (sRGB)
+				return VK_FORMAT_R8G8_SRGB;
+
+			return VK_FORMAT_R8G8_UNORM;
+		case PF_R8G8B8:
+			if (sRGB)
+				return VK_FORMAT_R8G8B8_SRGB;
+
+			return VK_FORMAT_R8G8B8_UNORM;
+		case PF_R8G8B8A8:
+			if (sRGB)
+				return VK_FORMAT_R8G8B8A8_SRGB;
+
+			return VK_FORMAT_R8G8B8A8_UNORM;
+		case PF_B8G8R8A8:
+		case PF_B8G8R8X8:
+			if (sRGB)
+				return VK_FORMAT_B8G8R8A8_SRGB;
+
+			return VK_FORMAT_B8G8R8A8_UNORM;
+		case PF_FLOAT16_R:
+			return VK_FORMAT_R16_SFLOAT;
+		case PF_FLOAT16_RG:
+			return VK_FORMAT_R16G16_SFLOAT;
+		case PF_FLOAT16_RGB:
+			return VK_FORMAT_R16G16B16_SFLOAT;
+		case PF_FLOAT16_RGBA:
+			return VK_FORMAT_R16G16B16A16_SFLOAT;
+		case PF_FLOAT32_R:
+			return VK_FORMAT_R32_SFLOAT;
+		case PF_FLOAT32_RG:
+			return VK_FORMAT_R32G32_SFLOAT;
+		case PF_FLOAT32_RGB:
+			return VK_FORMAT_R32G32B32_SFLOAT;
+		case PF_FLOAT32_RGBA:
+			return VK_FORMAT_R32G32B32A32_SFLOAT;
+		case PF_BC1:
+			if (sRGB)
+				return VK_FORMAT_BC1_RGB_SRGB_BLOCK;
+
+			return VK_FORMAT_BC1_RGB_UNORM_BLOCK;
+		case PF_BC1a:
+			if (sRGB)
+				return VK_FORMAT_BC1_RGBA_SRGB_BLOCK;
+
+			return VK_FORMAT_BC1_RGBA_UNORM_BLOCK;
+		case PF_BC2:
+			if (sRGB)
+				return VK_FORMAT_BC2_SRGB_BLOCK;
+
+			return VK_FORMAT_BC2_UNORM_BLOCK;
+		case PF_BC3:
+			if (sRGB)
+				return VK_FORMAT_BC3_SRGB_BLOCK;
+
+			return VK_FORMAT_BC3_UNORM_BLOCK;
+		case PF_BC4:
+			return VK_FORMAT_BC4_SNORM_BLOCK;
+		case PF_BC5:
+			return VK_FORMAT_BC5_UNORM_BLOCK;
+		case PF_BC6H:
+			return VK_FORMAT_BC6H_SFLOAT_BLOCK;
+		case PF_BC7:
+			if (sRGB)
+				return VK_FORMAT_BC7_SRGB_BLOCK;
+
+			return VK_FORMAT_BC7_UNORM_BLOCK;
+		case PF_D32_S8X24:
+			return VK_FORMAT_D32_SFLOAT_S8_UINT;
+		case PF_D24S8:
+			return VK_FORMAT_D24_UNORM_S8_UINT;
+		case PF_D32:
+			return VK_FORMAT_D32_SFLOAT;
+		case PF_D16:
+			return VK_FORMAT_D16_UNORM;
+		case PF_FLOAT_R11G11B10:
+			return VK_FORMAT_B10G11R11_UFLOAT_PACK32;
+		case PF_UNORM_R10G10B10A2:
+			return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
+		case PF_A8R8G8B8:
+		case PF_A8B8G8R8:
+		case PF_X8R8G8B8:
+		case PF_X8B8G8R8:
+		case PF_UNKNOWN:
+		default:
+			return VK_FORMAT_UNDEFINED;
+		}
+	}
+
+	VkSampleCountFlagBits VulkanUtility::getSampleFlags(UINT32 numSamples)
+	{
+		switch(numSamples)
+		{
+		case 1:
+			return VK_SAMPLE_COUNT_1_BIT;
+		case 2:
+			return VK_SAMPLE_COUNT_2_BIT;
+		case 4:
+			return VK_SAMPLE_COUNT_4_BIT;
+		case 8:
+			return VK_SAMPLE_COUNT_8_BIT;
+		case 16:
+			return VK_SAMPLE_COUNT_16_BIT;
+		case 32:
+			return VK_SAMPLE_COUNT_32_BIT;
+		case 64:
+			return VK_SAMPLE_COUNT_64_BIT;
+		}
+
+		BS_EXCEPT(RenderingAPIException, "Unsupported sample count: " + toString(numSamples));
+		return VK_SAMPLE_COUNT_1_BIT;
+	}
+}

+ 14 - 7
Source/BansheeVulkanRenderAPI/Source/Win32/BsWin32RenderWindow.cpp

@@ -9,6 +9,7 @@
 #include "BsRenderWindowManager.h"
 #include "BsRenderWindowManager.h"
 #include "BsVulkanRenderAPI.h"
 #include "BsVulkanRenderAPI.h"
 #include "BsVulkanDevice.h"
 #include "BsVulkanDevice.h"
+#include "BsVulkanSwapChain.h"
 #include "BsMath.h"
 #include "BsMath.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
@@ -33,9 +34,8 @@ namespace BansheeEngine
 			mWindow = nullptr;
 			mWindow = nullptr;
 		}
 		}
 
 
+		mSwapChain = nullptr;
 		vkDestroySurfaceKHR(mRenderAPI._getInstance(), mSurface, gVulkanAllocator);
 		vkDestroySurfaceKHR(mRenderAPI._getInstance(), mSurface, gVulkanAllocator);
-
-		// TODO - Destroy Vulkan resources
 	}
 	}
 
 
 	void Win32RenderWindowCore::initialize()
 	void Win32RenderWindowCore::initialize()
@@ -194,9 +194,10 @@ namespace BansheeEngine
 		bs_stack_free(formats);
 		bs_stack_free(formats);
 
 
 		// Create swap chain
 		// Create swap chain
-		// TODO - create it, then make sure to get actual width/height from it
+		mSwapChain = bs_shared_ptr_new<VulkanSwapChain>();
+		mSwapChain->rebuild(presentDevice, mSurface, props.mWidth, props.mHeight, props.mVSync, mColorFormat, mColorSpace);
 
 
-		
+		// TODO - Create a framebuffer for each swap chain buffer
 
 
 		// Make the window full screen if required
 		// Make the window full screen if required
 		if (!windowDesc.external)
 		if (!windowDesc.external)
@@ -243,9 +244,12 @@ namespace BansheeEngine
 
 
 	void Win32RenderWindowCore::swapBuffers()
 	void Win32RenderWindowCore::swapBuffers()
 	{
 	{
-		// TODO - Swap buffers
-
 		THROW_IF_NOT_CORE_THREAD;
 		THROW_IF_NOT_CORE_THREAD;
+
+		if (mShowOnSwap)
+			setHidden(false);
+
+		// TODO - Swap buffers
 	}
 	}
 
 
 	void Win32RenderWindowCore::move(INT32 left, INT32 top)
 	void Win32RenderWindowCore::move(INT32 left, INT32 top)
@@ -481,7 +485,10 @@ namespace BansheeEngine
 			props.mHeight = mWindow->getHeight();
 			props.mHeight = mWindow->getHeight();
 		}
 		}
 
 
-		// TODO - Resize swap chain
+		SPtr<VulkanDevice> presentDevice = mRenderAPI._getPresentDevice();
+		mSwapChain->rebuild(presentDevice, mSurface, props.mWidth, props.mHeight, props.mVSync, mColorFormat, mColorSpace);
+
+		// TODO - Update framebuffer?
 
 
 		RenderWindowCore::_windowMovedOrResized();
 		RenderWindowCore::_windowMovedOrResized();
 	}
 	}