Parse the OpTypeImage information into ImageInstructionSignature

This will provide everything we need to make GetTexelAddress() no longer
have to parse SPIR-V, which in turn will allow using it in a trampolined
sampling routine to support storageImageWriteWithoutFormat.

Bug: b/203730083
Change-Id: I374850f4d2137375a48f62b19f774c028c2c6229
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/59508
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 60750be..8bd25af 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -514,7 +514,6 @@
 	struct ImageInstructionSignature
 	{
 		ImageInstructionSignature(Variant variant, SamplerMethod samplerMethod)
-		    : signature(0)
 		{
 			this->variant = variant;
 			this->samplerMethod = samplerMethod;
@@ -557,6 +556,9 @@
 				Variant variant : BITS(VARIANT_LAST);
 				SamplerMethod samplerMethod : BITS(SAMPLER_METHOD_LAST);
 				uint32_t gatherComponent : 2;
+				uint32_t dim : BITS(spv::DimSubpassData);  // spv::Dim
+				uint32_t arrayed : 1;
+				uint32_t imageFormat : BITS(spv::ImageFormatR64i);  // spv::ImageFormat
 
 				// Parameters are passed to the sampling routine in this order:
 				uint32_t coordinates : 3;       // 1-4 (does not contain projection component)
@@ -567,7 +569,7 @@
 				uint32_t sample : 1;            // 0-1 scalar integer
 			};
 
-			uint32_t signature;
+			uint32_t signature = 0;
 		};
 	};
 
@@ -805,7 +807,7 @@
 	};
 
 	std::unordered_map<Object::ID, DescriptorDecorations> descriptorDecorations;
-	std::vector<VkFormat> inputAttachmentFormats;
+	std::vector<vk::Format> inputAttachmentFormats;
 
 	struct InterfaceComponent
 	{
diff --git a/src/Pipeline/SpirvShaderImage.cpp b/src/Pipeline/SpirvShaderImage.cpp
index edb8737..44e9ab2 100644
--- a/src/Pipeline/SpirvShaderImage.cpp
+++ b/src/Pipeline/SpirvShaderImage.cpp
@@ -23,7 +23,7 @@
 
 namespace {
 
-VkFormat SpirvFormatToVulkanFormat(spv::ImageFormat format)
+vk::Format SpirvFormatToVulkanFormat(spv::ImageFormat format)
 {
 	switch(format)
 	{
@@ -126,6 +126,21 @@
 		coordinateId = insn.word(4);
 	}
 
+	// `imageId` can represent either a Sampled Image, a samplerless Image, or a pointer to an Image.
+	// To get to the OpTypeImage operands, traverse the OpTypeSampledImage or OpTypePointer.
+	const Type &imageObjectType = spirv.getObjectType(imageId);
+	const Type &imageReferenceType = (imageObjectType.opcode() == spv::OpTypeSampledImage)
+	                                     ? spirv.getType(imageObjectType.definition.word(2))
+	                                     : imageObjectType;
+	const Type &imageType = ((imageReferenceType.opcode() == spv::OpTypePointer)
+	                             ? spirv.getType(imageReferenceType.element)
+	                             : imageReferenceType);
+
+	ASSERT(imageType.opcode() == spv::OpTypeImage);
+	dim = imageType.definition.word(3);
+	arrayed = imageType.definition.word(5);
+	imageFormat = imageType.definition.word(8);
+
 	const Object &coordinateObject = spirv.getObject(coordinateId);
 	const Type &coordinateType = spirv.getType(coordinateObject);
 	coordinates = coordinateType.componentCount - (isProj() ? 1 : 0);
@@ -703,25 +718,25 @@
 	auto &imageType = getType(image);
 
 	ASSERT(imageType.definition.opcode() == spv::OpTypeImage);
-	auto dim = static_cast<spv::Dim>(imageType.definition.word(3));
+	auto dim = static_cast<spv::Dim>(instruction.dim);
 
 	auto coordinate = Operand(this, state, instruction.coordinateId);
 	const DescriptorDecorations &d = descriptorDecorations.at(instruction.imageId);
 
 	// For subpass data, format in the instruction is spv::ImageFormatUnknown. Get it from
 	// the renderpass data instead. In all other cases, we can use the format in the instruction.
-	auto vkFormat = (dim == spv::DimSubpassData)
-	                    ? inputAttachmentFormats[d.InputAttachmentIndex]
-	                    : SpirvFormatToVulkanFormat(static_cast<spv::ImageFormat>(imageType.definition.word(8)));
+	auto imageFormat = (dim == spv::DimSubpassData)
+	                       ? inputAttachmentFormats[d.InputAttachmentIndex]
+	                       : SpirvFormatToVulkanFormat(static_cast<spv::ImageFormat>(instruction.imageFormat));
 
 	// Depth+Stencil image attachments select aspect based on the Sampled Type of the
 	// OpTypeImage. If float, then we want the depth aspect. If int, we want the stencil aspect.
-	auto useStencilAspect = (vkFormat == VK_FORMAT_D32_SFLOAT_S8_UINT &&
+	auto useStencilAspect = (imageFormat == VK_FORMAT_D32_SFLOAT_S8_UINT &&
 	                         getType(imageType.definition.word(2)).opcode() == spv::OpTypeInt);
 
 	if(useStencilAspect)
 	{
-		vkFormat = VK_FORMAT_S8_UINT;
+		imageFormat = VK_FORMAT_S8_UINT;
 	}
 
 	Pointer<Byte> descriptor = state->getPointer(instruction.imageId).base;  // vk::StorageImageDescriptor*
@@ -736,8 +751,8 @@
 	// VK_EXT_image_robustness requires replacing out-of-bounds access with zero.
 	// TODO(b/162327166): Only perform bounds checks when VK_EXT_image_robustness is enabled.
 	auto robustness = OutOfBoundsBehavior::Nullify;
+	const int texelSize = imageFormat.bytes();
 
-	auto texelSize = vk::Format(vkFormat).bytes();
 	auto texelPtr = GetTexelAddress(state, imageBase, imageSizeInBytes, coordinate, imageType, descriptor, texelSize, instruction.sampleId, useStencilAspect, robustness);
 
 	// Gather packed texel data. Texels larger than 4 bytes occupy multiple SIMD::Int elements.
@@ -783,7 +798,7 @@
 	// Format support requirements here come from two sources:
 	// - Minimum required set of formats for loads from storage images
 	// - Any format supported as a color or depth/stencil attachment, for input attachments
-	switch(vkFormat)
+	switch(imageFormat)
 	{
 	case VK_FORMAT_R32G32B32A32_SFLOAT:
 	case VK_FORMAT_R32G32B32A32_SINT:
@@ -1100,7 +1115,7 @@
 		dst.move(3, SIMD::Float(1.0f));
 		break;
 	default:
-		UNSUPPORTED("VkFormat %d", int(vkFormat));
+		UNSUPPORTED("VkFormat %d", int(imageFormat));
 		break;
 	}
 
@@ -1124,14 +1139,12 @@
 	Int imageSizeInBytes = *Pointer<Int>(descriptor + OFFSET(vk::StorageImageDescriptor, sizeInBytes));
 
 	SIMD::Int packed[4];
-	int texelSize = 0;
-	vk::Format format = SpirvFormatToVulkanFormat(static_cast<spv::ImageFormat>(imageType.definition.word(8)));
-	switch(format)
+	vk::Format imageFormat = SpirvFormatToVulkanFormat(static_cast<spv::ImageFormat>(instruction.imageFormat));
+	switch(imageFormat)
 	{
 	case VK_FORMAT_R32G32B32A32_SFLOAT:
 	case VK_FORMAT_R32G32B32A32_SINT:
 	case VK_FORMAT_R32G32B32A32_UINT:
-		texelSize = 16;
 		packed[0] = texel.Int(0);
 		packed[1] = texel.Int(1);
 		packed[2] = texel.Int(2);
@@ -1140,18 +1153,15 @@
 	case VK_FORMAT_R32_SFLOAT:
 	case VK_FORMAT_R32_SINT:
 	case VK_FORMAT_R32_UINT:
-		texelSize = 4;
 		packed[0] = texel.Int(0);
 		break;
 	case VK_FORMAT_R8G8B8A8_UNORM:
-		texelSize = 4;
 		packed[0] = (SIMD::UInt(Round(Min(Max(texel.Float(0), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(255.0f)))) |
 		            ((SIMD::UInt(Round(Min(Max(texel.Float(1), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(255.0f)))) << 8) |
 		            ((SIMD::UInt(Round(Min(Max(texel.Float(2), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(255.0f)))) << 16) |
 		            ((SIMD::UInt(Round(Min(Max(texel.Float(3), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(255.0f)))) << 24);
 		break;
 	case VK_FORMAT_R8G8B8A8_SNORM:
-		texelSize = 4;
 		packed[0] = (SIMD::Int(Round(Min(Max(texel.Float(0), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(127.0f))) &
 		             SIMD::Int(0xFF)) |
 		            ((SIMD::Int(Round(Min(Max(texel.Float(1), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(127.0f))) &
@@ -1166,131 +1176,108 @@
 		break;
 	case VK_FORMAT_R8G8B8A8_SINT:
 	case VK_FORMAT_R8G8B8A8_UINT:
-		texelSize = 4;
 		packed[0] = (SIMD::UInt(texel.UInt(0) & SIMD::UInt(0xff))) |
 		            (SIMD::UInt(texel.UInt(1) & SIMD::UInt(0xff)) << 8) |
 		            (SIMD::UInt(texel.UInt(2) & SIMD::UInt(0xff)) << 16) |
 		            (SIMD::UInt(texel.UInt(3) & SIMD::UInt(0xff)) << 24);
 		break;
 	case VK_FORMAT_R16G16B16A16_SFLOAT:
-		texelSize = 8;
 		packed[0] = floatToHalfBits(texel.UInt(0), false) | floatToHalfBits(texel.UInt(1), true);
 		packed[1] = floatToHalfBits(texel.UInt(2), false) | floatToHalfBits(texel.UInt(3), true);
 		break;
 	case VK_FORMAT_R16G16B16A16_SINT:
 	case VK_FORMAT_R16G16B16A16_UINT:
-		texelSize = 8;
 		packed[0] = SIMD::UInt(texel.UInt(0) & SIMD::UInt(0xFFFF)) | (SIMD::UInt(texel.UInt(1) & SIMD::UInt(0xFFFF)) << 16);
 		packed[1] = SIMD::UInt(texel.UInt(2) & SIMD::UInt(0xFFFF)) | (SIMD::UInt(texel.UInt(3) & SIMD::UInt(0xFFFF)) << 16);
 		break;
 	case VK_FORMAT_R32G32_SFLOAT:
 	case VK_FORMAT_R32G32_SINT:
 	case VK_FORMAT_R32G32_UINT:
-		texelSize = 8;
 		packed[0] = texel.Int(0);
 		packed[1] = texel.Int(1);
 		break;
 	case VK_FORMAT_R16G16_SFLOAT:
-		texelSize = 4;
 		packed[0] = floatToHalfBits(texel.UInt(0), false) | floatToHalfBits(texel.UInt(1), true);
 		break;
 	case VK_FORMAT_R16G16_SINT:
 	case VK_FORMAT_R16G16_UINT:
-		texelSize = 4;
 		packed[0] = SIMD::UInt(texel.UInt(0) & SIMD::UInt(0xFFFF)) | (SIMD::UInt(texel.UInt(1) & SIMD::UInt(0xFFFF)) << 16);
 		break;
 	case VK_FORMAT_B10G11R11_UFLOAT_PACK32:
-		texelSize = 4;
 		// Truncates instead of rounding. See b/147900455
 		packed[0] = ((floatToHalfBits(As<SIMD::UInt>(Max(texel.Float(0), SIMD::Float(0.0f))), false) & SIMD::UInt(0x7FF0)) >> 4) |
 		            ((floatToHalfBits(As<SIMD::UInt>(Max(texel.Float(1), SIMD::Float(0.0f))), false) & SIMD::UInt(0x7FF0)) << 7) |
 		            ((floatToHalfBits(As<SIMD::UInt>(Max(texel.Float(2), SIMD::Float(0.0f))), false) & SIMD::UInt(0x7FE0)) << 17);
 		break;
 	case VK_FORMAT_R16_SFLOAT:
-		texelSize = 2;
 		packed[0] = floatToHalfBits(texel.UInt(0), false);
 		break;
 	case VK_FORMAT_R16G16B16A16_UNORM:
-		texelSize = 8;
 		packed[0] = SIMD::UInt(Round(Min(Max(texel.Float(0), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(0xFFFF))) |
 		            (SIMD::UInt(Round(Min(Max(texel.Float(1), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(0xFFFF))) << 16);
 		packed[1] = SIMD::UInt(Round(Min(Max(texel.Float(2), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(0xFFFF))) |
 		            (SIMD::UInt(Round(Min(Max(texel.Float(3), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(0xFFFF))) << 16);
 		break;
 	case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
-		texelSize = 4;
 		packed[0] = (SIMD::UInt(Round(Min(Max(texel.Float(0), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(0x3FF)))) |
 		            ((SIMD::UInt(Round(Min(Max(texel.Float(1), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(0x3FF)))) << 10) |
 		            ((SIMD::UInt(Round(Min(Max(texel.Float(2), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(0x3FF)))) << 20) |
 		            ((SIMD::UInt(Round(Min(Max(texel.Float(3), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(0x3)))) << 30);
 		break;
 	case VK_FORMAT_R16G16_UNORM:
-		texelSize = 4;
 		packed[0] = SIMD::UInt(Round(Min(Max(texel.Float(0), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(0xFFFF))) |
 		            (SIMD::UInt(Round(Min(Max(texel.Float(1), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(0xFFFF))) << 16);
 		break;
 	case VK_FORMAT_R8G8_UNORM:
-		texelSize = 2;
 		packed[0] = SIMD::UInt(Round(Min(Max(texel.Float(0), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(0xFF))) |
 		            (SIMD::UInt(Round(Min(Max(texel.Float(1), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(0xFF))) << 8);
 		break;
 	case VK_FORMAT_R16_UNORM:
-		texelSize = 2;
 		packed[0] = SIMD::UInt(Round(Min(Max(texel.Float(0), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(0xFFFF)));
 		break;
 	case VK_FORMAT_R8_UNORM:
-		texelSize = 1;
 		packed[0] = SIMD::UInt(Round(Min(Max(texel.Float(0), SIMD::Float(0.0f)), SIMD::Float(1.0f)) * SIMD::Float(0xFF)));
 		break;
 	case VK_FORMAT_R16G16B16A16_SNORM:
-		texelSize = 8;
 		packed[0] = (SIMD::Int(Round(Min(Max(texel.Float(0), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(0x7FFF))) & SIMD::Int(0xFFFF)) |
 		            (SIMD::Int(Round(Min(Max(texel.Float(1), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(0x7FFF))) << 16);
 		packed[1] = (SIMD::Int(Round(Min(Max(texel.Float(2), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(0x7FFF))) & SIMD::Int(0xFFFF)) |
 		            (SIMD::Int(Round(Min(Max(texel.Float(3), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(0x7FFF))) << 16);
 		break;
 	case VK_FORMAT_R16G16_SNORM:
-		texelSize = 4;
 		packed[0] = (SIMD::Int(Round(Min(Max(texel.Float(0), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(0x7FFF))) & SIMD::Int(0xFFFF)) |
 		            (SIMD::Int(Round(Min(Max(texel.Float(1), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(0x7FFF))) << 16);
 		break;
 	case VK_FORMAT_R8G8_SNORM:
-		texelSize = 2;
 		packed[0] = (SIMD::Int(Round(Min(Max(texel.Float(0), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(0x7F))) & SIMD::Int(0xFF)) |
 		            (SIMD::Int(Round(Min(Max(texel.Float(1), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(0x7F))) << 8);
 		break;
 	case VK_FORMAT_R16_SNORM:
-		texelSize = 2;
 		packed[0] = SIMD::Int(Round(Min(Max(texel.Float(0), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(0x7FFF)));
 		break;
 	case VK_FORMAT_R8_SNORM:
-		texelSize = 1;
 		packed[0] = SIMD::Int(Round(Min(Max(texel.Float(0), SIMD::Float(-1.0f)), SIMD::Float(1.0f)) * SIMD::Float(0x7F)));
 		break;
 	case VK_FORMAT_R8G8_SINT:
 	case VK_FORMAT_R8G8_UINT:
-		texelSize = 2;
 		packed[0] = SIMD::UInt(texel.UInt(0) & SIMD::UInt(0xFF)) | (SIMD::UInt(texel.UInt(1) & SIMD::UInt(0xFF)) << 8);
 		break;
 	case VK_FORMAT_R16_SINT:
 	case VK_FORMAT_R16_UINT:
-		texelSize = 2;
 		packed[0] = SIMD::UInt(texel.UInt(0) & SIMD::UInt(0xFFFF));
 		break;
 	case VK_FORMAT_R8_SINT:
 	case VK_FORMAT_R8_UINT:
-		texelSize = 1;
 		packed[0] = SIMD::UInt(texel.UInt(0) & SIMD::UInt(0xFF));
 		break;
 	case VK_FORMAT_A2B10G10R10_UINT_PACK32:
-		texelSize = 4;
 		packed[0] = (SIMD::UInt(texel.UInt(0) & SIMD::UInt(0x3FF))) |
 		            (SIMD::UInt(texel.UInt(1) & SIMD::UInt(0x3FF)) << 10) |
 		            (SIMD::UInt(texel.UInt(2) & SIMD::UInt(0x3FF)) << 20) |
 		            (SIMD::UInt(texel.UInt(3) & SIMD::UInt(0x3)) << 30);
 		break;
 	default:
-		UNSUPPORTED("VkFormat %d", int(format));
+		UNSUPPORTED("VkFormat %d", int(imageFormat));
 		break;
 	}
 
@@ -1298,6 +1285,7 @@
 	//  validation. If the texel fails integer texel coordinate validation, then the write has no effect."
 	// - https://www.khronos.org/registry/vulkan/specs/1.2/html/chap16.html#textures-output-coordinate-validation
 	auto robustness = OutOfBoundsBehavior::Nullify;
+	const int texelSize = imageFormat.bytes();
 
 	auto texelPtr = GetTexelAddress(state, imageBase, imageSizeInBytes, coordinate, imageType, descriptor, texelSize, instruction.sampleId, false, robustness);
 
@@ -1354,9 +1342,6 @@
 	ASSERT(imageType.opcode() == spv::OpTypeImage);
 	ASSERT(resultType.storageClass == spv::StorageClassImage);
 
-	ASSERT(getType(resultType.element).opcode() == spv::OpTypeInt);
-	const int texelSize = sizeof(uint32_t);
-
 	auto coordinate = Operand(this, state, instruction.coordinateId);
 
 	Pointer<Byte> descriptor = state->getPointer(instruction.imageId).base;  // vk::StorageImageDescriptor*
@@ -1366,6 +1351,7 @@
 	// VK_EXT_image_robustness requires checking for out-of-bounds accesses.
 	// TODO(b/162327166): Only perform bounds checks when VK_EXT_image_robustness is enabled.
 	auto robustness = OutOfBoundsBehavior::Nullify;
+	const int texelSize = SpirvFormatToVulkanFormat(static_cast<spv::ImageFormat>(instruction.imageFormat)).bytes();
 
 	auto ptr = GetTexelAddress(state, imageBase, imageSizeInBytes, coordinate, imageType, descriptor, texelSize, instruction.sampleId, false, robustness);