Add support for Sample image operand in OpImageRead

Enables reading from multisampled input attachments and storage images

Bug: b/131171141
Test: dEQP-VK.renderpass.*.multisample.*
Change-Id: I719d54c31eb12f58ba7fbdd614ecf3073bfc6d40
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/30553
Presubmit-Ready: Chris Forbes <chrisforbes@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Tested-by: Chris Forbes <chrisforbes@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 16a4d73..2c912c7 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -4756,7 +4756,7 @@
 		return EmitResult::Continue;
 	}
 
-	SIMD::Pointer SpirvShader::GetTexelAddress(SpirvRoutine const *routine, SIMD::Pointer ptr, GenericValue const & coordinate, Type const & imageType, Pointer<Byte> descriptor, int texelSize) const
+	SIMD::Pointer SpirvShader::GetTexelAddress(SpirvRoutine const *routine, SIMD::Pointer ptr, GenericValue const & coordinate, Type const & imageType, Pointer<Byte> descriptor, int texelSize, Object::ID sampleId) const
 	{
 		bool isArrayed = imageType.definition.word(5) != 0;
 		auto dim = static_cast<spv::Dim>(imageType.definition.word(3));
@@ -4787,6 +4787,13 @@
 					*Pointer<Int>(descriptor + OFFSET(vk::StorageImageDescriptor, slicePitchBytes)));
 		}
 
+		if (sampleId.value())
+		{
+			GenericValue sample{this, routine, sampleId};
+			ptr += sample.Int(0) * SIMD::Int(
+					*Pointer<Int>(descriptor + OFFSET(vk::StorageImageDescriptor, samplePitchBytes)));
+		}
+
 		return ptr;
 	}
 
@@ -4798,8 +4805,21 @@
 		auto &imageType = getType(image.type);
 		Object::ID resultId = insn.word(2);
 
-		// Not handling any image operands yet.
-		ASSERT(insn.wordCount() == 5);
+		Object::ID sampleId = 0;
+
+		if (insn.wordCount() > 5)
+		{
+			int operand = 6;
+			auto imageOperands = insn.word(5);
+			if (imageOperands & spv::ImageOperandsSampleMask)
+			{
+				sampleId = insn.word(operand++);
+				imageOperands &= ~spv::ImageOperandsSampleMask;
+			}
+
+			// Should be no remaining image operands.
+			ASSERT(!imageOperands);
+		}
 
 		ASSERT(imageType.definition.opcode() == spv::OpTypeImage);
 		auto dim = static_cast<spv::Dim>(imageType.definition.word(3));
@@ -4821,7 +4841,7 @@
 						: SpirvFormatToVulkanFormat(static_cast<spv::ImageFormat>(imageType.definition.word(8)));
 		auto texelSize = vk::Format(vkFormat).bytes();
 		auto basePtr = SIMD::Pointer(imageBase, imageSizeInBytes);
-		auto texelPtr = GetTexelAddress(state->routine, basePtr, coordinate, imageType, binding, texelSize);
+		auto texelPtr = GetTexelAddress(state->routine, basePtr, coordinate, imageType, binding, texelSize, sampleId);
 
 		SIMD::Int packed[4];
 		// Round up texel size: for formats smaller than 32 bits per texel, we will emit a bunch
@@ -5156,7 +5176,7 @@
 		}
 
 		auto basePtr = SIMD::Pointer(imageBase, imageSizeInBytes);
-		auto texelPtr = GetTexelAddress(state->routine, basePtr, coordinate, imageType, binding, texelSize);
+		auto texelPtr = GetTexelAddress(state->routine, basePtr, coordinate, imageType, binding, texelSize, 0);
 
 		for (auto i = 0u; i < numPackedElements; i++)
 		{
@@ -5188,7 +5208,7 @@
 		auto imageSizeInBytes = *Pointer<Int>(binding + OFFSET(vk::StorageImageDescriptor, sizeInBytes));
 
 		auto basePtr = SIMD::Pointer(imageBase, imageSizeInBytes);
-		auto ptr = GetTexelAddress(state->routine, basePtr, coordinate, imageType, binding, sizeof(uint32_t));
+		auto ptr = GetTexelAddress(state->routine, basePtr, coordinate, imageType, binding, sizeof(uint32_t), 0);
 
 		state->routine->createPointer(resultId, ptr);
 
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index a97b376..b893073 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -909,7 +909,7 @@
 		EmitResult EmitCopyMemory(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitGroupNonUniform(InsnIterator insn, EmitState *state) const;
 
-		SIMD::Pointer GetTexelAddress(SpirvRoutine const * routine, SIMD::Pointer base, GenericValue const & coordinate, Type const & imageType, Pointer<Byte> descriptor, int texelSize) const;
+		SIMD::Pointer GetTexelAddress(SpirvRoutine const * routine, SIMD::Pointer base, GenericValue const & coordinate, Type const & imageType, Pointer<Byte> descriptor, int texelSize, Object::ID sampleId) const;
 		spv::Scope GetScope(Object::ID id) const;
 
 		// OpcodeName() returns the name of the opcode op.
diff --git a/src/Vulkan/VkDescriptorSetLayout.cpp b/src/Vulkan/VkDescriptorSetLayout.cpp
index e0566a1..d44bbab 100644
--- a/src/Vulkan/VkDescriptorSetLayout.cpp
+++ b/src/Vulkan/VkDescriptorSetLayout.cpp
@@ -490,9 +490,10 @@
 			descriptor[i].ptr = imageView->getOffsetPointer({0, 0, 0}, VK_IMAGE_ASPECT_COLOR_BIT, 0, 0);
 			descriptor[i].extent = imageView->getMipLevelExtent(0);
 			descriptor[i].rowPitchBytes = imageView->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
-			descriptor[i].slicePitchBytes = imageView->getSubresourceRange().layerCount > 1
-											? imageView->layerPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT)
-											: imageView->slicePitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
+			descriptor[i].samplePitchBytes = imageView->getSubresourceRange().layerCount > 1
+											 ? imageView->layerPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT)
+											 : imageView->slicePitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
+			descriptor[i].slicePitchBytes = descriptor[i].samplePitchBytes * imageView->getSampleCount();
 			descriptor[i].arrayLayers = imageView->getSubresourceRange().layerCount;
 			descriptor[i].sizeInBytes = imageView->getImageSizeInBytes();
 		}
@@ -508,6 +509,7 @@
 			descriptor[i].extent = {bufferView->getElementCount(), 1, 1};
 			descriptor[i].rowPitchBytes = 0;
 			descriptor[i].slicePitchBytes = 0;
+			descriptor[i].samplePitchBytes = 0;
 			descriptor[i].arrayLayers = 1;
 			descriptor[i].sizeInBytes = bufferView->getRangeInBytes();
 		}
diff --git a/src/Vulkan/VkDescriptorSetLayout.hpp b/src/Vulkan/VkDescriptorSetLayout.hpp
index e63bb4f..38722d5 100644
--- a/src/Vulkan/VkDescriptorSetLayout.hpp
+++ b/src/Vulkan/VkDescriptorSetLayout.hpp
@@ -44,6 +44,7 @@
 	VkExtent3D extent;
 	int rowPitchBytes;
 	int slicePitchBytes;
+	int samplePitchBytes;
 	int arrayLayers;
 	int sizeInBytes;
 };
diff --git a/src/Vulkan/VkImageView.hpp b/src/Vulkan/VkImageView.hpp
index 27d909c..6daa326 100644
--- a/src/Vulkan/VkImageView.hpp
+++ b/src/Vulkan/VkImageView.hpp
@@ -45,12 +45,23 @@
 
 	VkImageViewType getType() const { return viewType; }
 	Format getFormat(Usage usage = RAW) const;
-	int getSampleCount() const { return image->getSampleCountFlagBits(); }
 	int rowPitchBytes(VkImageAspectFlagBits aspect, uint32_t mipLevel, Usage usage = RAW) const;
 	int slicePitchBytes(VkImageAspectFlagBits aspect, uint32_t mipLevel, Usage usage = RAW) const;
 	int layerPitchBytes(VkImageAspectFlagBits aspect, Usage usage = RAW) const;
 	VkExtent3D getMipLevelExtent(uint32_t mipLevel) const;
 
+	int getSampleCount() const
+	{
+		switch (image->getSampleCountFlagBits())
+		{
+		case VK_SAMPLE_COUNT_1_BIT: return 1;
+		case VK_SAMPLE_COUNT_4_BIT: return 4;
+		default:
+			UNIMPLEMENTED("Sample count flags %d", image->getSampleCountFlagBits());
+			return 1;
+		}
+	}
+
 	void *getOffsetPointer(const VkOffset3D& offset, VkImageAspectFlagBits aspect, uint32_t mipLevel, uint32_t layer, Usage usage = RAW) const;
 	bool hasDepthAspect() const { return (subresourceRange.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0; }
 	bool hasStencilAspect() const { return (subresourceRange.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0; }