Selaa lähdekoodia

Vulkan render target binding and render pass start

BearishSun 9 vuotta sitten
vanhempi
sitoutus
0f68b63eb4

+ 1 - 2
Source/BansheeCore/Source/BsRenderAPI.cpp

@@ -116,8 +116,7 @@ namespace BansheeEngine
 
 	void RenderAPI::swapBuffers(CoreAccessor& accessor, const SPtr<RenderTarget>& target)
 	{
-		accessor.queueCommand(std::bind(&RenderAPICore::swapBuffers, RenderAPICore::instancePtr(), target->getCore(), 
-			nullptr));
+		accessor.queueCommand(std::bind(&RenderAPICore::swapBuffers, RenderAPICore::instancePtr(), target->getCore(), 1));
 	}
 
 	void RenderAPI::draw(CoreAccessor& accessor, UINT32 vertexOffset, UINT32 vertexCount, UINT32 instanceCount)

+ 16 - 1
Source/BansheeVulkanRenderAPI/Include/BsVulkanCommandBuffer.h

@@ -124,12 +124,21 @@ namespace BansheeEngine
 		/** Returns true if the command buffer is ready to be submitted to a queue. */
 		bool isReadyForSubmit() const { return mState == State::RecordingDone; }
 
+		/** Returns true if the command buffer is currently recording a render pass. */
+		bool isInRenderPass() const { return mState == State::RecordingRenderPass; }
+
 		/** Returns a counter that gets incremented whenever the command buffer is done executing. */
 		UINT32 getFenceCounter() const { return mFenceCounter; }
 
 		/** Checks the internal fence and changes command buffer state if done executing. */
 		void refreshFenceStatus();
 
+		/** 
+		 * Assigns a render target the the command buffer. This render target's framebuffer and render pass will be used
+		 * when beginRenderPass() is called. Command buffer must not be currently recording a render pass.
+		 */
+		void setRenderTarget(const SPtr<RenderTargetCore>& rt);
+
 		/** 
 		 * Lets the command buffer know that the provided resource has been queued on it, and will be used by the
 		 * device when the command buffer is submitted. If a resource is an image or a buffer use the more specific
@@ -189,11 +198,17 @@ namespace BansheeEngine
 		VkSemaphore mSemaphore;
 		UINT32 mFenceCounter;
 
+		VkFramebuffer mFramebuffer;
+		VkRenderPass mRenderPass;
+		VkSemaphore mPresentSemaphore;
+		UINT32 mRenderTargetWidth;
+		UINT32 mRenderTargetHeight;
+
 		UnorderedMap<VulkanResource*, ResourceUseHandle> mResources;
 		UnorderedMap<VulkanResource*, ImageInfo> mImages;
 		UnorderedMap<VulkanResource*, BufferInfo> mBuffers;
 
-		VkSemaphore mSemaphoresTemp[BS_MAX_COMMAND_BUFFERS];
+		VkSemaphore mSemaphoresTemp[BS_MAX_COMMAND_BUFFERS + 1]; // +1 for present semaphore
 		UnorderedMap<UINT32, TransitionInfo> mTransitionInfoTemp;
 	};
 

+ 2 - 3
Source/BansheeVulkanRenderAPI/Include/BsVulkanCommandBufferManager.h

@@ -37,11 +37,10 @@ namespace BansheeEngine
 		 * @param[in]	syncMask	Mask that has a bit enabled for each command buffer to retrieve the semaphore for.
 		 *							If the command buffer is not currently executing, semaphore won't be returned.
 		 * @param[out]	semaphores	List containing all the required semaphores. Semaphores are tightly packed at the
-		 *							beginning of the array.
+		 *							beginning of the array. Must be able to hold at least BS_MAX_COMMAND_BUFFERS entries.
 		 * @param[out]	count		Number of semaphores provided in the @p semaphores array.
 		 */
-		void getSyncSemaphores(UINT32 deviceIdx, UINT32 syncMask, VkSemaphore(&semaphores)[BS_MAX_COMMAND_BUFFERS], 
-			UINT32& count);
+		void getSyncSemaphores(UINT32 deviceIdx, UINT32 syncMask, VkSemaphore* semaphores, UINT32& count);
 
 		/** 
 		 * Checks if any of the active command buffers finished executing on the device and updates their states 

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

@@ -3,6 +3,7 @@
 #pragma once
 
 #include "BsVulkanPrerequisites.h"
+#include "BsVulkanFramebuffer.h"
 
 namespace BansheeEngine
 {
@@ -17,6 +18,9 @@ namespace BansheeEngine
 		VkImageView view;
 		VkSemaphore sync;
 		bool acquired;
+
+		VulkanFramebuffer* framebuffer;
+		VULKAN_FRAMEBUFFER_DESC framebufferDesc;
 	};
 
 	/** Vulkan swap chain containing two or more buffers for rendering and presenting onto the screen. */
@@ -54,11 +58,15 @@ namespace BansheeEngine
 		void present(VkQueue queue, VkSemaphore* semaphores, UINT32 numSemaphores);
 
 		/**
-		 * Returns the current back buffer image. 
+		 * Acquires a new back buffer image. Caller can retrieve the surface by calling getBackBuffer(). Caller must wait
+		 * on the semaphore provided by the surface before rendering to it.
 		 * 
 		 * @note Must only be called once in-between present() calls, or before the first present() call.
 		 */
-		SwapChainSurface acquireBackBuffer();
+		void acquireBackBuffer();
+
+		/** Returns information describing the current back buffer. */
+		const SwapChainSurface& getBackBuffer() { return mSurfaces[mCurrentBackBufferIdx]; }
 
 		/** Returns the number of available color surfaces. */
 		UINT32 getNumColorSurfaces() const { return (UINT32)mSurfaces.size(); }
@@ -88,4 +96,4 @@ namespace BansheeEngine
 	};
 
 	/** @} */
-}
+}

+ 5 - 10
Source/BansheeVulkanRenderAPI/Include/Win32/BsWin32RenderWindow.h

@@ -4,7 +4,6 @@
 
 #include "BsVulkanPrerequisites.h"
 #include "BsRenderWindow.h"
-#include "BsVulkanFramebuffer.h"
 
 namespace BansheeEngine
 {
@@ -75,11 +74,14 @@ namespace BansheeEngine
 		 */
 		void copyToMemory(PixelData &dst, FrameBuffer buffer);
 
+		/** Prepares the back buffer for rendering. Should be called before it is bound to the GPU. */
+		void acquireBackBuffer();
+
 		/** @copydoc RenderWindowCore::swapBuffers */
 		void swapBuffers(UINT32 syncMask = 0xFFFFFFFF) override;
 
 		/** @copydoc RenderWindowCore::getCustomAttribute */
-		void getCustomAttribute(const String& name, void* pData) const override;
+		void getCustomAttribute(const String& name, void* data) const override;
 
 		/** @copydoc RenderWindowCore::_windowMovedOrResized */
 		void _windowMovedOrResized() override;
@@ -102,13 +104,6 @@ namespace BansheeEngine
 		void syncProperties() override;
 
 	protected:
-		/** Contains an information regarding a framebuffer representing a single swap chain surface. */
-		struct FrameBufferInfo
-		{
-			UPtr<VulkanFramebuffer> framebuffer;
-			VULKAN_FRAMEBUFFER_DESC desc;
-		};
-
 		Win32Window* mWindow;
 		bool mIsChild;
 		bool mShowOnSwap;
@@ -121,8 +116,8 @@ namespace BansheeEngine
 		VkFormat mDepthFormat;
 		UINT32 mPresentQueueFamily;
 		SPtr<VulkanSwapChain> mSwapChain;
-		FrameBufferInfo mFramebufferInfos[BS_NUM_BACK_BUFFERS + 1];
 		VkSemaphore mSemaphoresTemp[BS_MAX_COMMAND_BUFFERS];
+		bool mRequiresNewBackBuffer;
 
 		Win32RenderWindowProperties mProperties;
 		Win32RenderWindowProperties mSyncedProperties;

+ 63 - 2
Source/BansheeVulkanRenderAPI/Source/BsVulkanCommandBuffer.cpp

@@ -99,6 +99,8 @@ namespace BansheeEngine
 
 	VulkanCmdBuffer::VulkanCmdBuffer(VulkanDevice& device, UINT32 id, VkCommandPool pool, UINT32 queueFamily, bool secondary)
 		: mId(id), mQueueFamily(queueFamily), mState(State::Ready), mDevice(device), mPool(pool), mFenceCounter(0)
+		, mFramebuffer(VK_NULL_HANDLE), mRenderPass(VK_NULL_HANDLE), mPresentSemaphore(VK_NULL_HANDLE)
+		, mRenderTargetWidth(0), mRenderTargetHeight(0)
 	{
 		VkCommandBufferAllocateInfo cmdBufferAllocInfo;
 		cmdBufferAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
@@ -212,8 +214,29 @@ namespace BansheeEngine
 	{
 		assert(mState == State::Recording);
 
-		// TODO
-		BS_EXCEPT(NotImplementedException, "Not implemented");
+		if (mFramebuffer == VK_NULL_HANDLE || mRenderPass == VK_NULL_HANDLE)
+		{
+			LOGWRN("Attempting to begin a render pass but no render target is bound to the command buffer.");
+			return;
+		}
+
+		VkRenderPassBeginInfo renderPassBeginInfo;
+		renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+		renderPassBeginInfo.pNext = nullptr;
+		renderPassBeginInfo.framebuffer = mFramebuffer;
+		renderPassBeginInfo.renderPass = mRenderPass;
+		renderPassBeginInfo.renderArea.offset.x = 0;
+		renderPassBeginInfo.renderArea.offset.y = 0;
+		renderPassBeginInfo.renderArea.extent.width = mRenderTargetWidth;
+		renderPassBeginInfo.renderArea.extent.height = mRenderTargetHeight;
+
+		// TODO: Handle clears (if provided) here. See VulkanRenderAPI::clearRenderTarget.
+		//  - Potential problem is that we might need different framebuffers depending on whether we use load or clear
+		//    ops during render pass start.
+		renderPassBeginInfo.clearValueCount = 0; // TODO
+		renderPassBeginInfo.pClearValues = nullptr; // TODO
+
+		vkCmdBeginRenderPass(mCmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
 
 		mState = State::RecordingRenderPass;
 	}
@@ -279,6 +302,37 @@ namespace BansheeEngine
 			assert(!signaled); // We reset the fence along with mState so this shouldn't be possible
 	}
 
+	void VulkanCmdBuffer::setRenderTarget(const SPtr<RenderTargetCore>& rt)
+	{
+		assert(mState != State::RecordingRenderPass && mState != State::Submitted);
+
+		if(rt == nullptr)
+		{
+			mFramebuffer = VK_NULL_HANDLE;
+			mRenderPass = VK_NULL_HANDLE;
+			mPresentSemaphore = VK_NULL_HANDLE;
+			mRenderTargetWidth = 0;
+			mRenderTargetHeight = 0;
+		}
+		else
+		{
+			rt->getCustomAttribute("FB", &mFramebuffer);
+			rt->getCustomAttribute("RP", &mRenderPass);
+			
+			if (rt->getProperties().isWindow())
+				rt->getCustomAttribute("PS", &mPresentSemaphore);
+			else
+				mPresentSemaphore = VK_NULL_HANDLE;
+
+			mRenderTargetWidth = rt->getProperties().getWidth();
+			mRenderTargetHeight = rt->getProperties().getHeight();
+		}
+
+
+
+		// TODO
+	}
+
 	void VulkanCmdBuffer::registerResource(VulkanResource* res, VulkanUseFlags flags)
 	{
 		auto insertResult = mResources.insert(std::make_pair(res, ResourceUseHandle()));
@@ -486,6 +540,13 @@ namespace BansheeEngine
 		UINT32 numSemaphores;
 		cbm.getSyncSemaphores(deviceIdx, syncMask, mSemaphoresTemp, numSemaphores);
 
+		// Wait on present (i.e. until the back buffer becomes available), if we're rendering to a window
+		if (mPresentSemaphore != VK_NULL_HANDLE)
+		{
+			mSemaphoresTemp[numSemaphores] = mPresentSemaphore;
+			numSemaphores++;
+		}
+
 		// Issue second part of transition pipeline barriers (on this queue)
 		for (auto& entry : mTransitionInfoTemp)
 		{

+ 2 - 2
Source/BansheeVulkanRenderAPI/Source/BsVulkanCommandBufferManager.cpp

@@ -50,8 +50,8 @@ namespace BansheeEngine
 		mDeviceData[deviceIdx].buffers[idx] = buffer;
 	}
 
-	void VulkanCommandBufferManager::getSyncSemaphores(UINT32 deviceIdx, UINT32 syncMask, 
-		VkSemaphore(&semaphores)[BS_MAX_COMMAND_BUFFERS], UINT32& count)
+	void VulkanCommandBufferManager::getSyncSemaphores(UINT32 deviceIdx, UINT32 syncMask, VkSemaphore* semaphores, 
+		UINT32& count)
 	{
 		assert(deviceIdx < mNumDevices);
 		const PerDeviceData& deviceData = mDeviceData[deviceIdx];

+ 18 - 4
Source/BansheeVulkanRenderAPI/Source/BsVulkanRenderAPI.cpp

@@ -16,7 +16,12 @@
 #include "BsVulkanCommandBuffer.h"
 #include "BsVulkanGpuParams.h"
 #include "BsVulkanVertexInputManager.h"
-#include "Win32/BsWin32VideoModeInfo.h"
+
+#if BS_PLATFORM == BS_PLATFORM_WIN32
+	#include "Win32/BsWin32VideoModeInfo.h"
+#else
+	static_assert(false, "Other platform includes go here.");
+#endif
 
 namespace BansheeEngine
 {
@@ -412,7 +417,8 @@ namespace BansheeEngine
 	void VulkanRenderAPI::clearViewport(UINT32 buffers, const Color& color, float depth, UINT16 stencil, UINT8 targetMask,
 		const SPtr<CommandBuffer>& commandBuffer)
 	{
-		// TODO
+		// TODO - If clearing the whole viewport, call clearRenderTarget, otherwise begin render pass (if needed), and
+		// execute vkCmdClearAttachments with a valid rect. If no RT is bound, this is a no-op (log warning)
 
 		BS_INC_RENDER_STAT(NumClears);
 	}
@@ -420,7 +426,8 @@ namespace BansheeEngine
 	void VulkanRenderAPI::clearRenderTarget(UINT32 buffers, const Color& color, float depth, UINT16 stencil,
 		UINT8 targetMask, const SPtr<CommandBuffer>& commandBuffer)
 	{
-		// TODO
+		// TODO - If currently within render pass, call vkCmdClearAttachments. Otherwise call cb->setClearValues
+		// which should then queue CB clear on render pass begin.
 
 		BS_INC_RENDER_STAT(NumClears);
 	}
@@ -428,7 +435,14 @@ namespace BansheeEngine
 	void VulkanRenderAPI::setRenderTarget(const SPtr<RenderTargetCore>& target, bool readOnlyDepthStencil,
 		const SPtr<CommandBuffer>& commandBuffer)
 	{
-		// TODO
+		VulkanCommandBuffer* cb = getCB(commandBuffer);
+		VulkanCmdBuffer* vkCB = cb->getInternal();
+
+		if(vkCB->isInRenderPass())
+			vkCB->endRenderPass();
+
+		// We don't actually begin a new render pass until the next render-pass specific command gets queued on the CB
+		vkCB->setRenderTarget(target);
 		
 		BS_INC_RENDER_STAT(NumRenderTargetChanges);
 	}

+ 13 - 1
Source/BansheeVulkanRenderAPI/Source/BsVulkanRenderTexture.cpp

@@ -12,7 +12,19 @@ namespace BansheeEngine
 
 	void VulkanRenderTextureCore::getCustomAttribute(const String& name, void* data) const
 	{
-		
+		if (name == "FB")
+		{
+			VkFramebuffer* fb = (VkFramebuffer*)data;
+			// TODO - Assign framebuffer
+			return;
+		}
+
+		if (name == "RP")
+		{
+			VkRenderPass* renderPass = (VkRenderPass*)data;
+			// TODO - Assign render pass
+			return;
+		}
 	}
 
 	VulkanRenderTexture::VulkanRenderTexture(const RENDER_TEXTURE_DESC& desc)

+ 28 - 2
Source/BansheeVulkanRenderAPI/Source/BsVulkanSwapChain.cpp

@@ -204,6 +204,31 @@ namespace BansheeEngine
 			result = vkCreateImageView(logicalDevice, &depthStencilViewCI, gVulkanAllocator, &mDepthStencilView);
 			assert(result == VK_SUCCESS);
 		}
+		else
+		{
+			mDepthStencilImage = VK_NULL_HANDLE;
+			mDepthStencilView = VK_NULL_HANDLE;
+			mDepthStencilMemory = VK_NULL_HANDLE;
+		}
+
+		// Create a framebuffer for each swap chain buffer
+		UINT32 numFramebuffers = (UINT32)mSurfaces.size();
+		for (UINT32 i = 0; i < numFramebuffers; i++)
+		{
+			VULKAN_FRAMEBUFFER_DESC& desc = mSurfaces[i].framebufferDesc;
+
+			desc.width = getWidth();
+			desc.height = getHeight();
+			desc.layers = 1;
+			desc.numSamples = 1;
+			desc.offscreen = false;
+			desc.color[0].format = colorFormat;
+			desc.color[0].view = mSurfaces[i].view;
+			desc.depth.format = depthFormat;
+			desc.depth.view = mDepthStencilView;
+
+			mSurfaces[i].framebuffer = bs_new<VulkanFramebuffer>(device, desc);
+		}
 	}
 
 	void VulkanSwapChain::present(VkQueue queue, VkSemaphore* semaphores, UINT32 numSemaphores)
@@ -235,7 +260,7 @@ namespace BansheeEngine
 		assert(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR);
 	}
 
-	SwapChainSurface VulkanSwapChain::acquireBackBuffer()
+	void VulkanSwapChain::acquireBackBuffer()
 	{
 		uint32_t imageIndex;
 
@@ -254,7 +279,6 @@ namespace BansheeEngine
 		mSurfaces[imageIndex].acquired = true;
 
 		mCurrentBackBufferIdx = imageIndex;
-		return mSurfaces[imageIndex];
 	}
 
 	void VulkanSwapChain::clear(VkSwapchainKHR swapChain)
@@ -264,6 +288,8 @@ namespace BansheeEngine
 		{
 			for (auto& surface : mSurfaces)
 			{
+				bs_delete(surface.framebuffer);
+
 				vkDestroySemaphore(logicalDevice, surface.sync, gVulkanAllocator);
 				vkDestroyImageView(logicalDevice, surface.view, gVulkanAllocator);
 			}

+ 37 - 50
Source/BansheeVulkanRenderAPI/Source/Win32/BsWin32RenderWindow.cpp

@@ -24,7 +24,7 @@ namespace BansheeEngine
 
 	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)
+		, mShowOnSwap(false), mDisplayFrequency(0), mRenderAPI(renderAPI), mRequiresNewBackBuffer(true)
 	{ }
 
 	Win32RenderWindowCore::~Win32RenderWindowCore()
@@ -38,10 +38,6 @@ namespace BansheeEngine
 			mWindow = nullptr;
 		}
 
-		UINT32 numFramebuffers = sizeof(mFramebufferInfos) / sizeof(mFramebufferInfos[0]);
-		for (UINT32 i = 0; i < numFramebuffers; i++)
-			mFramebufferInfos[i].framebuffer = nullptr;
-
 		mSwapChain = nullptr;
 		vkDestroySurfaceKHR(mRenderAPI._getInstance(), mSurface, gVulkanAllocator);
 	}
@@ -210,25 +206,6 @@ namespace BansheeEngine
 		mSwapChain->rebuild(presentDevice, mSurface, props.mWidth, props.mHeight, props.mVSync, mColorFormat, mColorSpace, 
 			mDesc.depthBuffer, mDepthFormat);
 
-		// Create a framebuffer for each swap chain buffer
-		UINT32 numFramebuffers = sizeof(mFramebufferInfos) / sizeof(mFramebufferInfos[0]);
-		assert(numFramebuffers == mSwapChain->getNumColorSurfaces());
-		for(UINT32 i = 0; i < numFramebuffers; i++)
-		{
-			FrameBufferInfo& framebufferInfo = mFramebufferInfos[i];
-			framebufferInfo.desc.width = mSwapChain->getWidth();
-			framebufferInfo.desc.height = mSwapChain->getHeight();
-			framebufferInfo.desc.layers = 1;
-			framebufferInfo.desc.numSamples = 1;
-			framebufferInfo.desc.offscreen = false;
-			framebufferInfo.desc.color[0].format = mColorFormat;
-			framebufferInfo.desc.color[0].view = mSwapChain->getColorView(i);
-			framebufferInfo.desc.depth.format = mDepthFormat;
-			framebufferInfo.desc.depth.view = mSwapChain->getDepthStencilView();
-
-			framebufferInfo.framebuffer = bs_unique_ptr_new<VulkanFramebuffer>(presentDevice, framebufferInfo.desc);
-		}
-
 		// Make the window full screen if required
 		if (!windowDesc.external)
 		{
@@ -270,6 +247,16 @@ namespace BansheeEngine
 		RenderWindowCore::initialize();
 	}
 
+	void Win32RenderWindowCore::acquireBackBuffer()
+	{
+		// We haven't presented the current back buffer yet, so just use that one
+		if (!mRequiresNewBackBuffer)
+			return;
+
+		mSwapChain->acquireBackBuffer();
+		mRequiresNewBackBuffer = false;
+	}
+
 	void Win32RenderWindowCore::swapBuffers(UINT32 syncMask)
 	{
 		THROW_IF_NOT_CORE_THREAD;
@@ -297,6 +284,7 @@ namespace BansheeEngine
 		cbm.getSyncSemaphores(deviceIdx, syncMask, mSemaphoresTemp, numSemaphores);
 
 		mSwapChain->present(queue->getHandle(), mSemaphoresTemp, numSemaphores);
+		mRequiresNewBackBuffer = true;
 	}
 
 	void Win32RenderWindowCore::move(INT32 left, INT32 top)
@@ -500,18 +488,37 @@ namespace BansheeEngine
 		return mWindow->getHWnd();
 	}
 
-	void Win32RenderWindowCore::getCustomAttribute(const String& name, void* pData) const
+	void Win32RenderWindowCore::getCustomAttribute(const String& name, void* data) const
 	{
+		if (name == "FB")
+		{
+			VkFramebuffer* fb = (VkFramebuffer*)data;
+			*fb = mSwapChain->getBackBuffer().framebuffer->getFramebuffer();
+			return;
+		}
+
+		if (name == "RP")
+		{
+			VkRenderPass* renderPass = (VkRenderPass*)data;
+			*renderPass = mSwapChain->getBackBuffer().framebuffer->getRenderPass();
+			return;
+		}
+
+		if(name == "PS")
+		{
+			VkSemaphore* presentSemaphore = (VkSemaphore*)data;
+			*presentSemaphore = mSwapChain->getBackBuffer().sync;
+			return;
+		}
+
 		if(name == "WINDOW")
 		{
-			UINT64 *pWnd = (UINT64*)pData;
+			UINT64 *pWnd = (UINT64*)data;
 			*pWnd = (UINT64)mWindow->getHWnd();
 			return;
 		}
 
-		// TODO - Retrieve renderpass/framebuffer
-
-		RenderWindowCore::getCustomAttribute(name, pData);
+		RenderWindowCore::getCustomAttribute(name, data);
 	}
 
 	void Win32RenderWindowCore::_windowMovedOrResized()
@@ -532,7 +539,7 @@ namespace BansheeEngine
 			props.mHeight = mWindow->getHeight();
 		}
 
-		// Resize swap chain and update framebuffers
+		// Resize swap chain
 		
 		//// Need to make sure nothing is using the swap buffer before we re-create it
 		// Note: Optionally I can detect exactly on which queues (if any) are the swap chain images used on, and only wait
@@ -540,29 +547,9 @@ namespace BansheeEngine
 		SPtr<VulkanDevice> presentDevice = mRenderAPI._getPresentDevice();
 		presentDevice->waitIdle();
 
-		UINT32 numFramebuffers = sizeof(mFramebufferInfos) / sizeof(mFramebufferInfos[0]);
-
-		//// First destroy existing frame buffers
-		for (UINT32 i = 0; i < numFramebuffers; i++)
-			mFramebufferInfos[i].framebuffer = nullptr;
-
-		//// Rebuild swap chain
 		mSwapChain->rebuild(presentDevice, mSurface, props.mWidth, props.mHeight, props.mVSync, mColorFormat, mColorSpace, 
 			mDesc.depthBuffer, mDepthFormat);
 
-		//// Rebuild framebuffers
-		assert(numFramebuffers == mSwapChain->getNumColorSurfaces());
-		for (UINT32 i = 0; i < numFramebuffers; i++)
-		{
-			FrameBufferInfo& framebufferInfo = mFramebufferInfos[i];
-			framebufferInfo.desc.width = mSwapChain->getWidth();
-			framebufferInfo.desc.height = mSwapChain->getHeight();
-			framebufferInfo.desc.color[0].view = mSwapChain->getColorView(i);
-			framebufferInfo.desc.depth.view = mSwapChain->getDepthStencilView();
-
-			framebufferInfo.framebuffer = bs_unique_ptr_new<VulkanFramebuffer>(presentDevice, framebufferInfo.desc);
-		}
-
 		RenderWindowCore::_windowMovedOrResized();
 	}