瀏覽代碼

Vulkan render window and swap chain (WIP)

BearishSun 9 年之前
父節點
當前提交
d38fc0c070

+ 4 - 0
Source/BansheeVulkanRenderAPI/CMakeSources.cmake

@@ -19,6 +19,8 @@ set(BS_BANSHEEVULKANRENDERAPI_INC_NOFILTER
 	"Include/BsVulkanDevice.h"
 	"Include/BsVulkanDevice.h"
 	"Include/BsVulkanRenderStateManager.h"
 	"Include/BsVulkanRenderStateManager.h"
 	"Include/BsVulkanGpuPipelineState.h"
 	"Include/BsVulkanGpuPipelineState.h"
+	"Include/BsVulkanSwapChain.h"
+	"Include/BsVulkanFramebuffer.h"
 )
 )
 
 
 set(BS_BANSHEEVULKANRENDERAPI_INC_MANAGERS
 set(BS_BANSHEEVULKANRENDERAPI_INC_MANAGERS
@@ -52,6 +54,8 @@ set(BS_BANSHEEVULKANRENDERAPI_SRC_NOFILTER
 	"Source/BsVulkanDevice.cpp"
 	"Source/BsVulkanDevice.cpp"
 	"Source/BsVulkanRenderStateManager.cpp"
 	"Source/BsVulkanRenderStateManager.cpp"
 	"Source/BsVulkanGpuPipelineState.cpp"
 	"Source/BsVulkanGpuPipelineState.cpp"
+	"Source/BsVulkanSwapChain.cpp"
+	"Source/BsVulkanFramebuffer.cpp"
 )
 )
 
 
 set(BS_BANSHEEVULKANRENDERAPI_SRC_MANAGERS
 set(BS_BANSHEEVULKANRENDERAPI_SRC_MANAGERS

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

@@ -55,6 +55,9 @@ namespace BansheeEngine
 
 
 		/** Returns queue of the specified type at the specified index. Index must be in range [0, getNumQueues()) */
 		/** Returns queue of the specified type at the specified index. Index must be in range [0, getNumQueues()) */
 		VkQueue getQueue(VulkanQueueType type, UINT32 idx) const { return mQueueInfos[(int)type].queues[idx]; }
 		VkQueue getQueue(VulkanQueueType type, UINT32 idx) const { return mQueueInfos[(int)type].queues[idx]; }
+
+		/** Returns index of the queue family for the specified queue type. */
+		UINT32 getQueueFamily(VulkanQueueType type) const { return mQueueInfos[(int)type].familyIdx; }
 	private:
 	private:
 		VkPhysicalDevice mPhysicalDevice;
 		VkPhysicalDevice mPhysicalDevice;
 		VkDevice mLogicalDevice;
 		VkDevice mLogicalDevice;

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

@@ -0,0 +1 @@
+#pragma once

+ 10 - 2
Source/BansheeVulkanRenderAPI/Include/BsVulkanPrerequisites.h

@@ -42,6 +42,14 @@ namespace BansheeEngine
 	/**	Vulkan specific types to track resource statistics for. */
 	/**	Vulkan specific types to track resource statistics for. */
 	enum VulkanRenderStatResourceType
 	enum VulkanRenderStatResourceType
 	{
 	{
-		RenderStatObject_PipelineState = 100,
+		RenderStatObject_PipelineState = 100
 	};
 	};
-}
+}
+
+// Macro to get a procedure address based on a Vulkan instance.
+#define GET_INSTANCE_PROC_ADDR(instance, name) \
+	vk##name = reinterpret_cast<PFN_vk##name>(vkGetInstanceProcAddr(instance, "vk"#name));
+
+// Macro to get a procedure address based on a Vulkan device.
+#define GET_DEVICE_PROC_ADDR(device, name) \
+	vk##name = reinterpret_cast<PFN_vk##name>(vkGetDeviceProcAddr(device, "vk"#name));

+ 17 - 0
Source/BansheeVulkanRenderAPI/Include/BsVulkanRenderAPI.h

@@ -116,9 +116,15 @@ namespace BansheeEngine
 		 * @{
 		 * @{
 		 */
 		 */
 
 
+		/** Returns the internal Vulkan instance object. */
+		VkInstance _getInstance() const { return mInstance; }
+
 		/** Returns a Vulkan device at the specified index. Must be in range [0, _getNumDevices()) */
 		/** Returns a Vulkan device at the specified index. Must be in range [0, _getNumDevices()) */
 		SPtr<VulkanDevice> _getDevice(UINT32 idx) const { return mDevices[idx]; }
 		SPtr<VulkanDevice> _getDevice(UINT32 idx) const { return mDevices[idx]; }
 
 
+		/** Returns the primary device that supports swap chain present operations. */
+		SPtr<VulkanDevice> _getPresentDevice() const { return mPrimaryDevices[0]; }
+
 		/** Gets the total number of Vulkan compatible devices available on this system. */
 		/** Gets the total number of Vulkan compatible devices available on this system. */
 		UINT32 _getNumDevices() const { return (UINT32)mDevices.size(); }
 		UINT32 _getNumDevices() const { return (UINT32)mDevices.size(); }
 
 
@@ -151,5 +157,16 @@ namespace BansheeEngine
 #endif
 #endif
 	};
 	};
 
 
+	extern PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR;
+	extern PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
+	extern PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
+	extern PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR;
+
+	extern PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR;
+	extern PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR;
+	extern PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR;
+	extern PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR;
+	extern PFN_vkQueuePresentKHR vkQueuePresentKHR;
+
 	/** @} */
 	/** @} */
 }
 }

+ 3 - 1
Source/BansheeVulkanRenderAPI/Include/BsVulkanRenderWindowManager.h

@@ -26,11 +26,13 @@ namespace BansheeEngine
 	class VulkanRenderWindowCoreManager : public RenderWindowCoreManager
 	class VulkanRenderWindowCoreManager : public RenderWindowCoreManager
 	{
 	{
 	public:
 	public:
-		VulkanRenderWindowCoreManager();
+		VulkanRenderWindowCoreManager(VulkanRenderAPI& renderAPI);
 
 
 	protected:
 	protected:
 		/** @copydoc RenderWindowCoreManager::createInternal */
 		/** @copydoc RenderWindowCoreManager::createInternal */
 		SPtr<RenderWindowCore> createInternal(RENDER_WINDOW_DESC& desc, UINT32 windowId) override;
 		SPtr<RenderWindowCore> createInternal(RENDER_WINDOW_DESC& desc, UINT32 windowId) override;
+
+		VulkanRenderAPI& mRenderAPI;
 	};
 	};
 
 
 	/** @} */
 	/** @} */

+ 54 - 0
Source/BansheeVulkanRenderAPI/Include/BsVulkanSwapChain.h

@@ -0,0 +1,54 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsVulkanPrerequisites.h"
+#include "BsEventQuery.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup Vulkan
+	 *  @{
+	 */
+
+	/** Vulkan swap chain containing two or more buffers for rendering and presenting onto the screen. */
+	class VulkanSwapChain
+	{
+	public:
+		~VulkanSwapChain();
+
+		/** Rebuilds the swap chain with the provided properties. Destroys any previously existing swap chain. */
+		void rebuild(const SPtr<VulkanDevice>& device, VkSurfaceKHR surface, UINT32 width, UINT32 height, bool vsync, 
+			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
+		 * wasn't supported.
+		 */
+		UINT32 getWidth() const { return mWidth; }
+
+		/** 
+		 * Returns the actual height of the swap chain, in pixels. This might differ from the requested size in case it
+		 * wasn't supported.
+		 */
+		UINT32 getHeight() const { return mHeight; }
+	private:
+		/** Description of a single swap chain surface. */
+		struct SwapChainSurface
+		{
+			VkImage image;
+			VkImageView view;
+		};
+
+		SPtr<VulkanDevice> mDevice;
+		VkSwapchainKHR mSwapChain = VK_NULL_HANDLE;
+
+		UINT32 mWidth = 0;
+		UINT32 mHeight = 0;
+		Vector<SwapChainSurface> mSurfaces;
+	};
+
+	/** @} */
+}

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

@@ -34,7 +34,7 @@ namespace BansheeEngine
 	class Win32RenderWindowCore : public RenderWindowCore
 	class Win32RenderWindowCore : public RenderWindowCore
 	{
 	{
 	public:
 	public:
-		Win32RenderWindowCore(const RENDER_WINDOW_DESC& desc, UINT32 windowId);
+		Win32RenderWindowCore(const RENDER_WINDOW_DESC& desc, UINT32 windowId, VulkanRenderAPI& renderAPI);
 		~Win32RenderWindowCore();
 		~Win32RenderWindowCore();
 
 
 		/** @copydoc RenderWindowCore::move */
 		/** @copydoc RenderWindowCore::move */
@@ -103,6 +103,14 @@ namespace BansheeEngine
 
 
 	protected:
 	protected:
 		Win32Window* mWindow;
 		Win32Window* mWindow;
+		bool mIsChild;
+		bool mShowOnSwap;
+		INT32 mDisplayFrequency;
+
+		VulkanRenderAPI& mRenderAPI;
+		VkSurfaceKHR mSurface;
+		VkColorSpaceKHR mColorSpace;
+		VkFormat mColorFormat;
 
 
 		Win32RenderWindowProperties mProperties;
 		Win32RenderWindowProperties mProperties;
 		Win32RenderWindowProperties mSyncedProperties;
 		Win32RenderWindowProperties mSyncedProperties;

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


+ 33 - 12
Source/BansheeVulkanRenderAPI/Source/BsVulkanRenderAPI.cpp

@@ -19,6 +19,16 @@ namespace BansheeEngine
 	PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT = nullptr;
 	PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT = nullptr;
 	PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT = nullptr;
 	PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT = nullptr;
 
 
+	PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR = nullptr;
+	PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR = nullptr;
+	PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr;
+	PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR = nullptr;
+	PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR = nullptr;
+	PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR = nullptr;
+	PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR = nullptr;
+	PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR = nullptr;
+	PFN_vkQueuePresentKHR vkQueuePresentKHR = nullptr;
+
 	VkBool32 debugMsgCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject,
 	VkBool32 debugMsgCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject,
 		size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg, void* pUserData)
 		size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg, void* pUserData)
 	{
 	{
@@ -140,10 +150,8 @@ namespace BansheeEngine
 		VkDebugReportFlagsEXT debugFlags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | 
 		VkDebugReportFlagsEXT debugFlags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | 
 			VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
 			VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
 
 
-		vkCreateDebugReportCallbackEXT = 
-			(PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(mInstance, "vkCreateDebugReportCallbackEXT");
-		vkDestroyDebugReportCallbackEXT = 
-			(PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(mInstance, "vkDestroyDebugReportCallbackEXT");
+		GET_INSTANCE_PROC_ADDR(mInstance, CreateDebugReportCallbackEXT);
+		GET_INSTANCE_PROC_ADDR(mInstance, DestroyDebugReportCallbackEXT);
 
 
 		VkDebugReportCallbackCreateInfoEXT debugInfo;
 		VkDebugReportCallbackCreateInfoEXT debugInfo;
 		debugInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
 		debugInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
@@ -171,7 +179,7 @@ namespace BansheeEngine
 			mDevices[i] = bs_shared_ptr_new<VulkanDevice>(physicalDevices[i]);
 			mDevices[i] = bs_shared_ptr_new<VulkanDevice>(physicalDevices[i]);
 
 
 		// Find primary device
 		// Find primary device
-		// TODO MULTIGPU - Detect multiple similar devices here if supporting multi-GPU
+		// Note: MULTIGPU - Detect multiple similar devices here if supporting multi-GPU
 		for (uint32_t i = 0; i < numDevices; i++)
 		for (uint32_t i = 0; i < numDevices; i++)
 		{
 		{
 			if (mDevices[i]->getDeviceProperties().deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
 			if (mDevices[i]->getDeviceProperties().deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
@@ -184,6 +192,25 @@ namespace BansheeEngine
 		if (mPrimaryDevices.size() == 0)
 		if (mPrimaryDevices.size() == 0)
 			mPrimaryDevices.push_back(mDevices[0]);
 			mPrimaryDevices.push_back(mDevices[0]);
 
 
+#if BS_PLATFORM == BS_PLATFORM_WIN32
+		mVideoModeInfo = bs_shared_ptr_new<Win32VideoModeInfo>();
+#else
+		static_assert(false, "mVideoModeInfo needs to be created.")
+#endif
+
+		// Get required extension functions
+		GET_INSTANCE_PROC_ADDR(mInstance, GetPhysicalDeviceSurfaceSupportKHR);
+		GET_INSTANCE_PROC_ADDR(mInstance, GetPhysicalDeviceSurfaceFormatsKHR);
+		GET_INSTANCE_PROC_ADDR(mInstance, GetPhysicalDeviceSurfaceCapabilitiesKHR);
+		GET_INSTANCE_PROC_ADDR(mInstance, GetPhysicalDeviceSurfacePresentModesKHR);
+
+		VkDevice presentDevice = _getPresentDevice()->getLogical();
+		GET_DEVICE_PROC_ADDR(presentDevice, CreateSwapchainKHR);
+		GET_DEVICE_PROC_ADDR(presentDevice, DestroySwapchainKHR);
+		GET_DEVICE_PROC_ADDR(presentDevice, GetSwapchainImagesKHR);
+		GET_DEVICE_PROC_ADDR(presentDevice, AcquireNextImageKHR);
+		GET_DEVICE_PROC_ADDR(presentDevice, QueuePresentKHR);
+
 		// Create the texture manager for use by others		
 		// Create the texture manager for use by others		
 		TextureManager::startUp<VulkanTextureManager>();
 		TextureManager::startUp<VulkanTextureManager>();
 		TextureCoreManager::startUp<VulkanTextureCoreManager>();
 		TextureCoreManager::startUp<VulkanTextureCoreManager>();
@@ -194,7 +221,7 @@ namespace BansheeEngine
 
 
 		// Create render window manager
 		// Create render window manager
 		RenderWindowManager::startUp<VulkanRenderWindowManager>();
 		RenderWindowManager::startUp<VulkanRenderWindowManager>();
-		RenderWindowCoreManager::startUp<VulkanRenderWindowCoreManager>();
+		RenderWindowCoreManager::startUp<VulkanRenderWindowCoreManager>(*this);
 
 
 		// Create query manager 
 		// Create query manager 
 		QueryManager::startUp<VulkanQueryManager>();
 		QueryManager::startUp<VulkanQueryManager>();
@@ -206,12 +233,6 @@ namespace BansheeEngine
 		RenderStateCoreManager::startUp<VulkanRenderStateCoreManager>();
 		RenderStateCoreManager::startUp<VulkanRenderStateCoreManager>();
 		GpuProgramCoreManager::instance().addFactory(mGLSLFactory);
 		GpuProgramCoreManager::instance().addFactory(mGLSLFactory);
 
 
-#if BS_PLATFORM == BS_PLATFORM_WIN32
-		mVideoModeInfo = bs_shared_ptr_new<Win32VideoModeInfo>();
-#else
-		static_assert(false, "mVideoModeInfo needs to be created.")
-#endif
-
 		// TODO - Create and populate capabilities, per device
 		// TODO - Create and populate capabilities, per device
 		mCurrentCapabilities->addShaderProfile("glsl");
 		mCurrentCapabilities->addShaderProfile("glsl");
 
 

+ 3 - 2
Source/BansheeVulkanRenderAPI/Source/BsVulkanRenderWindowManager.cpp

@@ -22,14 +22,15 @@ namespace BansheeEngine
 		return bs_core_ptr<Win32RenderWindow>(renderWindow);
 		return bs_core_ptr<Win32RenderWindow>(renderWindow);
 	}
 	}
 
 
-	VulkanRenderWindowCoreManager::VulkanRenderWindowCoreManager()
+	VulkanRenderWindowCoreManager::VulkanRenderWindowCoreManager(VulkanRenderAPI& renderAPI)
+		:mRenderAPI(renderAPI)
 	{ }
 	{ }
 
 
 	SPtr<RenderWindowCore> VulkanRenderWindowCoreManager::createInternal(RENDER_WINDOW_DESC& desc, UINT32 windowId)
 	SPtr<RenderWindowCore> VulkanRenderWindowCoreManager::createInternal(RENDER_WINDOW_DESC& desc, UINT32 windowId)
 	{
 	{
 		// Create the window
 		// Create the window
 		Win32RenderWindowCore* renderWindow =
 		Win32RenderWindowCore* renderWindow =
-			new (bs_alloc<Win32RenderWindowCore>()) Win32RenderWindowCore(desc, windowId);
+			new (bs_alloc<Win32RenderWindowCore>()) Win32RenderWindowCore(desc, windowId, mRenderAPI);
 
 
 		SPtr<Win32RenderWindowCore> renderWindowPtr = bs_shared_ptr<Win32RenderWindowCore>(renderWindow);
 		SPtr<Win32RenderWindowCore> renderWindowPtr = bs_shared_ptr<Win32RenderWindowCore>(renderWindow);
 		renderWindowPtr->_setThisPtr(renderWindowPtr);
 		renderWindowPtr->_setThisPtr(renderWindowPtr);

+ 164 - 0
Source/BansheeVulkanRenderAPI/Source/BsVulkanSwapChain.cpp

@@ -0,0 +1,164 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsVulkanSwapChain.h"
+#include "BsVulkanRenderAPI.h"
+#include "BsVulkanDevice.h"
+
+namespace BansheeEngine
+{
+	VulkanSwapChain::~VulkanSwapChain()
+	{
+		if (mSwapChain != VK_NULL_HANDLE)
+		{
+			VkDevice logicalDevice = mDevice->getLogical();
+			for (auto& surface : mSurfaces)
+				vkDestroyImageView(logicalDevice, surface.view, gVulkanAllocator);
+
+			vkDestroySwapchainKHR(logicalDevice, mSwapChain, gVulkanAllocator);
+		}
+	}
+
+	void VulkanSwapChain::rebuild(const SPtr<VulkanDevice>& device, VkSurfaceKHR surface, UINT32 width, UINT32 height, bool vsync,
+		VkFormat colorFormat, VkColorSpaceKHR colorSpace)
+	{
+		mDevice = device;
+
+		VkResult result;
+		VkPhysicalDevice physicalDevice = device->getPhysical();
+
+		// Determine swap chain dimensions
+		VkSurfaceCapabilitiesKHR surfaceCaps;
+		result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfaceCaps);
+		assert(result == VK_SUCCESS);
+
+		VkExtent2D swapchainExtent;
+		// If width/height is 0xFFFFFFFF, we can manually specify width, height
+		if (surfaceCaps.currentExtent.width == (uint32_t)-1 || surfaceCaps.currentExtent.height == (uint32_t)-1)
+		{
+			swapchainExtent.width = width;
+			swapchainExtent.height = height;
+		}
+		else // Otherwise we must use the size we're given
+			swapchainExtent = surfaceCaps.currentExtent;
+
+		mWidth = swapchainExtent.width;
+		mHeight = swapchainExtent.height;
+
+		// Find present mode
+		uint32_t numPresentModes;
+		result = vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &numPresentModes, nullptr);
+		assert(result == VK_SUCCESS);
+		assert(numPresentModes > 0);
+
+		VkPresentModeKHR* presentModes = bs_stack_alloc<VkPresentModeKHR>(numPresentModes);
+		result = vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &numPresentModes, presentModes);
+		assert(result == VK_SUCCESS);
+
+		VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
+		if(!vsync)
+		{
+			for (UINT32 i = 0; i < numPresentModes; i++)
+			{
+				if (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)
+				{
+					presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
+					break;
+				}
+
+				if (presentMode == VK_PRESENT_MODE_FIFO_RELAXED_KHR)
+					presentMode = VK_PRESENT_MODE_FIFO_RELAXED_KHR;
+			}
+		}
+		else
+		{
+			for (UINT32 i = 0; i < numPresentModes; i++)
+			{
+				if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR)
+				{
+					presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
+					break;
+				}
+			}
+		}
+
+		bs_stack_free(presentModes);
+
+		uint32_t numImages = std::min(surfaceCaps.minImageCount + 1, surfaceCaps.maxImageCount);
+
+		VkSurfaceTransformFlagsKHR transform;
+		if (surfaceCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
+			transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
+		else
+			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;
+
+		VkSwapchainKHR oldSwapChain = mSwapChain;
+		VkDevice logicalDevice = device->getLogical();
+		result = vkCreateSwapchainKHR(logicalDevice, &swapChainCreateInfo, gVulkanAllocator, &mSwapChain);
+		assert(result == VK_SUCCESS);
+
+		if (oldSwapChain != VK_NULL_HANDLE)
+		{
+			for(auto& entry : mSurfaces)
+				vkDestroyImageView(logicalDevice, entry.view, gVulkanAllocator);
+
+			vkDestroySwapchainKHR(logicalDevice, oldSwapChain, gVulkanAllocator);
+		}
+
+		result = vkGetSwapchainImagesKHR(logicalDevice, mSwapChain, &numImages, nullptr);
+		assert(result == VK_SUCCESS);
+
+		// Get the swap chain images
+		VkImage* images = bs_stack_alloc<VkImage>(numImages);
+		result = vkGetSwapchainImagesKHR(logicalDevice, mSwapChain, &numImages, images);
+		assert(result == VK_SUCCESS);
+
+		mSurfaces.resize(numImages);
+		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 = {
+				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.image = images[i];
+
+			mSurfaces[i].image = images[i];
+			result = vkCreateImageView(logicalDevice, &colorAttachmentView, gVulkanAllocator, &mSurfaces[i].view);
+			assert(result == VK_SUCCESS);
+		}
+
+		bs_stack_free(images);
+	}
+}

+ 282 - 2
Source/BansheeVulkanRenderAPI/Source/Win32/BsWin32RenderWindow.cpp

@@ -3,9 +3,13 @@
 #include "Win32/BsWin32RenderWindow.h"
 #include "Win32/BsWin32RenderWindow.h"
 #include "Win32/BsWin32Platform.h"
 #include "Win32/BsWin32Platform.h"
 #include "Win32/BsWin32Window.h"
 #include "Win32/BsWin32Window.h"
+#include "Win32/BsWin32VideoModeInfo.h"
 #include "BsCoreThread.h"
 #include "BsCoreThread.h"
 #include "BsRenderStats.h"
 #include "BsRenderStats.h"
 #include "BsRenderWindowManager.h"
 #include "BsRenderWindowManager.h"
+#include "BsVulkanRenderAPI.h"
+#include "BsVulkanDevice.h"
+#include "BsMath.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
@@ -13,23 +17,32 @@ namespace BansheeEngine
 		:RenderWindowProperties(desc)
 		:RenderWindowProperties(desc)
 	{ }
 	{ }
 
 
-	Win32RenderWindowCore::Win32RenderWindowCore(const RENDER_WINDOW_DESC& desc, UINT32 windowId)
-		: RenderWindowCore(desc, windowId), mProperties(desc), mSyncedProperties(desc), mWindow(nullptr)
+	Win32RenderWindowCore::Win32RenderWindowCore(const RENDER_WINDOW_DESC& desc, UINT32 windowId, VulkanRenderAPI& renderAPI)
+		: RenderWindowCore(desc, windowId), mProperties(desc), mSyncedProperties(desc), mWindow(nullptr), mIsChild(false)
+		, mShowOnSwap(false), mDisplayFrequency(0), mRenderAPI(renderAPI)
 	{ }
 	{ }
 
 
 	Win32RenderWindowCore::~Win32RenderWindowCore()
 	Win32RenderWindowCore::~Win32RenderWindowCore()
 	{ 
 	{ 
+		Win32RenderWindowProperties& props = mProperties;
+		props.mActive = false;
+
 		if (mWindow != nullptr)
 		if (mWindow != nullptr)
 		{
 		{
 			bs_delete(mWindow);
 			bs_delete(mWindow);
 			mWindow = nullptr;
 			mWindow = nullptr;
 		}
 		}
+
+		vkDestroySurfaceKHR(mRenderAPI._getInstance(), mSurface, gVulkanAllocator);
+
+		// TODO - Destroy Vulkan resources
 	}
 	}
 
 
 	void Win32RenderWindowCore::initialize()
 	void Win32RenderWindowCore::initialize()
 	{
 	{
 		Win32RenderWindowProperties& props = mProperties;
 		Win32RenderWindowProperties& props = mProperties;
 
 
+		// Create a window
 		WINDOW_DESC windowDesc;
 		WINDOW_DESC windowDesc;
 		windowDesc.border = mDesc.border;
 		windowDesc.border = mDesc.border;
 		windowDesc.enableDoubleClick = mDesc.enableDoubleClick;
 		windowDesc.enableDoubleClick = mDesc.enableDoubleClick;
@@ -61,8 +74,164 @@ namespace BansheeEngine
 		if (opt != mDesc.platformSpecific.end())
 		if (opt != mDesc.platformSpecific.end())
 			windowDesc.external = (HWND)parseUINT64(opt->second);
 			windowDesc.external = (HWND)parseUINT64(opt->second);
 		
 		
+		const Win32VideoModeInfo& videoModeInfo = static_cast<const Win32VideoModeInfo&>(RenderAPICore::instance().getVideoModeInfo());
+		UINT32 numOutputs = videoModeInfo.getNumOutputs();
+		if (numOutputs > 0)
+		{
+			UINT32 actualMonitorIdx = std::min(mDesc.videoMode.getOutputIdx(), numOutputs - 1);
+			const Win32VideoOutputInfo& outputInfo = static_cast<const Win32VideoOutputInfo&>(videoModeInfo.getOutputInfo(actualMonitorIdx));
+			windowDesc.monitor = outputInfo.getMonitorHandle();
+		}
+
+		// Update local properties
 		mWindow = bs_new<Win32Window>(windowDesc);
 		mWindow = bs_new<Win32Window>(windowDesc);
 
 
+		mIsChild = windowDesc.parent != nullptr;
+		mDisplayFrequency = Math::roundToInt(mDesc.videoMode.getRefreshRate());
+
+		props.mIsFullScreen = mDesc.fullscreen && !mIsChild;
+		props.mColorDepth = 32;
+		props.mActive = true;
+		props.mWidth = mWindow->getWidth();
+		props.mHeight = mWindow->getHeight();
+		props.mTop = mWindow->getTop();
+		props.mLeft = mWindow->getLeft();
+
+		if (!windowDesc.external)
+		{
+			mShowOnSwap = mDesc.hideUntilSwap;
+			props.mHidden = mDesc.hideUntilSwap || mDesc.hidden;
+		}
+
+		// Create Vulkan surface
+		VkWin32SurfaceCreateInfoKHR surfaceCreateInfo;
+		surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
+		surfaceCreateInfo.pNext = nullptr;
+		surfaceCreateInfo.flags = 0;
+		surfaceCreateInfo.hwnd = mWindow->getHWnd();
+		surfaceCreateInfo.hinstance = windowDesc.module;
+
+		VkInstance instance = mRenderAPI._getInstance();
+		VkResult result = vkCreateWin32SurfaceKHR(instance, &surfaceCreateInfo, gVulkanAllocator, &mSurface);
+		assert(result == VK_SUCCESS);
+
+		SPtr<VulkanDevice> presentDevice = mRenderAPI._getPresentDevice();
+		VkPhysicalDevice physicalDevice = presentDevice->getPhysical();
+
+		UINT32 presentQueueIdx = presentDevice->getQueueFamily(VQT_GRAPHICS);
+		
+		VkBool32 supportsPresent;
+		vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, presentQueueIdx, mSurface, &supportsPresent);
+
+		if(!supportsPresent)
+		{
+			// Note: Not supporting present only queues at the moment
+			// Note: Also present device can only return one family of graphics queue, while there could be more (some of
+			// which support present)
+			BS_EXCEPT(RenderingAPIException, "Cannot find a graphics queue that also supports present operations.");
+		}
+		
+		uint32_t numFormats;
+		result = vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, mSurface, &numFormats, nullptr);
+		assert(result == VK_SUCCESS);
+		assert(numFormats > 0);
+
+		VkSurfaceFormatKHR* formats = bs_stack_alloc<VkSurfaceFormatKHR>(numFormats);
+
+		Vector<VkSurfaceFormatKHR> surfaceFormats(numFormats);
+		result = vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, mSurface, &numFormats, surfaceFormats.data());
+		assert(result == VK_SUCCESS);
+
+		// If there is no preferred format, use standard RGBA
+		if ((numFormats == 1) && (surfaceFormats[0].format == VK_FORMAT_UNDEFINED))
+		{
+			if (mDesc.gamma)
+				mColorFormat = VK_FORMAT_R8G8B8A8_SRGB;
+			else
+				mColorFormat = VK_FORMAT_B8G8R8A8_UNORM;
+
+			mColorSpace = surfaceFormats[0].colorSpace;
+		}
+		else
+		{
+			bool foundFormat = false;
+
+			VkFormat wantedFormats[] = 
+			{
+				VK_FORMAT_R8G8B8A8_SRGB,
+				VK_FORMAT_A8B8G8R8_SRGB_PACK32,
+				VK_FORMAT_R8G8B8_SRGB,
+			};
+
+			UINT32 numWantedFormats = sizeof(wantedFormats) / sizeof(wantedFormats[0]);
+
+			for(UINT32 i = 0; i < numWantedFormats; i++)
+			{
+				for(UINT32 j = 0; j < numFormats; j++)
+				{
+					if(surfaceFormats[j].format == wantedFormats[i])
+					{
+						mColorFormat = surfaceFormats[j].format;
+						mColorSpace = surfaceFormats[j].colorSpace;
+
+						foundFormat = true;
+						break;
+					}
+				}
+			}
+
+			// If we haven't found anything, fall back to first available
+			if(!foundFormat)
+			{
+				mColorFormat = surfaceFormats[0].format;
+				mColorSpace = surfaceFormats[0].colorSpace;
+
+				if (mDesc.gamma)
+					LOGERR("Cannot find a valid sRGB format for a render window surface, falling back to a default format.");
+			}
+		}
+		
+		bs_stack_free(formats);
+
+		// Create swap chain
+		// TODO - create it, then make sure to get actual width/height from it
+
+		
+
+		// Make the window full screen if required
+		if (!windowDesc.external)
+		{
+			if (props.mIsFullScreen)
+			{
+				DEVMODE displayDeviceMode;
+
+				memset(&displayDeviceMode, 0, sizeof(displayDeviceMode));
+				displayDeviceMode.dmSize = sizeof(DEVMODE);
+				displayDeviceMode.dmBitsPerPel = props.mColorDepth;
+				displayDeviceMode.dmPelsWidth = props.mWidth;
+				displayDeviceMode.dmPelsHeight = props.mHeight;
+				displayDeviceMode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
+
+				if (mDisplayFrequency)
+				{
+					displayDeviceMode.dmDisplayFrequency = mDisplayFrequency;
+					displayDeviceMode.dmFields |= DM_DISPLAYFREQUENCY;
+
+					if (ChangeDisplaySettingsEx(NULL, &displayDeviceMode, NULL, CDS_FULLSCREEN | CDS_TEST, NULL) != DISP_CHANGE_SUCCESSFUL)
+					{
+						BS_EXCEPT(RenderingAPIException, "ChangeDisplaySettings with user display frequency failed.");
+					}
+				}
+
+				if (ChangeDisplaySettingsEx(NULL, &displayDeviceMode, NULL, CDS_FULLSCREEN, NULL) != DISP_CHANGE_SUCCESSFUL)
+				{
+					BS_EXCEPT(RenderingAPIException, "ChangeDisplaySettings failed.");
+				}
+			}
+		}
+
+		// TODO - Set hwGamma and multisample properties
+
 		{
 		{
 			ScopedSpinLock lock(mLock);
 			ScopedSpinLock lock(mLock);
 			mSyncedProperties = props;
 			mSyncedProperties = props;
@@ -74,6 +243,8 @@ namespace BansheeEngine
 
 
 	void Win32RenderWindowCore::swapBuffers()
 	void Win32RenderWindowCore::swapBuffers()
 	{
 	{
+		// TODO - Swap buffers
+
 		THROW_IF_NOT_CORE_THREAD;
 		THROW_IF_NOT_CORE_THREAD;
 	}
 	}
 
 
@@ -136,6 +307,7 @@ namespace BansheeEngine
 	{
 	{
 		THROW_IF_NOT_CORE_THREAD;
 		THROW_IF_NOT_CORE_THREAD;
 
 
+		mShowOnSwap = false;
 		mWindow->setHidden(hidden);
 		mWindow->setHidden(hidden);
 
 
 		RenderWindowCore::setHidden(hidden);
 		RenderWindowCore::setHidden(hidden);
@@ -165,16 +337,111 @@ namespace BansheeEngine
 	void Win32RenderWindowCore::setFullscreen(UINT32 width, UINT32 height, float refreshRate, UINT32 monitorIdx)
 	void Win32RenderWindowCore::setFullscreen(UINT32 width, UINT32 height, float refreshRate, UINT32 monitorIdx)
 	{
 	{
 		THROW_IF_NOT_CORE_THREAD;
 		THROW_IF_NOT_CORE_THREAD;
+
+		if (mIsChild)
+			return;
+
+		const Win32VideoModeInfo& videoModeInfo = static_cast<const Win32VideoModeInfo&>(RenderAPICore::instance().getVideoModeInfo());
+		UINT32 numOutputs = videoModeInfo.getNumOutputs();
+		if (numOutputs == 0)
+			return;
+
+		Win32RenderWindowProperties& props = mProperties;
+
+		UINT32 actualMonitorIdx = std::min(monitorIdx, numOutputs - 1);
+		const Win32VideoOutputInfo& outputInfo = static_cast<const Win32VideoOutputInfo&>(videoModeInfo.getOutputInfo(actualMonitorIdx));
+
+		mDisplayFrequency = Math::roundToInt(refreshRate);
+		props.mIsFullScreen = true;
+
+		DEVMODE displayDeviceMode;
+
+		memset(&displayDeviceMode, 0, sizeof(displayDeviceMode));
+		displayDeviceMode.dmSize = sizeof(DEVMODE);
+		displayDeviceMode.dmBitsPerPel = props.mColorDepth;
+		displayDeviceMode.dmPelsWidth = width;
+		displayDeviceMode.dmPelsHeight = height;
+		displayDeviceMode.dmDisplayFrequency = mDisplayFrequency;
+		displayDeviceMode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
+
+		HMONITOR hMonitor = outputInfo.getMonitorHandle();
+		MONITORINFOEX monitorInfo;
+
+		memset(&monitorInfo, 0, sizeof(MONITORINFOEX));
+		monitorInfo.cbSize = sizeof(MONITORINFOEX);
+		GetMonitorInfo(hMonitor, &monitorInfo);
+
+		if (ChangeDisplaySettingsEx(monitorInfo.szDevice, &displayDeviceMode, NULL, CDS_FULLSCREEN, NULL) != DISP_CHANGE_SUCCESSFUL)
+		{
+			BS_EXCEPT(RenderingAPIException, "ChangeDisplaySettings failed");
+		}
+
+		props.mTop = monitorInfo.rcMonitor.top;
+		props.mLeft = monitorInfo.rcMonitor.left;
+		props.mWidth = width;
+		props.mHeight = height;
+
+		SetWindowPos(mWindow->getHWnd(), HWND_TOP, props.mLeft, props.mTop, width, height, SWP_NOACTIVATE);
 	}
 	}
 
 
 	void Win32RenderWindowCore::setFullscreen(const VideoMode& mode)
 	void Win32RenderWindowCore::setFullscreen(const VideoMode& mode)
 	{
 	{
 		THROW_IF_NOT_CORE_THREAD;
 		THROW_IF_NOT_CORE_THREAD;
+
+		setFullscreen(mode.getWidth(), mode.getHeight(), mode.getRefreshRate(), mode.getOutputIdx());
 	}
 	}
 
 
 	void Win32RenderWindowCore::setWindowed(UINT32 width, UINT32 height)
 	void Win32RenderWindowCore::setWindowed(UINT32 width, UINT32 height)
 	{
 	{
 		THROW_IF_NOT_CORE_THREAD;
 		THROW_IF_NOT_CORE_THREAD;
+
+		Win32RenderWindowProperties& props = mProperties;
+
+		if (!props.mIsFullScreen)
+			return;
+
+		props.mIsFullScreen = false;
+		props.mWidth = width;
+		props.mHeight = height;
+
+		// Drop out of fullscreen
+		ChangeDisplaySettingsEx(NULL, NULL, NULL, 0, NULL);
+
+		UINT32 winWidth = 0;
+		UINT32 winHeight = 0;
+
+		RECT rect;
+		SetRect(&rect, 0, 0, winWidth, winHeight);
+
+		AdjustWindowRect(&rect, mWindow->getStyle(), false);
+		winWidth = rect.right - rect.left;
+		winHeight = rect.bottom - rect.top;
+
+		// Deal with centering when switching down to smaller resolution
+		HMONITOR hMonitor = MonitorFromWindow(mWindow->getHWnd(), MONITOR_DEFAULTTONEAREST);
+		MONITORINFO monitorInfo;
+		memset(&monitorInfo, 0, sizeof(MONITORINFO));
+		monitorInfo.cbSize = sizeof(MONITORINFO);
+		GetMonitorInfo(hMonitor, &monitorInfo);
+
+		LONG screenw = monitorInfo.rcWork.right - monitorInfo.rcWork.left;
+		LONG screenh = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top;
+
+		INT32 left = screenw > INT32(winWidth) ? ((screenw - INT32(winWidth)) / 2) : 0;
+		INT32 top = screenh > INT32(winHeight) ? ((screenh - INT32(winHeight)) / 2) : 0;
+
+		SetWindowLong(mWindow->getHWnd(), GWL_STYLE, mWindow->getStyle());
+		SetWindowLong(mWindow->getHWnd(), GWL_EXSTYLE, mWindow->getStyleEx());
+		SetWindowPos(mWindow->getHWnd(), HWND_NOTOPMOST, left, top, winWidth, winHeight,
+			SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOACTIVATE);
+
+		{
+			ScopedSpinLock lock(mLock);
+			mSyncedProperties.mWidth = props.mWidth;
+			mSyncedProperties.mHeight = props.mHeight;
+		}
+
+		RenderWindowManager::instance().notifySyncDataDirty(this);
 	}
 	}
 
 
 	HWND Win32RenderWindowCore::_getWindowHandle() const
 	HWND Win32RenderWindowCore::_getWindowHandle() const
@@ -191,6 +458,8 @@ namespace BansheeEngine
 			return;
 			return;
 		}
 		}
 
 
+		// TODO - Retrieve renderpass/framebuffer
+
 		RenderWindowCore::getCustomAttribute(name, pData);
 		RenderWindowCore::getCustomAttribute(name, pData);
 	}
 	}
 
 
@@ -203,6 +472,17 @@ namespace BansheeEngine
 
 
 		mWindow->_windowMovedOrResized();
 		mWindow->_windowMovedOrResized();
 
 
+		Win32RenderWindowProperties& props = mProperties;
+		if (!props.mIsFullScreen) // Fullscreen is handled directly by this object
+		{
+			props.mTop = mWindow->getTop();
+			props.mLeft = mWindow->getLeft();
+			props.mWidth = mWindow->getWidth();
+			props.mHeight = mWindow->getHeight();
+		}
+
+		// TODO - Resize swap chain
+
 		RenderWindowCore::_windowMovedOrResized();
 		RenderWindowCore::_windowMovedOrResized();
 	}
 	}