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

More work on Vulkan:
- GPU param descriptors are now updated on bind
- Handling image/buffer transitions between different queue families, as well as image layout transitions (WIP)

BearishSun 9 лет назад
Родитель
Сommit
99d14364e5

+ 3 - 0
Source/BansheeCore/Include/BsCommandBuffer.h

@@ -68,6 +68,9 @@ namespace BansheeEngine
 		/** Returns the index of the queue the command buffer will execute on. */
 		UINT32 getQueueIdx() const { return mQueueIdx; }
 
+		/** Returns the device index this buffer will execute on. */
+		UINT32 getDeviceIdx() const { return mDeviceIdx; }
+
 		/** @name Internal
 		 *  @{
 		 */

+ 4 - 1
Source/BansheeCore/Include/BsTexture.h

@@ -118,7 +118,10 @@ namespace BansheeEngine
         /**	Returns true if the texture has an alpha layer. */
         bool hasAlpha() const;
 
-        /**	Returns the number of faces this texture has. */
+        /**	
+         * Returns the number of faces this texture has. This includes array slices (if texture is an array texture),
+         * as well as cube-map faces.
+         */
         UINT32 getNumFaces() const;
 
 		/** Returns the number of array slices of the texture (if the texture is an array texture). */

+ 36 - 10
Source/BansheeVulkanRenderAPI/Include/BsVulkanCommandBuffer.h

@@ -15,7 +15,7 @@ namespace BansheeEngine
 
 	class VulkanCmdBuffer;
 
-#define BS_MAX_VULKAN_COMMAND_BUFFERS_PER_QUEUE 32
+#define BS_MAX_VULKAN_CB_PER_QUEUE_FAMILY BS_MAX_QUEUES_PER_TYPE * 32
 
 	/** Pool that allocates and distributes Vulkan command buffers. */
 	class VulkanCmdBufferPool
@@ -24,27 +24,26 @@ namespace BansheeEngine
 		VulkanCmdBufferPool(VulkanDevice& device);
 		~VulkanCmdBufferPool();
 
-		/** Attempts to find a free command buffer, or creates a new one if not found. */
-		VulkanCmdBuffer* getBuffer(GpuQueueType type, UINT32 queueIdx, bool secondary);
+		/** 
+		 * Attempts to find a free command buffer, or creates a new one if not found. Caller must guarantee the provided
+		 * queue family is valid. 
+		 */
+		VulkanCmdBuffer* getBuffer(UINT32 queueFamily, bool secondary);
 
 	private:
 		/** Command buffer pool and related information. */
 		struct PoolInfo
 		{
 			VkCommandPool pool = VK_NULL_HANDLE;
+			VulkanCmdBuffer* buffers[BS_MAX_VULKAN_CB_PER_QUEUE_FAMILY];
 			UINT32 queueFamily = -1;
 		};
 
 		/** Creates a new command buffer. */
-		VulkanCmdBuffer* createBuffer(GpuQueueType type, bool secondary);
-
-		/** Returns a Vulkan command pool for the specified queue type. */
-		const PoolInfo& getPool(GpuQueueType type);
+		VulkanCmdBuffer* createBuffer(UINT32 queueFamily, bool secondary);
 
 		VulkanDevice& mDevice;
-		PoolInfo mPools[GQT_COUNT];
-
-		VulkanCmdBuffer* mBuffers[GQT_COUNT][BS_MAX_QUEUES_PER_TYPE][BS_MAX_VULKAN_COMMAND_BUFFERS_PER_QUEUE];
+		UnorderedMap<UINT32, PoolInfo> mPools;
 		UINT32 mNextId;
 	};
 
@@ -79,6 +78,9 @@ namespace BansheeEngine
 		/** Returns the index of the queue family this command buffer is executing on. */
 		UINT32 getQueueFamily() const { return mQueueFamily; }
 
+		/** Returns the index of the device this command buffer will execute on. */
+		UINT32 getDeviceIdx() const;
+
 		/** Makes the command buffer ready to start recording commands. */
 		void begin();
 
@@ -121,6 +123,12 @@ namespace BansheeEngine
 		 */
 		void registerResource(VulkanResource* res, VulkanUseFlags flags);
 
+		/** 
+		 * Lets the command buffer know that the provided GPU params object has been bound to it, and its resources
+		 * and descriptors will be used by the device when the command buffer is submitted.
+		 */
+		void registerGpuParams(const SPtr<VulkanGpuParams>& params);
+
 	private:
 		friend class VulkanCmdBufferPool;
 		friend class VulkanCommandBuffer;
@@ -131,6 +139,15 @@ namespace BansheeEngine
 			VulkanUseFlags flags;
 		};
 
+		/** 
+		 * Called just before the buffer has been submitted to the queue.
+		 * 
+		 *  @param[out]	transitionInfo	Contains barriers that transition resources to appropriate queues families
+		 *								and/or transition image layouts. Caller should issue relevant pipeline barriers
+		 *								according to this structure, before submitting the command buffer.
+		 */
+		void prepareForSubmit(UnorderedMap<UINT32, TransitionInfo>& transitionInfo);
+
 		/** Called after the buffer has been submitted to the queue. */
 		void notifySubmit();
 
@@ -145,6 +162,7 @@ namespace BansheeEngine
 		UINT32 mFenceCounter;
 
 		UnorderedMap<VulkanResource*, ResourceInfo> mResources;
+		UnorderedSet<SPtr<VulkanGpuParams>> mBoundParams;
 	};
 
 	/** CommandBuffer implementation for Vulkan. */
@@ -162,6 +180,13 @@ namespace BansheeEngine
 		/** Checks if the submitted buffer finished executing, and updates state if it has. */
 		void refreshSubmitStatus();
 
+		/** 
+		 * Returns the internal command buffer. 
+		 * 
+		 * @note	This buffer will change after a submit() call.
+		 */
+		VulkanCmdBuffer* getInternal() const { return mBuffer; }
+
 	private:
 		friend class VulkanCommandBufferManager;
 
@@ -181,6 +206,7 @@ namespace BansheeEngine
 		UINT32 mIdMask;
 
 		VkSemaphore mSemaphoresTemp[BS_MAX_COMMAND_BUFFERS];
+		UnorderedMap<UINT32, TransitionInfo> mTransitionInfoTemp;
 	};
 
 	/** @} */

+ 5 - 1
Source/BansheeVulkanRenderAPI/Include/BsVulkanDevice.h

@@ -16,7 +16,7 @@ namespace BansheeEngine
 	class VulkanDevice
 	{
 	public:
-		VulkanDevice(VkPhysicalDevice device);
+		VulkanDevice(VkPhysicalDevice device, UINT32 deviceIdx);
 		~VulkanDevice();
 
 		/** Returns an object describing the physical properties of the device. */
@@ -28,6 +28,9 @@ namespace BansheeEngine
 		/** Returns true if the device is one of the primary GPU's. */
 		bool isPrimary() const { return mIsPrimary; }
 
+		/** Returns the unique index of the device. */
+		UINT32 getIndex() const { return mDeviceIdx; }
+
 		/** Blocks the calling thread until all operations on the device finish. */
 		void waitIdle() const;
 
@@ -94,6 +97,7 @@ namespace BansheeEngine
 		VkPhysicalDevice mPhysicalDevice;
 		VkDevice mLogicalDevice;
 		bool mIsPrimary;
+		UINT32 mDeviceIdx;
 
 		VulkanCmdBufferPool* mCommandBufferPool;
 		VulkanDescriptorManager* mDescriptorManager;

+ 12 - 0
Source/BansheeVulkanRenderAPI/Include/BsVulkanGpuParams.h

@@ -36,6 +36,18 @@ namespace BansheeEngine
 		/** @copydoc GpuParamsCore::setLoadStoreSurface */
 		void setLoadStoreSurface(UINT32 set, UINT32 slot, const TextureSurface& surface) override;
 
+		/** 
+		 * Notifies the object that a command buffer containing the object is about to be submitted to a queue. 
+		 *
+		 * @param[in]	buffer			Command buffer on which we're about to submit the GPU params.
+		 * @param[out]	transitionInfo	Contains barriers that transition resources to appropriate queues families
+		 *								and/or transition image layouts.
+		 */
+		void prepareForSubmit(VulkanCmdBuffer* buffer, UnorderedMap<UINT32, TransitionInfo>& transitionInfo);
+
+		/** Binds the internal descriptor sets to the provided command buffer. */
+		void bind(VulkanCommandBuffer& buffer);
+
 	protected:
 		/** Contains data about writing to either buffer or a texture descriptor. */
 		union WriteInfo

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

@@ -48,6 +48,7 @@ namespace BansheeEngine
 	class VulkanGpuParamBlockBufferCore;
 	class VulkanBuffer;
 	class VulkanDescriptorPool;
+	class VulkanGpuParams;
 
 	VkAllocationCallbacks* gVulkanAllocator = nullptr;
 
@@ -56,6 +57,13 @@ namespace BansheeEngine
 	{
 		RenderStatObject_PipelineState = 100
 	};
+
+	/** Contains lists of images and buffers that require pipeline barrier transitions. */
+	struct TransitionInfo
+	{
+		Vector<VkImageMemoryBarrier> imageBarriers;
+		Vector<VkBufferMemoryBarrier> bufferBarriers;
+	};
 }
 
 /** Macro to get a procedure address based on a Vulkan instance. */

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

@@ -134,6 +134,9 @@ namespace BansheeEngine
 		 */
 		const Vector<SPtr<VulkanDevice>> _getPrimaryDevices() const { return mPrimaryDevices; }
 
+		/** Returns the main command buffer, executing on the graphics queue. */
+		VulkanCommandBuffer* _getMainCommandBuffer() const { return mMainCommandBuffer.get(); }
+
 		/** @} */
 	protected:
 		friend class VulkanRenderAPIFactory;
@@ -147,11 +150,18 @@ namespace BansheeEngine
 		/** Creates and populates a set of render system capabilities describing which functionality is available. */
 		void initCapabilites();
 
+		/** 
+		 * Returns a valid command buffer. Uses the provided buffer if not null. Otherwise returns the default command 
+		 * buffer. 
+		 */
+		VulkanCommandBuffer* getCB(const SPtr<CommandBuffer>& buffer);
+
 	private:
 		VkInstance mInstance;
 
 		Vector<SPtr<VulkanDevice>> mDevices;
 		Vector<SPtr<VulkanDevice>> mPrimaryDevices;
+		SPtr<VulkanCommandBuffer> mMainCommandBuffer;
 
 		VulkanGLSLProgramFactory* mGLSLFactory;
 

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

@@ -76,6 +76,9 @@ namespace BansheeEngine
 		 */
 		UINT32 getQueueFamily() const { return mQueueFamily; }
 
+		/** Returns true if the resource is only allowed to be used by a single queue family at once. */
+		bool isExclusive() const { return mState != State::Shared; }
+
 		/** 
 		 * Destroys the resource and frees its memory. If the resource is currently being used on a device, the
 		 * destruction is delayed until the device is done with it.

+ 120 - 54
Source/BansheeVulkanRenderAPI/Source/BsVulkanCommandBuffer.cpp

@@ -4,12 +4,13 @@
 #include "BsVulkanCommandBufferManager.h"
 #include "BsVulkanUtility.h"
 #include "BsVulkanDevice.h"
+#include "BsVulkanGpuParams.h"
 #include "BsVulkanQueue.h"
 
 namespace BansheeEngine
 {
 	VulkanCmdBufferPool::VulkanCmdBufferPool(VulkanDevice& device)
-		:mDevice(device), mPools{}, mBuffers{}, mNextId(1)
+		:mDevice(device), mNextId(1)
 	{
 		for (UINT32 i = 0; i < GQT_COUNT; i++)
 		{
@@ -18,51 +19,48 @@ namespace BansheeEngine
 			if (familyIdx == (UINT32)-1)
 				continue;
 
-			VkCommandPoolCreateInfo poolInfo;
-			poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
-			poolInfo.pNext = nullptr;
-			poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
-			poolInfo.queueFamilyIndex = familyIdx;
+			VkCommandPoolCreateInfo poolCI;
+			poolCI.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+			poolCI.pNext = nullptr;
+			poolCI.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+			poolCI.queueFamilyIndex = familyIdx;
 
-			mPools[i].queueFamily = familyIdx;
-			vkCreateCommandPool(device.getLogical(), &poolInfo, gVulkanAllocator, &mPools[i].pool);
+			PoolInfo& poolInfo = mPools[familyIdx];
+			poolInfo.queueFamily = familyIdx;
+			memset(poolInfo.buffers, 0, sizeof(poolInfo.buffers));
+
+			vkCreateCommandPool(device.getLogical(), &poolCI, gVulkanAllocator, &poolInfo.pool);
 		}
 	}
 
 	VulkanCmdBufferPool::~VulkanCmdBufferPool()
 	{
-		for (UINT32 i = 0; i < GQT_COUNT; i++)
+		for(auto& entry : mPools)
 		{
-			for(UINT32 j = 0; j < BS_MAX_QUEUES_PER_TYPE; j++)
+			PoolInfo& poolInfo = entry.second;
+			for (UINT32 i = 0; i < BS_MAX_VULKAN_CB_PER_QUEUE_FAMILY; i++)
 			{
-				VulkanCmdBuffer** buffers = mBuffers[i][j];
-				for(UINT32 k = 0; k < BS_MAX_VULKAN_COMMAND_BUFFERS_PER_QUEUE; k++)
-				{
-					if (buffers[k] == nullptr)
-						break;
-
-					bs_delete(buffers[k]);
-				}
-			}
-		}
+				VulkanCmdBuffer* buffer = poolInfo.buffers[i];
+				if (buffer == nullptr)
+					break;
 
-		for (UINT32 i = 0; i < GQT_COUNT; i++)
-		{
-			if (mPools[i].pool == VK_NULL_HANDLE)
-				continue;
+				bs_delete(buffer);
+			}
 
-			vkDestroyCommandPool(mDevice.getLogical(), mPools[i].pool, gVulkanAllocator);
+			vkDestroyCommandPool(mDevice.getLogical(), poolInfo.pool, gVulkanAllocator);
 		}
 	}
 
-	VulkanCmdBuffer* VulkanCmdBufferPool::getBuffer(GpuQueueType type, UINT32 queueIdx, bool secondary)
+	VulkanCmdBuffer* VulkanCmdBufferPool::getBuffer(UINT32 queueFamily, bool secondary)
 	{
-		assert(queueIdx < BS_MAX_QUEUES_PER_TYPE);
+		auto iterFind = mPools.find(queueFamily);
+		if (iterFind != mPools.end())
+			return nullptr;
 
-		VulkanCmdBuffer** buffers = mBuffers[type][queueIdx];
+		VulkanCmdBuffer** buffers = iterFind->second.buffers;
 
 		UINT32 i = 0;
-		for(; i < BS_MAX_VULKAN_COMMAND_BUFFERS_PER_QUEUE; i++)
+		for(; i < BS_MAX_VULKAN_CB_PER_QUEUE_FAMILY; i++)
 		{
 			if (buffers[i] == nullptr)
 				break;
@@ -74,33 +72,28 @@ namespace BansheeEngine
 			}
 		}
 
-		assert(i < BS_MAX_VULKAN_COMMAND_BUFFERS_PER_QUEUE && 
-			"Too many command buffers allocated. Increment BS_MAX_VULKAN_COMMAND_BUFFERS_PER_QUEUE to a higher value. ");
+		assert(i < BS_MAX_VULKAN_CB_PER_QUEUE_FAMILY &&
+			"Too many command buffers allocated. Increment BS_MAX_VULKAN_CB_PER_QUEUE_FAMILY to a higher value. ");
 
-		buffers[i] = createBuffer(type, secondary);
+		buffers[i] = createBuffer(queueFamily, secondary);
 		buffers[i]->begin();
 
 		return buffers[i];
 	}
 
-	VulkanCmdBuffer* VulkanCmdBufferPool::createBuffer(GpuQueueType type, bool secondary)
+	VulkanCmdBuffer* VulkanCmdBufferPool::createBuffer(UINT32 queueFamily, bool secondary)
 	{
-		const PoolInfo& poolInfo = getPool(type);
+		auto iterFind = mPools.find(queueFamily);
+		if (iterFind != mPools.end())
+			return nullptr;
 
-		return bs_new<VulkanCmdBuffer>(mDevice, mNextId++, poolInfo.pool, poolInfo.queueFamily, secondary);
-	}
+		const PoolInfo& poolInfo = iterFind->second;
 
-	const VulkanCmdBufferPool::PoolInfo& VulkanCmdBufferPool::getPool(GpuQueueType type)
-	{
-		const PoolInfo* poolInfo = &mPools[type];
-		if (poolInfo->pool == VK_NULL_HANDLE)
-			poolInfo = &mPools[GQT_GRAPHICS]; // Graphics queue is guaranteed to exist
-
-		return *poolInfo;
+		return bs_new<VulkanCmdBuffer>(mDevice, mNextId++, poolInfo.pool, poolInfo.queueFamily, secondary);
 	}
 
 	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)
+		: mId(id), mQueueFamily(queueFamily), mState(State::Ready), mDevice(device), mPool(pool), mFenceCounter(0)
 	{
 		VkCommandBufferAllocateInfo cmdBufferAllocInfo;
 		cmdBufferAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
@@ -149,6 +142,11 @@ namespace BansheeEngine
 		vkFreeCommandBuffers(device, mPool, 1, &mCmdBuffer);
 	}
 
+	UINT32 VulkanCmdBuffer::getDeviceIdx() const
+	{
+		return mDevice.getIndex();
+	}
+
 	void VulkanCmdBuffer::begin()
 	{
 		assert(mState == State::Ready);
@@ -217,6 +215,7 @@ namespace BansheeEngine
 					entry.first->notifyDone(this);
 
 				mResources.clear();
+				mBoundParams.clear();
 			}
 		}
 		else
@@ -228,10 +227,19 @@ namespace BansheeEngine
 		mResources[res].flags |= flags;
 	}
 
-	void VulkanCmdBuffer::notifySubmit()
+	void VulkanCmdBuffer::registerGpuParams(const SPtr<VulkanGpuParams>& params)
 	{
-		// TODO - Issue pipeline barrier for resources transitioning to a new queue family
+		mBoundParams.insert(params);
+	}
+
+	void VulkanCmdBuffer::prepareForSubmit(UnorderedMap<UINT32, TransitionInfo>& transitionInfo)
+	{
+		for (auto& entry : mBoundParams)
+			entry->prepareForSubmit(this, transitionInfo);
+	}
 
+	void VulkanCmdBuffer::notifySubmit()
+	{
 		for (auto& entry : mResources)
 			entry.first->notifyUsed(this, entry.second.flags);
 	}
@@ -241,22 +249,20 @@ namespace BansheeEngine
 		: CommandBuffer(id, type, deviceIdx, queueIdx, secondary), mBuffer(nullptr), mSubmittedBuffer(nullptr)
 		, mDevice(device), mQueue(nullptr), mIdMask(0)
 	{
-		GpuQueueType queueType = mType;
-
-		UINT32 numQueues = device.getNumQueues(queueType);
-		if (numQueues == 0) // Fallback to graphics queue
+		UINT32 numQueues = device.getNumQueues(mType);
+		if (numQueues == 0) // Fall back to graphics queue
 		{
-			queueType = GQT_GRAPHICS;
+			mType = GQT_GRAPHICS;
 			numQueues = device.getNumQueues(GQT_GRAPHICS);
 		}
 
-		mQueue = device.getQueue(queueType, mQueueIdx % numQueues);
+		mQueue = device.getQueue(mType, mQueueIdx % numQueues);
 
 		// If multiple command buffer IDs map to the same queue, mark them in the mask
 		UINT32 curIdx = mQueueIdx;
 		while (curIdx < BS_MAX_QUEUES_PER_TYPE)
 		{
-			mIdMask |= CommandSyncMask::getGlobalQueueIdx(queueType, curIdx);
+			mIdMask |= CommandSyncMask::getGlobalQueueIdx(mType, curIdx);
 			curIdx += numQueues;
 		}
 
@@ -281,7 +287,8 @@ namespace BansheeEngine
 			assert(mBuffer->isSubmitted());
 
 		mSubmittedBuffer = mBuffer;
-		mBuffer = pool.getBuffer(mType, mQueueIdx, mIsSecondary);
+		UINT32 queueFamily = mDevice.getQueueFamily(mType);
+		mBuffer = pool.getBuffer(queueFamily, mIsSecondary);
 	}
 
 	void VulkanCommandBuffer::submit(UINT32 syncMask)
@@ -303,6 +310,57 @@ namespace BansheeEngine
 		// Ignore myself
 		syncMask &= ~mIdMask;
 
+		// Issue pipeline barriers for queue transitions (need to happen on original queue first, then on new queue)
+		mBuffer->prepareForSubmit(mTransitionInfoTemp);
+
+		UINT32 queueFamily = mDevice.getQueueFamily(mType);
+		for(auto& entry : mTransitionInfoTemp)
+		{
+			UINT32 entryQueueFamily = entry.first;
+
+			// No queue transition needed for entries on this queue (this entry is most likely an in image layout transition)
+			if (entryQueueFamily == queueFamily)
+				continue;
+
+			VkCommandBuffer cmdBuffer; // TODO - Get the command buffer on entryQueueFamily
+
+			TransitionInfo& barriers = entry.second;
+			UINT32 numImgBarriers = (UINT32)barriers.imageBarriers.size();
+			UINT32 numBufferBarriers = (UINT32)barriers.bufferBarriers.size();
+
+			vkCmdPipelineBarrier(cmdBuffer,
+								 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+								 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+								 0, 0, nullptr,
+								 numBufferBarriers, barriers.bufferBarriers.data(),
+								 numImgBarriers, barriers.imageBarriers.data());
+
+			// TODO - Submit the command buffer
+			// TODO - Register the command buffer in the sync mask so we wait on it
+
+			// If there are any layout transitions, reset them as we don't need them for the second pipeline barrier
+			for (auto& barrierEntry : barriers.imageBarriers)
+				barrierEntry.oldLayout = barrierEntry.newLayout;
+		}
+
+		// Issue second part of transition pipeline barriers (on this queue)
+		for (auto& entry : mTransitionInfoTemp)
+		{
+			VkCommandBuffer cmdBuffer; // TODO - Get the command buffer on queueFamily AND this exact queue
+									   //  - Probably best to just append it to current submitInfo as it is executed in order
+
+			TransitionInfo& barriers = entry.second;
+			UINT32 numImgBarriers = (UINT32)barriers.imageBarriers.size();
+			UINT32 numBufferBarriers = (UINT32)barriers.bufferBarriers.size();
+
+			vkCmdPipelineBarrier(cmdBuffer,
+								 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+								 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+								 0, 0, nullptr,
+								 numBufferBarriers, barriers.bufferBarriers.data(),
+								 numImgBarriers, barriers.imageBarriers.data());
+		}
+
 		VulkanCommandBufferManager& cmdBufManager = static_cast<VulkanCommandBufferManager&>(CommandBufferManager::instance());
 		cmdBufManager.getSyncSemaphores(mDeviceIdx, syncMask, mSemaphoresTemp, submitInfo.waitSemaphoreCount);
 
@@ -325,5 +383,13 @@ namespace BansheeEngine
 
 		mBuffer->mState = VulkanCmdBuffer::State::Submitted;
 		acquireNewBuffer();
+
+		// Clear vectors but don't clear the actual map, as we want to re-use the memory since we expect queue family
+		// indices to be the same
+		for(auto& entry : mTransitionInfoTemp)
+		{
+			entry.second.imageBarriers.clear();
+			entry.second.bufferBarriers.clear();
+		}
 	}
 }

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

@@ -7,8 +7,8 @@
 
 namespace BansheeEngine
 {
-	VulkanDevice::VulkanDevice(VkPhysicalDevice device)
-		:mPhysicalDevice(device), mLogicalDevice(nullptr), mIsPrimary(false), mQueueInfos()
+	VulkanDevice::VulkanDevice(VkPhysicalDevice device, UINT32 deviceIdx)
+		:mPhysicalDevice(device), mLogicalDevice(nullptr), mIsPrimary(false), mDeviceIdx(deviceIdx), mQueueInfos()
 	{
 		// Set to default
 		for (UINT32 i = 0; i < GQT_COUNT; i++)

+ 0 - 11
Source/BansheeVulkanRenderAPI/Source/BsVulkanGpuBuffer.cpp

@@ -84,15 +84,4 @@ namespace BansheeEngine
 	{
 		return mBuffer->getResource(deviceIdx);
 	}
-
-	GpuBufferView* VulkanGpuBufferCore::createView()
-	{
-		// Not used
-		return nullptr;
-	}
-
-	void VulkanGpuBufferCore::destroyView(GpuBufferView* view)
-	{
-		// Not used
-	}
 }

+ 223 - 0
Source/BansheeVulkanRenderAPI/Source/BsVulkanGpuParams.cpp

@@ -10,6 +10,8 @@
 #include "BsVulkanHardwareBuffer.h"
 #include "BsVulkanDescriptorSet.h"
 #include "BsVulkanSamplerState.h"
+#include "BsVulkanGpuBuffer.h"
+#include "BsVulkanCommandBuffer.h"
 #include "BsGpuParamDesc.h"
 
 namespace BansheeEngine
@@ -368,4 +370,225 @@ namespace BansheeEngine
 
 		mSetsDirty[set] = true;
 	}
+
+	void VulkanGpuParams::prepareForSubmit(VulkanCmdBuffer* buffer, UnorderedMap<UINT32, TransitionInfo>& transitionInfo)
+	{
+		UINT32 deviceIdx = buffer->getDeviceIdx();
+		UINT32 queueFamily = buffer->getQueueFamily();
+
+		const PerDeviceData& perDeviceData = mPerDeviceData[deviceIdx];
+		if (perDeviceData.perSetData == nullptr)
+			return;
+
+		for(UINT32 i = 0; i < perDeviceData.numSets; i++)
+		{
+			if (!mSetsDirty[i])
+				continue;
+
+			// Note: Currently I write to the entire set at once, but it might be beneficial to remember only the exact
+			// entries that were updated, and only write to them individually.
+			const PerSetData& perSetData = perDeviceData.perSetData[i];
+			perSetData.set->write(perSetData.writeSetInfos, perSetData.numElements);
+
+			mSetsDirty[i] = false;
+		}
+
+		auto registerBuffer = [&](VulkanBuffer* resource, VkAccessFlags accessFlags, VulkanUseFlags useFlags)
+		{
+			if (resource == nullptr)
+				return;
+
+			buffer->registerResource(resource, useFlags);
+
+			if (resource->isExclusive())
+			{
+				UINT32 currentQueueFamily = resource->getQueueFamily();
+				if (currentQueueFamily != queueFamily)
+				{
+					Vector<VkBufferMemoryBarrier>& barriers = transitionInfo[currentQueueFamily].bufferBarriers;
+
+					barriers.push_back(VkBufferMemoryBarrier());
+					VkBufferMemoryBarrier& barrier = barriers.back();
+					barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
+					barrier.pNext = nullptr;
+					barrier.srcAccessMask = accessFlags;
+					barrier.dstAccessMask = accessFlags;
+					barrier.srcQueueFamilyIndex = currentQueueFamily;
+					barrier.dstQueueFamilyIndex = queueFamily;
+					barrier.buffer = resource->getHandle();
+					barrier.offset = 0;
+					barrier.size = VK_WHOLE_SIZE;
+				}
+			}
+		};
+
+		auto registerImage = [&](VulkanImage* resource, VkAccessFlags accessFlags, VkImageLayout layout, 
+			const VkImageSubresourceRange& range, VulkanUseFlags useFlags)
+		{
+			buffer->registerResource(resource, useFlags);
+
+			UINT32 currentQueueFamily = resource->getQueueFamily();
+			bool queueMismatch = resource->isExclusive() && currentQueueFamily != queueFamily;
+
+			if (queueMismatch || resource->getLayout() != layout)
+			{
+				Vector<VkImageMemoryBarrier>& barriers = transitionInfo[currentQueueFamily].imageBarriers;
+
+				barriers.push_back(VkImageMemoryBarrier());
+				VkImageMemoryBarrier& barrier = barriers.back();
+				barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+				barrier.pNext = nullptr;
+				barrier.srcAccessMask = accessFlags;
+				barrier.dstAccessMask = accessFlags;
+				barrier.srcQueueFamilyIndex = currentQueueFamily;
+				barrier.dstQueueFamilyIndex = queueFamily;
+				barrier.oldLayout = resource->getLayout();
+				barrier.newLayout = layout;
+				barrier.image = resource->getHandle();
+				barrier.subresourceRange = range;
+
+				resource->setLayout(layout);
+			}
+		};
+
+		for(UINT32 i = 0; i < mNumElements[(UINT32)ElementType::ParamBlock]; i++)
+		{
+			if (mParamBlockBuffers[i] == nullptr)
+				continue;
+
+			VulkanGpuParamBlockBufferCore* element = static_cast<VulkanGpuParamBlockBufferCore*>(mParamBlockBuffers[i].get());
+
+			VulkanBuffer* resource = element->getResource(deviceIdx);
+			registerBuffer(resource, VK_ACCESS_UNIFORM_READ_BIT, VulkanUseFlag::Read);
+		}
+
+		for (UINT32 i = 0; i < mNumElements[(UINT32)ElementType::Buffer]; i++)
+		{
+			if (mBuffers[i] == nullptr)
+				continue;
+
+			VulkanGpuBufferCore* element = static_cast<VulkanGpuBufferCore*>(mBuffers[i].get());
+
+			VkAccessFlags accessFlags = VK_ACCESS_SHADER_READ_BIT;
+			VulkanUseFlags useFlags = VulkanUseFlag::Read;
+
+			if(element->getProperties().getRandomGpuWrite())
+			{
+				accessFlags |= VK_ACCESS_SHADER_WRITE_BIT;
+				useFlags |= VulkanUseFlag::Write;
+			}
+
+			VulkanBuffer* resource = element->getResource(deviceIdx);
+			registerBuffer(resource, accessFlags, useFlags);
+		}
+
+		for (UINT32 i = 0; i < mNumElements[(UINT32)ElementType::SamplerState]; i++)
+		{
+			if (mSamplerStates[i] == nullptr)
+				continue;
+
+			VulkanSamplerStateCore* element = static_cast<VulkanSamplerStateCore*>(mSamplerStates[i].get());
+
+			VulkanSampler* resource = element->getResource(deviceIdx);
+			if (resource == nullptr)
+				continue;
+
+			buffer->registerResource(resource, VulkanUseFlag::Read);
+		}
+
+		for (UINT32 i = 0; i < mNumElements[(UINT32)ElementType::LoadStoreTexture]; i++)
+		{
+			if (mLoadStoreTextures[i] == nullptr)
+				continue;
+
+			VulkanTextureCore* element = static_cast<VulkanTextureCore*>(mLoadStoreTextures[i].get());
+
+			VulkanImage* resource = element->getResource(deviceIdx);
+			if (resource == nullptr)
+				continue;
+
+			VkAccessFlags accessFlags = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+			VulkanUseFlags useFlags = VulkanUseFlag::Read | VulkanUseFlag::Write;
+
+			const TextureSurface& surface = mLoadStoreSurfaces[i];
+			VkImageSubresourceRange subresource;
+			subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+			subresource.baseArrayLayer = surface.arraySlice;
+			subresource.layerCount = surface.numArraySlices;
+			subresource.baseMipLevel = surface.mipLevel;
+			subresource.levelCount = surface.numMipLevels;
+
+			registerImage(resource, accessFlags, VK_IMAGE_LAYOUT_GENERAL, subresource, useFlags);
+		}
+
+		for (UINT32 i = 0; i < mNumElements[(UINT32)ElementType::Texture]; i++)
+		{
+			if (mTextures[i] == nullptr)
+				continue;
+
+			VulkanTextureCore* element = static_cast<VulkanTextureCore*>(mTextures[i].get());
+
+			VulkanImage* resource = element->getResource(deviceIdx);
+			if (resource == nullptr)
+				continue;
+
+			const TextureProperties& props = element->getProperties();
+
+			bool isDepthStencil = (props.getUsage() & TU_DEPTHSTENCIL) != 0;
+
+			VkImageSubresourceRange subresource;
+			subresource.aspectMask = isDepthStencil ? VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT
+													: VK_IMAGE_ASPECT_COLOR_BIT;
+
+			subresource.baseArrayLayer = 0;
+			subresource.layerCount = props.getNumFaces();
+			subresource.baseMipLevel = 0;
+			subresource.levelCount = props.getNumMipmaps();
+
+			registerImage(resource, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, subresource, 
+				VulkanUseFlag::Read);
+		}
+	}
+
+	void VulkanGpuParams::bind(VulkanCommandBuffer& buffer)
+	{
+		UINT32 deviceIdx = buffer.getDeviceIdx();
+
+		PerDeviceData& perDeviceData = mPerDeviceData[deviceIdx];
+		if (perDeviceData.perSetData == nullptr)
+			return;
+
+		VulkanCmdBuffer* internalCB = buffer.getInternal();
+		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>(perDeviceData.numSets);
+		for (UINT32 i = 0; i < perDeviceData.numSets; i++)
+		{
+			VulkanDescriptorSet* set = perDeviceData.perSetData[i].set;
+
+			internalCB->registerResource(set, VulkanUseFlag::Read);
+			sets[i] = set->getHandle();
+		}
+
+		vkCmdBindDescriptorSets(vkCB, bindPoint, perDeviceData.pipelineLayout, 0, 
+			perDeviceData.numSets, sets, 0, nullptr);
+
+		bs_stack_free(sets);
+	}
 }

+ 24 - 3
Source/BansheeVulkanRenderAPI/Source/BsVulkanRenderAPI.cpp

@@ -14,6 +14,7 @@
 #include "BsVulkanGLSLProgramFactory.h"
 #include "BsVulkanCommandBufferManager.h"
 #include "BsVulkanCommandBuffer.h"
+#include "BsVulkanGpuParams.h"
 #include "BsVulkanVertexInputManager.h"
 #include "Win32/BsWin32VideoModeInfo.h"
 
@@ -179,7 +180,7 @@ namespace BansheeEngine
 
 		mDevices.resize(numDevices);
 		for(uint32_t i = 0; i < numDevices; i++)
-			mDevices[i] = bs_shared_ptr_new<VulkanDevice>(physicalDevices[i]);
+			mDevices[i] = bs_shared_ptr_new<VulkanDevice>(physicalDevices[i], i);
 
 		// Find primary device
 		// Note: MULTIGPU - Detect multiple similar devices here if supporting multi-GPU
@@ -220,6 +221,9 @@ namespace BansheeEngine
 		// Create command buffer manager
 		CommandBufferManager::startUp<VulkanCommandBufferManager>(*this);
 
+		// Create main command buffer
+		mMainCommandBuffer = std::static_pointer_cast<VulkanCommandBuffer>(CommandBuffer::create(GQT_GRAPHICS));
+
 		// Create the texture manager for use by others		
 		TextureManager::startUp<VulkanTextureManager>();
 		TextureCoreManager::startUp<VulkanTextureCoreManager>();
@@ -270,6 +274,8 @@ namespace BansheeEngine
 		TextureCoreManager::shutDown();
 		TextureManager::shutDown();
 
+		mMainCommandBuffer = nullptr;
+
 		// Make sure everything finishes and all resources get freed
 		VulkanCommandBufferManager& cmdBufManager = static_cast<VulkanCommandBufferManager&>(CommandBufferManager::instance());
 		for (UINT32 i = 0; i < (UINT32)mDevices.size(); i++)
@@ -307,6 +313,12 @@ 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);
+
+		vulkanGpuParams->bind(*cb);
+		cb->getInternal()->registerGpuParams(vulkanGpuParams);
+
 		BS_INC_RENDER_STAT(NumGpuParamBinds);
 	}
 
@@ -406,10 +418,11 @@ namespace BansheeEngine
 	{
 		THROW_IF_NOT_CORE_THREAD;
 
+		VulkanCommandBuffer* cb = getCB(commandBuffer);
+
 		// TODO - Actually swap buffers
 
-		VulkanCommandBuffer& cmdBuffer = static_cast<VulkanCommandBuffer&>(*commandBuffer);
-		cmdBuffer.refreshSubmitStatus();
+		cb->refreshSubmitStatus();
 
 		BS_INC_RENDER_STAT(NumPresents);
 	}
@@ -570,4 +583,12 @@ namespace BansheeEngine
 			deviceIdx++;
 		}
 	}
+
+	VulkanCommandBuffer* VulkanRenderAPI::getCB(const SPtr<CommandBuffer>& buffer)
+	{
+		if (buffer != nullptr)
+			return static_cast<VulkanCommandBuffer*>(buffer.get());
+
+		return static_cast<VulkanCommandBuffer*>(mMainCommandBuffer.get());
+	}
 }

+ 0 - 1
Source/BansheeVulkanRenderAPI/Source/BsVulkanTexture.cpp

@@ -3,7 +3,6 @@
 #include "BsVulkanTexture.h"
 #include "BsVulkanDevice.h"
 #include "BsCoreThread.h"
-#include "BsException.h"
 #include "BsRenderStats.h"
 
 namespace BansheeEngine