Implement OpImageQuerySize

Tests: dEQP-VK.image.image_size.*
Bug: b/129523279
Change-Id: I6ec474bb5fcdab8853ac4afaa2967bbc4023c970
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/29268
Tested-by: Chris Forbes <chrisforbes@google.com>
Presubmit-Ready: Chris Forbes <chrisforbes@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 0e272cc..188b647 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -680,6 +680,7 @@
 			case spv::OpAtomicLoad:
 			case spv::OpPhi:
 			case spv::OpImageSampleImplicitLod:
+			case spv::OpImageQuerySize:
 				// Instructions that yield an intermediate value or divergent pointer
 				DefineResult(insn);
 				break;
@@ -2134,7 +2135,10 @@
 
 		case spv::OpImageSampleImplicitLod:
 			return EmitImageSampleImplicitLod(insn, state);
-			
+
+		case spv::OpImageQuerySize:
+			return EmitImageQuerySize(insn, state);
+
 		default:
 			UNIMPLEMENTED("opcode: %s", OpcodeName(opcode).c_str());
 			break;
@@ -4350,6 +4354,56 @@
 		return EmitResult::Continue;
 	}
 
+	SpirvShader::EmitResult SpirvShader::EmitImageQuerySize(InsnIterator insn, EmitState *state) const
+	{
+		auto &resultType = getType(Type::ID(insn.word(1)));
+		auto imageId = Object::ID(insn.word(3));
+		auto &image = getObject(imageId);
+		auto &imageType = getType(image.type);
+		Object::ID resultId = insn.word(2);
+
+		ASSERT(imageType.definition.opcode() == spv::OpTypeImage);
+		bool isArrayed = imageType.definition.word(5) != 0;
+		bool isCubeMap = imageType.definition.word(3) == spv::DimCube;
+
+		const DescriptorDecorations &d = descriptorDecorations.at(imageId);
+		uint32_t arrayIndex = 0;  // TODO(b/129523279)
+		auto setLayout = state->routine->pipelineLayout->getDescriptorSetLayout(d.DescriptorSet);
+		size_t bindingOffset = setLayout->getBindingOffset(d.Binding, arrayIndex);
+		auto &bindingLayout = setLayout->getBindingLayout(d.Binding);
+
+		Pointer<Byte> set = state->routine->descriptorSets[d.DescriptorSet];  // DescriptorSet*
+		Pointer<Byte> binding = Pointer<Byte>(set + bindingOffset);
+
+		auto &dst = state->routine->createIntermediate(resultId, resultType.sizeInComponents);
+
+		switch (bindingLayout.descriptorType)
+		{
+		case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
+		case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
+		{
+			Pointer<Byte> desc = binding; // StorageImageDescriptor*
+			Pointer<Int> extent = desc + OFFSET(vk::StorageImageDescriptor, extent); // int[3]*
+			auto dimensions = resultType.sizeInComponents - (isArrayed ? 1 : 0);
+			for (uint32_t i = 0; i < dimensions; i++)
+			{
+				dst.move(i, SIMD::Int(extent[i]));
+			}
+			if (isArrayed)
+			{
+				auto arrayLayers = *Pointer<Int>(desc + OFFSET(vk::StorageImageDescriptor, arrayLayers)); // uint32_t
+				auto numElements = isCubeMap ? arrayLayers / 6 : arrayLayers;
+				dst.move(dimensions, SIMD::Int(numElements));
+			}
+			break;
+		}
+		default:
+			UNIMPLEMENTED("EmitImageQuerySize image descriptorType: %d", int(bindingLayout.descriptorType));
+		}
+
+		return EmitResult::Continue;
+	}
+
 	void SpirvShader::emitEpilog(SpirvRoutine *routine) const
 	{
 		for (auto insn : *this)
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 16a97cc..48cb060 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -460,7 +460,7 @@
 			int32_t DescriptorSet = -1;
 			int32_t Binding = -1;
 
-			void Apply(DescriptorDecorations const &src); 
+			void Apply(DescriptorDecorations const &src);
 		};
 
 		std::unordered_map<Object::ID, DescriptorDecorations> descriptorDecorations;
@@ -723,6 +723,7 @@
 		EmitResult EmitKill(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitPhi(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitImageSampleImplicitLod(InsnIterator insn, EmitState *state) const;
+		EmitResult EmitImageQuerySize(InsnIterator insn, EmitState *state) const;
 
 		// OpcodeName() returns the name of the opcode op.
 		// If NDEBUG is defined, then OpcodeName() will only return the numerical code.