2
0
Эх сурвалжийг харах

Vulkan GPU parameter binding

BearishSun 9 жил өмнө
parent
commit
18ab7deb32

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

@@ -49,6 +49,17 @@ namespace BansheeEngine
 		UINT32 mNextId;
 	};
 
+	/** Determines where are the current descriptor sets bound to. */
+	enum class DescriptorSetBindFlag
+	{
+		None = 0,
+		Graphics = 1 << 0,
+		Compute = 1 << 1
+	};
+
+	typedef Flags<DescriptorSetBindFlag> DescriptorSetBindFlags;
+	BS_FLAGS_OPERATORS(DescriptorSetBindFlag)
+
 	/** 
 	 * Represents a direct wrapper over an internal Vulkan command buffer. This is unlike VulkanCommandBuffer which is a
 	 * higher level class, and it allows for re-use by internally using multiple low-level command buffers.
@@ -172,6 +183,9 @@ namespace BansheeEngine
 		/** Assigns a pipeline state to use for subsequent dispatch commands. */
 		void setPipelineState(const SPtr<ComputePipelineStateCore>& state);
 
+		/** Assign GPU params to the GPU programs bound by the pipeline state. */
+		void setGpuParams(const SPtr<GpuParamsCore>& gpuParams);
+
 		/** Sets the current viewport which determine to which portion of the render target to render to. */
 		void setViewport(const Rect2& area);
 
@@ -235,8 +249,8 @@ namespace BansheeEngine
 		/** Checks if all the prerequisites for rendering have been made (e.g. render target and pipeline state are set. */
 		bool isReadyForRender();
 
-		/** Binds the current graphics pipeline to the command buffer. */
-		void bindGraphicsPipeline();
+		/** Binds the current graphics pipeline to the command buffer. Returns true if bind was successful. */
+		bool bindGraphicsPipeline();
 
 		/** Binds any dynamic states to the pipeline, as required. 
 		 *
@@ -272,15 +286,18 @@ namespace BansheeEngine
 		Rect2I mScissor;
 		UINT32 mStencilRef;
 		DrawOperationType mDrawOp;
+		UINT32 mNumBoundDescriptorSets;
 		bool mGfxPipelineRequiresBind : 1;
 		bool mCmpPipelineRequiresBind : 1;
 		bool mViewportRequiresBind : 1;
 		bool mStencilRefRequiresBind : 1;
 		bool mScissorRequiresBind : 1;
+		DescriptorSetBindFlags mDescriptorSetsBindState;
 
 		VkSemaphore mSemaphoresTemp[BS_MAX_UNIQUE_QUEUES + 1]; // +1 for present semaphore
 		VkBuffer mVertexBuffersTemp[BS_MAX_BOUND_VERTEX_BUFFERS];
 		VkDeviceSize mVertexBufferOffsetsTemp[BS_MAX_BOUND_VERTEX_BUFFERS];
+		VkDescriptorSet* mDescriptorSetsTemp;
 		UnorderedMap<UINT32, TransitionInfo> mTransitionInfoTemp;
 	};
 

+ 13 - 4
Source/BansheeVulkanRenderAPI/Include/BsVulkanGpuParams.h

@@ -36,14 +36,23 @@ namespace BansheeEngine
 		/** @copydoc GpuParamsCore::setLoadStoreSurface */
 		void setLoadStoreSurface(UINT32 set, UINT32 slot, const TextureSurface& surface) override;
 
+		/** Returns the total number of descriptor sets used by this object. */
+		UINT32 getNumSets() const;
+
 		/** 
-		 * Binds the internal descriptor sets to the provided command buffer. Caller must perform external locking if
-		 * some other thread could write to this object while it is being bound. The same applies to any resources
-		 * held by this object.
+		 * Prepares the internal descriptor sets for a bind operation on the provided command buffer. It generates and/or
+		 * updates and descriptor sets, and registers the relevant resources with the command buffer.
+		 * 
+		 * Caller must perform external locking if some other thread could write to this object while it is being bound. 
+		 * The same applies to any resources held by this object.
+		 * 
+		 * @param[in]	buffer	Buffer on which the parameters will be bound to.
+		 * @param[out]	sets	Pre-allocated buffer in which the descriptor set handled will be written. Must be of
+		 *						getNumSets() size.
 		 * 
 		 * @note	Thread safe.
 		 */
-		void bind(VulkanCommandBuffer& buffer);
+		void prepareForBind(VulkanCmdBuffer& buffer, VkDescriptorSet* sets);
 
 	protected:
 		/** Contains data about writing to either buffer or a texture descriptor. */

+ 13 - 0
Source/BansheeVulkanRenderAPI/Include/BsVulkanGpuPipelineState.h

@@ -54,6 +54,12 @@ namespace BansheeEngine
 		VulkanPipeline* getPipeline(UINT32 deviceIdx, VulkanFramebuffer* framebuffer, bool readOnlyDepth, 
 			DrawOperationType drawOp, VkPipelineVertexInputStateCreateInfo* vertexInputState);
 
+		/** 
+		 * Returns a pipeline layout object for the specified device index. If the device index doesn't match a bit in the
+		 * device mask provided on pipeline creation, null is returned.
+		 */
+		VkPipelineLayout getPipelineLayout(UINT32 deviceIdx) const;
+
 	protected:
 		friend class VulkanRenderStateCoreManager;
 
@@ -117,6 +123,12 @@ namespace BansheeEngine
 		 */
 		VulkanPipeline* getPipeline(UINT32 deviceIdx) const;
 
+		/** 
+		 * Returns a pipeline layout object for the specified device index. If the device index doesn't match a bit in the
+		 * device mask provided on pipeline creation, null is returned.
+		 */
+		VkPipelineLayout getPipelineLayout(UINT32 deviceIdx) const;
+
 	protected:
 		friend class VulkanRenderStateCoreManager;
 
@@ -130,6 +142,7 @@ namespace BansheeEngine
 		{
 			VulkanDevice* device;
 			VulkanPipeline* pipeline;
+			VkPipelineLayout pipelineLayout;
 		};
 
 		GpuDeviceFlags mDeviceMask;

+ 68 - 14
Source/BansheeVulkanRenderAPI/Source/BsVulkanCommandBuffer.cpp

@@ -104,10 +104,13 @@ namespace BansheeEngine
 		: 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), mGlobalQueueIdx(-1), mViewport(0.0f, 0.0f, 1.0f, 1.0f), mScissor(0, 0, 0, 0)
-		, mStencilRef(0), mDrawOp(DOT_TRIANGLE_LIST), mGfxPipelineRequiresBind(true), mCmpPipelineRequiresBind(true)
-		, mViewportRequiresBind(true), mStencilRefRequiresBind(true), mScissorRequiresBind(true), mVertexBuffersTemp()
-		, mVertexBufferOffsetsTemp()
+		, mStencilRef(0), mDrawOp(DOT_TRIANGLE_LIST), mNumBoundDescriptorSets(0), mGfxPipelineRequiresBind(true)
+		, mCmpPipelineRequiresBind(true), mViewportRequiresBind(true), mStencilRefRequiresBind(true)
+		, mScissorRequiresBind(true), mVertexBuffersTemp(), mVertexBufferOffsetsTemp()
 	{
+		UINT32 maxBoundDescriptorSets = device.getDeviceProperties().limits.maxBoundDescriptorSets;
+		mDescriptorSetsTemp = (VkDescriptorSet*)bs_alloc(sizeof(VkDescriptorSet) * maxBoundDescriptorSets);
+
 		VkCommandBufferAllocateInfo cmdBufferAllocInfo;
 		cmdBufferAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
 		cmdBufferAllocInfo.pNext = nullptr;
@@ -183,6 +186,8 @@ namespace BansheeEngine
 		vkDestroyFence(device, mFence, gVulkanAllocator);
 		vkDestroySemaphore(device, mSemaphore, gVulkanAllocator);
 		vkFreeCommandBuffers(device, mPool, 1, &mCmdBuffer);
+
+		bs_free(mDescriptorSetsTemp);
 	}
 
 	UINT32 VulkanCmdBuffer::getDeviceIdx() const
@@ -575,6 +580,23 @@ namespace BansheeEngine
 		mCmpPipelineRequiresBind = true;
 	}
 
+	void VulkanCmdBuffer::setGpuParams(const SPtr<GpuParamsCore>& gpuParams)
+	{
+		SPtr<VulkanGpuParams> vulkanGpuParams = std::static_pointer_cast<VulkanGpuParams>(gpuParams);
+
+		if(vulkanGpuParams != nullptr)
+		{
+			mNumBoundDescriptorSets = vulkanGpuParams->getNumSets();
+			vulkanGpuParams->prepareForBind(*this, mDescriptorSetsTemp);
+		}
+		else
+		{
+			mNumBoundDescriptorSets = 0;
+		}
+
+		mDescriptorSetsBindState = DescriptorSetBindFlag::Graphics | DescriptorSetBindFlag::Compute;
+	}
+
 	void VulkanCmdBuffer::setViewport(const Rect2& area)
 	{
 		if (mViewport == area)
@@ -681,18 +703,16 @@ namespace BansheeEngine
 		return mFramebuffer != nullptr && mVertexDecl != nullptr;
 	}
 
-	void VulkanCmdBuffer::bindGraphicsPipeline()
+	bool VulkanCmdBuffer::bindGraphicsPipeline()
 	{
-
-		
 		// TODO - Begin render pass as needed
 		// TODO - Retrieve and bind pipeline
 		bindDynamicStates(true);
 
-
-
+		// TODO - Register pipeline resource
 		// TODO
-		// TODO - Bind GPU params
+
+		// TODO - Make sure to return false without reseting the flag below, if binding fails
 
 		mGfxPipelineRequiresBind = false;
 	}
@@ -749,10 +769,24 @@ namespace BansheeEngine
 			return;
 
 		if (mGfxPipelineRequiresBind)
-			bindGraphicsPipeline();
+		{
+			if (!bindGraphicsPipeline())
+				return;
+		}
 		else
 			bindDynamicStates(false);
 
+		if (mDescriptorSetsBindState.isSet(DescriptorSetBindFlag::Graphics))
+		{
+			UINT32 deviceIdx = mDevice.getIndex();
+			VkPipelineLayout pipelineLayout = mGraphicsPipeline->getPipelineLayout(deviceIdx);
+
+			vkCmdBindDescriptorSets(mCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0,
+									mNumBoundDescriptorSets, mDescriptorSetsTemp, 0, nullptr);
+
+			mDescriptorSetsBindState.unset(DescriptorSetBindFlag::Graphics);
+		}
+
 		vkCmdDraw(mCmdBuffer, vertexCount, instanceCount, vertexOffset, 0);
 	}
 
@@ -762,10 +796,24 @@ namespace BansheeEngine
 			return;
 
 		if (mGfxPipelineRequiresBind)
-			bindGraphicsPipeline();
+		{
+			if (!bindGraphicsPipeline())
+				return;
+		}
 		else
 			bindDynamicStates(false);
 
+		if (mDescriptorSetsBindState.isSet(DescriptorSetBindFlag::Graphics))
+		{
+			UINT32 deviceIdx = mDevice.getIndex();
+			VkPipelineLayout pipelineLayout = mGraphicsPipeline->getPipelineLayout(deviceIdx);
+
+			vkCmdBindDescriptorSets(mCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0,
+									mNumBoundDescriptorSets, mDescriptorSetsTemp, 0, nullptr);
+
+			mDescriptorSetsBindState.unset(DescriptorSetBindFlag::Graphics);
+		}
+
 		vkCmdDrawIndexed(mCmdBuffer, indexCount, instanceCount, startIndex, vertexOffset, 0);
 	}
 
@@ -777,10 +825,9 @@ namespace BansheeEngine
 		if (isInRenderPass())
 			endRenderPass();
 
+		UINT32 deviceIdx = mDevice.getIndex();
 		if(mCmpPipelineRequiresBind)
 		{
-			UINT32 deviceIdx = mDevice.getIndex();
-
 			VulkanPipeline* pipeline = mComputePipeline->getPipeline(deviceIdx);
 			if (pipeline == nullptr)
 				return;
@@ -791,7 +838,14 @@ namespace BansheeEngine
 			mCmpPipelineRequiresBind = false;
 		}
 
-		// TODO - Bind GpuParams
+		if(mDescriptorSetsBindState.isSet(DescriptorSetBindFlag::Compute))
+		{
+			VkPipelineLayout pipelineLayout = mComputePipeline->getPipelineLayout(deviceIdx);
+			vkCmdBindDescriptorSets(mCmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0,
+				mNumBoundDescriptorSets, mDescriptorSetsTemp, 0, nullptr);
+
+			mDescriptorSetsBindState.unset(DescriptorSetBindFlag::Compute);
+		}
 
 		vkCmdDispatch(mCmdBuffer, numGroupsX, numGroupsY, numGroupsZ);
 	}

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

@@ -289,7 +289,12 @@ namespace BansheeEngine
 		mSetsDirty[set] = true;
 	}
 
-	void VulkanGpuParams::bind(VulkanCommandBuffer& buffer)
+	UINT32 VulkanGpuParams::getNumSets() const
+	{
+		return mParamInfo->getNumSets();
+	}
+
+	void VulkanGpuParams::prepareForBind(VulkanCmdBuffer& buffer, VkDescriptorSet* sets)
 	{
 		UINT32 deviceIdx = buffer.getDeviceIdx();
 
@@ -307,7 +312,6 @@ namespace BansheeEngine
 		// Registers resources with the command buffer
 		// Note: Makes the assumption that this object (and all of the resources it holds) are externally locked, and will
 		// not be modified on another thread while being bound.
-		VulkanCmdBuffer* internalCB = buffer.getInternal();
 		for (UINT32 i = 0; i < numParamBlocks; i++)
 		{
 			if (mParamBlockBuffers[i] == nullptr)
@@ -316,7 +320,7 @@ namespace BansheeEngine
 			VulkanGpuParamBlockBufferCore* element = static_cast<VulkanGpuParamBlockBufferCore*>(mParamBlockBuffers[i].get());
 
 			VulkanBuffer* resource = element->getResource(deviceIdx);
-			internalCB->registerResource(resource, VK_ACCESS_UNIFORM_READ_BIT, VulkanUseFlag::Read);
+			buffer.registerResource(resource, VK_ACCESS_UNIFORM_READ_BIT, VulkanUseFlag::Read);
 		}
 
 		for (UINT32 i = 0; i < numBuffers; i++)
@@ -336,7 +340,7 @@ namespace BansheeEngine
 			}
 
 			VulkanBuffer* resource = element->getResource(deviceIdx);
-			internalCB->registerResource(resource, accessFlags, useFlags);
+			buffer.registerResource(resource, accessFlags, useFlags);
 		}
 
 		for (UINT32 i = 0; i < numSamplers; i++)
@@ -350,7 +354,7 @@ namespace BansheeEngine
 			if (resource == nullptr)
 				continue;
 
-			internalCB->registerResource(resource, VulkanUseFlag::Read);
+			buffer.registerResource(resource, VulkanUseFlag::Read);
 		}
 
 		for (UINT32 i = 0; i < numStorageTextures; i++)
@@ -375,7 +379,7 @@ namespace BansheeEngine
 			range.baseMipLevel = surface.mipLevel;
 			range.levelCount = surface.numMipLevels;
 
-			internalCB->registerResource(resource, accessFlags, VK_IMAGE_LAYOUT_GENERAL, range, useFlags);
+			buffer.registerResource(resource, accessFlags, VK_IMAGE_LAYOUT_GENERAL, range, useFlags);
 		}
 
 		for (UINT32 i = 0; i < numTextures; i++)
@@ -402,7 +406,7 @@ namespace BansheeEngine
 			range.baseMipLevel = 0;
 			range.levelCount = props.getNumMipmaps();
 
-			internalCB->registerResource(resource, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 
+			buffer.registerResource(resource, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
 				range, VulkanUseFlag::Read);
 		}
 
@@ -448,37 +452,12 @@ namespace BansheeEngine
 			mSetsDirty[i] = false;
 		}
 
-		// Actually bind the sets to the command buffer
-		VkCommandBuffer vkCB = internalCB->getHandle();
-
-		VkPipelineBindPoint bindPoint;
-		GpuQueueType queueType = buffer.getType();
-		switch(queueType)
-		{
-		case GQT_GRAPHICS:
-			bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
-			break;
-		case GQT_COMPUTE:
-			bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE;
-			break;
-		case GQT_UPLOAD:
-		default:
-			LOGERR("Cannot bind GpuParams on the upload queue. Ignoring.");
-			return;
-		}
-
-		VkDescriptorSet* sets = bs_stack_alloc<VkDescriptorSet>(numSets);
 		for (UINT32 i = 0; i < numSets; i++)
 		{
 			VulkanDescriptorSet* set = perDeviceData.perSetData[i].latestSet;
 
-			internalCB->registerResource(set, VulkanUseFlag::Read);
+			buffer.registerResource(set, VulkanUseFlag::Read);
 			sets[i] = set->getHandle();
 		}
-
-		vkCmdBindDescriptorSets(vkCB, bindPoint, perDeviceData.pipelineLayout, 0, 
-			numSets, sets, 0, nullptr);
-
-		bs_stack_free(sets);
 	}
 }

+ 12 - 0
Source/BansheeVulkanRenderAPI/Source/BsVulkanGpuPipelineState.cpp

@@ -260,6 +260,11 @@ namespace BansheeEngine
 		return nullptr;
 	}
 
+	VkPipelineLayout VulkanGraphicsPipelineStateCore::getPipelineLayout(UINT32 deviceIdx) const
+	{
+		return mPerDeviceData[deviceIdx].pipelineLayout;
+	}
+
 	VulkanPipeline* VulkanGraphicsPipelineStateCore::createPipeline(UINT32 deviceIdx, VulkanFramebuffer* framebuffer,
 														  bool readOnlyDepth, DrawOperationType drawOp,
 														  VkPipelineVertexInputStateCreateInfo* vertexInputState)
@@ -419,11 +424,13 @@ namespace BansheeEngine
 
 
 				mPerDeviceData[i].pipeline = rescManager.create<VulkanPipeline>(pipeline);
+				mPerDeviceData[i].pipelineLayout = pipelineCI.layout;
 				bs_stack_free(layouts);
 			}
 			else
 			{
 				mPerDeviceData[i].pipeline = nullptr;
+				mPerDeviceData[i].pipelineLayout = VK_NULL_HANDLE;
 			}
 		}
 
@@ -435,4 +442,9 @@ namespace BansheeEngine
 	{
 		return mPerDeviceData[deviceIdx].pipeline;
 	}
+
+	VkPipelineLayout VulkanComputePipelineStateCore::getPipelineLayout(UINT32 deviceIdx) const
+	{
+		return mPerDeviceData[deviceIdx].pipelineLayout;
+	}
 }

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

@@ -329,21 +329,21 @@ namespace BansheeEngine
 	void VulkanRenderAPI::setGpuParams(const SPtr<GpuParamsCore>& gpuParams, const SPtr<CommandBuffer>& commandBuffer)
 	{
 		VulkanCommandBuffer* cb = getCB(commandBuffer);
-		SPtr<VulkanGpuParams> vulkanGpuParams = std::static_pointer_cast<VulkanGpuParams>(gpuParams);
+		VulkanCmdBuffer* vkCB = cb->getInternal();
 
-		vulkanGpuParams->bind(*cb);
+		vkCB->setGpuParams(gpuParams);
 
 		BS_INC_RENDER_STAT(NumGpuParamBinds);
 	}
 
 	void VulkanRenderAPI::beginFrame(const SPtr<CommandBuffer>& commandBuffer)
 	{
-
+		// Do nothing
 	}
 
 	void VulkanRenderAPI::endFrame(const SPtr<CommandBuffer>& commandBuffer)
 	{
-
+		// Do nothing
 	}
 
 	void VulkanRenderAPI::setViewport(const Rect2& vp, const SPtr<CommandBuffer>& commandBuffer)