SpirvShader: Implement descriptor set arrays

Introduce a new Object::Kind - DescriptorSet.
This represents a pointer to a vk::DescriptorSet*, which is now
dereferenced to access the buffer data in OpAccessChain or OpLoad / OpStore.
This shuffling is required to handle descriptor array access
as the array index is only known at OpAccessChain time.

Tests: *dynamic*
Bug: b/126330097
Change-Id: Id754d966d8945f4e4fcf7895ed2210ce4f6ba713
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/28391
Tested-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 4db352a..e6662e5 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -203,6 +203,9 @@
 
 				case spv::StorageClassUniform:
 				case spv::StorageClassStorageBuffer:
+					object.kind = Object::Kind::DescriptorSet;
+					break;
+
 				case spv::StorageClassPushConstant:
 				case spv::StorageClassPrivate:
 				case spv::StorageClassFunction:
@@ -818,6 +821,33 @@
 			case Object::Kind::DivergentPointer:
 				return std::make_pair(routine->getPointer(id), routine->getIntermediate(id).Int(0));
 
+			case Object::Kind::DescriptorSet:
+			{
+				Decorations d = {};
+				ApplyDecorationsForId(&d, id);
+
+				ASSERT(d.DescriptorSet >= 0);
+				ASSERT(d.Binding >= 0);
+
+				auto set = routine->getPointer(id);
+				auto setLayout = routine->pipelineLayout->getDescriptorSetLayout(d.DescriptorSet);
+				size_t bindingOffset = setLayout->getBindingOffset(d.Binding, arrayIndex);
+
+				Pointer<Byte> bufferInfo = Pointer<Byte>(set + bindingOffset); // VkDescriptorBufferInfo*
+				Pointer<Byte> buffer = *Pointer<Pointer<Byte>>(bufferInfo + OFFSET(VkDescriptorBufferInfo, buffer)); // vk::Buffer*
+				Pointer<Byte> data = *Pointer<Pointer<Byte>>(buffer + vk::Buffer::DataOffset); // void*
+				Int offset = *Pointer<Int>(bufferInfo + OFFSET(VkDescriptorBufferInfo, offset));
+				if (setLayout->isBindingDynamic(d.Binding))
+				{
+					uint32_t dynamicBindingIndex =
+						routine->pipelineLayout->getDynamicOffsetBase(d.DescriptorSet) +
+						setLayout->getDynamicDescriptorOffset(d.Binding) +
+						arrayIndex;
+					offset += routine->descriptorDynamicOffsets[dynamicBindingIndex];
+				}
+				return std::make_pair(data + offset, SIMD::Int(0));
+			}
+
 			default:
 				UNREACHABLE("Invalid pointer kind %d", int(object.kind));
 				return std::make_pair(Pointer<Byte>(), SIMD::Int(0));
@@ -833,9 +863,24 @@
 		Decorations d = {};
 		ApplyDecorationsForId(&d, baseObject.type);
 
+		size_t arrayIndex = 0;
+		if (baseObject.kind == Object::Kind::DescriptorSet)
+		{
+			auto type = getType(typeId).definition.opcode();
+			if (type == spv::OpTypeArray || type == spv::OpTypeRuntimeArray)
+			{
+				ASSERT(getObject(indexIds[0]).kind == Object::Kind::Constant);
+				arrayIndex = GetConstantInt(indexIds[0]);
+
+				numIndexes--;
+				indexIds++;
+				typeId = getType(typeId).element;
+			}
+		}
+
 		SIMD::Int dynamicOffset;
 		Pointer<Byte> pointerBase;
-		std::tie(pointerBase, dynamicOffset) = GetPointerToData(id, 0, routine);
+		std::tie(pointerBase, dynamicOffset) = GetPointerToData(id, arrayIndex, routine);
 
 		int constantOffset = 0;
 
@@ -1735,27 +1780,7 @@
 			Decorations d{};
 			ApplyDecorationsForId(&d, resultId);
 			ASSERT(d.DescriptorSet >= 0);
-			ASSERT(d.Binding >= 0);
-
-			auto set = routine->descriptorSets[d.DescriptorSet]; // DescriptorSet*
-			auto setLayout = routine->pipelineLayout->getDescriptorSetLayout(d.DescriptorSet);
-			size_t arrayIndex = 0; // TODO: descriptor arrays
-			size_t bindingOffset = setLayout->getBindingOffset(d.Binding, arrayIndex);
-
-			Pointer<Byte> bufferInfo = Pointer<Byte>(set + bindingOffset); // VkDescriptorBufferInfo*
-			Pointer<Byte> buffer = *Pointer<Pointer<Byte>>(bufferInfo + OFFSET(VkDescriptorBufferInfo, buffer)); // vk::Buffer*
-			Pointer<Byte> data = *Pointer<Pointer<Byte>>(buffer + vk::Buffer::DataOffset); // void*
-			Int offset = *Pointer<Int>(bufferInfo + OFFSET(VkDescriptorBufferInfo, offset));
-			if (setLayout->isBindingDynamic(d.Binding))
-			{
-				uint32_t dynamicBindingIndex =
-					routine->pipelineLayout->getDynamicOffsetBase(d.DescriptorSet) +
-					setLayout->getDynamicDescriptorOffset(d.Binding) +
-					arrayIndex;
-				offset += routine->descriptorDynamicOffsets[dynamicBindingIndex];
-			}
-
-			routine->createPointer(resultId, data + offset);
+			routine->createPointer(resultId, routine->descriptorSets[d.DescriptorSet]);
 			break;
 		}
 		case spv::StorageClassPushConstant:
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 6b91160..7831401 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -263,6 +263,10 @@
 				// Pointer held by SpirvRoutine::pointers
 				NonDivergentPointer,
 
+				// A pointer to a vk::DescriptorSet*.
+				// Pointer held by SpirvRoutine::pointers.
+				DescriptorSet,
+
 			} kind = Kind::Unknown;
 		};
 
@@ -560,6 +564,7 @@
 
 		// Returns a base pointer and per-lane offset to the underlying data for
 		// the given pointer object. Handles objects of the following kinds:
+		//  • DescriptorSet
 		//  • DivergentPointer
 		//  • InterfaceVariable
 		//  • NonDivergentPointer