Add VK_KHR_imageless_framebuffer

Under normal operations, a framebuffer is created with each VkImageView
it will need to use. This extension allows users to specify a flag that
tells the driver to wait until render pass begin time. The user must
also request this feature during device creation, this behavior is not
enabled by default.

Bug: b/167223759
Tests: dEQP-VK.imageless_framebuffer.*
Change-Id: I2be5cec4d8174e20372eb9e148a05034dd338341
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/48488
Tested-by: Sean Risser <srisser@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
diff --git a/src/Vulkan/VkCommandBuffer.cpp b/src/Vulkan/VkCommandBuffer.cpp
index 2fde442..e140825 100644
--- a/src/Vulkan/VkCommandBuffer.cpp
+++ b/src/Vulkan/VkCommandBuffer.cpp
@@ -1405,10 +1405,18 @@
 }
 
 void CommandBuffer::beginRenderPass(RenderPass *renderPass, Framebuffer *framebuffer, VkRect2D renderArea,
-                                    uint32_t clearValueCount, const VkClearValue *clearValues, VkSubpassContents contents)
+                                    uint32_t clearValueCount, const VkClearValue *clearValues, VkSubpassContents contents,
+                                    const VkRenderPassAttachmentBeginInfo *attachmentInfo)
 {
 	ASSERT(state == RECORDING);
 
+	if(attachmentInfo)
+	{
+		for(uint32_t i = 0; i < attachmentInfo->attachmentCount; i++)
+		{
+			framebuffer->setAttachment(vk::Cast(attachmentInfo->pAttachments[i]), i);
+		}
+	}
 	addCommand<::CmdBeginRenderPass>(renderPass, framebuffer, renderArea, clearValueCount, clearValues);
 }
 
diff --git a/src/Vulkan/VkCommandBuffer.hpp b/src/Vulkan/VkCommandBuffer.hpp
index 9654514..7b6a61e 100644
--- a/src/Vulkan/VkCommandBuffer.hpp
+++ b/src/Vulkan/VkCommandBuffer.hpp
@@ -61,7 +61,8 @@
 	VkResult reset(VkCommandPoolResetFlags flags);
 
 	void beginRenderPass(RenderPass *renderPass, Framebuffer *framebuffer, VkRect2D renderArea,
-	                     uint32_t clearValueCount, const VkClearValue *pClearValues, VkSubpassContents contents);
+	                     uint32_t clearValueCount, const VkClearValue *pClearValues, VkSubpassContents contents,
+	                     const VkRenderPassAttachmentBeginInfo *attachmentBeginInfo);
 	void nextSubpass(VkSubpassContents contents);
 	void endRenderPass();
 	void executeCommands(uint32_t commandBufferCount, const VkCommandBuffer *pCommandBuffers);
diff --git a/src/Vulkan/VkFramebuffer.cpp b/src/Vulkan/VkFramebuffer.cpp
index 4c797e1..1ed2642 100644
--- a/src/Vulkan/VkFramebuffer.cpp
+++ b/src/Vulkan/VkFramebuffer.cpp
@@ -13,21 +13,54 @@
 // limitations under the License.
 
 #include "VkFramebuffer.hpp"
+
 #include "VkImageView.hpp"
 #include "VkRenderPass.hpp"
+#include "VkStringify.hpp"
+
 #include <memory.h>
 #include <algorithm>
 
 namespace vk {
 
 Framebuffer::Framebuffer(const VkFramebufferCreateInfo *pCreateInfo, void *mem)
-    : attachmentCount(pCreateInfo->attachmentCount)
-    , attachments(reinterpret_cast<ImageView **>(mem))
+    : attachments(reinterpret_cast<ImageView **>(mem))
     , extent{ pCreateInfo->width, pCreateInfo->height, pCreateInfo->layers }
 {
-	for(uint32_t i = 0; i < attachmentCount; i++)
+	const VkBaseInStructure *curInfo = reinterpret_cast<const VkBaseInStructure *>(pCreateInfo->pNext);
+	const VkFramebufferAttachmentsCreateInfo *attachmentsCreateInfo = nullptr;
+	while(curInfo)
 	{
-		attachments[i] = vk::Cast(pCreateInfo->pAttachments[i]);
+		switch(curInfo->sType)
+		{
+			case VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENTS_CREATE_INFO:
+				attachmentsCreateInfo = reinterpret_cast<const VkFramebufferAttachmentsCreateInfo *>(curInfo);
+				break;
+			default:
+				LOG_TRAP("pFramebufferCreateInfo->pNext->sType = %s", vk::Stringify(curInfo->sType).c_str());
+				break;
+		}
+		curInfo = curInfo->pNext;
+	}
+
+	if(pCreateInfo->flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT)
+	{
+		// If flags includes VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT, the pNext chain **must**
+		// include a VkFramebufferAttachmentsCreateInfo.
+		ASSERT(attachmentsCreateInfo != nullptr);
+		attachmentCount = attachmentsCreateInfo->attachmentImageInfoCount;
+		for(uint32_t i = 0; i < attachmentCount; i++)
+		{
+			attachments[i] = nullptr;
+		}
+	}
+	else
+	{
+		attachmentCount = pCreateInfo->attachmentCount;
+		for(uint32_t i = 0; i < attachmentCount; i++)
+		{
+			attachments[i] = vk::Cast(pCreateInfo->pAttachments[i]);
+		}
 	}
 }
 
@@ -115,6 +148,13 @@
 	}
 }
 
+void Framebuffer::setAttachment(ImageView *imageView, uint32_t index)
+{
+	ASSERT(index < attachmentCount);
+	ASSERT(attachments[index] == nullptr);
+	attachments[index] = imageView;
+}
+
 ImageView *Framebuffer::getAttachment(uint32_t index) const
 {
 	return attachments[index];
@@ -147,7 +187,32 @@
 
 size_t Framebuffer::ComputeRequiredAllocationSize(const VkFramebufferCreateInfo *pCreateInfo)
 {
-	return pCreateInfo->attachmentCount * sizeof(void *);
+	const VkBaseInStructure *curInfo = reinterpret_cast<const VkBaseInStructure *>(pCreateInfo->pNext);
+	const VkFramebufferAttachmentsCreateInfo *attachmentsInfo = nullptr;
+	while(curInfo)
+	{
+		switch(curInfo->sType)
+		{
+			case VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENTS_CREATE_INFO:
+				attachmentsInfo = reinterpret_cast<const VkFramebufferAttachmentsCreateInfo *>(curInfo);
+				break;
+			default:
+				LOG_TRAP("pFramebufferCreateInfo->pNext->sType = %s", vk::Stringify(curInfo->sType).c_str());
+				break;
+		}
+
+		curInfo = curInfo->pNext;
+	}
+
+	if(pCreateInfo->flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT)
+	{
+		ASSERT(attachmentsInfo != nullptr);
+		return attachmentsInfo->attachmentImageInfoCount * sizeof(void *);
+	}
+	else
+	{
+		return pCreateInfo->attachmentCount * sizeof(void *);
+	}
 }
 
 }  // namespace vk
diff --git a/src/Vulkan/VkFramebuffer.hpp b/src/Vulkan/VkFramebuffer.hpp
index e9839ee..7ad1157 100644
--- a/src/Vulkan/VkFramebuffer.hpp
+++ b/src/Vulkan/VkFramebuffer.hpp
@@ -32,6 +32,7 @@
 	void clearAttachment(const RenderPass *renderPass, uint32_t subpassIndex, const VkClearAttachment &attachment, const VkClearRect &rect);
 
 	static size_t ComputeRequiredAllocationSize(const VkFramebufferCreateInfo *pCreateInfo);
+	void setAttachment(ImageView *imageView, uint32_t index);
 	ImageView *getAttachment(uint32_t index) const;
 	void resolve(const RenderPass *renderPass, uint32_t subpassIndex);
 
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index 0d5c777..165db86 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -226,6 +226,12 @@
 }
 
 template<typename T>
+static void getPhysicalDeviceImagelessFramebufferFeatures(T *features)
+{
+	features->imagelessFramebuffer = VK_TRUE;
+}
+
+template<typename T>
 static void getPhysicalDeviceVulkan12Features(T *features)
 {
 	features->samplerMirrorClampToEdge = VK_FALSE;
@@ -257,7 +263,7 @@
 	features->runtimeDescriptorArray = VK_FALSE;
 	features->samplerFilterMinmax = VK_FALSE;
 	features->scalarBlockLayout = VK_FALSE;
-	features->imagelessFramebuffer = VK_FALSE;
+	getPhysicalDeviceImagelessFramebufferFeatures(features);
 	features->uniformBufferStandardLayout = VK_FALSE;
 	features->shaderSubgroupExtendedTypes = VK_FALSE;
 	getPhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR(features);
@@ -322,6 +328,9 @@
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT:
 				getPhysicalDeviceProvokingVertexFeaturesEXT(reinterpret_cast<VkPhysicalDeviceProvokingVertexFeaturesEXT *>(curExtension));
 				break;
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES:
+				getPhysicalDeviceImagelessFramebufferFeatures(reinterpret_cast<VkPhysicalDeviceImagelessFramebufferFeatures *>(curExtension));
+				break;
 			default:
 				WARN("curExtension->pNext->sType = %s", vk::Stringify(curExtension->sType).c_str());
 				break;
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 43cb19c..efb2bf4 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -379,7 +379,9 @@
 	{ VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, VK_EXT_PROVOKING_VERTEX_SPEC_VERSION },
 	{ VK_GOOGLE_SAMPLER_FILTERING_PRECISION_EXTENSION_NAME, VK_GOOGLE_SAMPLER_FILTERING_PRECISION_SPEC_VERSION },
 	{ VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, VK_EXT_DEPTH_RANGE_UNRESTRICTED_SPEC_VERSION },
-	{ VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, VK_KHR_IMAGE_FORMAT_LIST_SPEC_VERSION }
+	// Vulkan 1.2 promoted extensions
+	{ VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, VK_KHR_IMAGE_FORMAT_LIST_SPEC_VERSION },
+	{ VK_KHR_IMAGELESS_FRAMEBUFFER_EXTENSION_NAME, VK_KHR_IMAGELESS_FRAMEBUFFER_SPEC_VERSION }
 };
 
 static bool hasExtension(const char *extensionName, const VkExtensionProperties *extensionProperties, uint32_t extensionPropertiesCount)
@@ -828,6 +830,13 @@
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT:
 				ASSERT(!hasDeviceExtension(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME));
 				break;
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES_KHR:
+			{
+				const VkPhysicalDeviceImagelessFramebufferFeaturesKHR *imagelessFramebufferFeatures = reinterpret_cast<const VkPhysicalDeviceImagelessFramebufferFeaturesKHR *>(extensionCreateInfo);
+				// Always provide Imageless Framebuffers
+				(void)imagelessFramebufferFeatures->imagelessFramebuffer;
+			}
+			break;
 			default:
 				// "the [driver] must skip over, without processing (other than reading the sType and pNext members) any structures in the chain with sType values not defined by [supported extenions]"
 				LOG_TRAP("pCreateInfo->pNext sType = %s", vk::Stringify(extensionCreateInfo->sType).c_str());
@@ -2145,18 +2154,6 @@
 	TRACE("(VkDevice device = %p, const VkFramebufferCreateInfo* pCreateInfo = %p, const VkAllocationCallbacks* pAllocator = %p, VkFramebuffer* pFramebuffer = %p)",
 	      device, pCreateInfo, pAllocator, pFramebuffer);
 
-	if(pCreateInfo->flags != 0)
-	{
-		UNSUPPORTED("pCreateInfo->flags %d", int(pCreateInfo->flags));
-	}
-
-	auto *nextInfo = reinterpret_cast<const VkBaseInStructure *>(pCreateInfo->pNext);
-	while(nextInfo)
-	{
-		LOG_TRAP("pCreateInfo->pNext sType = %s", vk::Stringify(nextInfo->sType).c_str());
-		nextInfo = nextInfo->pNext;
-	}
-
 	return vk::Framebuffer::Create(pAllocator, pCreateInfo, pFramebuffer);
 }
 
@@ -2626,6 +2623,7 @@
 	      commandBuffer, pRenderPassBegin, contents);
 
 	const VkBaseInStructure *renderPassBeginInfo = reinterpret_cast<const VkBaseInStructure *>(pRenderPassBegin->pNext);
+	const VkRenderPassAttachmentBeginInfo *attachmentBeginInfo = nullptr;
 	while(renderPassBeginInfo)
 	{
 		switch(renderPassBeginInfo->sType)
@@ -2635,6 +2633,9 @@
 				// in order to distribute rendering between multiple physical devices.
 				// SwiftShader only has a single physical device, so this extension does nothing in this case.
 				break;
+			case VK_STRUCTURE_TYPE_RENDER_PASS_ATTACHMENT_BEGIN_INFO:
+				attachmentBeginInfo = reinterpret_cast<const VkRenderPassAttachmentBeginInfo *>(renderPassBeginInfo);
+				break;
 			default:
 				LOG_TRAP("pRenderPassBegin->pNext sType = %s", vk::Stringify(renderPassBeginInfo->sType).c_str());
 				break;
@@ -2643,7 +2644,7 @@
 		renderPassBeginInfo = renderPassBeginInfo->pNext;
 	}
 
-	vk::Cast(commandBuffer)->beginRenderPass(vk::Cast(pRenderPassBegin->renderPass), vk::Cast(pRenderPassBegin->framebuffer), pRenderPassBegin->renderArea, pRenderPassBegin->clearValueCount, pRenderPassBegin->pClearValues, contents);
+	vk::Cast(commandBuffer)->beginRenderPass(vk::Cast(pRenderPassBegin->renderPass), vk::Cast(pRenderPassBegin->framebuffer), pRenderPassBegin->renderArea, pRenderPassBegin->clearValueCount, pRenderPassBegin->pClearValues, contents, attachmentBeginInfo);
 }
 
 VKAPI_ATTR void VKAPI_CALL vkCmdBeginRenderPass2KHR(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin, const VkSubpassBeginInfoKHR *pSubpassBeginInfo)
@@ -2651,7 +2652,29 @@
 	TRACE("(VkCommandBuffer commandBuffer = %p, const VkRenderPassBeginInfo* pRenderPassBegin = %p, const VkSubpassBeginInfoKHR* pSubpassBeginInfo = %p)",
 	      commandBuffer, pRenderPassBegin, pSubpassBeginInfo);
 
-	vk::Cast(commandBuffer)->beginRenderPass(vk::Cast(pRenderPassBegin->renderPass), vk::Cast(pRenderPassBegin->framebuffer), pRenderPassBegin->renderArea, pRenderPassBegin->clearValueCount, pRenderPassBegin->pClearValues, pSubpassBeginInfo->contents);
+	const VkBaseInStructure *renderPassBeginInfo = reinterpret_cast<const VkBaseInStructure *>(pRenderPassBegin->pNext);
+	const VkRenderPassAttachmentBeginInfo *attachmentBeginInfo = nullptr;
+	while(renderPassBeginInfo)
+	{
+		switch(renderPassBeginInfo->sType)
+		{
+			case VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO:
+				// This extension controls which render area is used on which physical device,
+				// in order to distribute rendering between multiple physical devices.
+				// SwiftShader only has a single physical device, so this extension does nothing in this case.
+				break;
+			case VK_STRUCTURE_TYPE_RENDER_PASS_ATTACHMENT_BEGIN_INFO:
+				attachmentBeginInfo = reinterpret_cast<const VkRenderPassAttachmentBeginInfo *>(renderPassBeginInfo);
+				break;
+			default:
+				LOG_TRAP("pRenderPassBegin->pNext sType = %s", vk::Stringify(renderPassBeginInfo->sType).c_str());
+				break;
+		}
+
+		renderPassBeginInfo = renderPassBeginInfo->pNext;
+	}
+
+	vk::Cast(commandBuffer)->beginRenderPass(vk::Cast(pRenderPassBegin->renderPass), vk::Cast(pRenderPassBegin->framebuffer), pRenderPassBegin->renderArea, pRenderPassBegin->clearValueCount, pRenderPassBegin->pClearValues, pSubpassBeginInfo->contents, attachmentBeginInfo);
 }
 
 VKAPI_ATTR void VKAPI_CALL vkCmdNextSubpass(VkCommandBuffer commandBuffer, VkSubpassContents contents)