Просмотр исходного кода

Vulkan graphics pipeline binding and caching

BearishSun 9 лет назад
Родитель
Сommit
4581b43fe7

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

@@ -133,6 +133,9 @@ namespace BansheeEngine
 		/** Returns true if the command buffer is currently being processed by the device. */
 		bool isSubmitted() const { return mState == State::Submitted; }
 
+		/** Returns true if the command buffer is currently recording (but not within a render pass). */
+		bool isRecording() const { return mState == State::Recording; }
+
 		/** Returns true if the command buffer is ready to be submitted to a queue. */
 		bool isReadyForSubmit() const { return mState == State::RecordingDone; }
 

+ 30 - 4
Source/BansheeVulkanRenderAPI/Include/BsVulkanGpuPipelineState.h

@@ -46,13 +46,13 @@ namespace BansheeEngine
 		 * @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]	drawOp				Type of geometry that will be drawn using the pipeline.
-		 * @param[in]	vertexInputState	State describing inputs to the vertex program.
+		 * @param[in]	vertexInput			State describing inputs to the vertex program.
 		 * @return							Vulkan graphics pipeline object.
 		 * 
 		 * @note	Thread safe.
 		 */
 		VulkanPipeline* getPipeline(UINT32 deviceIdx, VulkanFramebuffer* framebuffer, bool readOnlyDepth, 
-			DrawOperationType drawOp, VkPipelineVertexInputStateCreateInfo* vertexInputState);
+			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
@@ -75,19 +75,45 @@ namespace BansheeEngine
 		 * @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]	drawOp				Type of geometry that will be drawn using the pipeline.
-		 * @param[in]	vertexInputState	State describing inputs to the vertex program.
+		 * @param[in]	vertexInput			State describing inputs to the vertex program.
 		 * @return							Vulkan graphics pipeline object.
 		 * 
 		 * @note	Thread safe.
 		 */
 		VulkanPipeline* createPipeline(UINT32 deviceIdx, VulkanFramebuffer* framebuffer, bool readOnlyDepth,
-								  DrawOperationType drawOp, VkPipelineVertexInputStateCreateInfo* vertexInputState);
+								  DrawOperationType drawOp, const SPtr<VulkanVertexInput>& vertexInput);
+
+		/**	Key uniquely identifying GPU pipelines. */
+		struct GpuPipelineKey
+		{
+			GpuPipelineKey(UINT32 framebufferId, UINT32 vertexInputId, bool readOnlyDepth, DrawOperationType drawOp);
+
+			UINT32 framebufferId;
+			UINT32 vertexInputId;
+			bool readOnlyDepth;
+			DrawOperationType drawOp;
+		};
+
+		/**	Creates a hash from GPU pipeline key. */
+		class HashFunc
+		{
+		public:
+			::std::size_t operator()(const GpuPipelineKey& key) const;
+		};
+
+		/**	Compares two GPU pipeline keys. */
+		class EqualFunc
+		{
+		public:
+			bool operator()(const GpuPipelineKey& a, const GpuPipelineKey& b) const;
+		};
 
 		/** Contains pipeline data specific to a single Vulkan device. */
 		struct PerDeviceData
 		{
 			VulkanDevice* device;
 			VkPipelineLayout pipelineLayout;
+			UnorderedMap<GpuPipelineKey, VulkanPipeline*, HashFunc, EqualFunc> pipelines;
 		};
 
 		VkPipelineShaderStageCreateInfo mShaderStageInfos[5];

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

@@ -59,6 +59,7 @@ namespace BansheeEngine
 	class VulkanEvent;
 	class VulkanQuery;
 	class VulkanQueryPool;
+	class VulkanVertexInput;
 
 	VkAllocationCallbacks* gVulkanAllocator = nullptr;
 

+ 3 - 5
Source/BansheeVulkanRenderAPI/Include/BsVulkanVertexInputManager.h

@@ -15,17 +15,15 @@ namespace BansheeEngine
 	class VulkanVertexInput
 	{
 	public:
+		VulkanVertexInput(UINT32 id, const VkPipelineVertexInputStateCreateInfo& createInfo);
+
 		/** Returns an object contining the necessary information to initialize the vertex input on a pipeline. */
-		const VkPipelineVertexInputStateCreateInfo& getCreateInfo() const { return mCreateInfo; }
+		const VkPipelineVertexInputStateCreateInfo* getCreateInfo() const { return &mCreateInfo; }
 
 		/** Returns an identifier which uniquely represents this vertex input configuration. */
 		UINT32 getId() const { return mId; }
 
 	private:
-		friend class VulkanVertexInputManager;
-
-		VulkanVertexInput(UINT32 id, const VkPipelineVertexInputStateCreateInfo& createInfo);
-
 		UINT32 mId;
 		VkPipelineVertexInputStateCreateInfo mCreateInfo;
 	};

+ 25 - 6
Source/BansheeVulkanRenderAPI/Source/BsVulkanCommandBuffer.cpp

@@ -11,6 +11,7 @@
 #include "BsVulkanVertexBuffer.h"
 #include "BsVulkanHardwareBuffer.h"
 #include "BsVulkanFramebuffer.h"
+#include "BsVulkanVertexInputManager.h"
 
 namespace BansheeEngine
 {
@@ -705,16 +706,22 @@ namespace BansheeEngine
 
 	bool VulkanCmdBuffer::bindGraphicsPipeline()
 	{
-		// TODO - Begin render pass as needed
-		// TODO - Retrieve and bind pipeline
-		bindDynamicStates(true);
+		SPtr<VertexDeclarationCore> inputDecl = mGraphicsPipeline->getInputDeclaration();
+		SPtr<VulkanVertexInput> vertexInput = VulkanVertexInputManager::instance().getVertexInfo(mVertexDecl, inputDecl);
+
+		VulkanPipeline* pipeline = mGraphicsPipeline->getPipeline(mDevice.getIndex(), mFramebuffer,
+																  mRenderTargetDepthReadOnly, mDrawOp, vertexInput);
+
+		if (pipeline == nullptr)
+			return false;
 
-		// TODO - Register pipeline resource
-		// TODO
+		registerResource(pipeline, VulkanUseFlag::Read);
 
-		// TODO - Make sure to return false without reseting the flag below, if binding fails
+		vkCmdBindPipeline(mCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->getHandle());
+		bindDynamicStates(true);
 
 		mGfxPipelineRequiresBind = false;
+		return true;
 	}
 
 	void VulkanCmdBuffer::bindDynamicStates(bool forceAll)
@@ -768,6 +775,9 @@ namespace BansheeEngine
 		if (!isReadyForRender())
 			return;
 
+		if (!isInRenderPass())
+			beginRenderPass();
+
 		if (mGfxPipelineRequiresBind)
 		{
 			if (!bindGraphicsPipeline())
@@ -795,6 +805,9 @@ namespace BansheeEngine
 		if (!isReadyForRender())
 			return;
 
+		if (!isInRenderPass())
+			beginRenderPass();
+
 		if (mGfxPipelineRequiresBind)
 		{
 			if (!bindGraphicsPipeline())
@@ -956,6 +969,12 @@ namespace BansheeEngine
 		// Ignore myself
 		syncMask &= ~mIdMask;
 
+		if (mBuffer->isInRenderPass())
+			mBuffer->endRenderPass();
+
+		if (mBuffer->isRecording())
+			mBuffer->end();
+
 		mBuffer->submit(mQueue, mQueueIdx, syncMask);
 
 		gVulkanCBManager().refreshStates(mDeviceIdx);

+ 65 - 9
Source/BansheeVulkanRenderAPI/Source/BsVulkanGpuPipelineState.cpp

@@ -7,6 +7,7 @@
 #include "BsVulkanUtility.h"
 #include "BsVulkanRenderAPI.h"
 #include "BsVulkanGpuPipelineParamInfo.h"
+#include "BsVulkanVertexInputManager.h"
 #include "BsRasterizerState.h"
 #include "BsDepthStencilState.h"
 #include "BsBlendState.h"
@@ -23,6 +24,41 @@ namespace BansheeEngine
 		vkDestroyPipeline(mOwner->getDevice().getLogical(), mPipeline, gVulkanAllocator);
 	}
 
+	VulkanGraphicsPipelineStateCore::GpuPipelineKey::GpuPipelineKey(
+		UINT32 framebufferId, UINT32 vertexInputId, bool readOnlyDepth, DrawOperationType drawOp)
+		:framebufferId(framebufferId), vertexInputId(vertexInputId), readOnlyDepth(readOnlyDepth), drawOp(drawOp)
+	{
+		
+	}
+
+	size_t VulkanGraphicsPipelineStateCore::HashFunc::operator()(const GpuPipelineKey& key) const
+	{
+		size_t hash = 0;
+		hash_combine(hash, key.framebufferId);
+		hash_combine(hash, key.vertexInputId);
+		hash_combine(hash, key.readOnlyDepth);
+		hash_combine(hash, key.drawOp);
+
+		return hash;
+	}
+
+	bool VulkanGraphicsPipelineStateCore::EqualFunc::operator()(const GpuPipelineKey& a, const GpuPipelineKey& b) const
+	{
+		if (a.framebufferId != b.framebufferId)
+			return false;
+
+		if (a.vertexInputId != b.vertexInputId)
+			return false;
+
+		if (a.readOnlyDepth != b.readOnlyDepth)
+			return false;
+
+		if (a.drawOp != b.drawOp)
+			return false;
+
+		return true;
+	}
+
 	VulkanGraphicsPipelineStateCore::VulkanGraphicsPipelineStateCore(const PIPELINE_STATE_CORE_DESC& desc,
 																	 GpuDeviceFlags deviceMask)
 		:GraphicsPipelineStateCore(desc, deviceMask), mScissorEnabled(false), mDeviceMask(deviceMask)
@@ -32,9 +68,16 @@ namespace BansheeEngine
 
 	VulkanGraphicsPipelineStateCore::~VulkanGraphicsPipelineStateCore()
 	{
-		BS_INC_RENDER_STAT_CAT(ResDestroyed, RenderStatObject_PipelineState);
+		for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
+		{
+			if (mPerDeviceData[i].device == nullptr)
+				continue;
 
-		// TODO - Destroy pipeline
+			for(auto& entry : mPerDeviceData[i].pipelines)
+				entry.second->destroy();
+		}
+
+		BS_INC_RENDER_STAT_CAT(ResDestroyed, RenderStatObject_PipelineState);
 	}
 
 	void VulkanGraphicsPipelineStateCore::initialize()
@@ -251,13 +294,26 @@ namespace BansheeEngine
 		GraphicsPipelineStateCore::initialize();
 	}
 
-	VulkanPipeline* VulkanGraphicsPipelineStateCore::getPipeline(UINT32 deviceIdx, VulkanFramebuffer* framebuffer, bool readOnlyDepth,
-								DrawOperationType drawOp, VkPipelineVertexInputStateCreateInfo* vertexInputState)
+	VulkanPipeline* VulkanGraphicsPipelineStateCore::getPipeline(
+		UINT32 deviceIdx, VulkanFramebuffer* framebuffer, bool readOnlyDepth, DrawOperationType drawOp, 
+		const SPtr<VulkanVertexInput>& vertexInput)
 	{
 		Lock(mMutex);
 
-		// TODO
-		return nullptr;
+		if (mPerDeviceData[deviceIdx].device == nullptr)
+			return nullptr;
+
+		GpuPipelineKey key(framebuffer->getId(), vertexInput->getId(), readOnlyDepth, 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, drawOp, vertexInput);
+		perDeviceData.pipelines[key] = newPipeline;
+
+		return newPipeline;
 	}
 
 	VkPipelineLayout VulkanGraphicsPipelineStateCore::getPipelineLayout(UINT32 deviceIdx) const
@@ -266,8 +322,8 @@ namespace BansheeEngine
 	}
 
 	VulkanPipeline* VulkanGraphicsPipelineStateCore::createPipeline(UINT32 deviceIdx, VulkanFramebuffer* framebuffer,
-														  bool readOnlyDepth, DrawOperationType drawOp,
-														  VkPipelineVertexInputStateCreateInfo* vertexInputState)
+														  bool readOnlyDepth, DrawOperationType drawOp, 
+														  const SPtr<VulkanVertexInput>& vertexInput)
 	{
 		mInputAssemblyInfo.topology = VulkanUtility::getDrawOp(drawOp);
 		mTesselationInfo.patchControlPoints = 3; // Not provided by our shaders for now
@@ -302,7 +358,7 @@ namespace BansheeEngine
 
 		mPipelineInfo.renderPass = framebuffer->getRenderPass();
 		mPipelineInfo.layout = mPerDeviceData[deviceIdx].pipelineLayout;
-		mPipelineInfo.pVertexInputState = vertexInputState;
+		mPipelineInfo.pVertexInputState = vertexInput->getCreateInfo();
 
 		if (framebuffer->hasDepthAttachment())
 			mPipelineInfo.pDepthStencilState = &mDepthStencilInfo;

+ 1 - 1
Source/BansheeVulkanRenderAPI/Source/BsVulkanSwapChain.cpp

@@ -229,7 +229,7 @@ namespace BansheeEngine
 			desc.depth.format = depthFormat;
 			desc.depth.view = mDepthStencilView;
 
-			mSurfaces[i].framebuffer = resManager.create<VulkanFramebuffer>(device, desc);
+			mSurfaces[i].framebuffer = resManager.create<VulkanFramebuffer>(desc);
 		}
 	}