Support for clearing Framebuffer attachments

This is the implementation for vkCmdClearAttachments.

It includes the new command along with the plumbing to
get the information to the Image clear function.

The only non trivial behavior here is that the provided
baseArrayLayer for the clear command is to be offset by
the ImageView's subresourceRange's baseArrayLayer so
that it clears the correct layers.

Bug b/119621736

Passes all tests in:
api.image_clearing.dedicated_allocation.clear_color_attachment.*

Change-Id: I19f86b63239ca2fb4d53f2c6c9cb75185794f061
Reviewed-on: https://swiftshader-review.googlesource.com/c/23748
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
diff --git a/src/Vulkan/VkCommandBuffer.cpp b/src/Vulkan/VkCommandBuffer.cpp
index b4ece01..59f77fc 100644
--- a/src/Vulkan/VkCommandBuffer.cpp
+++ b/src/Vulkan/VkCommandBuffer.cpp
@@ -36,10 +36,10 @@
 class BeginRenderPass : public CommandBuffer::Command
 {
 public:
-	BeginRenderPass(VkRenderPass pRenderPass, VkFramebuffer pFramebuffer, VkRect2D pRenderArea,
-	                uint32_t pClearValueCount, const VkClearValue* pClearValues) :
-		renderPass(pRenderPass), framebuffer(pFramebuffer), renderArea(pRenderArea),
-		clearValueCount(pClearValueCount)
+	BeginRenderPass(VkRenderPass renderPass, VkFramebuffer framebuffer, VkRect2D renderArea,
+	                uint32_t clearValueCount, const VkClearValue* pClearValues) :
+		renderPass(Cast(renderPass)), framebuffer(Cast(framebuffer)), renderArea(renderArea),
+		clearValueCount(clearValueCount)
 	{
 		// FIXME (b/119409619): use an allocator here so we can control all memory allocations
 		clearValues = new VkClearValue[clearValueCount];
@@ -54,18 +54,36 @@
 protected:
 	void play(CommandBuffer::ExecutionState& executionState)
 	{
-		Cast(renderPass)->begin();
-		Cast(framebuffer)->clear(clearValueCount, clearValues, renderArea);
+		executionState.renderPass = renderPass;
+		executionState.renderPassFramebuffer = framebuffer;
+		renderPass->begin();
+		framebuffer->clear(clearValueCount, clearValues, renderArea);
 	}
 
 private:
-	VkRenderPass renderPass;
-	VkFramebuffer framebuffer;
+	RenderPass* renderPass;
+	Framebuffer* framebuffer;
 	VkRect2D renderArea;
 	uint32_t clearValueCount;
 	VkClearValue* clearValues;
 };
 
+class NextSubpass : public CommandBuffer::Command
+{
+public:
+	NextSubpass()
+	{
+	}
+
+protected:
+	void play(CommandBuffer::ExecutionState& executionState)
+	{
+		executionState.renderPass->nextSubpass();
+	}
+
+private:
+};
+
 class EndRenderPass : public CommandBuffer::Command
 {
 public:
@@ -76,7 +94,9 @@
 protected:
 	void play(CommandBuffer::ExecutionState& executionState)
 	{
-		Cast(executionState.renderpass)->end();
+		executionState.renderPass->end();
+		executionState.renderPass = nullptr;
+		executionState.renderPassFramebuffer = nullptr;
 	}
 
 private:
@@ -93,7 +113,7 @@
 protected:
 	void play(CommandBuffer::ExecutionState& executionState)
 	{
-		executionState.pipelines[pipelineBindPoint] = pipeline;
+		executionState.pipelines[pipelineBindPoint] = Cast(pipeline);
 	}
 
 private:
@@ -127,7 +147,7 @@
 	void play(CommandBuffer::ExecutionState& executionState)
 	{
 		GraphicsPipeline* pipeline = static_cast<GraphicsPipeline*>(
-			Cast(executionState.pipelines[VK_PIPELINE_BIND_POINT_GRAPHICS]));
+			executionState.pipelines[VK_PIPELINE_BIND_POINT_GRAPHICS]);
 
 		sw::Context context = pipeline->getContext();
 		for(uint32_t i = 0; i < MAX_VERTEX_INPUT_BINDINGS; i++)
@@ -256,6 +276,23 @@
 	const VkImageSubresourceRange range;
 };
 
+struct ClearAttachment : public CommandBuffer::Command
+{
+	ClearAttachment(const VkClearAttachment& attachment, const VkClearRect& rect) :
+		attachment(attachment), rect(rect)
+	{
+	}
+
+	void play(CommandBuffer::ExecutionState& executionState)
+	{
+		executionState.renderPassFramebuffer->clear(attachment, rect);
+	}
+
+private:
+	const VkClearAttachment attachment;
+	const VkClearRect rect;
+};
+
 struct BlitImage : public CommandBuffer::Command
 {
 	BlitImage(VkImage srcImage, VkImage dstImage, const VkImageBlit& region, VkFilter filter) :
@@ -374,7 +411,9 @@
 
 void CommandBuffer::nextSubpass(VkSubpassContents contents)
 {
-	UNIMPLEMENTED();
+	ASSERT(state == RECORDING);
+
+	addCommand<NextSubpass>();
 }
 
 void CommandBuffer::endRenderPass()
@@ -661,7 +700,15 @@
 void CommandBuffer::clearAttachments(uint32_t attachmentCount, const VkClearAttachment* pAttachments,
 	uint32_t rectCount, const VkClearRect* pRects)
 {
-	UNIMPLEMENTED();
+	ASSERT(state == RECORDING);
+
+	for(uint32_t i = 0; i < attachmentCount; i++)
+	{
+		for(uint32_t j = 0; j < rectCount; j++)
+		{
+			addCommand<ClearAttachment>(pAttachments[i], pRects[j]);
+		}
+	}
 }
 
 void CommandBuffer::resolveImage(VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout,
diff --git a/src/Vulkan/VkCommandBuffer.hpp b/src/Vulkan/VkCommandBuffer.hpp
index 7e31b61..9b08bab 100644
--- a/src/Vulkan/VkCommandBuffer.hpp
+++ b/src/Vulkan/VkCommandBuffer.hpp
@@ -28,6 +28,10 @@
 namespace vk
 {
 
+class Framebuffer;
+class Pipeline;
+class RenderPass;
+
 class CommandBuffer
 {
 public:
@@ -118,8 +122,9 @@
 	struct ExecutionState
 	{
 		sw::Renderer* renderer = nullptr;
-		VkRenderPass renderpass = VK_NULL_HANDLE;
-		VkPipeline pipelines[VK_PIPELINE_BIND_POINT_RANGE_SIZE] = {};
+		RenderPass* renderPass = nullptr;
+		Framebuffer* renderPassFramebuffer = nullptr;
+		Pipeline* pipelines[VK_PIPELINE_BIND_POINT_RANGE_SIZE] = {};
 
 		struct VertexInputBinding
 		{
diff --git a/src/Vulkan/VkFramebuffer.cpp b/src/Vulkan/VkFramebuffer.cpp
index 379ef09..c7fd078 100644
--- a/src/Vulkan/VkFramebuffer.cpp
+++ b/src/Vulkan/VkFramebuffer.cpp
@@ -14,12 +14,14 @@
 
 #include "VkFramebuffer.hpp"
 #include "VkImageView.hpp"
+#include "VkRenderPass.hpp"
 #include <memory.h>
 
 namespace vk
 {
 
 Framebuffer::Framebuffer(const VkFramebufferCreateInfo* pCreateInfo, void* mem) :
+	renderPass(Cast(pCreateInfo->renderPass)),
 	attachmentCount(pCreateInfo->attachmentCount),
 	attachments(reinterpret_cast<ImageView**>(mem))
 {
@@ -44,6 +46,31 @@
 	}
 }
 
+void Framebuffer::clear(const VkClearAttachment& attachment, const VkClearRect& rect)
+{
+	if(attachment.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT)
+	{
+		if(attachment.colorAttachment != VK_ATTACHMENT_UNUSED)
+		{
+			VkSubpassDescription subpass = renderPass->getCurrentSubpass();
+
+			ASSERT(attachment.colorAttachment < subpass.colorAttachmentCount);
+			ASSERT(subpass.pColorAttachments[attachment.colorAttachment].attachment < attachmentCount);
+
+			attachments[subpass.pColorAttachments[attachment.colorAttachment].attachment]->clear(
+				attachment.clearValue, attachment.aspectMask, rect);
+		}
+	}
+	else if(attachment.aspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))
+	{
+		VkSubpassDescription subpass = renderPass->getCurrentSubpass();
+
+		ASSERT(subpass.pDepthStencilAttachment->attachment < attachmentCount);
+
+		attachments[subpass.pDepthStencilAttachment->attachment]->clear(attachment.clearValue, attachment.aspectMask, rect);
+	}
+}
+
 size_t Framebuffer::ComputeRequiredAllocationSize(const VkFramebufferCreateInfo* pCreateInfo)
 {
 	return pCreateInfo->attachmentCount * sizeof(void*);
diff --git a/src/Vulkan/VkFramebuffer.hpp b/src/Vulkan/VkFramebuffer.hpp
index 9049c17..2e16e99 100644
--- a/src/Vulkan/VkFramebuffer.hpp
+++ b/src/Vulkan/VkFramebuffer.hpp
@@ -21,6 +21,7 @@
 {
 
 class ImageView;
+class RenderPass;
 
 class Framebuffer : public Object<Framebuffer, VkFramebuffer>
 {
@@ -30,10 +31,12 @@
 	void destroy(const VkAllocationCallbacks* pAllocator);
 
 	void clear(uint32_t clearValueCount, const VkClearValue* pClearValues, const VkRect2D& renderArea);
+	void clear(const VkClearAttachment& attachment, const VkClearRect& rect);
 
 	static size_t ComputeRequiredAllocationSize(const VkFramebufferCreateInfo* pCreateInfo);
 
 private:
+	RenderPass* renderPass;
 	uint32_t    attachmentCount = 0;
 	ImageView** attachments = nullptr;
 };
diff --git a/src/Vulkan/VkImageView.cpp b/src/Vulkan/VkImageView.cpp
index e2c2113..293c8d7 100644
--- a/src/Vulkan/VkImageView.cpp
+++ b/src/Vulkan/VkImageView.cpp
@@ -89,4 +89,28 @@
 	image->clear(clearValue, renderArea, subresourceRange);
 }
 
+void ImageView::clear(const VkClearValue& clearValue, const VkImageAspectFlags aspectMask, const VkClearRect& renderArea)
+{
+	// Note: clearing ignores swizzling, so components is ignored.
+
+	if(!imageTypesMatch(image->getImageType()))
+	{
+		UNIMPLEMENTED();
+	}
+
+	if(image->getFormat() != format)
+	{
+		UNIMPLEMENTED();
+	}
+
+	VkImageSubresourceRange sr;
+	sr.aspectMask = aspectMask;
+	sr.baseMipLevel = subresourceRange.baseMipLevel;
+	sr.levelCount = subresourceRange.levelCount;
+	sr.baseArrayLayer = renderArea.baseArrayLayer + subresourceRange.baseArrayLayer;
+	sr.layerCount = renderArea.layerCount;
+
+	image->clear(clearValue, renderArea.rect, sr);
+}
+
 }
\ No newline at end of file
diff --git a/src/Vulkan/VkImageView.hpp b/src/Vulkan/VkImageView.hpp
index cdc3896..d2a0124 100644
--- a/src/Vulkan/VkImageView.hpp
+++ b/src/Vulkan/VkImageView.hpp
@@ -32,6 +32,7 @@
 	static size_t ComputeRequiredAllocationSize(const VkImageViewCreateInfo* pCreateInfo);
 
 	void clear(const VkClearValue& clearValues, const VkRect2D& renderArea);
+	void clear(const VkClearValue& clearValue, const VkImageAspectFlags aspectMask, const VkClearRect& renderArea);
 
 private:
 	bool                       imageTypesMatch(VkImageType imageType) const;