Only perform attachment load/store ops for used attachments

Tracks the first subpass to touch an attachment.

Test: dEQP-VK.renderpass.suballocation.unused*
Change-Id: Idc0998da022fd4c0b1785f6020c795916ba475f7
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/31308
Tested-by: Chris Forbes <chrisforbes@google.com>
Presubmit-Ready: Chris Forbes <chrisforbes@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
diff --git a/src/Vulkan/VkFramebuffer.cpp b/src/Vulkan/VkFramebuffer.cpp
index c5bdfde..f9047c1 100644
--- a/src/Vulkan/VkFramebuffer.cpp
+++ b/src/Vulkan/VkFramebuffer.cpp
@@ -43,6 +43,11 @@
 	const uint32_t count = std::min(clearValueCount, attachmentCount);
 	for(uint32_t i = 0; i < count; i++)
 	{
+		if (!renderPass->isAttachmentUsed(i))
+		{
+			continue;
+		}
+
 		const VkAttachmentDescription attachment = renderPass->getAttachment(i);
 		const Format format(attachment.format);
 		bool isDepth = format.isDepth();
diff --git a/src/Vulkan/VkRenderPass.cpp b/src/Vulkan/VkRenderPass.cpp
index 108e797..0ebce67 100644
--- a/src/Vulkan/VkRenderPass.cpp
+++ b/src/Vulkan/VkRenderPass.cpp
@@ -15,6 +15,15 @@
 #include "VkRenderPass.hpp"
 #include <cstring>
 
+namespace
+{
+	void MarkFirstUse(int& attachment, int subpass)
+	{
+		if (attachment == -1)
+			attachment = subpass;
+	}
+}
+
 namespace vk
 {
 
@@ -39,6 +48,12 @@
 		attachments = reinterpret_cast<VkAttachmentDescription*>(hostMemory);
 		memcpy(attachments, pCreateInfo->pAttachments, attachmentSize);
 		hostMemory += attachmentSize;
+
+		size_t firstUseSize = pCreateInfo->attachmentCount * sizeof(int);
+		attachmentFirstUse = reinterpret_cast<int*>(hostMemory);
+		for (auto i = 0u; i < pCreateInfo->attachmentCount; i++)
+			attachmentFirstUse[i] = -1;
+		hostMemory += firstUseSize;
 	}
 
 	// Deep copy subpasses
@@ -58,6 +73,12 @@
 			memcpy(const_cast<VkAttachmentReference*>(subpasses[i].pInputAttachments),
 			       pCreateInfo->pSubpasses[i].pInputAttachments, inputAttachmentsSize);
 			hostMemory += inputAttachmentsSize;
+
+			for (auto j = 0u; j < subpasses[i].inputAttachmentCount; j++)
+			{
+				if (subpass.pInputAttachments[j].attachment != VK_ATTACHMENT_UNUSED)
+					MarkFirstUse(attachmentFirstUse[subpass.pInputAttachments[j].attachment], i);
+			}
 		}
 
 		if(subpass.colorAttachmentCount > 0)
@@ -65,24 +86,36 @@
 			size_t colorAttachmentsSize = subpass.colorAttachmentCount * sizeof(VkAttachmentReference);
 			subpasses[i].pColorAttachments = reinterpret_cast<VkAttachmentReference*>(hostMemory);
 			memcpy(const_cast<VkAttachmentReference*>(subpasses[i].pColorAttachments),
-			       pCreateInfo->pSubpasses[i].pColorAttachments, colorAttachmentsSize);
+			       subpass.pColorAttachments, colorAttachmentsSize);
 			hostMemory += colorAttachmentsSize;
 
 			if(subpass.pResolveAttachments != nullptr)
 			{
 				subpasses[i].pResolveAttachments = reinterpret_cast<VkAttachmentReference*>(hostMemory);
 				memcpy(const_cast<VkAttachmentReference*>(subpasses[i].pResolveAttachments),
-				       pCreateInfo->pSubpasses[i].pResolveAttachments, colorAttachmentsSize);
+				       subpass.pResolveAttachments, colorAttachmentsSize);
 				hostMemory += colorAttachmentsSize;
 			}
+
+			for (auto j = 0u; j < subpasses[i].colorAttachmentCount; j++)
+			{
+				if (subpass.pColorAttachments[j].attachment != VK_ATTACHMENT_UNUSED)
+					MarkFirstUse(attachmentFirstUse[subpass.pColorAttachments[j].attachment], i);
+				if (subpass.pResolveAttachments &&
+					subpass.pResolveAttachments[j].attachment != VK_ATTACHMENT_UNUSED)
+					MarkFirstUse(attachmentFirstUse[subpass.pResolveAttachments[j].attachment], i);
+			}
 		}
 
 		if(subpass.pDepthStencilAttachment != nullptr)
 		{
 			subpasses[i].pDepthStencilAttachment = reinterpret_cast<VkAttachmentReference*>(hostMemory);
 			memcpy(const_cast<VkAttachmentReference*>(subpasses[i].pDepthStencilAttachment),
-				pCreateInfo->pSubpasses[i].pDepthStencilAttachment, sizeof(VkAttachmentReference));
+				subpass.pDepthStencilAttachment, sizeof(VkAttachmentReference));
 			hostMemory += sizeof(VkAttachmentReference);
+
+			if (subpass.pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED)
+				MarkFirstUse(attachmentFirstUse[subpass.pDepthStencilAttachment->attachment], i);
 		}
 
 		if(subpass.preserveAttachmentCount > 0)
@@ -92,6 +125,12 @@
 			memcpy(const_cast<uint32_t*>(subpasses[i].pPreserveAttachments),
 			       pCreateInfo->pSubpasses[i].pPreserveAttachments, preserveAttachmentSize);
 			hostMemory += preserveAttachmentSize;
+
+			for (auto j = 0u; j < subpasses[i].preserveAttachmentCount; j++)
+			{
+				if (subpass.pPreserveAttachments[j] != VK_ATTACHMENT_UNUSED)
+					MarkFirstUse(attachmentFirstUse[subpass.pPreserveAttachments[j]], i);
+			}
 		}
 	}
 
@@ -110,7 +149,8 @@
 
 size_t RenderPass::ComputeRequiredAllocationSize(const VkRenderPassCreateInfo* pCreateInfo)
 {
-	size_t attachmentSize = pCreateInfo->attachmentCount * sizeof(VkAttachmentDescription);
+	size_t attachmentSize = pCreateInfo->attachmentCount * sizeof(VkAttachmentDescription)
+			+ pCreateInfo->attachmentCount * sizeof(int);
 	size_t subpassesSize = 0;
 	for(uint32_t i = 0; i < pCreateInfo->subpassCount; ++i)
 	{
@@ -133,11 +173,11 @@
 	return attachmentSize + subpassesSize + dependenciesSize;
 }
 
-void RenderPass::getRenderAreaGranularity(VkExtent2D* pGranularity) const

-{

-	pGranularity->width = 1;

-	pGranularity->height = 1;

-}

+void RenderPass::getRenderAreaGranularity(VkExtent2D* pGranularity) const
+{
+	pGranularity->width = 1;
+	pGranularity->height = 1;
+}
 
 void RenderPass::begin()
 {
diff --git a/src/Vulkan/VkRenderPass.hpp b/src/Vulkan/VkRenderPass.hpp
index 8a8a63d..4b5677c 100644
--- a/src/Vulkan/VkRenderPass.hpp
+++ b/src/Vulkan/VkRenderPass.hpp
@@ -17,6 +17,8 @@
 
 #include "VkObject.hpp"
 
+#include <vector>
+
 namespace vk
 {
 
@@ -29,7 +31,7 @@
 
 	static size_t ComputeRequiredAllocationSize(const VkRenderPassCreateInfo* pCreateInfo);
 
-	void getRenderAreaGranularity(VkExtent2D* pGranularity) const;

+	void getRenderAreaGranularity(VkExtent2D* pGranularity) const;
 
 	void begin();
 	void nextSubpass();
@@ -70,6 +72,11 @@
 		return dependencies[i];
 	}
 
+	bool isAttachmentUsed(uint32_t i) const
+	{
+		return attachmentFirstUse[i] >= 0;
+	}
+
 private:
 	uint32_t                 attachmentCount = 0;
 	VkAttachmentDescription* attachments = nullptr;
@@ -78,6 +85,7 @@
 	uint32_t                 dependencyCount = 0;
 	VkSubpassDependency*     dependencies = nullptr;
 	uint32_t                 currentSubpass = 0;
+	int*                     attachmentFirstUse = nullptr;
 };
 
 static inline RenderPass* Cast(VkRenderPass object)