Treat the array coordinate separately

The array layer coordinate isn't subject to addressing modes like the
other coordinates. There's just one selection computation:
 https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/vkspec.html#textures-unnormalized-to-integer
This change eliminates the internally used ADDRESSING_LAYER mode, and
avoids using the 'w' coordinate as the array layer coordinate for 2D
array images (instead exclusively using it for 3D images).

Also, the cube face index is combined with the array layer in case of
cube array images. The division by 6 was eliminated by adjusting the
descriptor 'depth' field instead.

Bug: b/162315264
Change-Id: I4a541697ea265b3ad5b7d7c3a2420c045f46cbbe
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/47388
Presubmit-Ready: Nicolas Capens <nicolascapens@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
diff --git a/src/Device/Sampler.hpp b/src/Device/Sampler.hpp
index f212d9d..148d781 100644
--- a/src/Device/Sampler.hpp
+++ b/src/Device/Sampler.hpp
@@ -83,7 +83,6 @@
 	ADDRESSING_BORDER,    // Single color
 	ADDRESSING_SEAMLESS,  // Border of pixels
 	ADDRESSING_CUBEFACE,  // Cube face layer
-	ADDRESSING_LAYER,     // Array layer
 	ADDRESSING_TEXELFETCH,
 
 	ADDRESSING_LAST = ADDRESSING_TEXELFETCH
@@ -97,7 +96,6 @@
 	AddressingMode addressingModeU;
 	AddressingMode addressingModeV;
 	AddressingMode addressingModeW;
-	AddressingMode addressingModeA;
 	MipmapType mipmapFilter;
 	VkComponentMapping swizzle;
 	int gatherComponent;
@@ -115,6 +113,26 @@
 	float maxAnisotropy = 0.0f;
 	float minLod = 0.0f;
 	float maxLod = 0.0f;
+
+	bool isArrayed() const
+	{
+		switch(textureType)
+		{
+			case VK_IMAGE_VIEW_TYPE_1D:
+			case VK_IMAGE_VIEW_TYPE_2D:
+			case VK_IMAGE_VIEW_TYPE_3D:
+			case VK_IMAGE_VIEW_TYPE_CUBE:
+				return false;
+			case VK_IMAGE_VIEW_TYPE_1D_ARRAY:
+			case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
+			case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
+				return true;
+			default:
+				UNSUPPORTED("VkImageViewType %d", (int)textureType);
+		}
+
+		return false;
+	}
 };
 
 }  // namespace sw
diff --git a/src/Pipeline/SamplerCore.cpp b/src/Pipeline/SamplerCore.cpp
index 59f4573..7410af1 100644
--- a/src/Pipeline/SamplerCore.cpp
+++ b/src/Pipeline/SamplerCore.cpp
@@ -21,33 +21,6 @@
 
 #include <limits>
 
-namespace {
-
-void applySwizzle(VkComponentSwizzle swizzle, sw::Float4 &f, const sw::Vector4f &c, bool integer)
-{
-	switch(swizzle)
-	{
-		case VK_COMPONENT_SWIZZLE_R: f = c.x; break;
-		case VK_COMPONENT_SWIZZLE_G: f = c.y; break;
-		case VK_COMPONENT_SWIZZLE_B: f = c.z; break;
-		case VK_COMPONENT_SWIZZLE_A: f = c.w; break;
-		case VK_COMPONENT_SWIZZLE_ZERO: f = sw::Float4(0.0f, 0.0f, 0.0f, 0.0f); break;
-		case VK_COMPONENT_SWIZZLE_ONE:
-			if(integer)
-			{
-				f = rr::As<sw::Float4>(sw::Int4(1, 1, 1, 1));
-			}
-			else
-			{
-				f = sw::Float4(1.0f, 1.0f, 1.0f, 1.0f);
-			}
-			break;
-		default: ASSERT(false);
-	}
-}
-
-}  // anonymous namespace
-
 namespace sw {
 
 SamplerCore::SamplerCore(Pointer<Byte> &constants, const Sampler &state)
@@ -59,10 +32,22 @@
 {
 	Vector4f c;
 
-	Float4 uuuu = uvwa[0];
-	Float4 vvvv = uvwa[1];
-	Float4 wwww = uvwa[2];
-	Float4 cubeArrayCoord = uvwa[3];
+	Float4 u = uvwa[0];
+	Float4 v = uvwa[1];
+	Float4 w = uvwa[2];
+	Float4 a;  // Array layer coordinate
+	switch(state.textureType)
+	{
+		case VK_IMAGE_VIEW_TYPE_1D_ARRAY:  // Treated as 2D array
+		case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
+			a = uvwa[2];
+			break;
+		case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
+			a = uvwa[3];
+			break;
+		default:
+			break;
+	}
 
 	Float lod;
 	Float anisotropy;
@@ -72,8 +57,8 @@
 
 	if(isCube())
 	{
-		Int4 face = cubeFace(uuuu, vvvv, uvwa[0], uvwa[1], uvwa[2], M);
-		wwww = As<Float4>(face);
+		Int4 face = cubeFace(u, v, uvwa[0], uvwa[1], uvwa[2], M);
+		w = As<Float4>(face);
 	}
 
 	if(function == Implicit || function == Bias || function == Grad || function == Query)
@@ -82,7 +67,7 @@
 		{
 			if(!isCube())
 			{
-				computeLod(texture, lod, anisotropy, uDelta, vDelta, uuuu, vvvv, dsx, dsy, function);
+				computeLod(texture, lod, anisotropy, uDelta, vDelta, u, v, dsx, dsy, function);
 			}
 			else
 			{
@@ -91,7 +76,7 @@
 		}
 		else
 		{
-			computeLod3D(texture, lod, uuuu, vvvv, wwww, dsx, dsy, function);
+			computeLod3D(texture, lod, u, v, w, dsx, dsy, function);
 		}
 
 		Float bias = state.mipLodBias;
@@ -154,7 +139,7 @@
 
 	if(use32BitFiltering)
 	{
-		c = sampleFloatFilter(texture, uuuu, vvvv, wwww, cubeArrayCoord, dRef, offset, sample, lod, anisotropy, uDelta, vDelta, function);
+		c = sampleFloatFilter(texture, u, v, w, a, dRef, offset, sample, lod, anisotropy, uDelta, vDelta, function);
 
 		if(!hasFloatTexture() && !hasUnnormalizedIntegerTexture() && !state.compareEnable)
 		{
@@ -210,7 +195,7 @@
 	}
 	else  // 16-bit filtering.
 	{
-		Vector4s cs = sampleFilter(texture, uuuu, vvvv, wwww, cubeArrayCoord, offset, sample, lod, anisotropy, uDelta, vDelta, function);
+		Vector4s cs = sampleFilter(texture, u, v, w, a, offset, sample, lod, anisotropy, uDelta, vDelta, function);
 
 		switch(state.textureFormat)
 		{
@@ -276,12 +261,12 @@
 		   (state.swizzle.b != VK_COMPONENT_SWIZZLE_B) ||
 		   (state.swizzle.a != VK_COMPONENT_SWIZZLE_A))
 		{
-			const Vector4f col(c);
+			const Vector4f col = c;
 			bool integer = hasUnnormalizedIntegerTexture();
-			applySwizzle(state.swizzle.r, c.x, col, integer);
-			applySwizzle(state.swizzle.g, c.y, col, integer);
-			applySwizzle(state.swizzle.b, c.z, col, integer);
-			applySwizzle(state.swizzle.a, c.w, col, integer);
+			c.x = applySwizzle(col, state.swizzle.r, integer);
+			c.y = applySwizzle(col, state.swizzle.g, integer);
+			c.z = applySwizzle(col, state.swizzle.b, integer);
+			c.w = applySwizzle(col, state.swizzle.a, integer);
 		}
 	}
 	else  // Gather
@@ -305,6 +290,29 @@
 	return c;
 }
 
+Float4 SamplerCore::applySwizzle(const Vector4f &c, VkComponentSwizzle swizzle, bool integer)
+{
+	switch(swizzle)
+	{
+		default: UNSUPPORTED("VkComponentSwizzle %d", (int)swizzle);
+		case VK_COMPONENT_SWIZZLE_R: return c.x;
+		case VK_COMPONENT_SWIZZLE_G: return c.y;
+		case VK_COMPONENT_SWIZZLE_B: return c.z;
+		case VK_COMPONENT_SWIZZLE_A: return c.w;
+		case VK_COMPONENT_SWIZZLE_ZERO: return Float4(0.0f, 0.0f, 0.0f, 0.0f);
+		case VK_COMPONENT_SWIZZLE_ONE:
+			if(integer)
+			{
+				return Float4(As<Float4>(sw::Int4(1, 1, 1, 1)));
+			}
+			else
+			{
+				return Float4(1.0f, 1.0f, 1.0f, 1.0f);
+			}
+			break;
+	}
+};
+
 Short4 SamplerCore::offsetSample(Short4 &uvw, Pointer<Byte> &mipmap, int halfOffset, bool wrap, int count, Float &lod)
 {
 	Short4 offset = *Pointer<Short4>(mipmap + halfOffset);
@@ -342,9 +350,9 @@
 	return uvw;
 }
 
-Vector4s SamplerCore::sampleFilter(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &cubeArrayCoord, Vector4i &offset, const Int4 &sample, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, SamplerFunction function)
+Vector4s SamplerCore::sampleFilter(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &a, Vector4i &offset, const Int4 &sample, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, SamplerFunction function)
 {
-	Vector4s c = sampleAniso(texture, u, v, w, cubeArrayCoord, offset, sample, lod, anisotropy, uDelta, vDelta, false, function);
+	Vector4s c = sampleAniso(texture, u, v, w, a, offset, sample, lod, anisotropy, uDelta, vDelta, false, function);
 
 	if(function == Fetch)
 	{
@@ -353,7 +361,7 @@
 
 	if(state.mipmapFilter == MIPMAP_LINEAR)
 	{
-		Vector4s cc = sampleAniso(texture, u, v, w, cubeArrayCoord, offset, sample, lod, anisotropy, uDelta, vDelta, true, function);
+		Vector4s cc = sampleAniso(texture, u, v, w, a, offset, sample, lod, anisotropy, uDelta, vDelta, true, function);
 
 		lod *= Float(1 << 16);
 
@@ -411,17 +419,17 @@
 	return c;
 }
 
-Vector4s SamplerCore::sampleAniso(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &cubeArrayCoord, Vector4i &offset, const Int4 &sample, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, bool secondLOD, SamplerFunction function)
+Vector4s SamplerCore::sampleAniso(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &a, Vector4i &offset, const Int4 &sample, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, bool secondLOD, SamplerFunction function)
 {
 	Vector4s c;
 
 	if(state.textureFilter != FILTER_ANISOTROPIC || function == Lod || function == Fetch)
 	{
-		c = sampleQuad(texture, u, v, w, cubeArrayCoord, offset, sample, lod, secondLOD, function);
+		c = sampleQuad(texture, u, v, w, a, offset, sample, lod, secondLOD, function);
 	}
 	else
 	{
-		Int a = RoundInt(anisotropy);
+		Int N = RoundInt(anisotropy);
 
 		Vector4s cSum;
 
@@ -430,9 +438,9 @@
 		cSum.z = Short4(0);
 		cSum.w = Short4(0);
 
-		Float4 A = *Pointer<Float4>(constants + OFFSET(Constants, uvWeight) + 16 * a);
-		Float4 B = *Pointer<Float4>(constants + OFFSET(Constants, uvStart) + 16 * a);
-		UShort4 cw = *Pointer<UShort4>(constants + OFFSET(Constants, cWeight) + 8 * a);
+		Float4 A = *Pointer<Float4>(constants + OFFSET(Constants, uvWeight) + 16 * N);
+		Float4 B = *Pointer<Float4>(constants + OFFSET(Constants, uvStart) + 16 * N);
+		UShort4 cw = *Pointer<UShort4>(constants + OFFSET(Constants, cWeight) + 8 * N);
 		Short4 sw = Short4(cw >> 1);
 
 		Float4 du = uDelta;
@@ -448,7 +456,7 @@
 
 		Do
 		{
-			c = sampleQuad(texture, u0, v0, w, cubeArrayCoord, offset, sample, lod, secondLOD, function);
+			c = sampleQuad(texture, u0, v0, w, a, offset, sample, lod, secondLOD, function);
 
 			u0 += du;
 			v0 += dv;
@@ -472,7 +480,7 @@
 
 			i++;
 		}
-		Until(i >= a);
+		Until(i >= N);
 
 		if(hasUnsignedTextureComponent(0))
 			c.x = cSum.x;
@@ -495,11 +503,11 @@
 	return c;
 }
 
-Vector4s SamplerCore::sampleQuad(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &cubeArrayCoord, Vector4i &offset, const Int4 &sample, Float &lod, bool secondLOD, SamplerFunction function)
+Vector4s SamplerCore::sampleQuad(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &a, Vector4i &offset, const Int4 &sample, Float &lod, bool secondLOD, SamplerFunction function)
 {
 	if(state.textureType != VK_IMAGE_VIEW_TYPE_3D)
 	{
-		return sampleQuad2D(texture, u, v, w, cubeArrayCoord, offset, sample, lod, secondLOD, function);
+		return sampleQuad2D(texture, u, v, w, a, offset, sample, lod, secondLOD, function);
 	}
 	else
 	{
@@ -507,7 +515,7 @@
 	}
 }
 
-Vector4s SamplerCore::sampleQuad2D(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &cubeArrayCoord, Vector4i &offset, const Int4 &sample, Float &lod, bool secondLOD, SamplerFunction function)
+Vector4s SamplerCore::sampleQuad2D(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &a, Vector4i &offset, const Int4 &sample, Float &lod, bool secondLOD, SamplerFunction function)
 {
 	Vector4s c;
 
@@ -521,16 +529,11 @@
 	Short4 uuuu = address(u, state.addressingModeU, mipmap);
 	Short4 vvvv = address(v, state.addressingModeV, mipmap);
 	Short4 wwww = address(w, state.addressingModeW, mipmap);
-
-	Short4 cubeArrayLayer(0);
-	if(state.textureType == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)
-	{
-		cubeArrayLayer = address(cubeArrayCoord, state.addressingModeA, mipmap);
-	}
+	Short4 layerIndex = computeLayerIndex(a, mipmap);
 
 	if(state.textureFilter == FILTER_POINT)
 	{
-		c = sampleTexel(uuuu, vvvv, wwww, cubeArrayLayer, offset, sample, mipmap, buffer, function);
+		c = sampleTexel(uuuu, vvvv, wwww, layerIndex, offset, sample, mipmap, buffer, function);
 	}
 	else
 	{
@@ -539,10 +542,10 @@
 		Short4 uuuu1 = offsetSample(uuuu, mipmap, OFFSET(Mipmap, uHalf), state.addressingModeU == ADDRESSING_WRAP, +1, lod);
 		Short4 vvvv1 = offsetSample(vvvv, mipmap, OFFSET(Mipmap, vHalf), state.addressingModeV == ADDRESSING_WRAP, +1, lod);
 
-		Vector4s c00 = sampleTexel(uuuu0, vvvv0, wwww, cubeArrayLayer, offset, sample, mipmap, buffer, function);
-		Vector4s c10 = sampleTexel(uuuu1, vvvv0, wwww, cubeArrayLayer, offset, sample, mipmap, buffer, function);
-		Vector4s c01 = sampleTexel(uuuu0, vvvv1, wwww, cubeArrayLayer, offset, sample, mipmap, buffer, function);
-		Vector4s c11 = sampleTexel(uuuu1, vvvv1, wwww, cubeArrayLayer, offset, sample, mipmap, buffer, function);
+		Vector4s c00 = sampleTexel(uuuu0, vvvv0, wwww, layerIndex, offset, sample, mipmap, buffer, function);
+		Vector4s c10 = sampleTexel(uuuu1, vvvv0, wwww, layerIndex, offset, sample, mipmap, buffer, function);
+		Vector4s c01 = sampleTexel(uuuu0, vvvv1, wwww, layerIndex, offset, sample, mipmap, buffer, function);
+		Vector4s c11 = sampleTexel(uuuu1, vvvv1, wwww, layerIndex, offset, sample, mipmap, buffer, function);
 
 		if(!gather)  // Blend
 		{
@@ -864,9 +867,9 @@
 	return c_;
 }
 
-Vector4f SamplerCore::sampleFloatFilter(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &cubeArrayCoord, Float4 &dRef, Vector4i &offset, const Int4 &sample, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, SamplerFunction function)
+Vector4f SamplerCore::sampleFloatFilter(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &a, Float4 &dRef, Vector4i &offset, const Int4 &sample, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, SamplerFunction function)
 {
-	Vector4f c = sampleFloatAniso(texture, u, v, w, cubeArrayCoord, dRef, offset, sample, lod, anisotropy, uDelta, vDelta, false, function);
+	Vector4f c = sampleFloatAniso(texture, u, v, w, a, dRef, offset, sample, lod, anisotropy, uDelta, vDelta, false, function);
 
 	if(function == Fetch)
 	{
@@ -875,7 +878,7 @@
 
 	if(state.mipmapFilter == MIPMAP_LINEAR)
 	{
-		Vector4f cc = sampleFloatAniso(texture, u, v, w, cubeArrayCoord, dRef, offset, sample, lod, anisotropy, uDelta, vDelta, true, function);
+		Vector4f cc = sampleFloatAniso(texture, u, v, w, a, dRef, offset, sample, lod, anisotropy, uDelta, vDelta, true, function);
 
 		Float4 lod4 = Float4(Frac(lod));
 
@@ -888,17 +891,17 @@
 	return c;
 }
 
-Vector4f SamplerCore::sampleFloatAniso(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &cubeArrayCoord, Float4 &dRef, Vector4i &offset, const Int4 &sample, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, bool secondLOD, SamplerFunction function)
+Vector4f SamplerCore::sampleFloatAniso(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &a, Float4 &dRef, Vector4i &offset, const Int4 &sample, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, bool secondLOD, SamplerFunction function)
 {
 	Vector4f c;
 
 	if(state.textureFilter != FILTER_ANISOTROPIC || function == Lod || function == Fetch)
 	{
-		c = sampleFloat(texture, u, v, w, cubeArrayCoord, dRef, offset, sample, lod, secondLOD, function);
+		c = sampleFloat(texture, u, v, w, a, dRef, offset, sample, lod, secondLOD, function);
 	}
 	else
 	{
-		Int a = RoundInt(anisotropy);
+		Int N = RoundInt(anisotropy);
 
 		Vector4f cSum;
 
@@ -907,8 +910,8 @@
 		cSum.z = Float4(0.0f);
 		cSum.w = Float4(0.0f);
 
-		Float4 A = *Pointer<Float4>(constants + OFFSET(Constants, uvWeight) + 16 * a);
-		Float4 B = *Pointer<Float4>(constants + OFFSET(Constants, uvStart) + 16 * a);
+		Float4 A = *Pointer<Float4>(constants + OFFSET(Constants, uvWeight) + 16 * N);
+		Float4 B = *Pointer<Float4>(constants + OFFSET(Constants, uvStart) + 16 * N);
 
 		Float4 du = uDelta;
 		Float4 dv = vDelta;
@@ -923,7 +926,7 @@
 
 		Do
 		{
-			c = sampleFloat(texture, u0, v0, w, cubeArrayCoord, dRef, offset, sample, lod, secondLOD, function);
+			c = sampleFloat(texture, u0, v0, w, a, dRef, offset, sample, lod, secondLOD, function);
 
 			u0 += du;
 			v0 += dv;
@@ -935,7 +938,7 @@
 
 			i++;
 		}
-		Until(i >= a);
+		Until(i >= N);
 
 		c.x = cSum.x;
 		c.y = cSum.y;
@@ -946,11 +949,11 @@
 	return c;
 }
 
-Vector4f SamplerCore::sampleFloat(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &cubeArrayCoord, Float4 &dRef, Vector4i &offset, const Int4 &sample, Float &lod, bool secondLOD, SamplerFunction function)
+Vector4f SamplerCore::sampleFloat(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &a, Float4 &dRef, Vector4i &offset, const Int4 &sample, Float &lod, bool secondLOD, SamplerFunction function)
 {
 	if(state.textureType != VK_IMAGE_VIEW_TYPE_3D)
 	{
-		return sampleFloat2D(texture, u, v, w, cubeArrayCoord, dRef, offset, sample, lod, secondLOD, function);
+		return sampleFloat2D(texture, u, v, w, a, dRef, offset, sample, lod, secondLOD, function);
 	}
 	else
 	{
@@ -958,7 +961,7 @@
 	}
 }
 
-Vector4f SamplerCore::sampleFloat2D(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &cubeArrayCoord, Float4 &dRef, Vector4i &offset, const Int4 &sample, Float &lod, bool secondLOD, SamplerFunction function)
+Vector4f SamplerCore::sampleFloat2D(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &a, Float4 &dRef, Vector4i &offset, const Int4 &sample, Float &lod, bool secondLOD, SamplerFunction function)
 {
 	Vector4f c;
 
@@ -969,38 +972,49 @@
 	Pointer<Byte> buffer;
 	selectMipmap(texture, mipmap, buffer, lod, secondLOD);
 
-	Int4 x0, x1, y0, y1, z0;
-	Float4 fu, fv, fw;
+	Int4 x0, x1, y0, y1;
+	Float4 fu, fv;
 	Int4 filter = computeFilterOffset(lod);
 	address(u, x0, x1, fu, mipmap, offset.x, filter, OFFSET(Mipmap, width), state.addressingModeU, function);
 	address(v, y0, y1, fv, mipmap, offset.y, filter, OFFSET(Mipmap, height), state.addressingModeV, function);
-	address(w, z0, z0, fw, mipmap, offset.z, filter, OFFSET(Mipmap, depth), state.addressingModeW, function);
-
-	Int4 cubeArrayLayer(0);
-	if(state.textureType == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)
-	{
-		address(cubeArrayCoord, cubeArrayLayer, cubeArrayLayer, fw, mipmap, offset.w, filter, OFFSET(Mipmap, depth), state.addressingModeA, function);
-	}
 
 	Int4 pitchP = *Pointer<Int4>(mipmap + OFFSET(Mipmap, pitchP), 16);
 	y0 *= pitchP;
-	if(state.addressingModeW != ADDRESSING_UNUSED)
+
+	Int4 z;
+	if(state.addressingModeW == ADDRESSING_CUBEFACE || state.isArrayed())
 	{
-		z0 *= *Pointer<Int4>(mipmap + OFFSET(Mipmap, sliceP), 16);
+		Int4 face = As<Int4>(w);
+		Int4 layerIndex = computeLayerIndex(a, mipmap, function);
+
+		// For cube maps, the layer argument is per cube, each of which has 6 layers
+		if(state.textureType == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)
+		{
+			layerIndex *= Int4(6);
+		}
+
+		z = (state.addressingModeW == ADDRESSING_CUBEFACE) ? face : layerIndex;
+
+		if(state.textureType == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)
+		{
+			z += layerIndex;
+		}
+
+		z *= *Pointer<Int4>(mipmap + OFFSET(Mipmap, sliceP), 16);
 	}
 
 	if(state.textureFilter == FILTER_POINT || (function == Fetch))
 	{
-		c = sampleTexel(x0, y0, z0, cubeArrayLayer, dRef, sample, mipmap, buffer, function);
+		c = sampleTexel(x0, y0, z, dRef, sample, mipmap, buffer, function);
 	}
 	else
 	{
 		y1 *= pitchP;
 
-		Vector4f c00 = sampleTexel(x0, y0, z0, cubeArrayLayer, dRef, sample, mipmap, buffer, function);
-		Vector4f c10 = sampleTexel(x1, y0, z0, cubeArrayLayer, dRef, sample, mipmap, buffer, function);
-		Vector4f c01 = sampleTexel(x0, y1, z0, cubeArrayLayer, dRef, sample, mipmap, buffer, function);
-		Vector4f c11 = sampleTexel(x1, y1, z0, cubeArrayLayer, dRef, sample, mipmap, buffer, function);
+		Vector4f c00 = sampleTexel(x0, y0, z, dRef, sample, mipmap, buffer, function);
+		Vector4f c10 = sampleTexel(x1, y0, z, dRef, sample, mipmap, buffer, function);
+		Vector4f c01 = sampleTexel(x0, y1, z, dRef, sample, mipmap, buffer, function);
+		Vector4f c11 = sampleTexel(x1, y1, z, dRef, sample, mipmap, buffer, function);
 
 		if(!gather)  // Blend
 		{
@@ -1065,21 +1079,21 @@
 
 	if(state.textureFilter == FILTER_POINT || (function == Fetch))
 	{
-		c = sampleTexel(x0, y0, z0, 0, dRef, sample, mipmap, buffer, function);
+		c = sampleTexel(x0, y0, z0, dRef, sample, mipmap, buffer, function);
 	}
 	else
 	{
 		y1 *= pitchP;
 		z1 *= sliceP;
 
-		Vector4f c000 = sampleTexel(x0, y0, z0, 0, dRef, sample, mipmap, buffer, function);
-		Vector4f c100 = sampleTexel(x1, y0, z0, 0, dRef, sample, mipmap, buffer, function);
-		Vector4f c010 = sampleTexel(x0, y1, z0, 0, dRef, sample, mipmap, buffer, function);
-		Vector4f c110 = sampleTexel(x1, y1, z0, 0, dRef, sample, mipmap, buffer, function);
-		Vector4f c001 = sampleTexel(x0, y0, z1, 0, dRef, sample, mipmap, buffer, function);
-		Vector4f c101 = sampleTexel(x1, y0, z1, 0, dRef, sample, mipmap, buffer, function);
-		Vector4f c011 = sampleTexel(x0, y1, z1, 0, dRef, sample, mipmap, buffer, function);
-		Vector4f c111 = sampleTexel(x1, y1, z1, 0, dRef, sample, mipmap, buffer, function);
+		Vector4f c000 = sampleTexel(x0, y0, z0, dRef, sample, mipmap, buffer, function);
+		Vector4f c100 = sampleTexel(x1, y0, z0, dRef, sample, mipmap, buffer, function);
+		Vector4f c010 = sampleTexel(x0, y1, z0, dRef, sample, mipmap, buffer, function);
+		Vector4f c110 = sampleTexel(x1, y1, z0, dRef, sample, mipmap, buffer, function);
+		Vector4f c001 = sampleTexel(x0, y0, z1, dRef, sample, mipmap, buffer, function);
+		Vector4f c101 = sampleTexel(x1, y0, z1, dRef, sample, mipmap, buffer, function);
+		Vector4f c011 = sampleTexel(x0, y1, z1, dRef, sample, mipmap, buffer, function);
+		Vector4f c111 = sampleTexel(x1, y1, z1, dRef, sample, mipmap, buffer, function);
 
 		// Blend first slice
 		if(componentCount >= 1) c000.x = c000.x + fu * (c100.x - c000.x);
@@ -1346,7 +1360,7 @@
 	return As<Short4>(UShort4(tmp));
 }
 
-void SamplerCore::computeIndices(UInt index[4], Short4 uuuu, Short4 vvvv, Short4 wwww, const Short4 &cubeArrayLayer, Vector4i &offset, const Int4 &sample, const Pointer<Byte> &mipmap, SamplerFunction function)
+void SamplerCore::computeIndices(UInt index[4], Short4 uuuu, Short4 vvvv, Short4 wwww, const Short4 &layerIndex, Vector4i &offset, const Int4 &sample, const Pointer<Byte> &mipmap, SamplerFunction function)
 {
 	uuuu = MulHigh(As<UShort4>(uuuu), UShort4(*Pointer<Int4>(mipmap + OFFSET(Mipmap, width))));
 	vvvv = MulHigh(As<UShort4>(vvvv), UShort4(*Pointer<Int4>(mipmap + OFFSET(Mipmap, height))));
@@ -1363,59 +1377,52 @@
 	uuuu = As<Short4>(MulAdd(uuuu, *Pointer<Short4>(mipmap + OFFSET(Mipmap, onePitchP))));
 	uuu2 = As<Short4>(MulAdd(uuu2, *Pointer<Short4>(mipmap + OFFSET(Mipmap, onePitchP))));
 
-	if(hasThirdCoordinate())
-	{
-		if(state.textureType == VK_IMAGE_VIEW_TYPE_3D)
-		{
-			wwww = MulHigh(As<UShort4>(wwww), UShort4(*Pointer<Int4>(mipmap + OFFSET(Mipmap, depth))));
+	UInt4 uv(As<UInt2>(uuuu), As<UInt2>(uuu2));
 
-			if(function.offset)
-			{
-				wwww = applyOffset(wwww, offset.z, *Pointer<Int4>(mipmap + OFFSET(Mipmap, depth)), state.addressingModeW);
-			}
+	if(state.textureType == VK_IMAGE_VIEW_TYPE_3D)
+	{
+		wwww = MulHigh(As<UShort4>(wwww), UShort4(*Pointer<Int4>(mipmap + OFFSET(Mipmap, depth))));
+
+		if(function.offset)
+		{
+			wwww = applyOffset(wwww, offset.z, *Pointer<Int4>(mipmap + OFFSET(Mipmap, depth)), state.addressingModeW);
 		}
 
-		UInt4 uv(As<UInt2>(uuuu), As<UInt2>(uuu2));
 		uv += As<UInt4>(Int4(As<UShort4>(wwww))) * *Pointer<UInt4>(mipmap + OFFSET(Mipmap, sliceP));
-
-		index[0] = Extract(As<Int4>(uv), 0);
-		index[1] = Extract(As<Int4>(uv), 1);
-		index[2] = Extract(As<Int4>(uv), 2);
-		index[3] = Extract(As<Int4>(uv), 3);
 	}
-	else
+
+	if(state.isArrayed())
 	{
-		index[0] = Extract(As<Int2>(uuuu), 0);
-		index[1] = Extract(As<Int2>(uuuu), 1);
-		index[2] = Extract(As<Int2>(uuu2), 0);
-		index[3] = Extract(As<Int2>(uuu2), 1);
+		Int4 layer = Int4(As<UShort4>(layerIndex));
+
+		if(state.textureType == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)
+		{
+			layer *= Int4(6);
+		}
+
+		UInt4 layerOffset = As<UInt4>(layer) * *Pointer<UInt4>(mipmap + OFFSET(Mipmap, sliceP));
+
+		uv += layerOffset;
 	}
 
 	if(function.sample)
 	{
 		UInt4 sampleOffset = Min(As<UInt4>(sample), *Pointer<UInt4>(mipmap + OFFSET(Mipmap, sampleMax), 16)) *
 		                     *Pointer<UInt4>(mipmap + OFFSET(Mipmap, samplePitchP), 16);
-		for(int i = 0; i < 4; i++)
-		{
-			index[i] += Extract(sampleOffset, i);
-		}
+		uv += sampleOffset;
 	}
 
-	if(state.textureType == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)
-	{
-		UInt4 cubeLayerOffset = As<UInt4>(cubeArrayLayer) * *Pointer<UInt4>(mipmap + OFFSET(Mipmap, sliceP)) * UInt4(6);
-		for(int i = 0; i < 4; i++)
-		{
-			index[i] += Extract(cubeLayerOffset, i);
-		}
-	}
+	index[0] = Extract(As<Int4>(uv), 0);
+	index[1] = Extract(As<Int4>(uv), 1);
+	index[2] = Extract(As<Int4>(uv), 2);
+	index[3] = Extract(As<Int4>(uv), 3);
 }
 
-void SamplerCore::computeIndices(UInt index[4], Int4 uuuu, Int4 vvvv, Int4 wwww, const Int4 &cubeArrayLayer, const Int4 &sample, Int4 valid, const Pointer<Byte> &mipmap, SamplerFunction function)
+void SamplerCore::computeIndices(UInt index[4], Int4 uuuu, Int4 vvvv, Int4 wwww, const Int4 &sample, Int4 valid, const Pointer<Byte> &mipmap, SamplerFunction function)
 {
 	UInt4 indices = uuuu + vvvv;
 
-	if(state.addressingModeW != ADDRESSING_UNUSED)
+	if(state.addressingModeW != ADDRESSING_UNUSED || state.isArrayed())
 	{
 		indices += As<UInt4>(wwww);
 	}
@@ -1426,11 +1433,6 @@
 		           *Pointer<UInt4>(mipmap + OFFSET(Mipmap, samplePitchP), 16);
 	}
 
-	if(state.textureType == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)
-	{
-		indices += As<UInt4>(cubeArrayLayer) * *Pointer<UInt4>(mipmap + OFFSET(Mipmap, sliceP)) * UInt4(6);
-	}
-
 	if(borderModeActive())
 	{
 		// Texels out of range are still sampled before being replaced
@@ -1715,12 +1717,12 @@
 	return c;
 }
 
-Vector4s SamplerCore::sampleTexel(Short4 &uuuu, Short4 &vvvv, Short4 &wwww, const Short4 &cubeArrayLayer, Vector4i &offset, const Int4 &sample, Pointer<Byte> &mipmap, Pointer<Byte> buffer, SamplerFunction function)
+Vector4s SamplerCore::sampleTexel(Short4 &uuuu, Short4 &vvvv, Short4 &wwww, const Short4 &layerIndex, Vector4i &offset, const Int4 &sample, Pointer<Byte> &mipmap, Pointer<Byte> buffer, SamplerFunction function)
 {
 	Vector4s c;
 
 	UInt index[4];
-	computeIndices(index, uuuu, vvvv, wwww, cubeArrayLayer, offset, sample, mipmap, function);
+	computeIndices(index, uuuu, vvvv, wwww, layerIndex, offset, sample, mipmap, function);
 
 	if(isYcbcrFormat())
 	{
@@ -1741,7 +1743,7 @@
 
 		// Chroma
 		{
-			computeIndices(index, uuuu, vvvv, wwww, cubeArrayLayer, offset, sample, mipmap + sizeof(Mipmap), function);
+			computeIndices(index, uuuu, vvvv, wwww, layerIndex, offset, sample, mipmap + sizeof(Mipmap), function);
 			UShort4 U, V;
 
 			if(state.textureFormat == VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM)
@@ -1864,29 +1866,28 @@
 	return c;
 }
 
-Vector4f SamplerCore::sampleTexel(Int4 &uuuu, Int4 &vvvv, Int4 &wwww, const Int4 &cubeArrayLayer, Float4 &dRef, const Int4 &sample, Pointer<Byte> &mipmap, Pointer<Byte> buffer, SamplerFunction function)
+Vector4f SamplerCore::sampleTexel(Int4 &uuuu, Int4 &vvvv, Int4 &wwww, Float4 &dRef, const Int4 &sample, Pointer<Byte> &mipmap, Pointer<Byte> buffer, SamplerFunction function)
 {
 	Int4 valid;
 
 	if(borderModeActive())
 	{
 		// Valid texels have positive coordinates.
-		Int4 negative = Int4(0);
-		if(state.addressingModeU != ADDRESSING_UNUSED) negative |= uuuu;
+		Int4 negative = uuuu;
 		if(state.addressingModeV != ADDRESSING_UNUSED) negative |= vvvv;
-		if(state.addressingModeW != ADDRESSING_UNUSED) negative |= wwww;
-		if(state.addressingModeA != ADDRESSING_UNUSED) negative |= cubeArrayLayer;
+		if(state.addressingModeW != ADDRESSING_UNUSED || state.isArrayed()) negative |= wwww;
 		valid = CmpNLT(negative, Int4(0));
 	}
 
 	UInt index[4];
-	UInt4 t0, t1, t2, t3;
-	computeIndices(index, uuuu, vvvv, wwww, cubeArrayLayer, sample, valid, mipmap, function);
+	computeIndices(index, uuuu, vvvv, wwww, sample, valid, mipmap, function);
 
 	Vector4f c;
 
 	if(hasFloatTexture() || has32bitIntegerTextureComponents())
 	{
+		UInt4 t0, t1, t2, t3;
+
 		switch(state.textureFormat)
 		{
 			case VK_FORMAT_R16_SFLOAT:
@@ -2168,17 +2169,6 @@
 	{
 		return Short4();
 	}
-	else if(addressingMode == ADDRESSING_LAYER)
-	{
-		Int4 dim = *Pointer<Int4>(mipmap + OFFSET(Mipmap, depth));
-		// For cube maps, the layer argument is per cube, each of which has 6 layers
-		if(state.textureType == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)
-		{
-			dim = dim / Int4(6);
-		}
-
-		return Short4(Min(Max(RoundInt(uw), Int4(0)), dim - Int4(1)));
-	}
 	else if(addressingMode == ADDRESSING_CLAMP || addressingMode == ADDRESSING_BORDER)
 	{
 		Float4 clamp = Min(Max(uw, Float4(0.0f)), Float4(65535.0f / 65536.0f));
@@ -2211,6 +2201,18 @@
 	}
 }
 
+Short4 SamplerCore::computeLayerIndex(const Float4 &a, Pointer<Byte> &mipmap)
+{
+	if(!state.isArrayed())
+	{
+		return {};
+	}
+
+	Int4 layers = *Pointer<Int4>(mipmap + OFFSET(Mipmap, depth));
+
+	return Short4(Min(Max(RoundInt(a), Int4(0)), layers - Int4(1)));
+}
+
 // TODO: Eliminate when the gather + mirror addressing case is handled by mirroring the footprint.
 static Int4 mirror(Int4 n)
 {
@@ -2235,9 +2237,9 @@
 	Int4 dim = *Pointer<Int4>(mipmap + whd, 16);
 	Int4 maxXYZ = dim - Int4(1);
 
-	if(function == Fetch)
+	if(function == Fetch)  // Unnormalized coordinates
 	{
-		Int4 xyz = (function.offset && (addressingMode != ADDRESSING_LAYER)) ? As<Int4>(uvw) + offset : As<Int4>(uvw);
+		Int4 xyz = function.offset ? As<Int4>(uvw) + offset : As<Int4>(uvw);
 		xyz0 = Min(Max(xyz, Int4(0)), maxXYZ);
 
 		// VK_EXT_image_robustness requires checking for out-of-bounds accesses.
@@ -2247,16 +2249,6 @@
 		Int4 outOfBounds = CmpNEQ(xyz, xyz0);
 		xyz0 |= outOfBounds;
 	}
-	else if(addressingMode == ADDRESSING_LAYER)  // Note: Offset does not apply to array layers
-	{
-		// For cube maps, the layer argument is per cube, each of which has 6 layers
-		if(state.textureType == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)
-		{
-			dim = dim / Int4(6);
-		}
-
-		xyz0 = Min(Max(RoundInt(uvw), Int4(0)), dim - Int4(1));
-	}
 	else if(addressingMode == ADDRESSING_CUBEFACE)
 	{
 		xyz0 = As<Int4>(uvw);
@@ -2452,6 +2444,36 @@
 	}
 }
 
+Int4 SamplerCore::computeLayerIndex(const Float4 &a, Pointer<Byte> &mipmap, SamplerFunction function)
+{
+	if(!state.isArrayed())
+	{
+		return {};
+	}
+
+	Int4 layers = *Pointer<Int4>(mipmap + OFFSET(Mipmap, depth), 16);
+	Int4 maxLayer = layers - Int4(1);
+
+	if(function == Fetch)  // Unnormalized coordinates
+	{
+		Int4 xyz = As<Int4>(a);
+		Int4 xyz0 = Min(Max(xyz, Int4(0)), maxLayer);
+
+		// VK_EXT_image_robustness requires checking for out-of-bounds accesses.
+		// TODO(b/162327166): Only perform bounds checks when VK_EXT_image_robustness is enabled.
+		// If the above clamping altered the result, the access is out-of-bounds.
+		// In that case set the coordinate to -1 to perform texel replacement later.
+		Int4 outOfBounds = CmpNEQ(xyz, xyz0);
+		xyz0 |= outOfBounds;
+
+		return xyz0;
+	}
+	else
+	{
+		return Min(Max(RoundInt(a), Int4(0)), maxLayer);
+	}
+}
+
 void SamplerCore::convertSigned15(Float4 &cf, Short4 &cs)
 {
 	cf = Float4(cs) * Float4(1.0f / 0x7FFF);
diff --git a/src/Pipeline/SamplerCore.hpp b/src/Pipeline/SamplerCore.hpp
index 38ea555..1c029d3 100644
--- a/src/Pipeline/SamplerCore.hpp
+++ b/src/Pipeline/SamplerCore.hpp
@@ -61,16 +61,17 @@
 	Vector4f sampleTexture(Pointer<Byte> &texture, Float4 uvwa[4], Float4 &q, Float &&lodOrBias, Float4 &dsx, Float4 &dsy, Vector4i &offset, Int4 &sample, SamplerFunction function);
 
 private:
+	Float4 applySwizzle(const Vector4f &c, VkComponentSwizzle swizzle, bool integer);
 	Short4 offsetSample(Short4 &uvw, Pointer<Byte> &mipmap, int halfOffset, bool wrap, int count, Float &lod);
-	Vector4s sampleFilter(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &cubeArrayCoord, Vector4i &offset, const Int4 &sample, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, SamplerFunction function);
-	Vector4s sampleAniso(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &cubeArrayCoord, Vector4i &offset, const Int4 &sample, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, bool secondLOD, SamplerFunction function);
-	Vector4s sampleQuad(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &cubeArrayCoord, Vector4i &offset, const Int4 &sample, Float &lod, bool secondLOD, SamplerFunction function);
-	Vector4s sampleQuad2D(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &cubeArrayCoord, Vector4i &offset, const Int4 &sample, Float &lod, bool secondLOD, SamplerFunction function);
+	Vector4s sampleFilter(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &a, Vector4i &offset, const Int4 &sample, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, SamplerFunction function);
+	Vector4s sampleAniso(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &a, Vector4i &offset, const Int4 &sample, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, bool secondLOD, SamplerFunction function);
+	Vector4s sampleQuad(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &a, Vector4i &offset, const Int4 &sample, Float &lod, bool secondLOD, SamplerFunction function);
+	Vector4s sampleQuad2D(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &a, Vector4i &offset, const Int4 &sample, Float &lod, bool secondLOD, SamplerFunction function);
 	Vector4s sample3D(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, Vector4i &offset, const Int4 &sample, Float &lod, bool secondLOD, SamplerFunction function);
-	Vector4f sampleFloatFilter(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &cubeArrayCoord, Float4 &dRef, Vector4i &offset, const Int4 &sample, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, SamplerFunction function);
-	Vector4f sampleFloatAniso(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &cubeArrayCoord, Float4 &dRef, Vector4i &offset, const Int4 &sample, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, bool secondLOD, SamplerFunction function);
-	Vector4f sampleFloat(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &cubeArrayCoord, Float4 &dRef, Vector4i &offset, const Int4 &sample, Float &lod, bool secondLOD, SamplerFunction function);
-	Vector4f sampleFloat2D(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &cubeArrayCoord, Float4 &dRef, Vector4i &offset, const Int4 &sample, Float &lod, bool secondLOD, SamplerFunction function);
+	Vector4f sampleFloatFilter(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &a, Float4 &dRef, Vector4i &offset, const Int4 &sample, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, SamplerFunction function);
+	Vector4f sampleFloatAniso(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &a, Float4 &dRef, Vector4i &offset, const Int4 &sample, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, bool secondLOD, SamplerFunction function);
+	Vector4f sampleFloat(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &a, Float4 &dRef, Vector4i &offset, const Int4 &sample, Float &lod, bool secondLOD, SamplerFunction function);
+	Vector4f sampleFloat2D(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, const Float4 &a, Float4 &dRef, Vector4i &offset, const Int4 &sample, Float &lod, bool secondLOD, SamplerFunction function);
 	Vector4f sampleFloat3D(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, Float4 &dRef, Vector4i &offset, const Int4 &sample, Float &lod, bool secondLOD, SamplerFunction function);
 	Float log2sqrt(Float lod);
 	Float log2(Float lod);
@@ -80,14 +81,16 @@
 	Int4 cubeFace(Float4 &U, Float4 &V, Float4 &x, Float4 &y, Float4 &z, Float4 &M);
 	Short4 applyOffset(Short4 &uvw, Int4 &offset, const Int4 &whd, AddressingMode mode);
 	void computeIndices(UInt index[4], Short4 uuuu, Short4 vvvv, Short4 wwww, const Short4 &cubeArrayLayer, Vector4i &offset, const Int4 &sample, const Pointer<Byte> &mipmap, SamplerFunction function);
-	void computeIndices(UInt index[4], Int4 uuuu, Int4 vvvv, Int4 wwww, const Int4 &cubeArrayLayer, const Int4 &sample, Int4 valid, const Pointer<Byte> &mipmap, SamplerFunction function);
+	void computeIndices(UInt index[4], Int4 uuuu, Int4 vvvv, Int4 wwww, const Int4 &sample, Int4 valid, const Pointer<Byte> &mipmap, SamplerFunction function);
 	Vector4s sampleTexel(Short4 &u, Short4 &v, Short4 &w, const Short4 &cubeArrayLayer, Vector4i &offset, const Int4 &sample, Pointer<Byte> &mipmap, Pointer<Byte> buffer, SamplerFunction function);
 	Vector4s sampleTexel(UInt index[4], Pointer<Byte> buffer);
-	Vector4f sampleTexel(Int4 &u, Int4 &v, Int4 &w, const Int4 &cubeArrayLayer, Float4 &dRef, const Int4 &sample, Pointer<Byte> &mipmap, Pointer<Byte> buffer, SamplerFunction function);
+	Vector4f sampleTexel(Int4 &u, Int4 &v, Int4 &w, Float4 &dRef, const Int4 &sample, Pointer<Byte> &mipmap, Pointer<Byte> buffer, SamplerFunction function);
 	Vector4f replaceBorderTexel(const Vector4f &c, Int4 valid);
 	void selectMipmap(const Pointer<Byte> &texture, Pointer<Byte> &mipmap, Pointer<Byte> &buffer, const Float &lod, bool secondLOD);
-	Short4 address(const Float4 &uw, AddressingMode addressingMode, Pointer<Byte> &mipmap);
-	void address(const Float4 &uw, Int4 &xyz0, Int4 &xyz1, Float4 &f, Pointer<Byte> &mipmap, Int4 &offset, Int4 &filter, int whd, AddressingMode addressingMode, SamplerFunction function);
+	Short4 address(const Float4 &uvw, AddressingMode addressingMode, Pointer<Byte> &mipmap);
+	Short4 computeLayerIndex(const Float4 &a, Pointer<Byte> &mipmap);
+	void address(const Float4 &uvw, Int4 &xyz0, Int4 &xyz1, Float4 &f, Pointer<Byte> &mipmap, Int4 &offset, Int4 &filter, int whd, AddressingMode addressingMode, SamplerFunction function);
+	Int4 computeLayerIndex(const Float4 &a, Pointer<Byte> &mipmap, SamplerFunction function);
 	Int4 computeFilterOffset(Float &lod);
 
 	void convertSigned15(Float4 &cf, Short4 &ci);
diff --git a/src/Pipeline/SpirvShaderSampling.cpp b/src/Pipeline/SpirvShaderSampling.cpp
index 0c9c3df..53c7182 100644
--- a/src/Pipeline/SpirvShaderSampling.cpp
+++ b/src/Pipeline/SpirvShaderSampling.cpp
@@ -51,7 +51,6 @@
 		samplerState.addressingModeU = convertAddressingMode(0, sampler, type);
 		samplerState.addressingModeV = convertAddressingMode(1, sampler, type);
 		samplerState.addressingModeW = convertAddressingMode(2, sampler, type);
-		samplerState.addressingModeA = convertAddressingMode(3, sampler, type);
 
 		samplerState.mipmapFilter = convertMipmapMode(sampler);
 		samplerState.swizzle = imageDescriptor->swizzle;
@@ -281,13 +280,26 @@
 {
 	switch(imageViewType)
 	{
-		case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
-			if(coordinateIndex == 3)
+		case VK_IMAGE_VIEW_TYPE_1D:  // Treated as 2D texture with second coordinate 0. TODO(b/134669567)
+		case VK_IMAGE_VIEW_TYPE_1D_ARRAY:
+			if(coordinateIndex == 1)
 			{
-				return ADDRESSING_LAYER;
+				return ADDRESSING_WRAP;
 			}
-			// Fall through to CUBE case:
+			// Fall through to 2D case:
+		case VK_IMAGE_VIEW_TYPE_2D:
+		case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
+			if(coordinateIndex == 2)
+			{
+				return ADDRESSING_UNUSED;
+			}
+			break;
+
+		case VK_IMAGE_VIEW_TYPE_3D:
+			break;
+
 		case VK_IMAGE_VIEW_TYPE_CUBE:
+		case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
 			if(coordinateIndex <= 1)  // Cube faces themselves are addressed as 2D images.
 			{
 				// Vulkan 1.1 spec:
@@ -297,56 +309,11 @@
 				// This corresponds with our 'SEAMLESS' addressing mode.
 				return ADDRESSING_SEAMLESS;
 			}
-			else if(coordinateIndex == 2)
+			else  // coordinateIndex == 2
 			{
-				// The cube face is an index into array layers.
+				// The cube face is an index into 2D array layers.
 				return ADDRESSING_CUBEFACE;
 			}
-			else
-			{
-				return ADDRESSING_UNUSED;
-			}
-			break;
-
-		case VK_IMAGE_VIEW_TYPE_1D:  // Treated as 2D texture with second coordinate 0. TODO(b/134669567)
-			if(coordinateIndex == 1)
-			{
-				return ADDRESSING_WRAP;
-			}
-			else if(coordinateIndex >= 2)
-			{
-				return ADDRESSING_UNUSED;
-			}
-			break;
-
-		case VK_IMAGE_VIEW_TYPE_3D:
-			if(coordinateIndex >= 3)
-			{
-				return ADDRESSING_UNUSED;
-			}
-			break;
-
-		case VK_IMAGE_VIEW_TYPE_1D_ARRAY:  // Treated as 2D texture with second coordinate 0. TODO(b/134669567)
-			if(coordinateIndex == 1)
-			{
-				return ADDRESSING_WRAP;
-			}
-			// Fall through to 2D_ARRAY case:
-		case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
-			if(coordinateIndex == 2)
-			{
-				return ADDRESSING_LAYER;
-			}
-			else if(coordinateIndex >= 3)
-			{
-				return ADDRESSING_UNUSED;
-			}
-			// Fall through to 2D case:
-		case VK_IMAGE_VIEW_TYPE_2D:
-			if(coordinateIndex >= 2)
-			{
-				return ADDRESSING_UNUSED;
-			}
 			break;
 
 		default:
diff --git a/src/Vulkan/VkDescriptorSetLayout.cpp b/src/Vulkan/VkDescriptorSetLayout.cpp
index b24781a..7c5892b 100644
--- a/src/Vulkan/VkDescriptorSetLayout.cpp
+++ b/src/Vulkan/VkDescriptorSetLayout.cpp
@@ -410,9 +410,10 @@
 
 					int width = extent.width;
 					int height = extent.height;
-					int bytes = format.bytes();
-					int layers = imageView->getSubresourceRange().layerCount;  // TODO(b/129523279): Untangle depth vs layers throughout the sampler
+					int layers = imageView->getSubresourceRange().layerCount;
 					int depth = layers > 1 ? layers : extent.depth;
+					if(imageView->getType() == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY) depth /= 6;
+					int bytes = format.bytes();
 					int pitchP = imageView->rowPitchBytes(aspect, level, ImageView::SAMPLING) / bytes;
 					int sliceP = (layers > 1 ? imageView->layerPitchBytes(aspect, ImageView::SAMPLING) : imageView->slicePitchBytes(aspect, level, ImageView::SAMPLING)) / bytes;
 					int samplePitchP = imageView->getMipLevelSize(aspect, level, ImageView::SAMPLING) / bytes;
@@ -492,25 +493,14 @@
 {
 	if(level == 0)
 	{
-		texture->widthWidthHeightHeight[0] =
-		    texture->widthWidthHeightHeight[1] = static_cast<float>(width);
-		texture->widthWidthHeightHeight[2] =
-		    texture->widthWidthHeightHeight[3] = static_cast<float>(height);
+		texture->widthWidthHeightHeight[0] = static_cast<float>(width);
+		texture->widthWidthHeightHeight[1] = static_cast<float>(width);
+		texture->widthWidthHeightHeight[2] = static_cast<float>(height);
+		texture->widthWidthHeightHeight[3] = static_cast<float>(height);
 
-		texture->width[0] =
-		    texture->width[1] =
-		        texture->width[2] =
-		            texture->width[3] = static_cast<float>(width);
-
-		texture->height[0] =
-		    texture->height[1] =
-		        texture->height[2] =
-		            texture->height[3] = static_cast<float>(height);
-
-		texture->depth[0] =
-		    texture->depth[1] =
-		        texture->depth[2] =
-		            texture->depth[3] = static_cast<float>(depth);
+		texture->width = sw::float4(static_cast<float>(width));
+		texture->height = sw::float4(static_cast<float>(height));
+		texture->depth = sw::float4(static_cast<float>(depth));
 	}
 
 	sw::Mipmap &mipmap = texture->mipmap[level];
@@ -519,60 +509,23 @@
 	short halfTexelV = 0x8000 / height;
 	short halfTexelW = 0x8000 / depth;
 
-	mipmap.uHalf[0] =
-	    mipmap.uHalf[1] =
-	        mipmap.uHalf[2] =
-	            mipmap.uHalf[3] = halfTexelU;
+	mipmap.uHalf = sw::short4(halfTexelU);
+	mipmap.vHalf = sw::short4(halfTexelV);
+	mipmap.wHalf = sw::short4(halfTexelW);
 
-	mipmap.vHalf[0] =
-	    mipmap.vHalf[1] =
-	        mipmap.vHalf[2] =
-	            mipmap.vHalf[3] = halfTexelV;
-
-	mipmap.wHalf[0] =
-	    mipmap.wHalf[1] =
-	        mipmap.wHalf[2] =
-	            mipmap.wHalf[3] = halfTexelW;
-
-	mipmap.width[0] =
-	    mipmap.width[1] =
-	        mipmap.width[2] =
-	            mipmap.width[3] = width;
-
-	mipmap.height[0] =
-	    mipmap.height[1] =
-	        mipmap.height[2] =
-	            mipmap.height[3] = height;
-
-	mipmap.depth[0] =
-	    mipmap.depth[1] =
-	        mipmap.depth[2] =
-	            mipmap.depth[3] = depth;
+	mipmap.width = sw::int4(width);
+	mipmap.height = sw::int4(height);
+	mipmap.depth = sw::int4(depth);
 
 	mipmap.onePitchP[0] = 1;
 	mipmap.onePitchP[1] = static_cast<short>(pitchP);
 	mipmap.onePitchP[2] = 1;
 	mipmap.onePitchP[3] = static_cast<short>(pitchP);
 
-	mipmap.pitchP[0] = pitchP;
-	mipmap.pitchP[1] = pitchP;
-	mipmap.pitchP[2] = pitchP;
-	mipmap.pitchP[3] = pitchP;
-
-	mipmap.sliceP[0] = sliceP;
-	mipmap.sliceP[1] = sliceP;
-	mipmap.sliceP[2] = sliceP;
-	mipmap.sliceP[3] = sliceP;
-
-	mipmap.samplePitchP[0] = samplePitchP;
-	mipmap.samplePitchP[1] = samplePitchP;
-	mipmap.samplePitchP[2] = samplePitchP;
-	mipmap.samplePitchP[3] = samplePitchP;
-
-	mipmap.sampleMax[0] = sampleMax;
-	mipmap.sampleMax[1] = sampleMax;
-	mipmap.sampleMax[2] = sampleMax;
-	mipmap.sampleMax[3] = sampleMax;
+	mipmap.pitchP = sw::int4(pitchP);
+	mipmap.sliceP = sw::int4(sliceP);
+	mipmap.samplePitchP = sw::int4(samplePitchP);
+	mipmap.sampleMax = sw::int4(sampleMax);
 }
 
 void DescriptorSetLayout::WriteDescriptorSet(Device *device, const VkWriteDescriptorSet &writeDescriptorSet)