Sfoglia il codice sorgente

Vulkan: Better image access mask tracking and handling

BearishSun 9 anni fa
parent
commit
5350cca1a2

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

@@ -196,7 +196,6 @@ namespace bs
 		 * updates the externally visible image layout field to @p finalLayout (once submitted).
 		 * 
 		 * @param[in]	res						Image to register with the command buffer.
-		 * @param[in]	accessFlags				Destination access flags used for the provided layout transition.
 		 * @param[in]	newLayout				Layout the image needs to be transitioned in before use. Set to undefined
 		 *										layout if no transition is required.
 		 * @param[in]	finalLayout				Determines what value the externally visible image layout will be set after
@@ -206,8 +205,8 @@ namespace bs
 		 * @param[in]	isFBAttachment			Determines if the image is being used as a framebuffer attachment (if true),
 		 *										or just as regular shader input (if false).
 		 */
-		void registerResource(VulkanImage* res, VkAccessFlags accessFlags, VkImageLayout newLayout, 
-			VkImageLayout finalLayout, VulkanUseFlags flags, bool isFBAttachment = false);
+		void registerResource(VulkanImage* res, VkImageLayout newLayout, VkImageLayout finalLayout, VulkanUseFlags flags, 
+			bool isFBAttachment = false);
 
 		/** 
 		 * Lets the command buffer know that the provided image resource has been queued on it, and will be used by the
@@ -320,7 +319,6 @@ namespace bs
 		/** Contains information about a single Vulkan image resource bound/used on this command buffer. */
 		struct ImageInfo
 		{
-			VkAccessFlags accessFlags;
 			VkImageSubresourceRange range;
 			ResourceUseHandle useHandle;
 
@@ -333,6 +331,8 @@ namespace bs
 			bool isFBAttachment : 1;
 			bool isShaderInput : 1;
 			bool hasTransitioned : 1;
+			bool isReadOnly : 1;
+			bool isInitialReadOnly : 1;
 		};
 
 		/** Checks if all the prerequisites for rendering have been made (e.g. render target and pipeline state are set. */

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

@@ -135,8 +135,12 @@ namespace bs
 		/** 
 		 * Determines a set of access flags based on the current image and provided image layout. This method makes 
 		 * certain assumptions about image usage, so it might not be valid in all situations. 
+		 * 
+		 * @param[in]	layout		Layout the image is currently in.
+		 * @param[in]	readOnly	True if the image is only going to be read without writing, allows the system to
+		 *							set less general access flags. If unsure, set to false.
 		 */
-		VkAccessFlags getAccessFlags(VkImageLayout layout);
+		VkAccessFlags getAccessFlags(VkImageLayout layout, bool readOnly = false);
 
 	private:
 		/** Creates a new view of the provided part (or entirety) of surface. */

+ 18 - 32
Source/BansheeVulkanRenderAPI/Source/BsVulkanCommandBuffer.cpp

@@ -348,7 +348,9 @@ namespace bs
 		mQueuedEvents.clear();
 
 		// Update any layout transitions that were performed by subpass dependencies, reset flags that signal image usage
-		// and reset access flags
+		// and reset read-only state.
+		// Note: It's okay reset these even those they might still be bound on the GPU, because these values only matter
+		// for state transitions.
 		for (auto& entry : mImages)
 		{
 			UINT32 imageInfoIdx = entry.second;
@@ -356,8 +358,7 @@ namespace bs
 
 			imageInfo.isFBAttachment = false;
 			imageInfo.isShaderInput = false;
-
-			imageInfo.accessFlags = 0;
+			imageInfo.isReadOnly = true;
 		}
 
 		updateFinalLayouts();
@@ -465,7 +466,7 @@ namespace bs
 				barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
 				barrier.pNext = nullptr;
 				barrier.srcAccessMask = resource->getAccessFlags(currentLayout);
-				barrier.dstAccessMask = imageInfo.accessFlags;
+				barrier.dstAccessMask = resource->getAccessFlags(initialLayout, imageInfo.isInitialReadOnly);
 				barrier.oldLayout = currentLayout;
 				barrier.newLayout = initialLayout;
 				barrier.image = resource->getHandle();
@@ -1209,7 +1210,7 @@ namespace bs
 			barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
 			barrier.pNext = nullptr;
 			barrier.srcAccessMask = image->getAccessFlags(imageInfo.currentLayout);
-			barrier.dstAccessMask = imageInfo.accessFlags;
+			barrier.dstAccessMask = image->getAccessFlags(imageInfo.requiredLayout, imageInfo.isReadOnly);
 			barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
 			barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
 			barrier.oldLayout = imageInfo.currentLayout;
@@ -1218,7 +1219,7 @@ namespace bs
 			barrier.subresourceRange = imageInfo.range;
 
 			imageInfo.currentLayout = imageInfo.requiredLayout;
-			imageInfo.accessFlags = 0;
+			imageInfo.isReadOnly = true;
 			imageInfo.hasTransitioned = true;
 		};
 
@@ -1449,13 +1450,12 @@ namespace bs
 	void VulkanCmdBuffer::registerResource(VulkanImage* res, VulkanUseFlags flags)
 	{
 		VkImageLayout layout = res->getOptimalLayout();
-		VkAccessFlags accessFlags = res->getAccessFlags(layout);
 
-		registerResource(res, accessFlags, VK_IMAGE_LAYOUT_UNDEFINED, layout, flags, false);
+		registerResource(res, VK_IMAGE_LAYOUT_UNDEFINED, layout, flags, false);
 	}
 
-	void VulkanCmdBuffer::registerResource(VulkanImage* res, VkAccessFlags accessFlags, VkImageLayout newLayout, 
-		VkImageLayout finalLayout, VulkanUseFlags flags, bool isFBAttachment)
+	void VulkanCmdBuffer::registerResource(VulkanImage* res, VkImageLayout newLayout, VkImageLayout finalLayout, 
+		VulkanUseFlags flags, bool isFBAttachment)
 	{
 		// Note: I currently always perform pipeline barriers (layout transitions and similar), over the entire image.
 		//       In the case of render and storage images, the case is often that only a specific subresource requires
@@ -1473,7 +1473,6 @@ namespace bs
 			mImageInfos.push_back(ImageInfo());
 
 			ImageInfo& imageInfo = mImageInfos[imageInfoIdx];
-			imageInfo.accessFlags = accessFlags;
 			imageInfo.currentLayout = newLayout;
 			imageInfo.initialLayout = newLayout;
 			imageInfo.requiredLayout = newLayout;
@@ -1482,6 +1481,8 @@ namespace bs
 			imageInfo.isFBAttachment = isFBAttachment;
 			imageInfo.isShaderInput = !isFBAttachment;
 			imageInfo.hasTransitioned = false;
+			imageInfo.isReadOnly = !flags.isSet(VulkanUseFlag::Write);
+			imageInfo.isInitialReadOnly = imageInfo.isReadOnly;
 
 			imageInfo.useHandle.used = false;
 			imageInfo.useHandle.flags = flags;
@@ -1496,7 +1497,7 @@ namespace bs
 			assert(!imageInfo.useHandle.used);
 			imageInfo.useHandle.flags |= flags;
 
-			imageInfo.accessFlags |= accessFlags;
+			imageInfo.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)
@@ -1540,6 +1541,7 @@ namespace bs
 			{
 				imageInfo.initialLayout = imageInfo.requiredLayout;
 				imageInfo.currentLayout = imageInfo.requiredLayout;
+				imageInfo.isInitialReadOnly = imageInfo.isReadOnly;
 			}
 			else
 			{
@@ -1633,44 +1635,28 @@ namespace bs
 		{
 			const VulkanFramebufferAttachment& attachment = res->getColorAttachment(i);
 
-			VkImageLayout layout;
-			VkAccessFlags accessMask;
-
 			// If image is being loaded, we need to transfer it to correct layout, otherwise it doesn't matter
+			VkImageLayout layout;
 			if (loadMask.isSet((RenderSurfaceMaskBits)(1 << i)))
-			{
 				layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-				accessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
-			}
 			else
-			{
 				layout = VK_IMAGE_LAYOUT_UNDEFINED;
-				accessMask = 0;
-			}
 
-			registerResource(attachment.image, accessMask, layout, attachment.finalLayout, VulkanUseFlag::Write, true);
+			registerResource(attachment.image, layout, attachment.finalLayout, VulkanUseFlag::Write, true);
 		}
 
 		if(res->hasDepthAttachment())
 		{
 			const VulkanFramebufferAttachment& attachment = res->getDepthStencilAttachment();
 
-			VkImageLayout layout;
-			VkAccessFlags accessMask;
-
 			// If image is being loaded, we need to transfer it to correct layout, otherwise it doesn't matter
+			VkImageLayout layout;
 			if (loadMask.isSet(RT_DEPTH))
-			{
 				layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
-				accessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
-			}
 			else
-			{
 				layout = VK_IMAGE_LAYOUT_UNDEFINED;
-				accessMask = 0;
-			}
 
-			registerResource(attachment.image, accessMask, layout, attachment.finalLayout, VulkanUseFlag::Write, true);
+			registerResource(attachment.image, layout, attachment.finalLayout, VulkanUseFlag::Write, true);
 		}
 	}
 

+ 2 - 4
Source/BansheeVulkanRenderAPI/Source/BsVulkanGpuParams.cpp

@@ -648,10 +648,8 @@ namespace bs
 				continue;
 
 			// Register with command buffer
-			VkAccessFlags accessFlags = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
 			VulkanUseFlags useFlags = VulkanUseFlag::Read | VulkanUseFlag::Write;
-
-			buffer.registerResource(resource, accessFlags, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL, useFlags);
+			buffer.registerResource(resource, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL, useFlags);
 
 			// Check if internal resource changed from what was previously bound in the descriptor set
 			assert(perDeviceData.storageImages[i] != VK_NULL_HANDLE);
@@ -694,7 +692,7 @@ namespace bs
 			else
 				layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
 
-			buffer.registerResource(resource, VK_ACCESS_SHADER_READ_BIT, layout, layout, VulkanUseFlag::Read);
+			buffer.registerResource(resource, layout, layout, VulkanUseFlag::Read);
 
 			// Check if internal resource changed from what was previously bound in the descriptor set
 			assert(perDeviceData.sampledImages[i] != VK_NULL_HANDLE);

+ 23 - 6
Source/BansheeVulkanRenderAPI/Source/BsVulkanTexture.cpp

@@ -321,17 +321,34 @@ namespace bs
 		vkCmdCopyImageToBuffer(cb->getCB()->getHandle(), mImage, layout, destination->getHandle(), 1, &region);
 	}
 
-	VkAccessFlags VulkanImage::getAccessFlags(VkImageLayout layout)
+	VkAccessFlags VulkanImage::getAccessFlags(VkImageLayout layout, bool readOnly)
 	{
 		VkAccessFlags accessFlags;
 
 		switch (layout)
 		{
-		case VK_IMAGE_LAYOUT_GENERAL: // Only used for render targets that are also read by shaders, or for storage textures
-			if (mUsage == VulkanImageUsage::Storage)
-				accessFlags = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
-			else
-				accessFlags = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_SHADER_READ_BIT;
+		case VK_IMAGE_LAYOUT_GENERAL:
+			{
+				switch(mUsage)
+				{
+				case VulkanImageUsage::Storage:
+					accessFlags = VK_ACCESS_SHADER_READ_BIT;
+					
+					if(!readOnly)
+						accessFlags |= VK_ACCESS_SHADER_WRITE_BIT;
+					break;
+				case VulkanImageUsage::ColorAttachment:
+					accessFlags = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_SHADER_READ_BIT;
+					break;
+				case VulkanImageUsage::DepthAttachment:
+					accessFlags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_SHADER_READ_BIT;
+					break;
+				default:
+					accessFlags = VK_ACCESS_SHADER_READ_BIT;
+					break;
+				}
+			}
+
 			break;
 		case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
 			accessFlags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;