Jelajahi Sumber

Vulkan: Fixing render targets that require a load operation on bind (i.e. rendering to the same RT with multiple render passes)

BearishSun 9 tahun lalu
induk
melakukan
4239ebe070

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

@@ -195,7 +195,7 @@ namespace bs
 		 * device when the command buffer is submitted.
 		 * device when the command buffer is submitted.
 		 */
 		 */
 		void registerResource(VulkanImage* res, VkAccessFlags accessFlags, VkImageLayout currentLayout,
 		void registerResource(VulkanImage* res, VkAccessFlags accessFlags, VkImageLayout currentLayout,
-			VkImageLayout newLayout, VulkanUseFlags flags, bool isFBAttachment = false);
+			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
 		 * Lets the command buffer know that the provided image resource has been queued on it, and will be used by the
@@ -207,7 +207,7 @@ namespace bs
 		 * Lets the command buffer know that the provided framebuffer resource has been queued on it, and will be used by
 		 * Lets the command buffer know that the provided framebuffer resource has been queued on it, and will be used by
 		 * the device when the command buffer is submitted.
 		 * the device when the command buffer is submitted.
 		 */
 		 */
-		void registerResource(VulkanFramebuffer* res, VulkanUseFlags flags);
+		void registerResource(VulkanFramebuffer* res, RenderSurfaceMask loadMask, VulkanUseFlags flags);
 
 
 		/************************************************************************/
 		/************************************************************************/
 		/* 								COMMANDS	                     		*/
 		/* 								COMMANDS	                     		*/

+ 44 - 11
Source/BansheeVulkanRenderAPI/Source/BsVulkanCommandBuffer.cpp

@@ -723,7 +723,7 @@ namespace bs
 		setGpuParams(nullptr);
 		setGpuParams(nullptr);
 
 
 		if(mFramebuffer != nullptr)
 		if(mFramebuffer != nullptr)
-			registerResource(mFramebuffer, VulkanUseFlag::Write);
+			registerResource(mFramebuffer, loadMask, VulkanUseFlag::Write);
 
 
 		mGfxPipelineRequiresBind = true;
 		mGfxPipelineRequiresBind = true;
 	}
 	}
@@ -1353,7 +1353,7 @@ namespace bs
 	}
 	}
 
 
 	void VulkanCmdBuffer::registerResource(VulkanImage* res, VkAccessFlags accessFlags, VkImageLayout currentLayout,
 	void VulkanCmdBuffer::registerResource(VulkanImage* res, VkAccessFlags accessFlags, VkImageLayout currentLayout,
-			VkImageLayout newLayout, VulkanUseFlags flags, bool isFBAttachment)
+			VkImageLayout newLayout, VkImageLayout finalLayout, VulkanUseFlags flags, bool isFBAttachment)
 	{
 	{
 		// Note: I currently always perform pipeline barriers (layout transitions and similar), over the entire image.
 		// 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
 		//       In the case of render and storage images, the case is often that only a specific subresource requires
@@ -1374,7 +1374,7 @@ namespace bs
 			imageInfo.accessFlags = accessFlags;
 			imageInfo.accessFlags = accessFlags;
 			imageInfo.currentLayout = currentLayout;
 			imageInfo.currentLayout = currentLayout;
 			imageInfo.requiredLayout = newLayout;
 			imageInfo.requiredLayout = newLayout;
-			imageInfo.finalLayout = newLayout;
+			imageInfo.finalLayout = finalLayout;
 			imageInfo.range = range;
 			imageInfo.range = range;
 			imageInfo.isFBAttachment = isFBAttachment;
 			imageInfo.isFBAttachment = isFBAttachment;
 			imageInfo.isShaderInput = !isFBAttachment;
 			imageInfo.isShaderInput = !isFBAttachment;
@@ -1397,10 +1397,26 @@ namespace bs
 
 
 			imageInfo.accessFlags |= accessFlags;
 			imageInfo.accessFlags |= accessFlags;
 
 
-			// Check if the same image is used with different layouts, in which case we need to transfer to the general
-			// layout
-			if (imageInfo.requiredLayout != newLayout)
-				imageInfo.requiredLayout = VK_IMAGE_LAYOUT_GENERAL;
+			// We allow undefied layout to be passed as a convience. Essentially it means that no layout transition should
+			// happen.
+			if(newLayout != VK_IMAGE_LAYOUT_UNDEFINED)
+			{
+				bool firstUseInRenderPass = !imageInfo.isShaderInput && !imageInfo.isFBAttachment;
+
+				// If image was previously used this render pass, check if it is used with different layouts, in which case
+				// we need to transfer to the general layout
+				if (!firstUseInRenderPass && imageInfo.requiredLayout != newLayout)
+				{
+					// If required layout is set to undefined it only means didn't want to issue a layout transition,
+					// therefore no need to switch to GENERAL layout, instead just override the undefined layout.
+					if (imageInfo.requiredLayout != VK_IMAGE_LAYOUT_UNDEFINED)
+						imageInfo.requiredLayout = VK_IMAGE_LAYOUT_GENERAL;
+					else
+						imageInfo.requiredLayout = newLayout;
+				}
+				else
+					imageInfo.requiredLayout = newLayout;
+			}
 
 
 			// If attached to FB, then the final layout is set by the FB (provided as layout param here), otherwise its
 			// If attached to FB, then the final layout is set by the FB (provided as layout param here), otherwise its
 			// the same as required layout
 			// the same as required layout
@@ -1409,7 +1425,7 @@ namespace bs
 			else
 			else
 			{
 			{
 				if (isFBAttachment)
 				if (isFBAttachment)
-					imageInfo.finalLayout = newLayout;
+					imageInfo.finalLayout = finalLayout;
 			}
 			}
 
 
 			if (imageInfo.currentLayout != imageInfo.requiredLayout)
 			if (imageInfo.currentLayout != imageInfo.requiredLayout)
@@ -1476,7 +1492,7 @@ namespace bs
 		}
 		}
 	}
 	}
 
 
-	void VulkanCmdBuffer::registerResource(VulkanFramebuffer* res, VulkanUseFlags flags)
+	void VulkanCmdBuffer::registerResource(VulkanFramebuffer* res, RenderSurfaceMask loadMask, VulkanUseFlags flags)
 	{
 	{
 		auto insertResult = mResources.insert(std::make_pair(res, ResourceUseHandle()));
 		auto insertResult = mResources.insert(std::make_pair(res, ResourceUseHandle()));
 		if (insertResult.second) // New element
 		if (insertResult.second) // New element
@@ -1510,7 +1526,15 @@ namespace bs
 				accessMask = VK_ACCESS_MEMORY_READ_BIT;
 				accessMask = VK_ACCESS_MEMORY_READ_BIT;
 			}
 			}
 
 
-			registerResource(attachment.image, accessMask, attachment.image->getLayout(), attachment.finalLayout, 
+			VkImageLayout layout;
+
+			// If image is being loaded, we need to transfer it to correct layout, otherwise it doesn't matter
+			if (loadMask.isSet((RenderSurfaceMaskBits)(1 << i)))
+				layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+			else
+				layout = attachment.image->getLayout();
+
+			registerResource(attachment.image, accessMask, attachment.image->getLayout(), layout, attachment.finalLayout, 
 				VulkanUseFlag::Write, true);
 				VulkanUseFlag::Write, true);
 		}
 		}
 
 
@@ -1519,7 +1543,16 @@ namespace bs
 			const VulkanFramebufferAttachment& attachment = res->getDepthStencilAttachment();
 			const VulkanFramebufferAttachment& attachment = res->getDepthStencilAttachment();
 
 
 			VkAccessFlags accessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
 			VkAccessFlags accessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
-			registerResource(attachment.image, accessMask, attachment.image->getLayout(), attachment.finalLayout, 
+
+			VkImageLayout layout;
+
+			// If image is being loaded, we need to transfer it to correct layout, otherwise it doesn't matter
+			if (loadMask.isSet(RT_DEPTH))
+				layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+			else
+				layout = attachment.image->getLayout();
+
+			registerResource(attachment.image, accessMask, attachment.image->getLayout(), layout, attachment.finalLayout, 
 				VulkanUseFlag::Write, true);
 				VulkanUseFlag::Write, true);
 		}
 		}
 	}
 	}

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

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

+ 11 - 7
Source/BansheeVulkanRenderAPI/Source/BsVulkanTexture.cpp

@@ -731,7 +731,7 @@ namespace bs
 					copyImage(transferCB, dstImage, newImage, oldDstLayout, dstLayout);
 					copyImage(transferCB, dstImage, newImage, oldDstLayout, dstLayout);
 
 
 					VkAccessFlags accessMask = dstImage->getAccessFlags(oldDstLayout);
 					VkAccessFlags accessMask = dstImage->getAccessFlags(oldDstLayout);
-					transferCB->getCB()->registerResource(dstImage, accessMask, oldDstLayout, oldDstLayout,
+					transferCB->getCB()->registerResource(dstImage, accessMask, oldDstLayout, oldDstLayout, oldDstLayout,
 						VulkanUseFlag::Read);
 						VulkanUseFlag::Read);
 				}
 				}
 
 
@@ -777,8 +777,10 @@ namespace bs
 									transferDstLayout, dstLayout, dstRange);
 									transferDstLayout, dstLayout, dstRange);
 
 
 			// Notify the command buffer that these resources are being used on it
 			// Notify the command buffer that these resources are being used on it
-			transferCB->getCB()->registerResource(srcImage, srcAccessMask, srcLayout, srcLayout, VulkanUseFlag::Read);
-			transferCB->getCB()->registerResource(dstImage, dstAccessMask, dstLayout, dstLayout, VulkanUseFlag::Write);
+			transferCB->getCB()->registerResource(srcImage, srcAccessMask, srcLayout, srcLayout, srcLayout, 
+				VulkanUseFlag::Read);
+			transferCB->getCB()->registerResource(dstImage, dstAccessMask, dstLayout, dstLayout, dstLayout, 
+				VulkanUseFlag::Write);
 
 
 			// Need to wait if subresource we're reading from is being written, or if the subresource we're writing to is
 			// Need to wait if subresource we're reading from is being written, or if the subresource we're writing to is
 			// being accessed in any way
 			// being accessed in any way
@@ -1029,7 +1031,8 @@ namespace bs
 
 
 			transferCB->setLayout(image->getHandle(), VK_ACCESS_TRANSFER_READ_BIT, currentAccessMask,
 			transferCB->setLayout(image->getHandle(), VK_ACCESS_TRANSFER_READ_BIT, currentAccessMask,
 								  VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstLayout, range);
 								  VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstLayout, range);
-			transferCB->getCB()->registerResource(image, currentAccessMask, dstLayout, dstLayout, VulkanUseFlag::Read);
+			transferCB->getCB()->registerResource(image, currentAccessMask, dstLayout, dstLayout, dstLayout,
+				VulkanUseFlag::Read);
 
 
 			// Ensure data written to the staging buffer is visible
 			// Ensure data written to the staging buffer is visible
 			VkAccessFlags stagingAccessFlags;
 			VkAccessFlags stagingAccessFlags;
@@ -1147,8 +1150,8 @@ namespace bs
 							copyImage(transferCB, image, newImage, oldImgLayout, curLayout);
 							copyImage(transferCB, image, newImage, oldImgLayout, curLayout);
 
 
 							VkAccessFlags accessMask = image->getAccessFlags(oldImgLayout);
 							VkAccessFlags accessMask = image->getAccessFlags(oldImgLayout);
-							transferCB->getCB()->registerResource(image, accessMask, oldImgLayout, oldImgLayout,
-								VulkanUseFlag::Read);
+							transferCB->getCB()->registerResource(image, accessMask, oldImgLayout, oldImgLayout, 
+								oldImgLayout, VulkanUseFlag::Read);
 						}
 						}
 
 
 						image->destroy();
 						image->destroy();
@@ -1203,7 +1206,8 @@ namespace bs
 
 
 				// Notify the command buffer that these resources are being used on it
 				// 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(mStagingBuffer, VK_ACCESS_TRANSFER_READ_BIT, VulkanUseFlag::Read);
-				transferCB->getCB()->registerResource(image, currentAccessMask, dstLayout, dstLayout, VulkanUseFlag::Write);
+				transferCB->getCB()->registerResource(image, currentAccessMask, dstLayout, dstLayout, dstLayout,
+					VulkanUseFlag::Write);
 
 
 				// We don't actually flush the transfer buffer here since it's an expensive operation, but it's instead
 				// 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.
 				// done automatically before next "normal" command buffer submission.

+ 6 - 3
Source/RenderBeast/Source/BsRenderBeast.cpp

@@ -803,12 +803,11 @@ namespace bs
 
 
 		SPtr<RenderTargetCore> target = rtData.target;
 		SPtr<RenderTargetCore> target = rtData.target;
 
 
-		RenderAPICore::instance().setRenderTarget(target);
-		RenderAPICore::instance().setViewport(viewport->getNormArea());
-
 		// If first camera in render target, prepare the render target
 		// If first camera in render target, prepare the render target
 		if (camIdx == 0)
 		if (camIdx == 0)
 		{
 		{
+			RenderAPICore::instance().setRenderTarget(target);
+
 			UINT32 clearBuffers = 0;
 			UINT32 clearBuffers = 0;
 			if (viewport->getRequiresColorClear())
 			if (viewport->getRequiresColorClear())
 				clearBuffers |= FBT_COLOR;
 				clearBuffers |= FBT_COLOR;
@@ -825,6 +824,10 @@ namespace bs
 					viewport->getClearDepthValue(), viewport->getClearStencilValue());
 					viewport->getClearDepthValue(), viewport->getClearStencilValue());
 			}
 			}
 		}
 		}
+		else
+			RenderAPICore::instance().setRenderTarget(target, false, RT_COLOR0);
+
+		RenderAPICore::instance().setViewport(viewport->getNormArea());
 
 
 		// Trigger overlay callbacks
 		// Trigger overlay callbacks
 		auto iterRenderCallback = mCallbacks.begin();
 		auto iterRenderCallback = mCallbacks.begin();