Add support for indexing into arrays of image descriptors

- OpLoad of pointer to descriptor is passthrough; reflect that in Kind.
- Actually pass through all the descriptor types in EmitLoad
- Fix Walk*AccessChain to not create useless divergence; constant offset
  was added in the wrong place.
- Adjust WalkAccessChain to adjust pointers into descriptor arrays using
  the descriptor stride
- Adjust storage image descriptor content to not assume layerPitch ==
slicePitch, since that isn't true.

Bug: b/131082089
Test: dEQP-VK.binding_model.*
Test: dEQP-VK.spirv_assembly.*
Test: dEQP-VK.glsl.*
Change-Id: I6cc4ae7b0fdeb54ede111f532c7e3fd1f108803c
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/29549
Tested-by: Chris Forbes <chrisforbes@google.com>
Presubmit-Ready: Chris Forbes <chrisforbes@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 3d71c51..be8de93 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -1383,15 +1383,34 @@
 			case spv::OpTypeRuntimeArray:
 			{
 				// TODO: b/127950082: Check bounds.
-				auto stride = getType(type.element).sizeInComponents * sizeof(float);
-				auto & obj = getObject(indexIds[i]);
-				if (obj.kind == Object::Kind::Constant)
+				if (getType(baseObject.type).storageClass == spv::StorageClassUniformConstant)
 				{
-					constantOffset += stride * GetConstantInt(indexIds[i]);
+					// indexing into an array of descriptors.
+					auto &obj = getObject(indexIds[i]);
+					if (obj.kind != Object::Kind::Constant)
+					{
+						UNIMPLEMENTED("Nonconstant indexing of descriptor arrays is not supported");
+					}
+
+					auto d = descriptorDecorations.at(baseId);
+					ASSERT(d.DescriptorSet >= 0);
+					ASSERT(d.Binding >= 0);
+					auto setLayout = routine->pipelineLayout->getDescriptorSetLayout(d.DescriptorSet);
+					auto stride = setLayout->getBindingStride(d.Binding);
+					ptr.base += stride * GetConstantInt(indexIds[i]);
 				}
 				else
 				{
-					ptr.addOffset(SIMD::Int(stride) * routine->getIntermediate(indexIds[i]).Int(0));
+					auto stride = getType(type.element).sizeInComponents * sizeof(float);
+					auto & obj = getObject(indexIds[i]);
+					if (obj.kind == Object::Kind::Constant)
+					{
+						constantOffset += stride * GetConstantInt(indexIds[i]);
+					}
+					else
+					{
+						ptr.addOffset(SIMD::Int(stride) * routine->getIntermediate(indexIds[i]).Int(0));
+					}
 				}
 				typeId = type.element;
 				break;
@@ -1404,7 +1423,7 @@
 
 		if (constantOffset != 0)
 		{
-			ptr.addOffset(SIMD::Int(constantOffset));
+			ptr.addOffset(constantOffset);
 		}
 		return ptr;
 	}
@@ -1598,8 +1617,20 @@
 		Object::ID resultId = insn.word(2);
 		auto &object = defs[resultId];
 		object.type = typeId;
-		object.kind = (getType(typeId).opcode() == spv::OpTypePointer)
-			? Object::Kind::DivergentPointer : Object::Kind::Intermediate;
+
+		switch (getType(typeId).opcode())
+		{
+		case spv::OpTypePointer:
+		case spv::OpTypeImage:
+		case spv::OpTypeSampledImage:
+		case spv::OpTypeSampler:
+			object.kind = Object::Kind::DivergentPointer;
+			break;
+
+		default:
+			object.kind = Object::Kind::Intermediate;
+		}
+
 		object.definition = insn;
 	}
 
@@ -2302,7 +2333,7 @@
 		ASSERT(Type::ID(insn.word(1)) == result.type);
 		ASSERT(!atomic || getType(getType(pointer.type).element).opcode() == spv::OpTypeInt);  // Vulkan 1.1: "Atomic instructions must declare a scalar 32-bit integer type, for the value pointed to by Pointer."
 
-		if(pointer.kind == Object::Kind::SampledImage)
+		if(pointerTy.storageClass == spv::StorageClassUniformConstant)
 		{
 			// Just propagate the pointer.
 			// TODO(b/129523279)
@@ -4358,12 +4389,7 @@
 
 		Pointer<Byte> constants;  // FIXME(b/129523279)
 
-		const DescriptorDecorations &d = descriptorDecorations.at(sampledImageId);
-		uint32_t arrayIndex = 0;  // TODO(b/129523279)
-		auto setLayout = state->routine->pipelineLayout->getDescriptorSetLayout(d.DescriptorSet);
-		size_t bindingOffset = setLayout->getBindingOffset(d.Binding, arrayIndex);
-
-		auto descriptor = state->routine->descriptorSets[d.DescriptorSet] + bindingOffset; // vk::SampledImageDescriptor*
+		auto descriptor = sampledImage.base; // vk::SampledImageDescriptor*
 		auto sampler = *Pointer<Pointer<Byte>>(descriptor + OFFSET(vk::SampledImageDescriptor, sampler)); // vk::Sampler*
 		auto imageView = *Pointer<Pointer<Byte>>(descriptor + OFFSET(vk::SampledImageDescriptor, imageView)); // vk::ImageView*
 
@@ -4469,7 +4495,9 @@
 
 		auto coordinate = GenericValue(this, state->routine, insn.word(4));
 
-		Pointer<Byte> binding = state->routine->getPointer(imageId).base;
+		auto pointer = state->routine->getPointer(imageId);
+		ASSERT(pointer.uniform);
+		Pointer<Byte> binding = pointer.base;
 		Pointer<Byte> imageBase = *Pointer<Pointer<Byte>>(binding + OFFSET(vk::StorageImageDescriptor, ptr));
 
 		auto &dst = state->routine->createIntermediate(resultId, resultType.sizeInComponents);
diff --git a/src/Vulkan/VkDescriptorSetLayout.cpp b/src/Vulkan/VkDescriptorSetLayout.cpp
index ded7cd0..3367b8d 100644
--- a/src/Vulkan/VkDescriptorSetLayout.cpp
+++ b/src/Vulkan/VkDescriptorSetLayout.cpp
@@ -176,6 +176,12 @@
 	return bindingCount;
 }
 
+size_t DescriptorSetLayout::getBindingStride(uint32_t binding) const
+{
+	uint32_t index = getBindingIndex(binding);
+	return GetDescriptorSize(bindings[index].descriptorType);
+}
+
 size_t DescriptorSetLayout::getBindingOffset(uint32_t binding, size_t arrayElement) const
 {
 	uint32_t index = getBindingIndex(binding);
@@ -442,7 +448,9 @@
 			descriptor[i].ptr = imageView->getOffsetPointer({0, 0, 0}, VK_IMAGE_ASPECT_COLOR_BIT);
 			descriptor[i].extent = imageView->getMipLevelExtent(0);
 			descriptor[i].rowPitchBytes = imageView->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
-			descriptor[i].slicePitchBytes = imageView->slicePitchBytes(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].arrayLayers = imageView->getSubresourceRange().layerCount;
 		}
 	}
diff --git a/src/Vulkan/VkDescriptorSetLayout.hpp b/src/Vulkan/VkDescriptorSetLayout.hpp
index 621e13f..05e074a 100644
--- a/src/Vulkan/VkDescriptorSetLayout.hpp
+++ b/src/Vulkan/VkDescriptorSetLayout.hpp
@@ -70,6 +70,9 @@
 	// the given binding and array element within that binding.
 	size_t getBindingOffset(uint32_t binding, size_t arrayElement) const;
 
+	// Returns the stride of an array of descriptors
+	size_t getBindingStride(uint32_t binding) const;
+
 	// Returns the number of descriptors across all bindings that are dynamic
 	// (see isBindingDynamic).
 	uint32_t getDynamicDescriptorCount() const;
diff --git a/src/Vulkan/VkImage.hpp b/src/Vulkan/VkImage.hpp
index 0ad40a5..0ac2c9a 100644
--- a/src/Vulkan/VkImage.hpp
+++ b/src/Vulkan/VkImage.hpp
@@ -67,6 +67,7 @@
 	void*                    getTexelPointer(const VkOffset3D& offset, const VkImageSubresourceLayers& subresource) const;
 	bool                     isCube() const;
 	uint8_t*                 end() const;
+	VkDeviceSize             getLayerSize(VkImageAspectFlagBits aspect) const;
 
 	static Format            GetFormat(const vk::Format& format, VkImageAspectFlagBits aspect);
 
@@ -75,7 +76,6 @@
 	VkDeviceSize getStorageSize(VkImageAspectFlags flags) const;
 	VkDeviceSize getMipLevelSize(VkImageAspectFlagBits aspect, uint32_t mipLevel) const;
 	VkDeviceSize getMultiSampledLevelSize(VkImageAspectFlagBits aspect, uint32_t mipLevel) const;
-	VkDeviceSize getLayerSize(VkImageAspectFlagBits aspect) const;
 	VkDeviceSize getMemoryOffset(VkImageAspectFlagBits aspect, uint32_t mipLevel) const;
 	VkDeviceSize getMemoryOffset(VkImageAspectFlagBits aspect, uint32_t mipLevel, uint32_t layer) const;
 	VkDeviceSize texelOffsetBytesInStorage(const VkOffset3D& offset, const VkImageSubresourceLayers& subresource) const;
diff --git a/src/Vulkan/VkImageView.hpp b/src/Vulkan/VkImageView.hpp
index 4e54f8a..70311a5 100644
--- a/src/Vulkan/VkImageView.hpp
+++ b/src/Vulkan/VkImageView.hpp
@@ -41,6 +41,7 @@
 	int getSampleCount() const { return image->getSampleCountFlagBits(); }
 	int rowPitchBytes(VkImageAspectFlagBits aspect, uint32_t mipLevel) const { return image->rowPitchBytes(aspect, subresourceRange.baseMipLevel + mipLevel); }
 	int slicePitchBytes(VkImageAspectFlagBits aspect, uint32_t mipLevel) const { return image->slicePitchBytes(aspect, subresourceRange.baseMipLevel + mipLevel); }
+	int layerPitchBytes(VkImageAspectFlagBits aspect) const { return static_cast<int>(image->getLayerSize(aspect)); }
 	VkExtent3D getMipLevelExtent(uint32_t mipLevel) const { return image->getMipLevelExtent(subresourceRange.baseMipLevel + mipLevel); }
 
 	void *getOffsetPointer(const VkOffset3D& offset, VkImageAspectFlagBits aspect) const;