Add spirv-level support for splitting and combining sampler+image

- All of these objects are single pointers
- When consuming the result of OpSampledImage, look through to find the
  pointer to the sampler descriptor

Remove the SampledImage kind as it's no longer necessary.

Test: dEQP-VK.image.*
Test: dEQP-VK.pipeline.*
Test: dEQP-VK.binding_model.*
Change-Id: I94f4cd8a3e5175c08f263fb464e61e5ba1043833
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/29989
Tested-by: Chris Forbes <chrisforbes@google.com>
Presubmit-Ready: Chris Forbes <chrisforbes@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 2c0a1ef..eb45c6c 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -559,14 +559,8 @@
 				case spv::StorageClassPushConstant:
 				case spv::StorageClassPrivate:
 				case spv::StorageClassFunction:
-					break; // Correctly handled.
-
 				case spv::StorageClassUniformConstant:
-					// This storage class is for data stored within the descriptor itself,
-					// unlike StorageClassUniform which contains handles to buffers.
-					// For Vulkan it corresponds with samplers, images, or combined image samplers.
-					object.kind = Object::Kind::SampledImage;
-					break;
+					break; // Correctly handled.
 
 				case spv::StorageClassWorkgroup:
 				case spv::StorageClassCrossWorkgroup:
@@ -711,6 +705,8 @@
 			case spv::OpLoad:
 			case spv::OpAccessChain:
 			case spv::OpInBoundsAccessChain:
+			case spv::OpSampledImage:
+			case spv::OpImage:
 				{
 					// Propagate the descriptor decorations to the result.
 					Object::ID resultId = insn.word(2);
@@ -2165,6 +2161,7 @@
 		case spv::OpTypeFunction:
 		case spv::OpTypeImage:
 		case spv::OpTypeSampledImage:
+		case spv::OpTypeSampler:
 		case spv::OpExecutionMode:
 		case spv::OpMemoryModel:
 		case spv::OpFunction:
@@ -2405,6 +2402,10 @@
 		case spv::OpImageTexelPointer:
 			return EmitImageTexelPointer(insn, state);
 
+		case spv::OpSampledImage:
+		case spv::OpImage:
+			return EmitSampledImageCombineOrSplit(insn, state);
+
 		default:
 			UNIMPLEMENTED("%s", OpcodeName(opcode).c_str());
 			break;
@@ -2508,10 +2509,8 @@
 		if(pointerTy.storageClass == spv::StorageClassUniformConstant)
 		{
 			// Just propagate the pointer.
-			// TODO(b/129523279)
 			auto &ptr = routine->getPointer(pointerId);
 			routine->createPointer(resultId, ptr);
-
 			return EmitResult::Continue;
 		}
 
@@ -4484,13 +4483,18 @@
 		auto &resultType = getType(resultTypeId);
 
 		auto &result = state->routine->createIntermediate(resultId, resultType.sizeInComponents);
-		auto &sampledImage = state->routine->getPointer(sampledImageId);
+		auto imageDescriptor = state->routine->getPointer(sampledImageId).base; // vk::SampledImageDescriptor*
+
+		// If using a separate sampler, look through the OpSampledImage instruction to find the sampler descriptor
+		auto &sampledImage = getObject(sampledImageId);
+		auto samplerDescriptor = (sampledImage.opcode() == spv::OpSampledImage) ?
+				state->routine->getPointer(sampledImage.definition.word(4)).base : imageDescriptor;
+
 		auto coordinate = GenericValue(this, state->routine, coordinateId);
 		auto &coordinateType = getType(coordinate.type);
 
-		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*
+		auto sampler = *Pointer<Pointer<Byte>>(samplerDescriptor + OFFSET(vk::SampledImageDescriptor, sampler)); // vk::Sampler*
+		auto imageView = *Pointer<Pointer<Byte>>(imageDescriptor + OFFSET(vk::SampledImageDescriptor, imageView)); // vk::ImageView*
 
 		uint32_t imageOperands = spv::ImageOperandsMaskNone;
 		bool bias = false;
@@ -4607,7 +4611,7 @@
 		auto samplerFunc = Call(getImageSampler, instruction.parameters, imageView, sampler);
 
 		Array<SIMD::Float> out(4);
-		Call<ImageSampler>(samplerFunc, sampledImage.base, &in[0], &out[0], state->routine->constants);
+		Call<ImageSampler>(samplerFunc, imageDescriptor, &in[0], &out[0], state->routine->constants);
 
 		for (int i = 0; i < 4; i++) { result.move(i, out[i]); }
 
@@ -5057,6 +5061,19 @@
 		return EmitResult::Continue;
 	}
 
+	SpirvShader::EmitResult SpirvShader::EmitSampledImageCombineOrSplit(InsnIterator insn, EmitState *state) const
+	{
+		// Propagate the image pointer in both cases.
+		// Consumers of OpSampledImage will look through to find the sampler pointer.
+
+		Object::ID resultId = insn.word(2);
+		Object::ID imageId = insn.word(3);
+
+		state->routine->createPointer(resultId, state->routine->getPointer(imageId));
+
+		return EmitResult::Continue;
+	}
+
 	SpirvShader::EmitResult SpirvShader::EmitAtomicOp(InsnIterator insn, EmitState *state) const
 	{
 		auto &resultType = getType(Type::ID(insn.word(1)));
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 0cc6e91..600547a 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -389,10 +389,6 @@
 				// A pointer to a vk::DescriptorSet*.
 				// Pointer held by SpirvRoutine::pointers.
 				DescriptorSet,
-
-				// Pointer to an image/sampler descriptor.
-				// Pointer held by SpirvRoutine::pointers.
-				SampledImage,
 			};
 
 			Kind kind = Kind::Unknown;
@@ -880,6 +876,7 @@
 		EmitResult EmitImageTexelPointer(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitAtomicOp(InsnIterator insn, EmitState *state) const;
 		EmitResult EmitAtomicCompareExchange(InsnIterator insn, EmitState *state) const;
+		EmitResult EmitSampledImageCombineOrSplit(InsnIterator insn, EmitState *state) const;
 
 		SIMD::Pointer GetTexelAddress(SpirvRoutine const * routine, SIMD::Pointer base, GenericValue const & coordinate, Type const & imageType, Pointer<Byte> descriptor, int texelSize) const;