Implement cubemap sampling

This still uses the legacy approach with 6 pointers per mipmap level
for each cube face. Will be refactored to use a single pointer to access
faces as consecutive layers.

Bug: b/129523279
Tests: dEQP-VK.texture.filtering.cube.formats.r8g8b8a8_unorm.*
Change-Id: I1f59f121d2989e0ba79eb3441529b812a8d9111a
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/29969
Presubmit-Ready: Nicolas Capens <nicolascapens@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
diff --git a/src/Device/Renderer.cpp b/src/Device/Renderer.cpp
index 03de441..9886bce 100644
--- a/src/Device/Renderer.cpp
+++ b/src/Device/Renderer.cpp
@@ -504,7 +504,7 @@
 
 				if(draw->renderTarget[index])
 				{
-					data->colorBuffer[index] = (unsigned int*)context->renderTarget[index]->getOffsetPointer({0, 0, 0}, VK_IMAGE_ASPECT_COLOR_BIT, 0);
+					data->colorBuffer[index] = (unsigned int*)context->renderTarget[index]->getOffsetPointer({0, 0, 0}, VK_IMAGE_ASPECT_COLOR_BIT, 0, 0);
 					data->colorPitchB[index] = context->renderTarget[index]->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
 					data->colorSliceB[index] = context->renderTarget[index]->slicePitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
 				}
@@ -515,14 +515,14 @@
 
 			if(draw->depthBuffer)
 			{
-				data->depthBuffer = (float*)context->depthBuffer->getOffsetPointer({0, 0, 0}, VK_IMAGE_ASPECT_DEPTH_BIT, 0);
+				data->depthBuffer = (float*)context->depthBuffer->getOffsetPointer({0, 0, 0}, VK_IMAGE_ASPECT_DEPTH_BIT, 0, 0);
 				data->depthPitchB = context->depthBuffer->rowPitchBytes(VK_IMAGE_ASPECT_DEPTH_BIT, 0);
 				data->depthSliceB = context->depthBuffer->slicePitchBytes(VK_IMAGE_ASPECT_DEPTH_BIT, 0);
 			}
 
 			if(draw->stencilBuffer)
 			{
-				data->stencilBuffer = (unsigned char*)context->stencilBuffer->getOffsetPointer({0, 0, 0}, VK_IMAGE_ASPECT_STENCIL_BIT, 0);
+				data->stencilBuffer = (unsigned char*)context->stencilBuffer->getOffsetPointer({0, 0, 0}, VK_IMAGE_ASPECT_STENCIL_BIT, 0, 0);
 				data->stencilPitchB = context->stencilBuffer->rowPitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT, 0);
 				data->stencilSliceB = context->stencilBuffer->slicePitchBytes(VK_IMAGE_ASPECT_STENCIL_BIT, 0);
 			}
diff --git a/src/Pipeline/SamplerCore.cpp b/src/Pipeline/SamplerCore.cpp
index 2d675d6..377d10a 100644
--- a/src/Pipeline/SamplerCore.cpp
+++ b/src/Pipeline/SamplerCore.cpp
@@ -24,16 +24,17 @@
 	{
 		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_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.0f, 1.0f, 1.0f, 1.0f));
-			} else
+				f = rr::As<sw::Float4>(sw::Int4(1, 1, 1, 1));
+			}
+			else
 			{
 				f = sw::Float4(1.0f, 1.0f, 1.0f, 1.0f);
 			}
@@ -127,7 +128,7 @@
 		{
 			Vector4s cs = sampleFilter(texture, uuuu, vvvv, wwww, offset, lod, anisotropy, uDelta, vDelta, face, function);
 
-			if(state.textureFormat ==  VK_FORMAT_R5G6B5_UNORM_PACK16)
+			if(state.textureFormat == VK_FORMAT_R5G6B5_UNORM_PACK16)
 			{
 				c.x = Float4(As<UShort4>(cs.x)) * Float4(1.0f / 0xF800);
 				c.y = Float4(As<UShort4>(cs.y)) * Float4(1.0f / 0xFC00);
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 2068bcc..995a47a 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -879,7 +879,7 @@
 		static sw::TextureType convertTextureType(VkImageViewType imageViewType);
 		static sw::FilterType convertFilterMode(const vk::Sampler *sampler);
 		static sw::MipmapType convertMipmapMode(const vk::Sampler *sampler);
-		static sw::AddressingMode convertAddressingMode(VkSamplerAddressMode);
+		static sw::AddressingMode convertAddressingMode(VkSamplerAddressMode addressMode, VkImageViewType imageViewType);
 	};
 
 	class SpirvRoutine
diff --git a/src/Pipeline/SpirvShaderSampling.cpp b/src/Pipeline/SpirvShaderSampling.cpp
index ea4a802..593cdbb 100644
--- a/src/Pipeline/SpirvShaderSampling.cpp
+++ b/src/Pipeline/SpirvShaderSampling.cpp
@@ -43,6 +43,7 @@
 {
 	return getImageSampler(Implicit, imageView, sampler);
 }
+
 SpirvShader::ImageSampler *SpirvShader::getImageSamplerExplicitLod(const vk::ImageView *imageView, const vk::Sampler *sampler)
 {
 	return getImageSampler(Lod, imageView, sampler);
@@ -83,9 +84,9 @@
 	samplerState.textureFormat = imageView->getFormat();
 	samplerState.textureFilter = convertFilterMode(sampler);
 
-	samplerState.addressingModeU = convertAddressingMode(sampler->addressModeU);
-	samplerState.addressingModeV = convertAddressingMode(sampler->addressModeV);
-	samplerState.addressingModeW = convertAddressingMode(sampler->addressModeW);
+	samplerState.addressingModeU = convertAddressingMode(sampler->addressModeU, imageView->getType());
+	samplerState.addressingModeV = convertAddressingMode(sampler->addressModeV, imageView->getType());
+	samplerState.addressingModeW = convertAddressingMode(sampler->addressModeW, imageView->getType());
 	samplerState.mipmapFilter = convertMipmapMode(sampler);
 	samplerState.sRGB = imageView->getFormat().isSRGBformat();
 	samplerState.swizzle = imageView->getComponentMapping();
@@ -102,8 +103,7 @@
 	SamplerCore s(constants, samplerState);
 
 	Pointer<Byte> texture = image + OFFSET(vk::SampledImageDescriptor, texture);  // sw::Texture*
-	SIMD::Float uv[2];
-	SIMD::Float w(0);     // TODO(b/129523279)
+	SIMD::Float uvw[3];
 	SIMD::Float q(0);     // TODO(b/129523279)
 	SIMD::Float bias(0);
 	Vector4f dsx;         // TODO(b/129523279)
@@ -111,22 +111,31 @@
 	Vector4f offset;      // TODO(b/129523279)
 	SamplerFunction samplerFunction = { samplerMethod, None };  // TODO(b/129523279)
 
-	// TODO(b/129523279): Currently 1D textures are treated as 2D by setting the second coordinate to 0.
-	// Implement optimized 1D sampling.
-	uv[1] = SIMD::Float(0);
-
 	int coordinateCount = 0;
 	switch(imageView->getType())
 	{
-	case VK_IMAGE_VIEW_TYPE_1D: coordinateCount = 1; break;
-	case VK_IMAGE_VIEW_TYPE_2D: coordinateCount = 2; break;
+	case VK_IMAGE_VIEW_TYPE_1D:         coordinateCount = 1; break;
+	case VK_IMAGE_VIEW_TYPE_2D:         coordinateCount = 2; break;
+//	case VK_IMAGE_VIEW_TYPE_3D:         coordinateCount = 3; break;
+	case VK_IMAGE_VIEW_TYPE_CUBE:       coordinateCount = 3; break;
+//	case VK_IMAGE_VIEW_TYPE_1D_ARRAY:   coordinateCount = 2; break;
+//	case VK_IMAGE_VIEW_TYPE_2D_ARRAY:   coordinateCount = 3; break;
+//	case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY: coordinateCount = 4; break;
 	default:
 		UNIMPLEMENTED("imageView type %d", imageView->getType());
 	}
 
 	for(int i = 0; i < coordinateCount; i++)
 	{
-		uv[i] = in[i];
+		uvw[i] = in[i];
+	}
+
+	// TODO(b/129523279): Currently 1D textures are treated as 2D by setting the second coordinate to 0.
+	// Implement optimized 1D sampling.
+	If(imageView->getType() == VK_IMAGE_VIEW_TYPE_1D ||
+	   imageView->getType() == VK_IMAGE_VIEW_TYPE_1D_ARRAY)
+	{
+		uvw[1] = SIMD::Float(0);
 	}
 
 	if(samplerMethod == Lod)
@@ -136,7 +145,7 @@
 		bias = in[coordinateCount];
 	}
 
-	Vector4f sample = s.sampleTexture(texture, uv[0], uv[1], w, q, bias, dsx, dsy, offset, samplerFunction);
+	Vector4f sample = s.sampleTexture(texture, uvw[0], uvw[1], uvw[2], q, bias, dsx, dsy, offset, samplerFunction);
 
 	Pointer<SIMD::Float> rgba = out;
 	rgba[0] = sample.x;
@@ -152,7 +161,7 @@
 	case VK_IMAGE_VIEW_TYPE_1D:         return TEXTURE_1D;
 	case VK_IMAGE_VIEW_TYPE_2D:         return TEXTURE_2D;
 //	case VK_IMAGE_VIEW_TYPE_3D:         return TEXTURE_3D;
-//	case VK_IMAGE_VIEW_TYPE_CUBE:       return TEXTURE_CUBE;
+	case VK_IMAGE_VIEW_TYPE_CUBE:       return TEXTURE_CUBE;
 //	case VK_IMAGE_VIEW_TYPE_1D_ARRAY:   return TEXTURE_1D_ARRAY;
 //	case VK_IMAGE_VIEW_TYPE_2D_ARRAY:   return TEXTURE_2D_ARRAY;
 //	case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY: return TEXTURE_CUBE_ARRAY;
@@ -204,8 +213,29 @@
 	}
 }
 
-sw::AddressingMode SpirvShader::convertAddressingMode(VkSamplerAddressMode addressMode)
+sw::AddressingMode SpirvShader::convertAddressingMode(VkSamplerAddressMode addressMode, VkImageViewType imageViewType)
 {
+	// Vulkan 1.1 spec:
+	// "Cube images ignore the wrap modes specified in the sampler. Instead, if VK_FILTER_NEAREST is used within a mip level then
+	//  VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE is used, and if VK_FILTER_LINEAR is used within a mip level then sampling at the edges
+	//  is performed as described earlier in the Cube map edge handling section."
+	// This corresponds with our 'seamless' addressing mode.
+	switch(imageViewType)
+	{
+	case VK_IMAGE_VIEW_TYPE_CUBE:
+	case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
+		return ADDRESSING_SEAMLESS;
+	case VK_IMAGE_VIEW_TYPE_1D:
+	case VK_IMAGE_VIEW_TYPE_2D:
+//	case VK_IMAGE_VIEW_TYPE_3D:
+//	case VK_IMAGE_VIEW_TYPE_1D_ARRAY:
+//	case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
+		break;
+	default:
+		UNIMPLEMENTED("imageViewType %d", imageViewType);
+		return ADDRESSING_WRAP;
+	}
+
 	switch(addressMode)
 	{
 	case VK_SAMPLER_ADDRESS_MODE_REPEAT:               return ADDRESSING_WRAP;
diff --git a/src/Vulkan/VkDescriptorSetLayout.cpp b/src/Vulkan/VkDescriptorSetLayout.cpp
index 5442e45..a383c22 100644
--- a/src/Vulkan/VkDescriptorSetLayout.cpp
+++ b/src/Vulkan/VkDescriptorSetLayout.cpp
@@ -301,12 +301,26 @@
 				int level = mipmapLevel - baseLevel;  // Level within the image view
 				level = sw::clamp(level, 0, (int)subresourceRange.levelCount - 1);
 
-				VkOffset3D offset = {0, 0, 0};
 				VkImageAspectFlagBits aspect = VK_IMAGE_ASPECT_COLOR_BIT;
-				void *buffer = imageView->getOffsetPointer(offset, aspect, level);
-
 				sw::Mipmap &mipmap = texture->mipmap[mipmapLevel];
-				mipmap.buffer[0] = buffer;
+
+				if(imageView->getType() == VK_IMAGE_VIEW_TYPE_CUBE)
+				{
+					for(int face = 0; face < 6; face++)
+					{
+						// Obtain the pointer to the corner of the level including the border, for seamless sampling.
+						// This is taken into account in the sampling routine, which can't handle negative texel coordinates.
+						VkOffset3D offset = {-1, -1, 0};
+
+						// TODO(b/129523279): Implement as 6 consecutive layers instead of separate pointers.
+						mipmap.buffer[face] = imageView->getOffsetPointer(offset, aspect, level, face);
+					}
+				}
+				else
+				{
+					VkOffset3D offset = {0, 0, 0};
+					mipmap.buffer[0] = imageView->getOffsetPointer(offset, aspect, level, 0);
+				}
 
 				VkExtent3D extent = imageView->getMipLevelExtent(level);
 				Format format = imageView->getFormat();
@@ -445,7 +459,7 @@
 		{
 			auto update = reinterpret_cast<VkDescriptorImageInfo const *>(src + entry.offset + entry.stride * i);
 			auto imageView = Cast(update->imageView);
-			descriptor[i].ptr = imageView->getOffsetPointer({0, 0, 0}, VK_IMAGE_ASPECT_COLOR_BIT, 0);
+			descriptor[i].ptr = imageView->getOffsetPointer({0, 0, 0}, VK_IMAGE_ASPECT_COLOR_BIT, 0, 0);
 			descriptor[i].extent = imageView->getMipLevelExtent(0);
 			descriptor[i].rowPitchBytes = imageView->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
 			descriptor[i].slicePitchBytes = imageView->getSubresourceRange().layerCount > 1
diff --git a/src/Vulkan/VkImageView.cpp b/src/Vulkan/VkImageView.cpp
index 65e1245..23f79c8 100644
--- a/src/Vulkan/VkImageView.cpp
+++ b/src/Vulkan/VkImageView.cpp
@@ -171,7 +171,7 @@
 	image->copyTo(*(resolveAttachment->image), region);
 }
 
-void *ImageView::getOffsetPointer(const VkOffset3D& offset, VkImageAspectFlagBits aspect, uint32_t mipLevel) const
+void *ImageView::getOffsetPointer(const VkOffset3D& offset, VkImageAspectFlagBits aspect, uint32_t mipLevel, uint32_t layer) const
 {
 	ASSERT(mipLevel < subresourceRange.levelCount);
 
@@ -179,7 +179,7 @@
 	{
 		static_cast<VkImageAspectFlags>(aspect),
 		subresourceRange.baseMipLevel + mipLevel,
-		subresourceRange.baseArrayLayer,
+		subresourceRange.baseArrayLayer + layer,
 		subresourceRange.layerCount
 	};
 	return image->getTexelPointer(offset, imageSubresourceLayers);
diff --git a/src/Vulkan/VkImageView.hpp b/src/Vulkan/VkImageView.hpp
index 96d7760..fad7bfb 100644
--- a/src/Vulkan/VkImageView.hpp
+++ b/src/Vulkan/VkImageView.hpp
@@ -46,7 +46,7 @@
 	int layerPitchBytes(VkImageAspectFlagBits aspect) const { return static_cast<int>(image->getLayerSize(aspect)); }
 	VkExtent3D getMipLevelExtent(uint32_t mipLevel) const { return image->getMipLevelExtent(subresourceRange.baseMipLevel + mipLevel); }
 
-	void *getOffsetPointer(const VkOffset3D& offset, VkImageAspectFlagBits aspect, uint32_t mipLevel) const;
+	void *getOffsetPointer(const VkOffset3D& offset, VkImageAspectFlagBits aspect, uint32_t mipLevel, uint32_t layer) const;
 	bool hasDepthAspect() const { return (subresourceRange.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0; }
 	bool hasStencilAspect() const { return (subresourceRange.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0; }