Implement Bias image operand

Also fix Lod + Offset combination, and rename the parameter that holds
either lod or bias to lodOrBias.

Bug: b/129523279
Test: dEQP-VK.glsl.texture_functions.*
Change-Id: I397959421651e990d041f4297c4bcf55e1c00f03
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/30229
Reviewed-by: Chris Forbes <chrisforbes@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Pipeline/SamplerCore.cpp b/src/Pipeline/SamplerCore.cpp
index e1c6485..d60baa8 100644
--- a/src/Pipeline/SamplerCore.cpp
+++ b/src/Pipeline/SamplerCore.cpp
@@ -50,7 +50,7 @@
 	{
 	}
 
-	Vector4f SamplerCore::sampleTexture(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, Float4 &q, Float4 &bias, Vector4f &dsx, Vector4f &dsy, Vector4f &offset, SamplerFunction function)
+	Vector4f SamplerCore::sampleTexture(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, Float4 &q, Float4 &lodOrBias, Vector4f &dsx, Vector4f &dsy, Vector4f &offset, SamplerFunction function)
 	{
 		Vector4f c;
 
@@ -78,18 +78,18 @@
 		{
 			if(state.textureType != TEXTURE_CUBE)
 			{
-				computeLod(texture, lod, anisotropy, uDelta, vDelta, uuuu, vvvv, bias.x, dsx, dsy, function);
+				computeLod(texture, lod, anisotropy, uDelta, vDelta, uuuu, vvvv, lodOrBias.x, dsx, dsy, function);
 			}
 			else
 			{
 				Float4 M;
 				cubeFace(face, uuuu, vvvv, u, v, w, M);
-				computeLodCube(texture, lod, u, v, w, bias.x, dsx, dsy, M, function);
+				computeLodCube(texture, lod, u, v, w, lodOrBias.x, dsx, dsy, M, function);
 			}
 		}
 		else
 		{
-			computeLod3D(texture, lod, uuuu, vvvv, wwww, bias.x, dsx, dsy, function);
+			computeLod3D(texture, lod, uuuu, vvvv, wwww, lodOrBias.x, dsx, dsy, function);
 		}
 
 		// FIXME: YUV is not supported by the floating point path
@@ -1137,7 +1137,7 @@
 		return lod;
 	}
 
-	void SamplerCore::computeLod(Pointer<Byte> &texture, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, Float4 &uuuu, Float4 &vvvv, const Float &lodBias, Vector4f &dsx, Vector4f &dsy, SamplerFunction function)
+	void SamplerCore::computeLod(Pointer<Byte> &texture, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, Float4 &uuuu, Float4 &vvvv, const Float &lodOrBias, Vector4f &dsx, Vector4f &dsy, SamplerFunction function)
 	{
 		if(function != Lod && function != Fetch)
 		{
@@ -1186,17 +1186,17 @@
 
 			if(function == Bias)
 			{
-				lod += lodBias;
+				lod += lodOrBias;
 			}
 		}
 		else if(function == Lod)
 		{
-			lod = lodBias;
+			lod = lodOrBias;
 		}
 		else if(function == Fetch)
 		{
 			// TODO: Eliminate int-float-int conversion.
-			lod = Float(As<Int>(lodBias));
+			lod = Float(As<Int>(lodOrBias));
 		}
 		else if(function == Base)
 		{
@@ -1208,7 +1208,7 @@
 		lod = Min(lod, *Pointer<Float>(texture + OFFSET(Texture, maxLod)));
 	}
 
-	void SamplerCore::computeLodCube(Pointer<Byte> &texture, Float &lod, Float4 &u, Float4 &v, Float4 &w, const Float &lodBias, Vector4f &dsx, Vector4f &dsy, Float4 &M, SamplerFunction function)
+	void SamplerCore::computeLodCube(Pointer<Byte> &texture, Float &lod, Float4 &u, Float4 &v, Float4 &w, const Float &lodOrBias, Vector4f &dsx, Vector4f &dsy, Float4 &M, SamplerFunction function)
 	{
 		if(function != Lod && function != Fetch)
 		{
@@ -1252,17 +1252,17 @@
 
 			if(function == Bias)
 			{
-				lod += lodBias;
+				lod += lodOrBias;
 			}
 		}
 		else if(function == Lod)
 		{
-			lod = lodBias;
+			lod = lodOrBias;
 		}
 		else if(function == Fetch)
 		{
 			// TODO: Eliminate int-float-int conversion.
-			lod = Float(As<Int>(lodBias));
+			lod = Float(As<Int>(lodOrBias));
 		}
 		else if(function == Base)
 		{
@@ -1274,7 +1274,7 @@
 		lod = Min(lod, *Pointer<Float>(texture + OFFSET(Texture, maxLod)));
 	}
 
-	void SamplerCore::computeLod3D(Pointer<Byte> &texture, Float &lod, Float4 &uuuu, Float4 &vvvv, Float4 &wwww, const Float &lodBias, Vector4f &dsx, Vector4f &dsy, SamplerFunction function)
+	void SamplerCore::computeLod3D(Pointer<Byte> &texture, Float &lod, Float4 &uuuu, Float4 &vvvv, Float4 &wwww, const Float &lodOrBias, Vector4f &dsx, Vector4f &dsy, SamplerFunction function)
 	{
 		if(function != Lod && function != Fetch)
 		{
@@ -1311,17 +1311,17 @@
 
 			if(function == Bias)
 			{
-				lod += lodBias;
+				lod += lodOrBias;
 			}
 		}
 		else if(function == Lod)
 		{
-			lod = lodBias;
+			lod = lodOrBias;
 		}
 		else if(function == Fetch)
 		{
 			// TODO: Eliminate int-float-int conversion.
-			lod = Float(As<Int>(lodBias));
+			lod = Float(As<Int>(lodOrBias));
 		}
 		else if(function == Base)
 		{
diff --git a/src/Pipeline/SamplerCore.hpp b/src/Pipeline/SamplerCore.hpp
index 03817fe..9255e60 100644
--- a/src/Pipeline/SamplerCore.hpp
+++ b/src/Pipeline/SamplerCore.hpp
@@ -60,7 +60,7 @@
 	public:
 		SamplerCore(Pointer<Byte> &constants, const Sampler::State &state);
 
-		Vector4f sampleTexture(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, Float4 &q, Float4 &bias, Vector4f &dsx, Vector4f &dsy, Vector4f &offset, SamplerFunction function);
+		Vector4f sampleTexture(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, Float4 &q, Float4 &lodOrBias, Vector4f &dsx, Vector4f &dsy, Vector4f &offset, SamplerFunction function);
 
 	private:
 		void border(Short4 &mask, Float4 &coordinates);
@@ -78,9 +78,9 @@
 		Vector4f sampleFloat3D(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, Vector4f &offset, Float &lod, bool secondLOD, SamplerFunction function);
 		Float log2sqrt(Float lod);
 		Float log2(Float lod);
-		void computeLod(Pointer<Byte> &texture, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, Float4 &u, Float4 &v, const Float &lodBias, Vector4f &dsx, Vector4f &dsy, SamplerFunction function);
-		void computeLodCube(Pointer<Byte> &texture, Float &lod, Float4 &u, Float4 &v, Float4 &w, const Float &lodBias, Vector4f &dsx, Vector4f &dsy, Float4 &M, SamplerFunction function);
-		void computeLod3D(Pointer<Byte> &texture, Float &lod, Float4 &u, Float4 &v, Float4 &w, const Float &lodBias, Vector4f &dsx, Vector4f &dsy, SamplerFunction function);
+		void computeLod(Pointer<Byte> &texture, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, Float4 &u, Float4 &v, const Float &lodOrBias, Vector4f &dsx, Vector4f &dsy, SamplerFunction function);
+		void computeLodCube(Pointer<Byte> &texture, Float &lod, Float4 &u, Float4 &v, Float4 &w, const Float &lodOrBias, Vector4f &dsx, Vector4f &dsy, Float4 &M, SamplerFunction function);
+		void computeLod3D(Pointer<Byte> &texture, Float &lod, Float4 &u, Float4 &v, Float4 &w, const Float &lodOrBias, Vector4f &dsx, Vector4f &dsy, SamplerFunction function);
 		void cubeFace(Int face[4], Float4 &U, Float4 &V, Float4 &x, Float4 &y, Float4 &z, Float4 &M);
 		Short4 applyOffset(Short4 &uvw, Float4 &offset, const Int4 &whd, AddressingMode mode);
 		void computeIndices(UInt index[4], Short4 uuuu, Short4 vvvv, Short4 wwww, Vector4f &offset, const Pointer<Byte> &mipmap, SamplerFunction function);
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 11cf404..b3c7abe 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -4507,6 +4507,7 @@
 	SpirvShader::EmitResult SpirvShader::EmitImageSampleExplicitLod(Variant variant, InsnIterator insn, EmitState *state) const
 	{
 		uint32_t imageOperands = static_cast<spv::ImageOperandsMask>(insn.word(5));
+		imageOperands &= ~spv::ImageOperandsConstOffsetMask;  // Dealt with later.
 
 		if((imageOperands & spv::ImageOperandsLodMask) == imageOperands)
 		{
@@ -4548,9 +4549,8 @@
 		auto imageView = *Pointer<Pointer<Byte>>(imageDescriptor + OFFSET(vk::SampledImageDescriptor, imageView)); // vk::ImageView*
 
 		uint32_t imageOperands = spv::ImageOperandsMaskNone;
-		bool bias = false;
-		bool lod = false;
-		Object::ID lodId = 0;
+		bool lodOrBias = false;
+		Object::ID lodOrBiasId = 0;
 		bool grad = false;
 		Object::ID gradDxId = 0;
 		Object::ID gradDyId = 0;
@@ -4565,22 +4565,26 @@
 
 			if(imageOperands & spv::ImageOperandsBiasMask)
 			{
-				UNIMPLEMENTED("Image operand %x", spv::ImageOperandsBiasMask); (void)bias;
-				bias = true;
+				lodOrBias = true;
+				lodOrBiasId = insn.word(operand);
+				operand++;
 				imageOperands &= ~spv::ImageOperandsBiasMask;
+
+				ASSERT(instruction.samplerMethod == Implicit);
+				instruction.samplerMethod = Bias;
 			}
 
 			if(imageOperands & spv::ImageOperandsLodMask)
 			{
-				lod = true;
-				lodId = insn.word(operand);
+				lodOrBias = true;
+				lodOrBiasId = insn.word(operand);
 				operand++;
 				imageOperands &= ~spv::ImageOperandsLodMask;
 			}
 
 			if(imageOperands & spv::ImageOperandsGradMask)
 			{
-				ASSERT(!lod);  // SPIR-V 1.3: "It is invalid to set both the Lod and Grad bits."
+				ASSERT(!lodOrBias);  // SPIR-V 1.3: "It is invalid to set both the Lod and Grad bits." Bias is for ImplicitLod, Grad for ExplicitLod.
 				grad = true;
 				gradDxId = insn.word(operand + 0);
 				gradDyId = insn.word(operand + 1);
@@ -4632,9 +4636,9 @@
 			UNIMPLEMENTED("OpImageSample*Dref*");  // TODO(b/129523279)
 		}
 
-		if(lod)
+		if(lodOrBias)
 		{
-			auto lodValue = GenericValue(this, state->routine, lodId);
+			auto lodValue = GenericValue(this, state->routine, lodOrBiasId);
 			in[i] = lodValue.Float(0);
 			i++;
 		}
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 99b70e6..cf38289 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -515,7 +515,7 @@
 
 					// Parameters are passed to the sampling routine in this order:
 					uint32_t coordinates : 3;       // 1-4 (does not contain projection component)
-				//	uint32_t lod : 1;               // Indicated by SamplerMethod::Lod
+				//	uint32_t lodOrBias : 1;         // Indicated by SamplerMethod::Lod|Bias
 					uint32_t gradComponents : 2;    // 0-3 (for each of dx / dy)
 					uint32_t offsetComponents : 2;  // 0-3
 				};
diff --git a/src/Pipeline/SpirvShaderSampling.cpp b/src/Pipeline/SpirvShaderSampling.cpp
index 0762ec0..7fcd8a0 100644
--- a/src/Pipeline/SpirvShaderSampling.cpp
+++ b/src/Pipeline/SpirvShaderSampling.cpp
@@ -97,7 +97,7 @@
 	Pointer<Byte> texture = image + OFFSET(vk::SampledImageDescriptor, texture);  // sw::Texture*
 	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).
+	SIMD::Float lodOrBias(0);  // Explicit level-of-detail, or bias added to the implicit level-of-detail (depending on samplerMethod).
 	Vector4f dsx;
 	Vector4f dsy;
 	Vector4f offset;
@@ -117,10 +117,10 @@
 		uvw[1] = SIMD::Float(0);
 	}
 
-	// Lod and Grad are explicit-lod image operands, and always come after the coordinates.
-	if(instruction.samplerMethod == Lod)
+	if(instruction.samplerMethod == Lod || instruction.samplerMethod == Bias)
 	{
-		bias = in[instruction.coordinates];
+		lodOrBias = in[i];
+		i++;
 	}
 	else if(instruction.samplerMethod == Grad)
 	{
@@ -143,7 +143,7 @@
 		}
 	}
 
-	Vector4f sample = s.sampleTexture(texture, uvw[0], uvw[1], uvw[2], q, bias, dsx, dsy, offset, samplerFunction);
+	Vector4f sample = s.sampleTexture(texture, uvw[0], uvw[1], uvw[2], q, lodOrBias, dsx, dsy, offset, samplerFunction);
 
 	Pointer<SIMD::Float> rgba = out;
 	rgba[0] = sample.x;