Implement mipmap base/max level.

Change-Id: I611815fb0dcbba97e67f3c146dffb463f133447e
Reviewed-on: https://swiftshader-review.googlesource.com/15248
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/OpenGL/libGLESv2/Context.cpp b/src/OpenGL/libGLESv2/Context.cpp
index 8590b54..6d6be82 100644
--- a/src/OpenGL/libGLESv2/Context.cpp
+++ b/src/OpenGL/libGLESv2/Context.cpp
@@ -3172,7 +3172,7 @@
 	}
 	else UNREACHABLE(type);
 
-	sw::Resource *resource = 0;
+	sw::Resource *resource = nullptr;
 
 	if(baseTexture && textureUsed)
 	{
@@ -3183,7 +3183,8 @@
 
 	if(baseTexture && textureUsed)
 	{
-		int topLevel = baseTexture->getTopLevel();
+		int baseLevel = baseTexture->getBaseLevel();
+		int maxLevel = std::min(baseTexture->getTopLevel(), baseTexture->getMaxLevel());
 
 		if(baseTexture->getTarget() == GL_TEXTURE_2D || baseTexture->getTarget() == GL_TEXTURE_EXTERNAL_OES)
 		{
@@ -3193,13 +3194,13 @@
 			{
 				int surfaceLevel = mipmapLevel;
 
-				if(surfaceLevel < 0)
+				if(surfaceLevel < baseLevel)
 				{
-					surfaceLevel = 0;
+					surfaceLevel = baseLevel;
 				}
-				else if(surfaceLevel > topLevel)
+				else if(surfaceLevel > maxLevel)
 				{
-					surfaceLevel = topLevel;
+					surfaceLevel = maxLevel;
 				}
 
 				egl::Image *surface = texture->getImage(surfaceLevel);
@@ -3214,13 +3215,13 @@
 			{
 				int surfaceLevel = mipmapLevel;
 
-				if(surfaceLevel < 0)
+				if(surfaceLevel < baseLevel)
 				{
-					surfaceLevel = 0;
+					surfaceLevel = baseLevel;
 				}
-				else if(surfaceLevel > topLevel)
+				else if(surfaceLevel > maxLevel)
 				{
-					surfaceLevel = topLevel;
+					surfaceLevel = maxLevel;
 				}
 
 				egl::Image *surface = texture->getImage(surfaceLevel);
@@ -3235,13 +3236,13 @@
 			{
 				int surfaceLevel = mipmapLevel;
 
-				if(surfaceLevel < 0)
+				if(surfaceLevel < baseLevel)
 				{
-					surfaceLevel = 0;
+					surfaceLevel = baseLevel;
 				}
-				else if(surfaceLevel > topLevel)
+				else if(surfaceLevel > maxLevel)
 				{
-					surfaceLevel = topLevel;
+					surfaceLevel = maxLevel;
 				}
 
 				egl::Image *surface = texture->getImage(surfaceLevel);
@@ -3260,13 +3261,13 @@
 				{
 					int surfaceLevel = mipmapLevel;
 
-					if(surfaceLevel < 0)
+					if(surfaceLevel < baseLevel)
 					{
-						surfaceLevel = 0;
+						surfaceLevel = baseLevel;
 					}
-					else if(surfaceLevel > topLevel)
+					else if(surfaceLevel > maxLevel)
 					{
-						surfaceLevel = topLevel;
+						surfaceLevel = maxLevel;
 					}
 
 					egl::Image *surface = cubeTexture->getImage(face, surfaceLevel);
diff --git a/src/OpenGL/libGLESv2/Texture.cpp b/src/OpenGL/libGLESv2/Texture.cpp
index fa5817d..e5202a2 100644
--- a/src/OpenGL/libGLESv2/Texture.cpp
+++ b/src/OpenGL/libGLESv2/Texture.cpp
@@ -182,6 +182,11 @@
 
 bool Texture::setBaseLevel(GLint baseLevel)
 {
+	if(baseLevel < 0)
+	{
+		return false;
+	}
+
 	mBaseLevel = baseLevel;
 	return true;
 }
@@ -844,12 +849,14 @@
 			return false;
 		}
 
-		if(image[level]->getWidth() != std::max(1, width >> level))
+		int i = level - mBaseLevel;
+
+		if(image[level]->getWidth() != std::max(1, width >> i))
 		{
 			return false;
 		}
 
-		if(image[level]->getHeight() != std::max(1, height >> level))
+		if(image[level]->getHeight() != std::max(1, height >> i))
 		{
 			return false;
 		}
@@ -1213,7 +1220,9 @@
 				return false;
 			}
 
-			if(image[face][level]->getWidth() != std::max(1, size >> level))
+			int i = level - mBaseLevel;
+
+			if(image[face][level]->getWidth() != std::max(1, size >> i))
 			{
 				return false;
 			}
@@ -1833,17 +1842,19 @@
 			return false;
 		}
 
-		if(image[level]->getWidth() != std::max(1, width >> level))
+		int i = level - mBaseLevel;
+
+		if(image[level]->getWidth() != std::max(1, width >> i))
 		{
 			return false;
 		}
 
-		if(image[level]->getHeight() != std::max(1, height >> level))
+		if(image[level]->getHeight() != std::max(1, height >> i))
 		{
 			return false;
 		}
 
-		int levelDepth = isTexture2DArray ? depth : std::max(1, depth >> level);
+		int levelDepth = isTexture2DArray ? depth : std::max(1, depth >> i);
 		if(image[level]->getDepth() != levelDepth)
 		{
 			return false;
diff --git a/src/Shader/SamplerCore.cpp b/src/Shader/SamplerCore.cpp
index 8aebbd8..53383cd 100644
--- a/src/Shader/SamplerCore.cpp
+++ b/src/Shader/SamplerCore.cpp
@@ -525,17 +525,17 @@
 		mask = As<Int4>(CmpLT(Abs(coordinates - Float4(0.5f)), Float4(0.5f)));
 	}
 
-	Short4 SamplerCore::offsetSample(Short4 &uvw, Pointer<Byte> &mipmap, int halfOffset, bool wrap, int count, Float &lod)
+	Short4 SamplerCore::offsetSample(Pointer<Byte> &texture, Short4 &uvw, Pointer<Byte> &mipmap, int halfOffset, bool wrap, int count, Float &lod)
 	{
 		Short4 offset = *Pointer<Short4>(mipmap + halfOffset);
 
 		if(state.textureFilter == FILTER_MIN_LINEAR_MAG_POINT)
 		{
-			offset &= Short4(CmpNLE(Float4(lod), Float4(0.0f)));
+			offset &= Short4(CmpNLE(Float4(lod), Float4(Float(*Pointer<Int>(texture + OFFSET(Texture,baseLevel))))));
 		}
 		else if(state.textureFilter == FILTER_MIN_POINT_MAG_LINEAR)
 		{
-			offset &= Short4(CmpLE(Float4(lod), Float4(0.0f)));
+			offset &= Short4(CmpLE(Float4(lod), Float4(Float(*Pointer<Int>(texture + OFFSET(Texture,baseLevel))))));
 		}
 
 		if(wrap)
@@ -759,10 +759,10 @@
 		}
 		else
 		{
-			Short4 uuuu0 = offsetSample(uuuu, mipmap, OFFSET(Mipmap,uHalf), state.addressingModeU == ADDRESSING_WRAP, gather ? 0 : -1, lod);
-			Short4 vvvv0 = offsetSample(vvvv, mipmap, OFFSET(Mipmap,vHalf), state.addressingModeV == ADDRESSING_WRAP, gather ? 0 : -1, lod);
-			Short4 uuuu1 = offsetSample(uuuu, mipmap, OFFSET(Mipmap,uHalf), state.addressingModeU == ADDRESSING_WRAP, gather ? 2 : +1, lod);
-			Short4 vvvv1 = offsetSample(vvvv, mipmap, OFFSET(Mipmap,vHalf), state.addressingModeV == ADDRESSING_WRAP, gather ? 2 : +1, lod);
+			Short4 uuuu0 = offsetSample(texture, uuuu, mipmap, OFFSET(Mipmap,uHalf), state.addressingModeU == ADDRESSING_WRAP, gather ? 0 : -1, lod);
+			Short4 vvvv0 = offsetSample(texture, vvvv, mipmap, OFFSET(Mipmap,vHalf), state.addressingModeV == ADDRESSING_WRAP, gather ? 0 : -1, lod);
+			Short4 uuuu1 = offsetSample(texture, uuuu, mipmap, OFFSET(Mipmap,uHalf), state.addressingModeU == ADDRESSING_WRAP, gather ? 2 : +1, lod);
+			Short4 vvvv1 = offsetSample(texture, vvvv, mipmap, OFFSET(Mipmap,vHalf), state.addressingModeV == ADDRESSING_WRAP, gather ? 2 : +1, lod);
 
 			Vector4s c0 = sampleTexel(uuuu0, vvvv0, wwww, offset, mipmap, buffer, function);
 			Vector4s c1 = sampleTexel(uuuu1, vvvv0, wwww, offset, mipmap, buffer, function);
@@ -966,9 +966,9 @@
 				{
 					for(int k = 0; k < 2; k++)
 					{
-						u[i][j][k] = offsetSample(uuuu, mipmap, OFFSET(Mipmap,uHalf), state.addressingModeU == ADDRESSING_WRAP, i * 2 - 1, lod);
-						v[i][j][k] = offsetSample(vvvv, mipmap, OFFSET(Mipmap,vHalf), state.addressingModeV == ADDRESSING_WRAP, j * 2 - 1, lod);
-						s[i][j][k] = offsetSample(wwww, mipmap, OFFSET(Mipmap,wHalf), state.addressingModeW == ADDRESSING_WRAP, k * 2 - 1, lod);
+						u[i][j][k] = offsetSample(texture, uuuu, mipmap, OFFSET(Mipmap,uHalf), state.addressingModeU == ADDRESSING_WRAP, i * 2 - 1, lod);
+						v[i][j][k] = offsetSample(texture, vvvv, mipmap, OFFSET(Mipmap,vHalf), state.addressingModeV == ADDRESSING_WRAP, j * 2 - 1, lod);
+						s[i][j][k] = offsetSample(texture, wwww, mipmap, OFFSET(Mipmap,wHalf), state.addressingModeW == ADDRESSING_WRAP, k * 2 - 1, lod);
 					}
 				}
 			}
@@ -1219,7 +1219,7 @@
 
 		Int4 x0, x1, y0, y1, z0;
 		Float4 fu, fv;
-		Int4 filter = computeFilterOffset(lod);
+		Int4 filter = computeFilterOffset(texture, 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, fv, mipmap, offset.z, filter, OFFSET(Mipmap, depth), state.addressingModeW, function);
@@ -1288,7 +1288,7 @@
 
 		Int4 x0, x1, y0, y1, z0, z1;
 		Float4 fu, fv, fw;
-		Int4 filter = computeFilterOffset(lod);
+		Int4 filter = computeFilterOffset(texture, 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, z1, fw, mipmap, offset.z, filter, OFFSET(Mipmap, depth), state.addressingModeW, function);
@@ -1431,19 +1431,22 @@
 		}
 		else if(function == Lod)
 		{
-			lod = lodBias + Float(*Pointer<Int>(texture + OFFSET(Texture,baseLevel)));
+			lod = lodBias;
 		}
 		else if(function == Fetch)
 		{
 			// TODO: Eliminate int-float-int conversion.
-			lod = Float(As<Int>(lodBias)) + Float(*Pointer<Int>(texture + OFFSET(Texture,baseLevel)));
+			lod = Float(As<Int>(lodBias));
 		}
 		else if(function == Base)
 		{
-			lod = Float(*Pointer<Int>(texture + OFFSET(Texture,baseLevel)));
+			lod = Float(0);
 		}
 		else assert(false);
 
+		// TODO: Avoid float conversion.
+		lod += Float(*Pointer<Int>(texture + OFFSET(Texture,baseLevel)));
+
 		lod = Max(lod, *Pointer<Float>(texture + OFFSET(Texture, minLod)));
 		lod = Min(lod, *Pointer<Float>(texture + OFFSET(Texture, maxLod)));
 	}
@@ -1497,19 +1500,22 @@
 		}
 		else if(function == Lod)
 		{
-			lod = lodBias + Float(*Pointer<Int>(texture + OFFSET(Texture,baseLevel)));
+			lod = lodBias;
 		}
 		else if(function == Fetch)
 		{
 			// TODO: Eliminate int-float-int conversion.
-			lod = Float(As<Int>(lodBias)) + Float(*Pointer<Int>(texture + OFFSET(Texture,baseLevel)));
+			lod = Float(As<Int>(lodBias));
 		}
 		else if(function == Base)
 		{
-			lod = Float(*Pointer<Int>(texture + OFFSET(Texture,baseLevel)));
+			lod = Float(0);
 		}
 		else assert(false);
 
+		// TODO: Avoid float conversion.
+		lod += Float(*Pointer<Int>(texture + OFFSET(Texture,baseLevel)));
+
 		lod = Max(lod, *Pointer<Float>(texture + OFFSET(Texture, minLod)));
 		lod = Min(lod, *Pointer<Float>(texture + OFFSET(Texture, maxLod)));
 	}
@@ -1561,19 +1567,22 @@
 			}
 			else if(function == Lod)
 			{
-				lod = lodBias + Float(*Pointer<Int>(texture + OFFSET(Texture,baseLevel)));
+				lod = lodBias;
 			}
 			else if(function == Fetch)
 			{
 				// TODO: Eliminate int-float-int conversion.
-				lod = Float(As<Int>(lodBias)) + Float(*Pointer<Int>(texture + OFFSET(Texture,baseLevel)));
+				lod = Float(As<Int>(lodBias));
 			}
 			else if(function == Base)
 			{
-				lod = Float(*Pointer<Int>(texture + OFFSET(Texture,baseLevel)));
+				lod = Float(0);
 			}
 			else assert(false);
 
+			// TODO: Avoid float conversion.
+			lod += Float(*Pointer<Int>(texture + OFFSET(Texture,baseLevel)));
+
 			lod = Max(lod, *Pointer<Float>(texture + OFFSET(Texture, minLod)));
 			lod = Min(lod, *Pointer<Float>(texture + OFFSET(Texture, maxLod)));
 		}
@@ -2277,7 +2286,7 @@
 		}
 	}
 
-	Int4 SamplerCore::computeFilterOffset(Float &lod)
+	Int4 SamplerCore::computeFilterOffset(Pointer<Byte> &texture, Float &lod)
 	{
 		Int4 filter = -1;
 
@@ -2287,11 +2296,11 @@
 		}
 		else if(state.textureFilter == FILTER_MIN_LINEAR_MAG_POINT)
 		{
-			filter = CmpNLE(Float4(lod), Float4(0.0f));
+			filter = CmpNLE(Float4(lod), Float4(Float(*Pointer<Int>(texture + OFFSET(Texture,baseLevel)))));
 		}
 		else if(state.textureFilter == FILTER_MIN_POINT_MAG_LINEAR)
 		{
-			filter = CmpLE(Float4(lod), Float4(0.0f));
+			filter = CmpLE(Float4(lod), Float4(Float(*Pointer<Int>(texture + OFFSET(Texture,baseLevel)))));
 		}
 
 		return filter;
diff --git a/src/Shader/SamplerCore.hpp b/src/Shader/SamplerCore.hpp
index 684c1a7..31d6668 100644
--- a/src/Shader/SamplerCore.hpp
+++ b/src/Shader/SamplerCore.hpp
@@ -59,7 +59,7 @@
 
 		void border(Short4 &mask, Float4 &coordinates);
 		void border(Int4 &mask, Float4 &coordinates);
-		Short4 offsetSample(Short4 &uvw, Pointer<Byte> &mipmap, int halfOffset, bool wrap, int count, Float &lod);
+		Short4 offsetSample(Pointer<Byte> &texture, Short4 &uvw, Pointer<Byte> &mipmap, int halfOffset, bool wrap, int count, Float &lod);
 		Vector4s sampleFilter(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, Vector4f &offset, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, Int face[4], SamplerFunction function);
 		Vector4s sampleAniso(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, Vector4f &offset, Float &lod, Float &anisotropy, Float4 &uDelta, Float4 &vDelta, Int face[4], bool secondLOD, SamplerFunction function);
 		Vector4s sampleQuad(Pointer<Byte> &texture, Float4 &u, Float4 &v, Float4 &w, Vector4f &offset, Float &lod, Int face[4], bool secondLOD, SamplerFunction function);
@@ -85,7 +85,7 @@
 		void selectMipmap(Pointer<Byte> &texture, Pointer<Byte> buffer[4], Pointer<Byte> &mipmap, Float &lod, Int face[4], bool secondLOD);
 		Short4 address(Float4 &uw, AddressingMode addressingMode, Pointer<Byte>& mipmap);
 		void address(Float4 &uw, Int4& xyz0, Int4& xyz1, Float4& f, Pointer<Byte>& mipmap, Float4 &texOffset, Int4 &filter, int whd, AddressingMode addressingMode, SamplerFunction function);
-		Int4 computeFilterOffset(Float &lod);
+		Int4 computeFilterOffset(Pointer<Byte> &texture, Float &lod);
 
 		void convertFixed12(Short4 &ci, Float4 &cf);
 		void convertFixed12(Vector4s &cs, Vector4f &cf);