Fix image sampling with divergent LOD

Currently our SamplerCore code performs sampling for four SIMD lanes
simultaneously. With implicit LOD calculation for fragment shaders, all
four pixels in a quad share the same LOD and thus sample from the same
mipmap level. But for the vertex shader the LOD is always explicitly
provided, and can vary significantly between completely unrelated
vertices. Previously we only used the LOD of the first one in each group
of four.

As a workaround, process explicit-lod sampling instructions in a
lane-by-lane manner.

Bug: b/133868964
Tests: dEQP-VK.glsl.texture_functions.*
Change-Id: If4e0d3c04d29529300111d73801124080cb4b544
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/32488
Presubmit-Ready: Nicolas Capens <nicolascapens@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
diff --git a/src/Pipeline/SpirvShaderSampling.cpp b/src/Pipeline/SpirvShaderSampling.cpp
index 72f3d77..c80228b 100644
--- a/src/Pipeline/SpirvShaderSampling.cpp
+++ b/src/Pipeline/SpirvShaderSampling.cpp
@@ -145,7 +145,7 @@
 			i++;
 		}
 
-		// TODO(b/129523279): Currently 1D textures are treated as 2D by setting the second coordinate to 0.
+		// TODO(b/134669567): Currently 1D textures are treated as 2D by setting the second coordinate to 0.
 		// Implement optimized 1D sampling.
 		if(samplerState.textureType == TEXTURE_1D)
 		{
@@ -184,13 +184,52 @@
 		}
 
 		SamplerCore s(constants, samplerState);
-		Vector4f sample = s.sampleTexture(texture, sampler, uvw[0], uvw[1], uvw[2], q, lodOrBias, dsx, dsy, offset, samplerFunction);
 
-		Pointer<SIMD::Float> rgba = out;
-		rgba[0] = sample.x;
-		rgba[1] = sample.y;
-		rgba[2] = sample.z;
-		rgba[3] = sample.w;
+		// For explicit-lod instructions the LOD can be different per SIMD lane. SamplerCore currently assumes
+		// a single LOD per four elements, so we sample the image again for each LOD separately.
+		if(samplerFunction.method == Lod || samplerFunction.method == Grad)  // TODO(b/133868964): Also handle divergent Bias and Fetch with Lod.
+		{
+			auto lod = Pointer<Float>(&lodOrBias);
+
+			For(Int i = 0, i < SIMD::Width, i++)
+			{
+				SIMD::Float dPdx;
+				SIMD::Float dPdy;
+
+				dPdx.x = Pointer<Float>(&dsx.x)[i];
+				dPdx.y = Pointer<Float>(&dsx.y)[i];
+				dPdx.z = Pointer<Float>(&dsx.z)[i];
+
+				dPdy.x = Pointer<Float>(&dsy.x)[i];
+				dPdy.y = Pointer<Float>(&dsy.y)[i];
+				dPdy.z = Pointer<Float>(&dsy.z)[i];
+
+				// 1D textures are treated as 2D texture with second coordinate 0, so we also need to zero out the second grad component. TODO(b/134669567)
+				if(samplerState.textureType == TEXTURE_1D || samplerState.textureType == TEXTURE_1D_ARRAY)
+				{
+					dPdx.y = Float(0.0f);
+					dPdy.y = Float(0.0f);
+				}
+
+				Vector4f sample = s.sampleTexture(texture, sampler, uvw[0], uvw[1], uvw[2], q, lod[i], dPdx, dPdy, offset, samplerFunction);
+
+				Pointer<Float> rgba = out;
+				rgba[0 * SIMD::Width + i] = Pointer<Float>(&sample.x)[i];
+				rgba[1 * SIMD::Width + i] = Pointer<Float>(&sample.y)[i];
+				rgba[2 * SIMD::Width + i] = Pointer<Float>(&sample.z)[i];
+				rgba[3 * SIMD::Width + i] = Pointer<Float>(&sample.w)[i];
+			}
+		}
+		else
+		{
+			Vector4f sample = s.sampleTexture(texture, sampler, uvw[0], uvw[1], uvw[2], q, lodOrBias.x, (dsx.x), (dsy.x), offset, samplerFunction);
+
+			Pointer<SIMD::Float> rgba = out;
+			rgba[0] = sample.x;
+			rgba[1] = sample.y;
+			rgba[2] = sample.z;
+			rgba[3] = sample.w;
+		}
 	}
 
 	return (ImageSampler*)function("sampler")->getEntry();
@@ -292,7 +331,7 @@
 		}
 		break;
 
-	case VK_IMAGE_VIEW_TYPE_1D:  // Treated as 2D texture with second coordinate 0.
+	case VK_IMAGE_VIEW_TYPE_1D:  // Treated as 2D texture with second coordinate 0. TODO(b/134669567)
 		if(coordinateIndex == 1)
 		{
 			return ADDRESSING_WRAP;
@@ -310,7 +349,7 @@
 		}
 		break;
 
-	case VK_IMAGE_VIEW_TYPE_1D_ARRAY:  // Treated as 2D texture with second coordinate 0.
+	case VK_IMAGE_VIEW_TYPE_1D_ARRAY:  // Treated as 2D texture with second coordinate 0. TODO(b/134669567)
 		if(coordinateIndex == 1)
 		{
 			return ADDRESSING_WRAP;