Browse Source

Added a way to create multiple variations of Vulkan framebuffer

BearishSun 9 years ago
parent
commit
6b749fa064

+ 22 - 0
Source/BansheeCore/Include/BsCommonTypes.h

@@ -474,6 +474,28 @@ namespace bs
 		Convex
 	};
 
+	/** 
+	 * Bits that map to a specific surface of a render target. Combine the bits to generate a mask that references
+	 * only specific render target surfaces.
+	 */
+	enum RenderSurfaceMaskBits
+	{
+		RT_NONE = 0,
+		RT_COLOR0 = 1 << 0,
+		RT_COLOR1 = 1 << 1,
+		RT_COLOR2 = 1 << 2,
+		RT_COLOR3 = 1 << 3,
+		RT_COLOR4 = 1 << 4,
+		RT_COLOR5 = 1 << 5,
+		RT_COLOR6 = 1 << 6,
+		RT_COLOR7 = 1 << 7,
+		RT_DEPTH = 1 << 31,
+		RT_ALL = 0xFF
+	};
+
+	typedef Flags<RenderSurfaceMaskBits> RenderSurfaceMask;
+	BS_FLAGS_OPERATORS(RenderSurfaceMaskBits);
+
 	/**	Texture addressing mode, per component. */
 	struct UVWAddressingMode
 	{

+ 9 - 7
Source/BansheeCore/Include/BsRenderAPI.h

@@ -109,7 +109,7 @@ namespace bs
 		 * @note This is an @ref asyncMethod "asynchronous method".
 		 */
 		static void setRenderTarget(const SPtr<RenderTarget>& target, bool readOnlyDepthStencil = false, 
-			bool preserveContents = false);
+			RenderSurfaceMask loadMask = RT_NONE);
 
 		/** 
 		 * @see RenderAPICore::beginFrame() 
@@ -476,17 +476,19 @@ namespace bs
 		 * @param[in]	readOnlyDepthStencil	If true the caller guarantees he won't write to the depth/stencil buffer 
 		 *										(if any was provided). This allows the depth buffer to be bound for depth 
 		 *										testing, as well as reading in a shader, at the same time.
-		 * @param[in]	preserveContents		Determines will the current contents of the render target be preserved.
-		 *										Perserving the contents comes at a performance cost, so it's best to set
-		 *										to false if you are sure you will overwrite or clear the contents later.
-		 *										Set to true if you need to perform blending or similar operations with the
-		 *										existing contents of the render target.
+		 * @param[in]	loadMask				Determines which render target surfaces will have their current contents
+		 *										preserved. By default when a render target is bound its contents will be
+		 *										lost. You might need to preserve contents if you need to perform blending 
+		 *										or similar operations with the existing contents of the render target. 
+		 *										
+		 *										Use the mask to select exactly which surfaces of the render target need
+		 *										their contents preserved.
 		 * @param[in]	commandBuffer			Optional command buffer to queue the operation on. If not provided operation
 		 *										is executed immediately. Otherwise it is executed when executeCommands() is
 		 *										called. Buffer must support graphics operations.
 		 */
         virtual void setRenderTarget(const SPtr<RenderTargetCore>& target, bool readOnlyDepthStencil = false,
-			bool preserveContents = false, const SPtr<CommandBuffer>& commandBuffer = nullptr) = 0;
+			RenderSurfaceMask loadMask = RT_NONE, const SPtr<CommandBuffer>& commandBuffer = nullptr) = 0;
 
 		/**
 		 * Clears the currently active render target.

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

@@ -85,10 +85,10 @@ namespace bs
 	}
 
 	void RenderAPI::setRenderTarget(const SPtr<RenderTarget>& target, bool readOnlyDepthStencil,
-									bool preserveContents)
+									RenderSurfaceMask loadMask)
 	{
 		gCoreThread().queueCommand(std::bind(&RenderAPICore::setRenderTarget, 
-			RenderAPICore::instancePtr(), target->getCore(), readOnlyDepthStencil, preserveContents, nullptr));
+			RenderAPICore::instancePtr(), target->getCore(), readOnlyDepthStencil, loadMask, nullptr));
 	}
 
 	void RenderAPI::beginRender()

+ 1 - 1
Source/BansheeD3D11RenderAPI/Include/BsD3D11RenderAPI.h

@@ -52,7 +52,7 @@ namespace bs
 
 		/** @copydoc RenderAPICore::setRenderTarget */
 		void setRenderTarget(const SPtr<RenderTargetCore>& target, bool readOnlyDepthStencil = false, 
-			bool preserveContents = false, const SPtr<CommandBuffer>& commandBuffer = nullptr) override;
+			RenderSurfaceMask loadMask = RT_NONE, const SPtr<CommandBuffer>& commandBuffer = nullptr) override;
 
 		/** @copydoc RenderAPICore::setViewport */
 		void setViewport(const Rect2& area, const SPtr<CommandBuffer>& commandBuffer = nullptr) override;

+ 1 - 1
Source/BansheeD3D11RenderAPI/Source/BsD3D11RenderAPI.cpp

@@ -1024,7 +1024,7 @@ namespace bs
 	}
 
 	void D3D11RenderAPI::setRenderTarget(const SPtr<RenderTargetCore>& target, bool readOnlyDepthStencil, 
-		bool preserveContents, const SPtr<CommandBuffer>& commandBuffer)
+		RenderSurfaceMask loadMask, const SPtr<CommandBuffer>& commandBuffer)
 	{
 		auto executeRef = [&](const SPtr<RenderTargetCore>& target, bool readOnlyDepthStencil)
 		{

+ 1 - 1
Source/BansheeGLRenderAPI/Include/BsGLRenderAPI.h

@@ -87,7 +87,7 @@ namespace bs
 
 		/** @copydoc RenderAPICore::setRenderTarget() */
 		void setRenderTarget(const SPtr<RenderTargetCore>& target, bool readOnlyDepthStencil = false, 
-			bool preserveContents = false, const SPtr<CommandBuffer>& commandBuffer = nullptr) override;
+			RenderSurfaceMask loadMask = RT_NONE, const SPtr<CommandBuffer>& commandBuffer = nullptr) override;
 
 		/** @copydoc RenderAPICore::clearRenderTarget() */
 		void clearRenderTarget(UINT32 buffers, const Color& color = Color::Black, float depth = 1.0f, UINT16 stencil = 0, 

+ 1 - 1
Source/BansheeGLRenderAPI/Source/BsGLRenderAPI.cpp

@@ -740,7 +740,7 @@ namespace bs
 	}
 
 	void GLRenderAPI::setRenderTarget(const SPtr<RenderTargetCore>& target, bool readOnlyDepthStencil, 
-		bool preserveContents, const SPtr<CommandBuffer>& commandBuffer)
+		RenderSurfaceMask loadMask, const SPtr<CommandBuffer>& commandBuffer)
 	{
 		auto executeRef = [&](const SPtr<RenderTargetCore>& target, bool readOnlyDepthStencil)
 		{

+ 17 - 0
Source/BansheeUtility/Include/BsFlags.h

@@ -270,3 +270,20 @@ namespace bs
 	/** @endcond */
 	/** @} */
 }
+
+/** @cond STDLIB */
+
+namespace std
+{
+	/** Hash value generator for Flags<Enum, Storage>. */
+	template<class Enum, class Storage>
+	struct hash<bs::Flags<Enum, Storage>>
+	{
+		size_t operator()(const bs::Flags<Enum, Storage>& key) const
+		{
+			return (Storage)key;
+		}
+	};
+}
+
+/** @endcond */

+ 2 - 2
Source/BansheeVulkanRenderAPI/Include/BsVulkanCommandBuffer.h

@@ -182,7 +182,7 @@ namespace bs
 		 * 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, bool readOnlyDepthStencil, bool preserveContents);
+		void setRenderTarget(const SPtr<RenderTargetCore>& rt, bool readOnlyDepthStencil, RenderSurfaceMask loadMask);
 
 		/** Clears the entirety currently bound render target. */
 		void clearRenderTarget(UINT32 buffers, const Color& color, float depth, UINT16 stencil, UINT8 targetMask);
@@ -290,7 +290,7 @@ namespace bs
 		UINT32 mRenderTargetWidth;
 		UINT32 mRenderTargetHeight;
 		bool mRenderTargetDepthReadOnly;
-		bool mRenderTargetPreserveContents;
+		RenderSurfaceMask mRenderTargetLoadMask;
 
 		UnorderedMap<VulkanResource*, ResourceUseHandle> mResources;
 		UnorderedMap<VulkanResource*, ImageInfo> mImages;

+ 47 - 12
Source/BansheeVulkanRenderAPI/Include/BsVulkanFramebuffer.h

@@ -70,20 +70,18 @@ namespace bs
 		/** 
 		 * Gets internal Vulkan render pass object. 
 		 * 
-		 * @param[in]	preserveContents	If true, returns render pass that preserves existing framebuffer attachment 
-		 *									contents on load. Otherwise returns render pass that discards them (more 
-		 *									efficient).
+		 * @param[in]	loadMask	Mask that control which render target surface contents should be preserved on load.
+		 * @param[in]	readMask	Mask that controls which render targets can be read by shaders while they're bound.
 		 */
-		VkRenderPass getRenderPass(bool preserveContents) const;
+		VkRenderPass getRenderPass(RenderSurfaceMask loadMask, RenderSurfaceMask readMask) const;
 
 		/**
 		 * Gets internal Vulkan framebuffer object.
 		 *
-		 * @param[in]	preserveContents	If true, returns render pass that preserves existing framebuffer attachment
-		 *									contents on load. Otherwise returns render pass that discards them (more
-		 *									efficient).
+		 * @param[in]	loadMask	Mask that control which render target surface contents should be preserved on load.
+		 * @param[in]	readMask	Mask that controls which render targets can be read by shaders while they're bound.
 		 */
-		VkFramebuffer getFramebuffer(bool preserveContents) const;
+		VkFramebuffer getFramebuffer(RenderSurfaceMask loadMask, RenderSurfaceMask readMask) const;
 
 		/** 
 		 * Gets the number of layers in each framebuffer surface. A layer is an element in a texture array, or a depth 
@@ -115,12 +113,41 @@ namespace bs
 		/** Returns sample flags that determine if the framebuffer supports multi-sampling, and for how many samples. */
 		VkSampleCountFlagBits getSampleFlags() const { return mSampleFlags; }
 	private:
+		/** Information about a single frame-buffer variant. */
+		struct Variant
+		{
+			VkRenderPass renderPass;
+			VkFramebuffer framebuffer;
+		};
+
+		/** Key used for identifying different types of frame-buffer variants. */
+		struct VariantKey
+		{
+			VariantKey(RenderSurfaceMask loadMask, RenderSurfaceMask readMask);
+
+			class HashFunction
+			{
+			public:
+				size_t operator()(const VariantKey& key) const;
+			};
+
+			class EqualFunction
+			{
+			public:
+				bool operator()(const VariantKey& lhs, const VariantKey& rhs) const;
+			};
+
+			RenderSurfaceMask loadMask;
+			RenderSurfaceMask readMask;
+		};
+
+		/** Creates a new variant of the framebuffer. */
+		Variant createVariant(RenderSurfaceMask loadMask, RenderSurfaceMask readMask) const;
+
 		UINT32 mId;
-		VkRenderPass mRenderPassDiscard;
-		VkFramebuffer mFramebufferDiscard;
 
-		VkRenderPass mRenderPassPreserve;
-		VkFramebuffer mFramebufferPreserve;
+		Variant mDefault;
+		mutable UnorderedMap<VariantKey, Variant, VariantKey::HashFunction, VariantKey::EqualFunction> mVariants;
 
 		UINT32 mNumAttachments;
 		UINT32 mNumColorAttachments;
@@ -132,6 +159,14 @@ namespace bs
 		bool mHasDepth;
 		VkSampleCountFlagBits mSampleFlags;
 
+		mutable VkAttachmentDescription mAttachments[BS_MAX_MULTIPLE_RENDER_TARGETS + 1];
+		mutable VkImageView mAttachmentViews[BS_MAX_MULTIPLE_RENDER_TARGETS + 1];
+		mutable VkAttachmentReference mColorReferences[BS_MAX_MULTIPLE_RENDER_TARGETS];
+		mutable VkAttachmentReference mDepthReference;
+		mutable VkSubpassDependency mDependencies[2];
+		mutable VkRenderPassCreateInfo mRenderPassCI;
+		mutable VkFramebufferCreateInfo mFramebufferCI;
+
 		static UINT32 sNextValidId;
 	};
 

+ 16 - 9
Source/BansheeVulkanRenderAPI/Include/BsVulkanGpuPipelineState.h

@@ -45,8 +45,10 @@ namespace bs
 		 * @param[in]	deviceIdx			Index of the device to retrieve the pipeline for.
 		 * @param[in]	framebuffer			Framebuffer object that defines the surfaces this pipeline will render to.
 		 * @param[in]	readOnlyDepth		True if the pipeline is only allowed to read the depth buffer, without writes.
-		 * @param[in]	preserveContents	True if the existing framebuffer contents should be preserved for potential read
-		 *									or blend operations.
+		 * @param[in]	loadMask			Mask that controls for which framebuffer surfaces should the existing contents
+		 *									be preserved for.
+		 * @param[in]	readMask			Mask that controls which framebuffer surfaces can be read from the shader while
+		 *									they're bound for rendering.
 		 * @param[in]	drawOp				Type of geometry that will be drawn using the pipeline.
 		 * @param[in]	vertexInput			State describing inputs to the vertex program.
 		 * @return							Vulkan graphics pipeline object.
@@ -54,7 +56,8 @@ namespace bs
 		 * @note	Thread safe.
 		 */
 		VulkanPipeline* getPipeline(UINT32 deviceIdx, VulkanFramebuffer* framebuffer, bool readOnlyDepth, 
-			bool preserveContents,DrawOperationType drawOp, const SPtr<VulkanVertexInput>& vertexInput);
+			RenderSurfaceMask loadMask, RenderSurfaceMask readMask, DrawOperationType drawOp, 
+			const SPtr<VulkanVertexInput>& vertexInput);
 
 		/** 
 		 * Returns a pipeline layout object for the specified device index. If the device index doesn't match a bit in the
@@ -82,8 +85,10 @@ namespace bs
 		 * @param[in]	deviceIdx			Index of the device to create the pipeline for.
 		 * @param[in]	framebuffer			Framebuffer object that defines the surfaces this pipeline will render to.
 		 * @param[in]	readOnlyDepth		True if the pipeline is only allowed to read the depth buffer, without writes.
-		 * @param[in]	preserveContents	True if the existing framebuffer contents should be preserved for potential read
-		 *									or blend operations.
+		 * @param[in]	loadMask			Mask that controls for which framebuffer surfaces should the existing contents 
+		 *									be preserved for.
+		 * @param[in]	readMask			Mask that controls which framebuffer surfaces can be read from the shader while
+		 *									they're bound for rendering.
 		 * @param[in]	drawOp				Type of geometry that will be drawn using the pipeline.
 		 * @param[in]	vertexInput			State describing inputs to the vertex program.
 		 * @return							Vulkan graphics pipeline object.
@@ -91,18 +96,20 @@ namespace bs
 		 * @note	Thread safe.
 		 */
 		VulkanPipeline* createPipeline(UINT32 deviceIdx, VulkanFramebuffer* framebuffer, bool readOnlyDepth, 
-			bool preserveContents, DrawOperationType drawOp, const SPtr<VulkanVertexInput>& vertexInput);
+			RenderSurfaceMask loadMask, RenderSurfaceMask readMask, DrawOperationType drawOp, 
+			const SPtr<VulkanVertexInput>& vertexInput);
 
 		/**	Key uniquely identifying GPU pipelines. */
 		struct GpuPipelineKey
 		{
-			GpuPipelineKey(UINT32 framebufferId, UINT32 vertexInputId, bool readOnlyDepth, bool preserveContents,
-				DrawOperationType drawOp);
+			GpuPipelineKey(UINT32 framebufferId, UINT32 vertexInputId, bool readOnlyDepth, RenderSurfaceMask loadMask,
+						   RenderSurfaceMask readMask, DrawOperationType drawOp);
 
 			UINT32 framebufferId;
 			UINT32 vertexInputId;
 			bool readOnlyDepth;
-			bool preserveContents;
+			RenderSurfaceMask loadMask;
+			RenderSurfaceMask readMask;
 			DrawOperationType drawOp;
 		};
 

+ 1 - 1
Source/BansheeVulkanRenderAPI/Include/BsVulkanRenderAPI.h

@@ -52,7 +52,7 @@ namespace bs
 
 		/** @copydoc RenderAPICore::setRenderTarget */
 		void setRenderTarget(const SPtr<RenderTargetCore>& target, bool readOnlyDepthStencil = false,
-			bool preserveContents = false, const SPtr<CommandBuffer>& commandBuffer = nullptr) override;
+			RenderSurfaceMask loadMask = RT_NONE, const SPtr<CommandBuffer>& commandBuffer = nullptr) override;
 
 		/** @copydoc RenderAPICore::setViewport */
 		void setViewport(const Rect2& area, const SPtr<CommandBuffer>& commandBuffer = nullptr) override;

+ 8 - 8
Source/BansheeVulkanRenderAPI/Source/BsVulkanCommandBuffer.cpp

@@ -104,7 +104,7 @@ namespace bs
 	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(nullptr), mPresentSemaphore(VK_NULL_HANDLE), mRenderTargetWidth(0), mRenderTargetHeight(0)
-		, mRenderTargetDepthReadOnly(false), mRenderTargetPreserveContents(false), mGlobalQueueIdx(-1)
+		, mRenderTargetDepthReadOnly(false), mRenderTargetLoadMask(RT_NONE), mGlobalQueueIdx(-1)
 		, mViewport(0.0f, 0.0f, 1.0f, 1.0f), mScissor(0, 0, 0, 0), mStencilRef(0), mDrawOp(DOT_TRIANGLE_LIST)
 		, mNumBoundDescriptorSets(0), mGfxPipelineRequiresBind(true), mCmpPipelineRequiresBind(true)
 		, mViewportRequiresBind(true), mStencilRefRequiresBind(true), mScissorRequiresBind(true), mVertexBuffersTemp()
@@ -236,8 +236,8 @@ namespace bs
 		VkRenderPassBeginInfo renderPassBeginInfo;
 		renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
 		renderPassBeginInfo.pNext = nullptr;
-		renderPassBeginInfo.framebuffer = mFramebuffer->getFramebuffer(mRenderTargetPreserveContents);
-		renderPassBeginInfo.renderPass = mFramebuffer->getRenderPass(mRenderTargetPreserveContents);
+		renderPassBeginInfo.framebuffer = mFramebuffer->getFramebuffer(mRenderTargetLoadMask, RT_NONE);
+		renderPassBeginInfo.renderPass = mFramebuffer->getRenderPass(mRenderTargetLoadMask, RT_NONE);
 		renderPassBeginInfo.renderArea.offset.x = 0;
 		renderPassBeginInfo.renderArea.offset.y = 0;
 		renderPassBeginInfo.renderArea.extent.width = mRenderTargetWidth;
@@ -532,7 +532,7 @@ namespace bs
 	}
 
 	void VulkanCmdBuffer::setRenderTarget(const SPtr<RenderTargetCore>& rt, bool readOnlyDepthStencil, 
-		bool preserveContents)
+		RenderSurfaceMask loadMask)
 	{
 		assert(mState != State::RecordingRenderPass && mState != State::Submitted);
 
@@ -545,7 +545,7 @@ namespace bs
 			mRenderTargetWidth = 0;
 			mRenderTargetHeight = 0;
 			mRenderTargetDepthReadOnly = false;
-			mRenderTargetPreserveContents = false;
+			mRenderTargetLoadMask = RT_NONE;
 		}
 		else
 		{
@@ -559,7 +559,7 @@ namespace bs
 			mRenderTargetWidth = rt->getProperties().getWidth();
 			mRenderTargetHeight = rt->getProperties().getHeight();
 			mRenderTargetDepthReadOnly = readOnlyDepthStencil;
-			mRenderTargetPreserveContents = preserveContents;
+			mRenderTargetLoadMask = loadMask;
 
 			registerResource(mFramebuffer, VulkanUseFlag::Write);
 		}
@@ -833,8 +833,8 @@ namespace bs
 		SPtr<VulkanVertexInput> vertexInput = VulkanVertexInputManager::instance().getVertexInfo(mVertexDecl, inputDecl);
 
 		VulkanPipeline* pipeline = mGraphicsPipeline->getPipeline(mDevice.getIndex(), mFramebuffer,
-																  mRenderTargetDepthReadOnly, mRenderTargetPreserveContents,
-																  mDrawOp, vertexInput);
+																  mRenderTargetDepthReadOnly, mRenderTargetLoadMask,
+																  RT_NONE, mDrawOp, vertexInput);
 
 		if (pipeline == nullptr)
 			return false;

+ 147 - 89
Source/BansheeVulkanRenderAPI/Source/BsVulkanFramebuffer.cpp

@@ -7,6 +7,25 @@
 
 namespace bs
 {
+	VulkanFramebuffer::VariantKey::VariantKey(RenderSurfaceMask loadMask, RenderSurfaceMask readMask)
+		:loadMask(loadMask), readMask(readMask)
+	{ }
+
+	size_t VulkanFramebuffer::VariantKey::HashFunction::operator()(const VariantKey& v) const
+	{
+		size_t hash = 0;
+		hash_combine(hash, v.readMask);
+		hash_combine(hash, v.loadMask);
+
+		return hash;
+	}
+
+	bool VulkanFramebuffer::VariantKey::EqualFunction::operator()(const VariantKey& lhs,
+																			 const VariantKey& rhs) const
+	{
+		return lhs.loadMask == rhs.loadMask && lhs.readMask == rhs.readMask;
+	}
+
 	UINT32 VulkanFramebuffer::sNextValidId = 1;
 
 	VulkanFramebuffer::VulkanFramebuffer(VulkanResourceManager* owner, const VULKAN_FRAMEBUFFER_DESC& desc)
@@ -16,12 +35,6 @@ namespace bs
 	{
 		mId = sNextValidId++;
 
-		// 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;
-
 		mSampleFlags = VulkanUtility::getSampleFlags(desc.numSamples);
 
 		UINT32 attachmentIdx = 0;
@@ -30,7 +43,7 @@ namespace bs
 			if (desc.color[i].image == nullptr)
 				continue;
 
-			VkAttachmentDescription& attachmentDesc = attachments[attachmentIdx];
+			VkAttachmentDescription& attachmentDesc = mAttachments[attachmentIdx];
 			attachmentDesc.flags = 0;
 			attachmentDesc.format = desc.color[i].format;
 			attachmentDesc.samples = mSampleFlags;
@@ -49,12 +62,12 @@ namespace bs
 			mColorBaseLayers[attachmentIdx] = desc.color[i].baseLayer;
 			mColorImages[attachmentIdx] = desc.color[i].image;
 
-			VkAttachmentReference& ref = colorReferences[attachmentIdx];
+			VkAttachmentReference& ref = mColorReferences[attachmentIdx];
 			ref.attachment = attachmentIdx;
 			ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 
-			attachmentViews[attachmentIdx] = desc.color[i].view;
-;			attachmentIdx++;
+			mAttachmentViews[attachmentIdx] = desc.color[i].view;
+			attachmentIdx++;
 		}
 
 		mNumColorAttachments = attachmentIdx;
@@ -62,7 +75,7 @@ namespace bs
 
 		if (mHasDepth)
 		{
-			VkAttachmentDescription& attachmentDesc = attachments[attachmentIdx];
+			VkAttachmentDescription& attachmentDesc = mAttachments[attachmentIdx];
 			attachmentDesc.flags = 0;
 			attachmentDesc.format = desc.depth.format;
 			attachmentDesc.samples = mSampleFlags;
@@ -76,11 +89,11 @@ namespace bs
 			mDepthBaseLayer = desc.depth.baseLayer;
 			mDepthStencilImage = desc.depth.image;
 
-			VkAttachmentReference& ref = depthReference;
+			VkAttachmentReference& ref = mDepthReference;
 			ref.attachment = attachmentIdx;
 			ref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
 
-			attachmentViews[attachmentIdx] = desc.depth.view;
+			mAttachmentViews[attachmentIdx] = desc.depth.view;
 			attachmentIdx++;
 		}
 
@@ -97,117 +110,162 @@ namespace bs
 		subpassDesc.pResolveAttachments = nullptr;
 
 		if (mNumColorAttachments > 0)
-			subpassDesc.pColorAttachments = colorReferences;
+			subpassDesc.pColorAttachments = mColorReferences;
 		else
 			subpassDesc.pColorAttachments = nullptr;
 
 		if (mHasDepth)
-			subpassDesc.pDepthStencilAttachment = &depthReference;
+			subpassDesc.pDepthStencilAttachment = &mDepthReference;
 		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 = mNumAttachments;
-		renderPassCI.pAttachments = attachments;
-		renderPassCI.subpassCount = 1;
-		renderPassCI.pSubpasses = &subpassDesc;
-		renderPassCI.dependencyCount = 2;
-		renderPassCI.pDependencies = dependencies;
+		mDependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
+		mDependencies[0].dstSubpass = 0;
+		mDependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
+		mDependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+		mDependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
+		mDependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; // Note: Do we really need read access?
+		mDependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; // Note: Is this really required?
+
+		mDependencies[1].srcSubpass = 0;
+		mDependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
+		mDependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+		mDependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
+		mDependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+		mDependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
+		mDependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;// Note: Is this really required?
+
+		// Create render pass and frame buffer create infos
+		mRenderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+		mRenderPassCI.pNext = nullptr;
+		mRenderPassCI.flags = 0;
+		mRenderPassCI.attachmentCount = mNumAttachments;
+		mRenderPassCI.pAttachments = mAttachments;
+		mRenderPassCI.subpassCount = 1;
+		mRenderPassCI.pSubpasses = &subpassDesc;
+		mRenderPassCI.dependencyCount = 2;
+		mRenderPassCI.pDependencies = mDependencies;
+
+		mFramebufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+		mFramebufferCI.pNext = nullptr;
+		mFramebufferCI.flags = 0;
+		mFramebufferCI.renderPass = VK_NULL_HANDLE;
+		mFramebufferCI.attachmentCount = mNumAttachments;
+		mFramebufferCI.pAttachments = mAttachmentViews;
+		mFramebufferCI.width = desc.width;
+		mFramebufferCI.height = desc.height;
+		mFramebufferCI.layers = desc.layers;
+
+		mDefault = createVariant(RT_NONE, RT_NONE);		
+	}
 
+	VulkanFramebuffer::~VulkanFramebuffer()
+	{
 		VkDevice device = mOwner->getDevice().getLogical();
 
-		// Create discard render pass and frame buffer
-		VkResult result = vkCreateRenderPass(device, &renderPassCI, gVulkanAllocator, &mRenderPassDiscard);
-		assert(result == VK_SUCCESS);
+		vkDestroyFramebuffer(device, mDefault.framebuffer, gVulkanAllocator);
+		vkDestroyRenderPass(device, mDefault.renderPass, gVulkanAllocator);
 
-		//// Create frame buffer
-		VkFramebufferCreateInfo framebufferCI;
-		framebufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
-		framebufferCI.pNext = nullptr;
-		framebufferCI.flags = 0;
-		framebufferCI.renderPass = mRenderPassDiscard;
-		framebufferCI.attachmentCount = mNumAttachments;
-		framebufferCI.pAttachments = attachmentViews;
-		framebufferCI.width = desc.width;
-		framebufferCI.height = desc.height;
-		framebufferCI.layers = desc.layers;
-
-		result = vkCreateFramebuffer(device, &framebufferCI, gVulkanAllocator, &mFramebufferDiscard);
-		assert(result == VK_SUCCESS);
+		for(auto& entry : mVariants)
+		{
+			vkDestroyFramebuffer(device, entry.second.framebuffer, gVulkanAllocator);
+			vkDestroyRenderPass(device, entry.second.renderPass, gVulkanAllocator);
+		}
+	}
 
-		// Create preserving render pass and frame buffer
+	VulkanFramebuffer::Variant VulkanFramebuffer::createVariant(RenderSurfaceMask loadMask, 
+		RenderSurfaceMask readMask) const
+	{
 		for (UINT32 i = 0; i < mNumColorAttachments; i++)
 		{
-			VkAttachmentDescription& attachmentDesc = attachments[i];
-			attachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
-			attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+			VkAttachmentDescription& attachmentDesc = mAttachments[i];
+			VkAttachmentReference& attachmentRef = mColorReferences[i];
+
+			if (loadMask.isSet((RenderSurfaceMaskBits)i))
+			{
+				attachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+				attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+			}
+			else
+			{
+				attachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+				attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+			}
+
+			if(readMask.isSet((RenderSurfaceMaskBits)i))
+				attachmentRef.layout = VK_IMAGE_LAYOUT_GENERAL;
+			else
+				attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 		}
 
 		if (mHasDepth)
 		{
-			VkAttachmentDescription& attachmentDesc = attachments[mNumColorAttachments];
-			attachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
-			attachmentDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
-			attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+			VkAttachmentDescription& attachmentDesc = mAttachments[mNumColorAttachments];
+			VkAttachmentReference& attachmentRef = mDepthReference;
+
+			if (loadMask.isSet(RT_DEPTH))
+			{
+				attachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+				attachmentDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+				attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+			}
+			else
+			{
+				attachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+				attachmentDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+				attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+			}
+
+			if (readMask.isSet(RT_DEPTH))
+				attachmentRef.layout = VK_IMAGE_LAYOUT_GENERAL;
+			else
+				attachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
 		}
 
-		vkCreateRenderPass(device, &renderPassCI, gVulkanAllocator, &mRenderPassPreserve);
+		VkDevice device = mOwner->getDevice().getLogical();
+
+		Variant variant;
+		VkResult result = vkCreateRenderPass(device, &mRenderPassCI, gVulkanAllocator, &variant.renderPass);
 		assert(result == VK_SUCCESS);
 
-		//// Create frame buffer
-		framebufferCI.renderPass = mRenderPassPreserve;
+		mFramebufferCI.renderPass = variant.renderPass;
 
-		result = vkCreateFramebuffer(device, &framebufferCI, gVulkanAllocator, &mFramebufferPreserve);
+		result = vkCreateFramebuffer(device, &mFramebufferCI, gVulkanAllocator, &variant.framebuffer);
 		assert(result == VK_SUCCESS);
 
+		return variant;
 	}
 
-	VulkanFramebuffer::~VulkanFramebuffer()
+	VkRenderPass VulkanFramebuffer::getRenderPass(RenderSurfaceMask loadMask, RenderSurfaceMask readMask) const
 	{
-		VkDevice device = mOwner->getDevice().getLogical();
+		if (loadMask == RT_NONE && readMask == RT_NONE)
+			return mDefault.renderPass;
 
-		vkDestroyFramebuffer(device, mFramebufferDiscard, gVulkanAllocator);
-		vkDestroyRenderPass(device, mRenderPassDiscard, gVulkanAllocator);
+		VariantKey key(loadMask, readMask);
+		auto iterFind = mVariants.find(key);
+		if (iterFind != mVariants.end())
+			return iterFind->second.renderPass;
 
-		vkDestroyFramebuffer(device, mFramebufferPreserve, gVulkanAllocator);
-		vkDestroyRenderPass(device, mRenderPassPreserve, gVulkanAllocator);
-	}
+		Variant newVariant = createVariant(loadMask, readMask);
+		mVariants[key] = newVariant;
 
-	VkRenderPass VulkanFramebuffer::getRenderPass(bool preserveContents) const
-	{
-		if (preserveContents)
-			return mRenderPassPreserve;
-		
-		return mRenderPassDiscard;
+		return newVariant.renderPass;
 	}
 
-	VkFramebuffer VulkanFramebuffer::getFramebuffer(bool preserveContents) const
+	VkFramebuffer VulkanFramebuffer::getFramebuffer(RenderSurfaceMask loadMask, RenderSurfaceMask readMask) const
 	{
-		if (preserveContents)
-			return mFramebufferPreserve;
+		if (loadMask == RT_NONE && readMask == RT_NONE)
+			return mDefault.framebuffer;
+
+		VariantKey key(loadMask, readMask);
+		auto iterFind = mVariants.find(key);
+		if (iterFind != mVariants.end())
+			return iterFind->second.framebuffer;
+
+		Variant newVariant = createVariant(loadMask, readMask);
+		mVariants[key] = newVariant;
 
-		return mFramebufferDiscard;
+		return newVariant.framebuffer;
 	}
 }

+ 16 - 10
Source/BansheeVulkanRenderAPI/Source/BsVulkanGpuPipelineState.cpp

@@ -26,9 +26,10 @@ namespace bs
 	}
 
 	VulkanGraphicsPipelineStateCore::GpuPipelineKey::GpuPipelineKey(
-		UINT32 framebufferId, UINT32 vertexInputId, bool readOnlyDepth, bool preserveContents, DrawOperationType drawOp)
+		UINT32 framebufferId, UINT32 vertexInputId, bool readOnlyDepth, RenderSurfaceMask loadMask, 
+		RenderSurfaceMask readMask, DrawOperationType drawOp)
 		: framebufferId(framebufferId), vertexInputId(vertexInputId), readOnlyDepth(readOnlyDepth)
-		, preserveContents(preserveContents), drawOp(drawOp)
+		, loadMask(loadMask), readMask(readMask), drawOp(drawOp)
 	{
 		
 	}
@@ -39,7 +40,8 @@ namespace bs
 		hash_combine(hash, key.framebufferId);
 		hash_combine(hash, key.vertexInputId);
 		hash_combine(hash, key.readOnlyDepth);
-		hash_combine(hash, key.preserveContents);
+		hash_combine(hash, key.loadMask);
+		hash_combine(hash, key.readMask);
 		hash_combine(hash, key.drawOp);
 
 		return hash;
@@ -56,7 +58,10 @@ namespace bs
 		if (a.readOnlyDepth != b.readOnlyDepth)
 			return false;
 
-		if (a.preserveContents != b.preserveContents)
+		if (a.loadMask != b.loadMask)
+			return false;
+
+		if (a.readMask != b.readMask)
 			return false;
 
 		if (a.drawOp != b.drawOp)
@@ -301,22 +306,22 @@ namespace bs
 	}
 
 	VulkanPipeline* VulkanGraphicsPipelineStateCore::getPipeline(
-		UINT32 deviceIdx, VulkanFramebuffer* framebuffer, bool readOnlyDepth, bool preserveContents,
-		DrawOperationType drawOp, const SPtr<VulkanVertexInput>& vertexInput)
+		UINT32 deviceIdx, VulkanFramebuffer* framebuffer, bool readOnlyDepth, RenderSurfaceMask loadMask, 
+		RenderSurfaceMask readMask, DrawOperationType drawOp, const SPtr<VulkanVertexInput>& vertexInput)
 	{
 		Lock(mMutex);
 
 		if (mPerDeviceData[deviceIdx].device == nullptr)
 			return nullptr;
 
-		GpuPipelineKey key(framebuffer->getId(), vertexInput->getId(), readOnlyDepth, preserveContents, drawOp);
+		GpuPipelineKey key(framebuffer->getId(), vertexInput->getId(), readOnlyDepth, loadMask, readMask, drawOp);
 
 		PerDeviceData& perDeviceData = mPerDeviceData[deviceIdx];
 		auto iterFind = perDeviceData.pipelines.find(key);
 		if (iterFind != perDeviceData.pipelines.end())
 			return iterFind->second;
 
-		VulkanPipeline* newPipeline = createPipeline(deviceIdx, framebuffer, readOnlyDepth, preserveContents,
+		VulkanPipeline* newPipeline = createPipeline(deviceIdx, framebuffer, readOnlyDepth, loadMask, readMask,
 			drawOp, vertexInput);
 		perDeviceData.pipelines[key] = newPipeline;
 
@@ -353,7 +358,8 @@ namespace bs
 	}
 
 	VulkanPipeline* VulkanGraphicsPipelineStateCore::createPipeline(UINT32 deviceIdx, VulkanFramebuffer* framebuffer,
-		bool readOnlyDepth, bool preserveContents, DrawOperationType drawOp, const SPtr<VulkanVertexInput>& vertexInput)
+		bool readOnlyDepth, RenderSurfaceMask loadMask, RenderSurfaceMask readMask, DrawOperationType drawOp, 
+		const SPtr<VulkanVertexInput>& vertexInput)
 	{
 		mInputAssemblyInfo.topology = VulkanUtility::getDrawOp(drawOp);
 		mTesselationInfo.patchControlPoints = 3; // Not provided by our shaders for now
@@ -386,7 +392,7 @@ namespace bs
 			mDepthStencilInfo.back.depthFailOp = VK_STENCIL_OP_KEEP;
 		}
 
-		mPipelineInfo.renderPass = framebuffer->getRenderPass(preserveContents);
+		mPipelineInfo.renderPass = framebuffer->getRenderPass(loadMask, readMask);
 		mPipelineInfo.layout = mPerDeviceData[deviceIdx].pipelineLayout;
 		mPipelineInfo.pVertexInputState = vertexInput->getCreateInfo();
 

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

@@ -493,12 +493,12 @@ namespace bs
 	}
 
 	void VulkanRenderAPI::setRenderTarget(const SPtr<RenderTargetCore>& target, bool readOnlyDepthStencil,
-		bool preserveContents, const SPtr<CommandBuffer>& commandBuffer)
+		RenderSurfaceMask loadMask, const SPtr<CommandBuffer>& commandBuffer)
 	{
 		VulkanCommandBuffer* cb = getCB(commandBuffer);
 		VulkanCmdBuffer* vkCB = cb->getInternal();
 
-		vkCB->setRenderTarget(target, readOnlyDepthStencil, preserveContents);
+		vkCB->setRenderTarget(target, readOnlyDepthStencil, loadMask);
 		
 		BS_INC_RENDER_STAT(NumRenderTargetChanges);
 	}

+ 10 - 7
Source/BansheeVulkanRenderAPI/Source/BsVulkanTexture.cpp

@@ -183,8 +183,8 @@ namespace bs
 		vkGetImageSubresourceLayout(device.getLogical(), mImage, &range, &layout);
 
 		assert(layout.size == output.getSize());
-		output.setRowPitch(layout.rowPitch);
-		output.setSlicePitch(layout.depthPitch);
+		output.setRowPitch((UINT32)layout.rowPitch);
+		output.setSlicePitch((UINT32)layout.depthPitch);
 
 		UINT8* data;
 		VkResult result = vkMapMemory(device.getLogical(), mMemory, layout.offset, layout.size, 0, (void**)&data);
@@ -255,8 +255,6 @@ namespace bs
 		mImageCI.pNext = nullptr;
 		mImageCI.flags = 0;
 
-		// Note: If usage is dynamic I might consider creating a VK_IMAGE_TILING_LINEAR (if supported by the device)
-
 		TextureType texType = props.getTextureType();
 		switch(texType)
 		{
@@ -275,16 +273,19 @@ namespace bs
 			break;
 		}
 
+		// Note: I force rendertarget and depthstencil types to be readable in shader. Depending on performance impact
+		// it might be beneficial to allow the user to enable this explicitly only when needed.
+
 		int usage = props.getUsage();
 		if ((usage & TU_RENDERTARGET) != 0)
 		{
-			mImageCI.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+			mImageCI.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
 			mSupportsGPUWrites = true;
 			mAccessFlags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
 		}
 		else if ((usage & TU_DEPTHSTENCIL) != 0)
 		{
-			mImageCI.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+			mImageCI.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
 			mSupportsGPUWrites = true;
 			mAccessFlags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
 		}
@@ -400,7 +401,9 @@ namespace bs
 		result = vkBindBufferMemory(vkDevice, buffer, memory, 0);
 		assert(result == VK_SUCCESS);
 
-		return device.getResourceManager().create<VulkanBuffer>(buffer, VK_NULL_HANDLE, memory, 
+		VkBufferView view = VK_NULL_HANDLE;
+
+		return device.getResourceManager().create<VulkanBuffer>(buffer, view, memory,
 			pixelData.getRowPitch(), pixelData.getSlicePitch());
 	}