Enabled 2D array textures

- Implemented mipmap generation and completeness checks for
  Texture2DArray.
- Fixed texture parameters setters and getters along with
  mipmap generation functions to use the proper 2D array
  texture instead of the 3D texture for 2D array textures.
- Enabled the same path as 3D texture for 2D array textures
  in the sampler.
- Added an address function for the w component, which
  simply clamps the rounded value for 2D array textures and
  adapted SamplerCore::computeIndices to this new behavior.

Change-Id: Ida0659afac75330bfd9af4052cfd2625c729f9ef
Reviewed-on: https://swiftshader-review.googlesource.com/4310
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <capn@google.com>
diff --git a/src/OpenGL/libGLESv2/Texture.cpp b/src/OpenGL/libGLESv2/Texture.cpp
index a20ff09..79bc28e 100644
--- a/src/OpenGL/libGLESv2/Texture.cpp
+++ b/src/OpenGL/libGLESv2/Texture.cpp
@@ -1725,8 +1725,10 @@
 	GLsizei width = image[mBaseLevel]->getWidth();

 	GLsizei height = image[mBaseLevel]->getHeight();

 	GLsizei depth = image[mBaseLevel]->getDepth();

+	bool isTexture2DArray = getTarget() == GL_TEXTURE_2D_ARRAY;

 

-	int q = std::min(log2(std::max(std::max(width, height), depth)), mMaxLevel);

+	int q = isTexture2DArray ? std::min(log2(std::max(width, height)), mMaxLevel) :

+	        std::min(log2(std::max(std::max(width, height), depth)), mMaxLevel);

 

 	for(int level = mBaseLevel + 1; level <= q; level++)

 	{

@@ -1755,7 +1757,8 @@
 			return false;

 		}

 

-		if(image[level]->getDepth() != std::max(1, depth >> level))

+		int levelDepth = isTexture2DArray ? depth : std::max(1, depth >> level);

+		if(image[level]->getDepth() != levelDepth)

 		{

 			return false;

 		}

@@ -1867,7 +1870,39 @@
 

 void Texture2DArray::generateMipmaps()

 {

-	UNIMPLEMENTED();

+	int depth = image[0] ? image[0]->getDepth() : 0;

+	if(!depth)

+	{

+		return;   // FIXME: error?

+	}

+

+	unsigned int q = log2(std::max(image[0]->getWidth(), image[0]->getHeight()));

+

+	for(unsigned int i = 1; i <= q; i++)

+	{

+		if(image[i])

+		{

+			image[i]->unbind(this);

+		}

+

+		GLsizei w = std::max(image[0]->getWidth() >> i, 1);

+		GLsizei h = std::max(image[0]->getHeight() >> i, 1);

+		image[i] = new egl::Image(this, w, h, depth, image[0]->getFormat(), image[0]->getType());

+

+		if(!image[i])

+		{

+			return error(GL_OUT_OF_MEMORY);

+		}

+

+		GLsizei srcw = image[i - 1]->getWidth();

+		GLsizei srch = image[i - 1]->getHeight();

+		for(int z = 0; z < depth; ++z)

+		{

+			sw::SliceRect srcRect(0, 0, srcw, srch, z);

+			sw::SliceRect dstRect(0, 0, w, h, z);

+			getDevice()->stretchRect(image[i - 1], &srcRect, image[i], &dstRect, true);

+		}

+	}

 }

 

 TextureExternal::TextureExternal(GLuint name) : Texture2D(name)

diff --git a/src/OpenGL/libGLESv2/libGLESv2.cpp b/src/OpenGL/libGLESv2/libGLESv2.cpp
index 1e140af..e4c568c 100644
--- a/src/OpenGL/libGLESv2/libGLESv2.cpp
+++ b/src/OpenGL/libGLESv2/libGLESv2.cpp
@@ -2414,10 +2414,9 @@
 			}

 			else

 			{

-				UNIMPLEMENTED();

-				texture = context->getTexture3D();

-				break;

+				texture = context->getTexture2DArray();

 			}

+			break;

 		case GL_TEXTURE_3D_OES:

 			texture = context->getTexture3D();

 			break;

@@ -3716,10 +3715,9 @@
 			}

 			else

 			{

-				UNIMPLEMENTED();

-				texture = context->getTexture3D();

-				break;

+				texture = context->getTexture2DArray();

 			}

+			break;

 		case GL_TEXTURE_3D_OES:

 			texture = context->getTexture3D();

 			break;

@@ -3863,10 +3861,9 @@
 			}

 			else

 			{

-				UNIMPLEMENTED();

-				texture = context->getTexture3D();

-				break;

+				texture = context->getTexture2DArray();

 			}

+			break;

 		case GL_TEXTURE_3D_OES:

 			texture = context->getTexture3D();

 			break;

@@ -5955,10 +5952,9 @@
 			}

 			else

 			{

-				UNIMPLEMENTED();

-				texture = context->getTexture3D();

-				break;

+				texture = context->getTexture2DArray();

 			}

+			break;

 		case GL_TEXTURE_3D_OES:

 			texture = context->getTexture3D();

 			break;

@@ -6111,10 +6107,9 @@
 			}

 			else

 			{

-				UNIMPLEMENTED();

-				texture = context->getTexture3D();

-				break;

+				texture = context->getTexture2DArray();

 			}

+			break;

 		case GL_TEXTURE_3D_OES:

 			texture = context->getTexture3D();

 			break;

diff --git a/src/Renderer/Sampler.cpp b/src/Renderer/Sampler.cpp
index 5859b56..3de645c 100644
--- a/src/Renderer/Sampler.cpp
+++ b/src/Renderer/Sampler.cpp
@@ -368,7 +368,7 @@
 
 	bool Sampler::hasVolumeTexture() const
 	{
-		return textureType == TEXTURE_3D;
+		return textureType == TEXTURE_3D || textureType == TEXTURE_2D_ARRAY;
 	}
 
 	const Texture &Sampler::getTextureData()
diff --git a/src/Shader/SamplerCore.cpp b/src/Shader/SamplerCore.cpp
index 1bc2c4d..6fb8fda 100644
--- a/src/Shader/SamplerCore.cpp
+++ b/src/Shader/SamplerCore.cpp
@@ -63,7 +63,7 @@
 		#endif
 
 		bool cubeTexture = state.textureType == TEXTURE_CUBE;
-		bool volumeTexture = state.textureType == TEXTURE_3D;
+		bool volumeTexture = state.textureType == TEXTURE_3D || state.textureType == TEXTURE_2D_ARRAY;
 
 		Float4 uuuu = u;
 		Float4 vvvv = v;
@@ -302,7 +302,7 @@
 		#endif
 
 		bool cubeTexture = state.textureType == TEXTURE_CUBE;
-		bool volumeTexture = state.textureType == TEXTURE_3D;
+		bool volumeTexture = state.textureType == TEXTURE_3D || state.textureType == TEXTURE_2D_ARRAY;
 
 		if(state.textureType == TEXTURE_NULL)
 		{
@@ -590,7 +590,7 @@
 
 	void SamplerCore::sampleFilter(Pointer<Byte> &texture, Vector4s &c, Float4 &u, Float4 &v, Float4 &w, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, Int face[4], bool lodProvided)
 	{
-		bool volumeTexture = state.textureType == TEXTURE_3D;
+		bool volumeTexture = state.textureType == TEXTURE_3D || state.textureType == TEXTURE_2D_ARRAY;
 
 		sampleAniso(texture, c, u, v, w, lod, anisotropy, uDelta, vDelta, face, false, lodProvided);
 
@@ -744,7 +744,7 @@
 
 	void SamplerCore::sampleQuad(Pointer<Byte> &texture, Vector4s &c, Float4 &u, Float4 &v, Float4 &w, Float &lod, Int face[4], bool secondLOD)
 	{
-		if(state.textureType != TEXTURE_3D)
+		if(state.textureType != TEXTURE_3D && state.textureType != TEXTURE_2D_ARRAY)
 		{
 			sampleQuad2D(texture, c, u, v, lod, face, secondLOD);
 		}
@@ -977,7 +977,7 @@
 
 		address(uuuu, u_, state.addressingModeU);
 		address(vvvv, v_, state.addressingModeV);
-		address(wwww, w_, state.addressingModeW);
+		addressW(wwww, w_, mipmap);
 
 		if(state.textureFilter <= FILTER_POINT)
 		{
@@ -1098,7 +1098,7 @@
 
 	void SamplerCore::sampleFloatFilter(Pointer<Byte> &texture, Vector4f &c, Float4 &u, Float4 &v, Float4 &w, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, Int face[4], bool lodProvided)
 	{
-		bool volumeTexture = state.textureType == TEXTURE_3D;
+		bool volumeTexture = state.textureType == TEXTURE_3D || state.textureType == TEXTURE_2D_ARRAY;
 
 		sampleFloatAniso(texture, c, u, v, w, lod, anisotropy, uDelta, vDelta, face, false, lodProvided);
 
@@ -1229,7 +1229,7 @@
 
 	void SamplerCore::sampleFloat(Pointer<Byte> &texture, Vector4f &c, Float4 &u, Float4 &v, Float4 &w, Float &lod, Int face[4], bool secondLOD)
 	{
-		if(state.textureType != TEXTURE_3D)
+		if(state.textureType != TEXTURE_3D && state.textureType != TEXTURE_2D_ARRAY)
 		{
 			sampleFloat2D(texture, c, u, v, w, lod, face, secondLOD);
 		}
@@ -1323,7 +1323,7 @@
 
 		address(uuuu, u, state.addressingModeU);
 		address(vvvv, v, state.addressingModeV);
-		address(wwww, w, state.addressingModeW);
+		addressW(wwww, w, mipmap);
 
 		if(state.textureFilter <= FILTER_POINT)
 		{
@@ -1625,22 +1625,6 @@
 			uuu2 = As<Short4>(UnpackHigh(uuu2, vvvv));
 			uuuu = As<Short4>(As<UInt2>(uuuu) >> *Pointer<Long1>(mipmap + OFFSET(Mipmap,uFrac)));
 			uuu2 = As<Short4>(As<UInt2>(uuu2) >> *Pointer<Long1>(mipmap + OFFSET(Mipmap,uFrac)));
-
-			if(state.textureType == TEXTURE_3D)
-			{
-				wwww = As<UShort4>(wwww) >> *Pointer<Long1>(mipmap + OFFSET(Mipmap,wFrac));
-				Short4 www2 = wwww;
-				wwww = As<Short4>(UnpackLow(wwww, wwww));
-				www2 = As<Short4>(UnpackHigh(www2, www2));
-				wwww = As<Short4>(As<UInt2>(wwww) >> 16);
-				www2 = As<Short4>(As<UInt2>(www2) >> 16);
-				wwww = As<Short4>(As<Int2>(wwww) << *Pointer<Long1>(mipmap + OFFSET(Mipmap,uInt)));
-				www2 = As<Short4>(As<Int2>(www2) << *Pointer<Long1>(mipmap + OFFSET(Mipmap,uInt)));
-				wwww = As<Short4>(As<Int2>(wwww) << *Pointer<Long1>(mipmap + OFFSET(Mipmap,vInt)));   // FIXME: Combine uInt and vInt shift
-				www2 = As<Short4>(As<Int2>(www2) << *Pointer<Long1>(mipmap + OFFSET(Mipmap,vInt)));
-				uuuu = As<Short4>(As<Int2>(uuuu) + As<Int2>(wwww));
-				uuu2 = As<Short4>(As<Int2>(uuu2) + As<Int2>(www2));
-			}
 		}
 		else
 		{
@@ -1651,18 +1635,21 @@
 			uuu2 = As<Short4>(UnpackHigh(uuu2, vvvv));
 			uuuu = As<Short4>(MulAdd(uuuu, *Pointer<Short4>(mipmap + OFFSET(Mipmap,onePitchP))));
 			uuu2 = As<Short4>(MulAdd(uuu2, *Pointer<Short4>(mipmap + OFFSET(Mipmap,onePitchP))));
+		}
 
-			if(state.textureType == TEXTURE_3D)
+		if((state.textureType == TEXTURE_3D) || (state.textureType == TEXTURE_2D_ARRAY))
+		{
+			if(state.textureType != TEXTURE_2D_ARRAY)
 			{
-				wwww = MulHigh(As<UShort4>(wwww), *Pointer<UShort4>(mipmap + OFFSET(Mipmap,depth)));
-				Short4 www2 = wwww;
-				wwww = As<Short4>(UnpackLow(wwww, Short4(0x0000, 0x0000, 0x0000, 0x0000)));
-				www2 = As<Short4>(UnpackHigh(www2, Short4(0x0000, 0x0000, 0x0000, 0x0000)));
-				wwww = As<Short4>(MulAdd(wwww, *Pointer<Short4>(mipmap + OFFSET(Mipmap,sliceP))));
-				www2 = As<Short4>(MulAdd(www2, *Pointer<Short4>(mipmap + OFFSET(Mipmap,sliceP))));
-				uuuu = As<Short4>(As<Int2>(uuuu) + As<Int2>(wwww));
-				uuu2 = As<Short4>(As<Int2>(uuu2) + As<Int2>(www2));
+				wwww = MulHigh(As<UShort4>(wwww), *Pointer<UShort4>(mipmap + OFFSET(Mipmap, depth)));
 			}
+			Short4 www2 = wwww;
+			wwww = As<Short4>(UnpackLow(wwww, Short4(0x0000, 0x0000, 0x0000, 0x0000)));
+			www2 = As<Short4>(UnpackHigh(www2, Short4(0x0000, 0x0000, 0x0000, 0x0000)));
+			wwww = As<Short4>(MulAdd(wwww, *Pointer<Short4>(mipmap + OFFSET(Mipmap,sliceP))));
+			www2 = As<Short4>(MulAdd(www2, *Pointer<Short4>(mipmap + OFFSET(Mipmap,sliceP))));
+			uuuu = As<Short4>(As<Int2>(uuuu) + As<Int2>(wwww));
+			uuu2 = As<Short4>(As<Int2>(uuu2) + As<Int2>(www2));
 		}
 
 		index[0] = Extract(As<Int2>(uuuu), 0);
@@ -2071,6 +2058,18 @@
 		}
 	}
 
+	void SamplerCore::addressW(Short4 &wwww, Float4 &w, Pointer<Byte>& mipmap)
+	{
+		if(state.textureType == TEXTURE_2D_ARRAY)
+		{
+			wwww = Min(Max(Short4(RoundInt(w)), Short4(0)), *Pointer<Short4>(mipmap + OFFSET(Mipmap, depth)) - Short4(1));
+		}
+		else
+		{
+			address(wwww, w, state.addressingModeW);
+		}
+	}
+
 	void SamplerCore::convertFixed12(Short4 &cs, Float4 &cf)
 	{
 		cs = RoundShort4(cf * Float4(0x1000));
diff --git a/src/Shader/SamplerCore.hpp b/src/Shader/SamplerCore.hpp
index 12c6cd6..e8c9905 100644
--- a/src/Shader/SamplerCore.hpp
+++ b/src/Shader/SamplerCore.hpp
@@ -47,6 +47,7 @@
 		void sampleTexel(Vector4f &c, Short4 &u, Short4 &v, Short4 &s, Float4 &z, Pointer<Byte> &mipmap, Pointer<Byte> buffer[4]);

 		void selectMipmap(Pointer<Byte> &texture, Pointer<Byte> buffer[4], Pointer<Byte> &mipmap, Float &lod, Int face[4], bool secondLOD);

 		void address(Short4 &uuuu, Float4 &uw, AddressingMode addressingMode);

+		void addressW(Short4 &uuuu, Float4 &uw, Pointer<Byte>& mipmap);

 

 		void convertFixed12(Short4 &ci, Float4 &cf);

 		void convertFixed12(Vector4s &cs, Vector4f &cf);