فهرست منبع

WIP: Getting Vulkan up to date
- Various fixes relating to image layout transitions, mostly ensuring the validation layers don't complain on latest SDK version
- Memory barriers will now properly use valid pipeline stages as supported by the access flags
- Executing a dispatch call will now properly clear 'isFBAttachment' flags on current framebuffer subresources (if any). This was causing images to be bound using the wrong layout.
- When executing buffer memory barrier due to previous shader writes, make sure to first end the render pass if active
- Transfer operations will now properly update their current layout on the executing command buffer (failure to do so was causing incorrect layout transitions).
- Transfer operations will mark image layout as transitioned, so the system doesn't try to transition the layout during the submit() call.
- Transfer operations will no longer set various other flags that are only relevant for render/compute operations (this was causing render/compute operations to perform non-optimally in some cases).

BearishSun 8 سال پیش
والد
کامیت
86d1724689

+ 180 - 73
Source/BansheeVulkanRenderAPI/BsVulkanCommandBuffer.cpp

@@ -129,6 +129,61 @@ namespace bs { namespace ct
 		return bs_new<VulkanCmdBuffer>(mDevice, mNextId++, poolInfo.pool, poolInfo.queueFamily, secondary);
 	}
 
+	/** Returns a set of pipeline stages that can are allowed to be used for the specified set of access flags. */
+	VkPipelineStageFlags getPipelineStageFlags(VkAccessFlags accessFlags)
+	{
+		VkPipelineStageFlags flags = 0;
+
+		if ((accessFlags & VK_ACCESS_INDIRECT_COMMAND_READ_BIT) != 0)
+			flags |= VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT;
+
+		if ((accessFlags & (VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT)) != 0)
+			flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
+
+		if ((accessFlags & (VK_ACCESS_UNIFORM_READ_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT)) != 0)
+		{
+			flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT;
+			flags |= VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT | VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
+			flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
+		}
+
+		if ((accessFlags & VK_ACCESS_INPUT_ATTACHMENT_READ_BIT) != 0)
+			flags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
+
+		if ((accessFlags & (VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT)) != 0)
+			flags |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+
+		if ((accessFlags & (VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT)) != 0)
+			flags |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
+
+		if ((accessFlags & (VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT)) != 0)
+			flags |= VK_PIPELINE_STAGE_TRANSFER_BIT;
+
+		if ((accessFlags & (VK_ACCESS_HOST_READ_BIT | VK_ACCESS_HOST_WRITE_BIT)) != 0)
+			flags |= VK_PIPELINE_STAGE_HOST_BIT;
+
+		if (flags == 0)
+			flags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+
+		return flags;
+	}
+
+	template<class T>
+	void getPipelineStageFlags(const Vector<T>& barriers, VkPipelineStageFlags& src, VkPipelineStageFlags& dst)
+	{
+		for(auto& entry : barriers)
+		{
+			src |= getPipelineStageFlags(entry.srcAccessMask);
+			dst |= getPipelineStageFlags(entry.dstAccessMask);
+		}
+
+		if (src == 0)
+			src = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+
+		if (dst == 0)
+			dst = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+	}
+
 	VulkanCmdBuffer::VulkanCmdBuffer(VulkanDevice& device, UINT32 id, VkCommandPool pool, UINT32 queueFamily, bool secondary)
 		: mId(id), mQueueFamily(queueFamily), mState(State::Ready), mDevice(device), mPool(pool)
 		, mIntraQueueSemaphore(nullptr), mInterQueueSemaphores(), mNumUsedInterQueueSemaphores(0)
@@ -315,15 +370,6 @@ namespace bs { namespace ct
 				readMask.set(RT_STENCIL);
 		}
 
-		for(auto& entry : mPassTouchedSubresourceInfos)
-		{
-			ImageSubresourceInfo& subresourceInfo = mSubresourceInfoStorage[entry];
-			subresourceInfo.needsBarrier = false;
-		}
-
-		for (auto& entry : mBuffers)
-			entry.second.needsBarrier = false;
-
 		VkRenderPassBeginInfo renderPassBeginInfo;
 		renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
 		renderPassBeginInfo.pNext = nullptr;
@@ -361,8 +407,12 @@ namespace bs { namespace ct
 			ImageSubresourceInfo& subresourceInfo = mSubresourceInfoStorage[entry];
 			subresourceInfo.isShaderInput = false;
 			subresourceInfo.isReadOnly = true;
+			subresourceInfo.needsBarrier = false;
 		}
 
+		for (auto& entry : mBuffers)
+			entry.second.needsBarrier = false;
+
 		mPassTouchedSubresourceInfos.clear();
 
 		updateFinalLayouts();
@@ -559,9 +609,12 @@ namespace bs { namespace ct
 			UINT32 numImgBarriers = (UINT32)barriers.imageBarriers.size();
 			UINT32 numBufferBarriers = (UINT32)barriers.bufferBarriers.size();
 
+			VkPipelineStageFlags srcStage = 0;
+			VkPipelineStageFlags dstStage = 0;
+			getPipelineStageFlags(barriers.imageBarriers, srcStage, dstStage);
+
 			vkCmdPipelineBarrier(vkCmdBuffer,
-								 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // Note: VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT might be more correct here, according to the spec
-								 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, //       The main idea is that the barrier executes before the semaphore triggers, no actual stage dependencies are needed.
+								 srcStage, dstStage,
 								 0, 0, nullptr,
 								 numBufferBarriers, barriers.bufferBarriers.data(),
 								 numImgBarriers, barriers.imageBarriers.data());
@@ -645,9 +698,12 @@ namespace bs { namespace ct
 			UINT32 numImgBarriers = (UINT32)barriers.imageBarriers.size();
 			UINT32 numBufferBarriers = (UINT32)barriers.bufferBarriers.size();
 
+			VkPipelineStageFlags srcStage = 0;
+			VkPipelineStageFlags dstStage = 0;
+			getPipelineStageFlags(barriers.imageBarriers, srcStage, dstStage);
+
 			vkCmdPipelineBarrier(vkCmdBuffer,
-								 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // Note: VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT might be more correct here, according to the spec
-								 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 
+								 srcStage, dstStage,
 								 0, 0, nullptr,
 								 numBufferBarriers, barriers.bufferBarriers.data(),
 								 numImgBarriers, barriers.imageBarriers.data());
@@ -1374,9 +1430,12 @@ namespace bs { namespace ct
 			createLayoutTransitionBarrier(entry.first, imageInfo);
 		}
 
+		VkPipelineStageFlags srcStage = 0;
+		VkPipelineStageFlags dstStage = 0;
+		getPipelineStageFlags(mLayoutTransitionBarriersTemp, srcStage, dstStage);
+
 		vkCmdPipelineBarrier(mCmdBuffer,
-							 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, // Note: VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT might be more correct here, according to the spec
-							 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+							 srcStage, dstStage,
 							 0, 0, nullptr,
 							 0, nullptr,
 							 (UINT32)mLayoutTransitionBarriersTemp.size(), mLayoutTransitionBarriersTemp.data());
@@ -1445,13 +1504,12 @@ namespace bs { namespace ct
 		if (!isReadyForRender())
 			return;
 
-		// Note: Must begin render pass before binding GPU params as some GPU param related data gets cleared on begin, and
-		// we don't want to clear the currently used one
+		// Need to bind gpu params before starting render pass, in order to make sure any layout transitions execute
+		bindGpuParams();
+
 		if (!isInRenderPass())
 			beginRenderPass();
 
-		bindGpuParams();
-
 		if (mGfxPipelineRequiresBind)
 		{
 			if (!bindGraphicsPipeline())
@@ -1485,13 +1543,12 @@ namespace bs { namespace ct
 		if (!isReadyForRender())
 			return;
 
-		// Note: Must begin render pass before binding GPU params as some GPU param related data gets cleared on begin, and
-		// we don't want to clear the currently used one
+		// Need to bind gpu params before starting render pass, in order to make sure any layout transitions execute
+		bindGpuParams();
+
 		if (!isInRenderPass())
 			beginRenderPass();
 
-		bindGpuParams();
-
 		if (mGfxPipelineRequiresBind)
 		{
 			if (!bindGraphicsPipeline())
@@ -1528,6 +1585,11 @@ namespace bs { namespace ct
 		if (isInRenderPass())
 			endRenderPass();
 
+		// Note: Should I restore the render target after? Note that this is only being done is framebuffer subresources
+		// have their "isFBAttachment" flag reset, potentially I can just clear/restore those
+		setRenderTarget(nullptr, 0, RT_ALL);
+
+		// Need to bind gpu params before starting render pass, in order to make sure any layout transitions execute
 		bindGpuParams();
 		executeLayoutTransitions();
 
@@ -1566,8 +1628,12 @@ namespace bs { namespace ct
 			ImageSubresourceInfo& subresourceInfo = mSubresourceInfoStorage[entry];
 			subresourceInfo.isShaderInput = false;
 			subresourceInfo.isReadOnly = true;
+			subresourceInfo.needsBarrier = false;
 		}
 
+		for (auto& entry : mBuffers)
+			entry.second.needsBarrier = false;
+
 		mPassTouchedSubresourceInfos.clear();
 	}
 
@@ -1648,9 +1714,12 @@ namespace bs { namespace ct
 		barrier.image = image;
 		barrier.subresourceRange = range;
 
+		VkPipelineStageFlags srcStage = getPipelineStageFlags(srcAccessFlags);
+		VkPipelineStageFlags dstStage = getPipelineStageFlags(dstAccessFlags);
+		
 		vkCmdPipelineBarrier(getHandle(),
-							 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
-							 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+							 srcStage,
+							 dstStage,
 							 0, 0, nullptr,
 							 0, nullptr,
 							 1, &barrier);
@@ -1676,18 +1745,20 @@ namespace bs { namespace ct
 		}
 	}
 
-	void VulkanCmdBuffer::registerResource(VulkanImage* res, const VkImageSubresourceRange& range, VulkanUseFlags flags)
+	void VulkanCmdBuffer::registerResource(VulkanImage* res, const VkImageSubresourceRange& range, VulkanUseFlags flags,
+		ResourceUsage usage)
 	{
 		VkImageLayout layout = res->getOptimalLayout();
 
-		registerResource(res, range, VK_IMAGE_LAYOUT_UNDEFINED, layout, flags, false);
+		registerResource(res, range, layout, layout, flags, usage);
 	}
 
 	void VulkanCmdBuffer::registerResource(VulkanImage* res, const VkImageSubresourceRange& range, VkImageLayout newLayout,
-		VkImageLayout finalLayout, VulkanUseFlags flags, bool isFBAttachment)
+		VkImageLayout finalLayout, VulkanUseFlags flags, ResourceUsage usage)
 	{
-		// Check if we're binding for shader use, or as a color attachment, or just for transfer purposes
-		bool isShaderBind = !isFBAttachment && newLayout != VK_IMAGE_LAYOUT_UNDEFINED;
+		bool isFBAttachment = usage == ResourceUsage::Framebuffer;
+		bool isTransfer = usage == ResourceUsage::Transfer;
+		bool isShaderBind = usage == ResourceUsage::ShaderBind;
 
 		// If binding it for write in a shader (not as color attachment or transfer op), we will need to issue a memory
 		// barrier if the image gets used again during this render pass, so remember this information.
@@ -1704,9 +1775,19 @@ namespace bs { namespace ct
 			subresourceInfo.finalLayout = finalLayout;
 			subresourceInfo.range = subresourceRange;
 			subresourceInfo.isFBAttachment = isFBAttachment;
-			subresourceInfo.isShaderInput = !isFBAttachment;
-			subresourceInfo.hasTransitioned = false;
-			subresourceInfo.isReadOnly = !flags.isSet(VulkanUseFlag::Write);
+			subresourceInfo.isShaderInput = isShaderBind;
+
+			if (!isTransfer)
+			{
+				subresourceInfo.hasTransitioned = false;
+				subresourceInfo.isReadOnly = !flags.isSet(VulkanUseFlag::Write);
+			}
+			else
+			{
+				subresourceInfo.hasTransitioned = true; // Transfers handle layout transitions externally (at this point they are assumed to already be done)
+				subresourceInfo.isReadOnly = true; // Doesn't matter for transfers
+			}
+
 			subresourceInfo.isInitialReadOnly = subresourceInfo.isReadOnly;
 			subresourceInfo.needsBarrier = needsBarrier;
 
@@ -1754,8 +1835,7 @@ namespace bs { namespace ct
 						subresources[i].range.baseMipLevel == range.baseMipLevel)
 					{
 						// Just update existing range
-						updateSubresourceInfo(res, imageInfoIdx, subresources[i], newLayout, finalLayout, flags, 
-							isFBAttachment);
+						updateSubresourceInfo(res, imageInfoIdx, subresources[i], newLayout, finalLayout, flags, usage);
 						mPassTouchedSubresourceInfos.insert(imageInfo.subresourceInfoIdx + i);
 
 						foundRange = true;
@@ -1803,8 +1883,7 @@ namespace bs { namespace ct
 								if(VulkanUtility::rangeOverlaps(tempCutRanges[j], range))
 								{
 									// Update overlapping sub-resource range with new data from this range
-									updateSubresourceInfo(res, imageInfoIdx, newInfo, newLayout, finalLayout, flags, 
-														  isFBAttachment);
+									updateSubresourceInfo(res, imageInfoIdx, newInfo, newLayout, finalLayout, flags, usage);
 
 									// Keep track of the overlapping ranges for later
 									cutOverlappingRanges.push_back((UINT32)mSubresourceInfoStorage.size());
@@ -1908,6 +1987,11 @@ namespace bs { namespace ct
 			bool isShaderRead = (accessFlags & VK_ACCESS_SHADER_READ_BIT) != 0;
 			if(bufferInfo.needsBarrier && (isShaderRead || isShaderWrite))
 			{
+				// Need to end render pass in order to execute the barrier. Hopefully this won't trigger much since most
+				// shader writes are done during compute
+				if (isInRenderPass())
+					endRenderPass();
+
 				VkPipelineStageFlags stages =
 					VK_PIPELINE_STAGE_VERTEX_SHADER_BIT |
 					VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT |
@@ -1961,7 +2045,8 @@ namespace bs { namespace ct
 				layout = VK_IMAGE_LAYOUT_UNDEFINED;
 
 			VkImageSubresourceRange range = attachment.image->getRange(attachment.surface);
-			registerResource(attachment.image, range, layout, attachment.finalLayout, VulkanUseFlag::Write, true);
+			registerResource(attachment.image, range, layout, attachment.finalLayout, VulkanUseFlag::Write, 
+				ResourceUsage::Framebuffer);
 		}
 
 		if(res->hasDepthAttachment())
@@ -1981,38 +2066,51 @@ namespace bs { namespace ct
 				layout = VK_IMAGE_LAYOUT_UNDEFINED;
 
 			VkImageSubresourceRange range = attachment.image->getRange(attachment.surface);
-			registerResource(attachment.image, range, layout, attachment.finalLayout, useFlag, true);
+			registerResource(attachment.image, range, layout, attachment.finalLayout, useFlag, ResourceUsage::Framebuffer);
 		}
 	}
 
 	bool VulkanCmdBuffer::updateSubresourceInfo(VulkanImage* image, UINT32 imageInfoIdx, 
 			ImageSubresourceInfo& subresourceInfo, VkImageLayout newLayout, VkImageLayout finalLayout, VulkanUseFlags flags, 
-			bool isFBAttachment)
+			ResourceUsage usage)
 	{
-		subresourceInfo.isReadOnly &= !flags.isSet(VulkanUseFlag::Write);
-
-		// New layout is valid, check for transitions (UNDEFINED signifies the caller doesn't want a layout transition)
-		if (newLayout != VK_IMAGE_LAYOUT_UNDEFINED)
-		{
-			// If layout transition was requested by framebuffer bind, respect it because render-pass will only accept a
-			// specific layout (in certain cases), and we have no choice.
-			// In the case when a FB attachment is also bound for shader reads, this will override the layout required for
-			// shader read (GENERAL or DEPTH_READ_ONLY), but that is fine because those transitions are handled
-			// automatically by render-pass layout transitions.
-			// Any other texture (non FB attachment) will only even be bound in a single layout and we can keep the one it
-			// was originally registered with.
-			if (isFBAttachment)
-				subresourceInfo.requiredLayout = newLayout;
-			else if (!subresourceInfo.isFBAttachment) // Layout transition is not being done on a FB image
-			{
-				// Check if the image had a layout previously assigned, and if so check if multiple different layouts
-				// were requested. In that case we wish to transfer the image to GENERAL layout.
+		bool isTransfer = usage == ResourceUsage::Transfer;
+		bool isFBAttachment = usage == ResourceUsage::Framebuffer;
+		bool isShaderBind = usage == ResourceUsage::ShaderBind;
+
+		// Transfers are always considered read only because this only matters for destination access mask, and transfers
+		// handle that externally
+		if(!isTransfer)
+			subresourceInfo.isReadOnly &= !flags.isSet(VulkanUseFlag::Write);
 
-				bool firstUseInRenderPass = !subresourceInfo.isShaderInput && !subresourceInfo.isFBAttachment;
-				if (firstUseInRenderPass || subresourceInfo.requiredLayout == VK_IMAGE_LAYOUT_UNDEFINED)
+		// For transfers, just assign new layout, external code does all the work
+		if (isTransfer)
+			subresourceInfo.requiredLayout = newLayout;
+		else
+		{
+			// New layout is valid, check for transitions (UNDEFINED signifies the caller doesn't want a layout transition)
+			if (newLayout != VK_IMAGE_LAYOUT_UNDEFINED)
+			{
+				// If layout transition was requested by framebuffer bind, respect it because render-pass will only accept a
+				// specific layout (in certain cases), and we have no choice.
+				// In the case when a FB attachment is also bound for shader reads, this will override the layout required for
+				// shader read (GENERAL or DEPTH_READ_ONLY), but that is fine because those transitions are handled
+				// automatically by render-pass layout transitions.
+				// Any other texture (non FB attachment) will only even be bound in a single layout and we can keep the one it
+				// was originally registered with.
+				if (isFBAttachment)
 					subresourceInfo.requiredLayout = newLayout;
-				else if (subresourceInfo.requiredLayout != newLayout)
-					subresourceInfo.requiredLayout = VK_IMAGE_LAYOUT_GENERAL;
+				else if (!subresourceInfo.isFBAttachment) // Layout transition is not being done on a FB image
+				{
+					// Check if the image had a layout previously assigned, and if so check if multiple different layouts
+					// were requested. In that case we wish to transfer the image to GENERAL layout.
+
+					bool firstUseInRenderPass = !subresourceInfo.isShaderInput && !subresourceInfo.isFBAttachment;
+					if (firstUseInRenderPass || subresourceInfo.requiredLayout == VK_IMAGE_LAYOUT_UNDEFINED)
+						subresourceInfo.requiredLayout = newLayout;
+					else if (subresourceInfo.requiredLayout != newLayout)
+						subresourceInfo.requiredLayout = VK_IMAGE_LAYOUT_GENERAL;
+				}
 			}
 		}
 
@@ -2026,19 +2124,30 @@ namespace bs { namespace ct
 				subresourceInfo.finalLayout = finalLayout;
 		}
 
-		// If we haven't done a layout transition yet, we can just overwrite the previously written values, and the
-		// transition will be handled as the first thing in submit(), otherwise we queue a non-initial transition
-		// below.
-		if (!subresourceInfo.hasTransitioned)
+		// Queue layout transition, if not transfer. Transfer handle layout transitions externally.
+		if (!isTransfer)
 		{
-			subresourceInfo.initialLayout = subresourceInfo.requiredLayout;
-			subresourceInfo.currentLayout = subresourceInfo.requiredLayout;
-			subresourceInfo.isInitialReadOnly = subresourceInfo.isReadOnly;
+			// If we haven't done a layout transition yet, we can just overwrite the previously written values, and the
+			// transition will be handled as the first thing in submit(), otherwise we queue a non-initial transition
+			// below.
+			if (!subresourceInfo.hasTransitioned)
+			{
+				subresourceInfo.initialLayout = subresourceInfo.requiredLayout;
+				subresourceInfo.currentLayout = subresourceInfo.requiredLayout;
+				subresourceInfo.isInitialReadOnly = subresourceInfo.isReadOnly;
+			}
+			else
+			{
+				if (subresourceInfo.currentLayout != subresourceInfo.requiredLayout)
+					mQueuedLayoutTransitions[image] = imageInfoIdx;
+			}
 		}
 		else
 		{
-			if (subresourceInfo.currentLayout != subresourceInfo.requiredLayout)
-				mQueuedLayoutTransitions[image] = imageInfoIdx;
+			// Resource has already transitioned its layout externally since this is a transfer. We cannot allow a
+			// transition in submit().
+			subresourceInfo.currentLayout = subresourceInfo.requiredLayout;
+			subresourceInfo.hasTransitioned = true;
 		}
 
 		// If a FB attachment was just bound as a shader input, we might need to restart the render pass with a FB
@@ -2086,8 +2195,6 @@ namespace bs { namespace ct
 			// in which case we need to issue a memory barrier so those writes are visible.
 
 			// Memory barrier only matters if image is bound for shader use (no need for color attachments or transfers)
-			bool isShaderBind = !isFBAttachment && newLayout != VK_IMAGE_LAYOUT_UNDEFINED;
-
 			if(subresourceInfo.needsBarrier && isShaderBind)
 			{
 				bool isWrite = flags.isSet(VulkanUseFlag::Write);

+ 14 - 7
Source/BansheeVulkanRenderAPI/BsVulkanCommandBuffer.h

@@ -77,6 +77,14 @@ namespace bs { namespace ct
 		Compute = 1 << 1
 	};
 
+	/** Specifies for what purpose is a resource being bound to a command buffer. */
+	enum class ResourceUsage
+	{
+		ShaderBind,
+		Framebuffer,
+		Transfer
+	};
+
 	typedef Flags<DescriptorSetBindFlag> DescriptorSetBindFlags;
 	BS_FLAGS_OPERATORS(DescriptorSetBindFlag)
 
@@ -213,18 +221,17 @@ namespace bs { namespace ct
 		 *										submit() is called. Normally this will be same as @p newLayout, but can be
 		 *										different if some form of automatic layout transitions are happening.
 		 * @param[in]	flags					Flags that determine how will be command buffer be using the buffer.
-		 * @param[in]	isFBAttachment			Determines if the image is being used as a framebuffer attachment (if true),
-		 *										or just as regular shader input (if false).
+		 * @param[in]	usage					Determines for what purpose is the resource being registered for.
 		 */
 		void registerResource(VulkanImage* res, const VkImageSubresourceRange& range, VkImageLayout newLayout, 
-							  VkImageLayout finalLayout, VulkanUseFlags flags, bool isFBAttachment = false);
+							  VkImageLayout finalLayout, VulkanUseFlags flags, ResourceUsage usage);
 
 		/** 
 		 * Lets the command buffer know that the provided image resource has been queued on it, and will be used by the
-		 * device when the command buffer is submitted. Performs no layout transitions on the image, they must be performed
-		 * by the caller, or not required at all.
+		 * device when the command buffer is submitted. Assumes the image is in its optimal layout.
 		 */
-		void registerResource(VulkanImage* res, const VkImageSubresourceRange& range, VulkanUseFlags flags);
+		void registerResource(VulkanImage* res, const VkImageSubresourceRange& range, VulkanUseFlags flags, 
+			ResourceUsage usage);
 
 		/** 
 		 * Lets the command buffer know that the provided image resource has been queued on it, and will be used by the
@@ -435,7 +442,7 @@ namespace bs { namespace ct
 		 * the bound sub-resource is a read-only framebuffer attachment.
 		 */
 		bool updateSubresourceInfo(VulkanImage* image, UINT32 imageInfoIdx, ImageSubresourceInfo& subresourceInfo, 
-			VkImageLayout newLayout, VkImageLayout finalLayout, VulkanUseFlags flags, bool isFBAttachment);
+			VkImageLayout newLayout, VkImageLayout finalLayout, VulkanUseFlags flags, ResourceUsage usage);
 
 		/** Finds a subresource info structure containing the specified face and mip level of the provided image. */
 		ImageSubresourceInfo& findSubresourceInfo(VulkanImage* image, UINT32 face, UINT32 mip);

+ 3 - 2
Source/BansheeVulkanRenderAPI/BsVulkanGpuParams.cpp

@@ -680,7 +680,8 @@ namespace bs { namespace ct
 
 			// Register with command buffer
 			VulkanUseFlags useFlags = VulkanUseFlag::Read | VulkanUseFlag::Write;
-			buffer.registerResource(resource, range, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL, useFlags);
+			buffer.registerResource(resource, range, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL, useFlags, 
+				ResourceUsage::ShaderBind);
 
 			// Check if internal resource changed from what was previously bound in the descriptor set
 			assert(perDeviceData.storageImages[i] != VK_NULL_HANDLE);
@@ -724,7 +725,7 @@ namespace bs { namespace ct
 			const TextureSurface& surface = mSampledTextureData[i].surface;
 			VkImageSubresourceRange range = resource->getRange(surface);
 
-			buffer.registerResource(resource, range, layout, layout, VulkanUseFlag::Read);
+			buffer.registerResource(resource, range, layout, layout, VulkanUseFlag::Read, ResourceUsage::ShaderBind);
 
 			// Check if internal resource changed from what was previously bound in the descriptor set
 			assert(perDeviceData.sampledImages[i] != VK_NULL_HANDLE);

+ 6 - 6
Source/BansheeVulkanRenderAPI/BsVulkanTexture.cpp

@@ -820,8 +820,8 @@ namespace bs { namespace ct
 		cb->setLayout(dstImage->getHandle(), VK_ACCESS_TRANSFER_WRITE_BIT, dstAccessMask,
 							  transferDstLayout, dstFinalLayout, range);
 
-		cb->getCB()->registerResource(srcImage, range, VulkanUseFlag::Read);
-		cb->getCB()->registerResource(dstImage, range, VulkanUseFlag::Write);
+		cb->getCB()->registerResource(srcImage, range, VulkanUseFlag::Read, ResourceUsage::Transfer);
+		cb->getCB()->registerResource(dstImage, range, VulkanUseFlag::Write, ResourceUsage::Transfer);
 
 		bs_stack_free(imageRegions);
 	}
@@ -963,8 +963,8 @@ namespace bs { namespace ct
 			dstLayout, dstRange);
 
 		// Notify the command buffer that these resources are being used on it
-		vkCB->registerResource(srcImage, srcRange, VulkanUseFlag::Read);
-		vkCB->registerResource(dstImage, dstRange, VulkanUseFlag::Write);
+		vkCB->registerResource(srcImage, srcRange, VulkanUseFlag::Read, ResourceUsage::Transfer);
+		vkCB->registerResource(dstImage, dstRange, VulkanUseFlag::Write, ResourceUsage::Transfer);
 	}
 
 	PixelData VulkanTexture::lockImpl(GpuLockOptions options, UINT32 mipLevel, UINT32 face, UINT32 deviceIdx,
@@ -1196,7 +1196,7 @@ namespace bs { namespace ct
 
 			transferCB->setLayout(image->getHandle(), VK_ACCESS_TRANSFER_READ_BIT, currentAccessMask,
 								  VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstLayout, range);
-			transferCB->getCB()->registerResource(image, range, VulkanUseFlag::Read);
+			transferCB->getCB()->registerResource(image, range, VulkanUseFlag::Read, ResourceUsage::Transfer);
 
 			// Ensure data written to the staging buffer is visible
 			VkAccessFlags stagingAccessFlags;
@@ -1358,7 +1358,7 @@ namespace bs { namespace ct
 
 				// Notify the command buffer that these resources are being used on it
 				transferCB->getCB()->registerResource(mStagingBuffer, VK_ACCESS_TRANSFER_READ_BIT, VulkanUseFlag::Read);
-				transferCB->getCB()->registerResource(image, range, VulkanUseFlag::Write);
+				transferCB->getCB()->registerResource(image, range, VulkanUseFlag::Write, ResourceUsage::Transfer);
 
 				// We don't actually flush the transfer buffer here since it's an expensive operation, but it's instead
 				// done automatically before next "normal" command buffer submission.