Add support for OpImageTexelPointer

Bug: b/130768731
Test: dEQP-VK.image.*
Change-Id: Ifdac16163b729a552df5369fdf089cae3dee8ecf
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/29452
Tested-by: Chris Forbes <chrisforbes@google.com>
Presubmit-Ready: Chris Forbes <chrisforbes@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-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 50de374..53974fe 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -682,6 +682,7 @@
 			case spv::OpImageSampleImplicitLod:
 			case spv::OpImageQuerySize:
 			case spv::OpImageRead:
+			case spv::OpImageTexelPointer:
 				// Instructions that yield an intermediate value or divergent pointer
 				DefineResult(insn);
 				break;
@@ -2147,6 +2148,9 @@
 		case spv::OpImageWrite:
 			return EmitImageWrite(insn, state);
 
+		case spv::OpImageTexelPointer:
+			return EmitImageTexelPointer(insn, state);
+
 		default:
 			UNIMPLEMENTED("opcode: %s", OpcodeName(opcode).c_str());
 			break;
@@ -4664,6 +4668,40 @@
 		return EmitResult::Continue;
 	}
 
+	SpirvShader::EmitResult SpirvShader::EmitImageTexelPointer(InsnIterator insn, EmitState *state) const
+	{
+		auto &resultType = getType(Type::ID(insn.word(1)));
+		auto imageId = Object::ID(insn.word(3));
+		auto &image = getObject(imageId);
+		// Note: OpImageTexelPointer is unusual in that the image is passed by pointer.
+		// Look through to get the actual image type.
+		auto &imageType = getType(getType(image.type).element);
+		Object::ID resultId = insn.word(2);
+
+		ASSERT(imageType.opcode() == spv::OpTypeImage);
+		ASSERT(resultType.storageClass == spv::StorageClassImage);
+		ASSERT(getType(resultType.element).opcode() == spv::OpTypeInt);
+
+		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 coordinate = GenericValue(this, state->routine, insn.word(4));
+
+		Pointer<Byte> set = state->routine->descriptorSets[d.DescriptorSet];  // DescriptorSet*
+		Pointer<Byte> binding = Pointer<Byte>(set + bindingOffset);
+		Pointer<Byte> imageBase = *Pointer<Pointer<Byte>>(binding + OFFSET(vk::StorageImageDescriptor, ptr));
+
+		state->routine->createPointer(resultId, imageBase);
+
+		SIMD::Int texelOffset = GetTexelOffset(coordinate, imageType, binding, sizeof(uint32_t));
+		auto &dst = state->routine->createIntermediate(resultId, resultType.sizeInComponents);
+		dst.move(0, texelOffset);
+
+		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 f8862b6..76474b4 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -728,6 +728,7 @@
 		EmitResult EmitImageQuerySize(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitImageRead(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitImageWrite(InsnIterator insn, EmitState *state) const;
+		EmitResult EmitImageTexelPointer(InsnIterator insn, EmitState *state) const;
 
 		SIMD::Int GetTexelOffset(GenericValue const & coordinate, Type const & imageType, Pointer<Byte> descriptor, int texelSize) const;