Implement seamless cubemap sampling.

The addressing is offset by 1 to account for the border. Note that this
could be avoided by locking at (0, 0) instead of (-1, -1) instead, but
then negative address offsets have to be allowed and this complicates
the subsequent calculations and only unsigned extension from 32-bit to
64-bit is typically for free during memory accesses.

Change-Id: I5fb843401d440a9d77d141782124a9c260765830
Reviewed-on: https://swiftshader-review.googlesource.com/13289
Tested-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Renderer/Sampler.cpp b/src/Renderer/Sampler.cpp
index 71a6eaf..5862996 100644
--- a/src/Renderer/Sampler.cpp
+++ b/src/Renderer/Sampler.cpp
@@ -61,6 +61,7 @@
 		sRGB = false;
 		gather = false;
 		highPrecisionFiltering = false;
+		border = 0;
 
 		swizzleR = SWIZZLE_RED;
 		swizzleG = SWIZZLE_GREEN;
@@ -117,7 +118,8 @@
 		{
 			Mipmap &mipmap = texture.mipmap[level];
 
-			mipmap.buffer[face] = surface->lockInternal(0, 0, 0, LOCK_UNLOCKED, PRIVATE);
+			border = surface->getBorder();
+			mipmap.buffer[face] = surface->lockInternal(-border, -border, 0, LOCK_UNLOCKED, PRIVATE);
 
 			if(face == 0)
 			{
@@ -456,7 +458,7 @@
 	{
 		if(textureType == TEXTURE_CUBE)
 		{
-			return ADDRESSING_CLAMP;
+			return border ? ADDRESSING_SEAMLESS : ADDRESSING_CLAMP;
 		}
 
 		return addressingModeU;
@@ -466,7 +468,7 @@
 	{
 		if(textureType == TEXTURE_CUBE)
 		{
-			return ADDRESSING_CLAMP;
+			return border ? ADDRESSING_SEAMLESS : ADDRESSING_CLAMP;
 		}
 
 		return addressingModeV;
diff --git a/src/Renderer/Sampler.hpp b/src/Renderer/Sampler.hpp
index ad2145c..9918acf 100644
--- a/src/Renderer/Sampler.hpp
+++ b/src/Renderer/Sampler.hpp
@@ -103,7 +103,8 @@
 		ADDRESSING_CLAMP,
 		ADDRESSING_MIRROR,
 		ADDRESSING_MIRRORONCE,
-		ADDRESSING_BORDER,
+		ADDRESSING_BORDER,     // Single color
+		ADDRESSING_SEAMLESS,   // Border of pixels
 		ADDRESSING_LAYER,
 		ADDRESSING_TEXELFETCH,
 
@@ -224,6 +225,7 @@
 		bool sRGB;
 		bool gather;
 		bool highPrecisionFiltering;
+		int border;
 
 		SwizzleType swizzleR;
 		SwizzleType swizzleG;
diff --git a/src/Shader/SamplerCore.cpp b/src/Shader/SamplerCore.cpp
index 1c97628..942cf87 100644
--- a/src/Shader/SamplerCore.cpp
+++ b/src/Shader/SamplerCore.cpp
@@ -316,7 +316,8 @@
 		{
 			// FIXME: YUV and sRGB are not supported by the floating point path
 			bool forceFloatFiltering = state.highPrecisionFiltering && !state.sRGB && !hasYuvFormat() && (state.textureFilter != FILTER_POINT);
-			if(hasFloatTexture() || hasUnnormalizedIntegerTexture() || forceFloatFiltering)   // FIXME: Mostly identical to integer sampling
+			bool seamlessCube = (state.addressingModeU == ADDRESSING_SEAMLESS);
+			if(hasFloatTexture() || hasUnnormalizedIntegerTexture() || forceFloatFiltering || seamlessCube)   // FIXME: Mostly identical to integer sampling
 			{
 				Float4 uuuu = u;
 				Float4 vvvv = v;
@@ -1666,7 +1667,7 @@
 
 		M = Max(Max(absX, absY), absZ);
 
-		// U = xMajor ? (neg ^ -z) : (zMajor & neg) ^ x)
+		// U = xMajor ? (neg ^ -z) : ((zMajor & neg) ^ x)
 		U = As<Float4>((xMajor & (n ^ As<Int4>(-z))) | (~xMajor & ((zMajor & n) ^ As<Int4>(x))));
 
 		// V = !yMajor ? -y : (n ^ z)
@@ -1695,6 +1696,8 @@
 			break;
 		case ADDRESSING_TEXELFETCH:
 			break;
+		case AddressingMode::ADDRESSING_SEAMLESS:
+			ASSERT(false);   // Cube sampling doesn't support offset.
 		default:
 			ASSERT(false);
 		}
@@ -2388,11 +2391,20 @@
 			const int oneBits  = 0x3F7FFFFF;   // Value just under 1.0f
 			const int twoBits  = 0x3FFFFFFF;   // Value just under 2.0f
 
+			bool pointFilter = state.textureFilter == FILTER_POINT ||
+			                   state.textureFilter == FILTER_MIN_POINT_MAG_LINEAR ||
+			                   state.textureFilter == FILTER_MIN_LINEAR_MAG_POINT;
+
 			Float4 coord = uvw;
+
 			switch(addressingMode)
 			{
 			case ADDRESSING_CLAMP:
 			case ADDRESSING_BORDER:
+			case ADDRESSING_SEAMLESS:
+				// Linear filtering of cube doesn't require clamping because the coordinates
+				// are already in [0, 1] range and numerical imprecision is tolerated.
+				if(addressingMode != ADDRESSING_SEAMLESS || pointFilter)
 				{
 					Float4 one = As<Float4>(Int4(oneBits));
 					coord = Min(Max(coord, Float4(0.0f)), one);
@@ -2448,12 +2460,19 @@
 				xyz0 += As<Int4>(texOffset);
 			}
 
+			if(addressingMode == ADDRESSING_SEAMLESS)
+			{
+				xyz0 += Int4(1);
+			}
+
 			xyz1 = xyz0 - filter;   // Increment
 
 			if(function.option == Offset)
 			{
 				switch(addressingMode)
 				{
+				case ADDRESSING_SEAMLESS:
+					ASSERT(false);   // Cube sampling doesn't support offset.
 				case ADDRESSING_MIRROR:
 				case ADDRESSING_MIRRORONCE:
 				case ADDRESSING_BORDER:
@@ -2473,6 +2492,8 @@
 			{
 				switch(addressingMode)
 				{
+				case ADDRESSING_SEAMLESS:
+					break;
 				case ADDRESSING_MIRROR:
 				case ADDRESSING_MIRRORONCE:
 				case ADDRESSING_BORDER: