Implement ConstOffset image operand

Bug: b/129523279
Test: dEQP-VK.glsl.texture_functions.textureoffset.sampler2d_fixed_fragment
Change-Id: Ifc91f69e9eec8bde6b1e0cfaaed4d39dec672d06
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/30192
Presubmit-Ready: Nicolas Capens <nicolascapens@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
diff --git a/src/Pipeline/SamplerCore.hpp b/src/Pipeline/SamplerCore.hpp
index 030cb97..03817fe 100644
--- a/src/Pipeline/SamplerCore.hpp
+++ b/src/Pipeline/SamplerCore.hpp
@@ -41,7 +41,8 @@
 	enum SamplerOption
 	{
 		None,
-		Offset   // Offset sample location by provided integer coordinates.
+		Offset,   // Offset sample location by provided integer coordinates.
+		SAMPLER_OPTION_LAST = Offset,
 	};
 
 	// TODO(b/129523279): Eliminate and use SpirvShader::ImageInstruction instead.
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 41d77b6..2c0a1ef 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -4500,6 +4500,7 @@
 		Object::ID gradDxId = 0;
 		Object::ID gradDyId = 0;
 		bool constOffset = false;
+		Object::ID offsetId = 0;
 		bool sample = false;
 
 		if(insn.wordCount() > 5)
@@ -4534,8 +4535,9 @@
 
 			if(imageOperands & spv::ImageOperandsConstOffsetMask)
 			{
-				UNIMPLEMENTED("Image operand %x", spv::ImageOperandsConstOffsetMask); (void)constOffset;
 				constOffset = true;
+				offsetId = insn.word(operand);
+				operand++;
 				imageOperands &= ~spv::ImageOperandsConstOffsetMask;
 			}
 
@@ -4575,16 +4577,28 @@
 
 			instruction.gradComponents = dxyType.sizeInComponents;
 
-			for(uint32_t j = 0; j < dxyType.sizeInComponents; j++)
+			for(uint32_t j = 0; j < dxyType.sizeInComponents; j++, i++)
 			{
 				in[i] = dxValue.Float(j);
-				i++;
 			}
 
-			for(uint32_t j = 0; j < dxyType.sizeInComponents; j++)
+			for(uint32_t j = 0; j < dxyType.sizeInComponents; j++, i++)
 			{
 				in[i] = dyValue.Float(j);
-				i++;
+			}
+		}
+
+		if(constOffset)
+		{
+			auto offsetValue = GenericValue(this, state->routine, offsetId);
+			auto &offsetType = getType(offsetValue.type);
+
+			instruction.samplerOption = Offset;
+			instruction.offsetComponents = offsetType.sizeInComponents;
+
+			for(uint32_t j = 0; j < offsetType.sizeInComponents; j++, i++)
+			{
+				in[i] = offsetValue.Float(j);  // Integer values, but transfered as float.
 			}
 		}
 
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index c94f61b..0cc6e91 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -481,9 +481,9 @@
 			// Unmarshal from raw 32-bit data
 			ImageInstruction(uint32_t parameters) : parameters(parameters) {}
 
-			SamplerMethod getSamplerMethod() const
+			SamplerFunction getSamplerFunction() const
 			{
-				return static_cast<SamplerMethod>(samplerMethod);
+				return { static_cast<SamplerMethod>(samplerMethod), static_cast<SamplerOption>(samplerOption) };
 			}
 
 			union
@@ -491,8 +491,10 @@
 				struct
 				{
 					uint32_t samplerMethod : BITS(SAMPLER_METHOD_LAST);
-					uint32_t coordinates : 3;     // 1-4
-					uint32_t gradComponents : 2;  // 0-3 (for each of dx / dy)
+					uint32_t samplerOption : BITS(SAMPLER_OPTION_LAST);
+					uint32_t coordinates : 3;       // 1-4
+					uint32_t gradComponents : 2;    // 0-3 (for each of dx / dy)
+					uint32_t offsetComponents : 2;  // 0-3
 				};
 
 				uint32_t parameters = 0;
diff --git a/src/Pipeline/SpirvShaderSampling.cpp b/src/Pipeline/SpirvShaderSampling.cpp
index 3fb8575..68710d4 100644
--- a/src/Pipeline/SpirvShaderSampling.cpp
+++ b/src/Pipeline/SpirvShaderSampling.cpp
@@ -98,12 +98,13 @@
 	SIMD::Float uvw[3];
 	SIMD::Float q(0);     // TODO(b/129523279)
 	SIMD::Float bias(0);  // Bias added to the implicit level-of-detail, or explicit level-of-detail (depending on samplerMethod).
-	Vector4f dsx;         // TODO(b/129523279)
-	Vector4f dsy;         // TODO(b/129523279)
-	Vector4f offset;      // TODO(b/129523279)
-	SamplerFunction samplerFunction = { instruction.getSamplerMethod(), None };  // TODO(b/129523279)
+	Vector4f dsx;
+	Vector4f dsy;
+	Vector4f offset;
+	SamplerFunction samplerFunction = instruction.getSamplerFunction();
 
-	for(uint32_t i = 0; i < instruction.coordinates; i++)
+	uint32_t i = 0;
+	for( ; i < instruction.coordinates; i++)
 	{
 		uvw[i] = in[i];
 	}
@@ -123,14 +124,22 @@
 	}
 	else if(instruction.samplerMethod == Grad)
 	{
-		for(uint32_t i = 0; i < instruction.gradComponents; i++)
+		for(uint32_t j = 0; j < instruction.gradComponents; j++, i++)
 		{
-			dsx[i] = in[instruction.coordinates + i];
+			dsx[j] = in[i];
 		}
 
-		for(uint32_t i = 0; i < instruction.gradComponents; i++)
+		for(uint32_t j = 0; j < instruction.gradComponents; j++, i++)
 		{
-			dsy[i] = in[instruction.coordinates + instruction.gradComponents + i];
+			dsy[j] = in[i];
+		}
+	}
+
+	if(instruction.samplerOption == Offset)
+	{
+		for(uint32_t j = 0; j < instruction.offsetComponents; j++, i++)
+		{
+			offset[j] = in[i];
 		}
 	}