Fix internalformat handling.

- Use internalformat parameter if valid, instead of deriving from
  format/type parameters.
- Validate format/type/internalformat parameters in CopyTexSubImage().
- Moved early-out optimizations after validation.
- Removed duplicate validation.
- Use GLint consistently for internalformat parameters.

Change-Id: I377c6bb5381602d13d281f19985aa4f11d201099
Reviewed-on: https://swiftshader-review.googlesource.com/14488
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/Framebuffer.cpp b/src/OpenGL/libGLESv2/Framebuffer.cpp
index fa01ff3..fc0c4a3 100644
--- a/src/OpenGL/libGLESv2/Framebuffer.cpp
+++ b/src/OpenGL/libGLESv2/Framebuffer.cpp
@@ -328,7 +328,7 @@
 
 			if(IsRenderbuffer(mColorbufferType[i]))
 			{
-				if(!IsColorRenderable(colorbuffer->getFormat(), egl::getClientVersion(), false))
+				if(!IsColorRenderable(colorbuffer->getFormat(), egl::getClientVersion()))
 				{
 					return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
 				}
@@ -337,7 +337,7 @@
 			{
 				GLenum format = colorbuffer->getFormat();
 
-				if(!IsColorRenderable(format, egl::getClientVersion(), true))
+				if(!IsColorRenderable(format, egl::getClientVersion()))
 				{
 					return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
 				}
diff --git a/src/OpenGL/libGLESv2/Texture.cpp b/src/OpenGL/libGLESv2/Texture.cpp
index ff58214..77e6d3f 100644
--- a/src/OpenGL/libGLESv2/Texture.cpp
+++ b/src/OpenGL/libGLESv2/Texture.cpp
@@ -431,22 +431,7 @@
 		return error(GL_INVALID_OPERATION);
 	}
 
-	if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight() || depth + zoffset > image->getDepth())
-	{
-		return error(GL_INVALID_VALUE);
-	}
-
-	if(IsCompressed(image->getFormat(), egl::getClientVersion()))
-	{
-		return error(GL_INVALID_OPERATION);
-	}
-
-	if(format != image->getFormat())
-	{
-		return error(GL_INVALID_OPERATION);
-	}
-
-	if(pixels)
+	if(pixels && width > 0 && height > 0 && depth > 0)
 	{
 		image->loadImageData(context, xoffset, yoffset, zoffset, width, height, depth, format, type, unpackInfo, pixels);
 	}
@@ -459,16 +444,6 @@
 		return error(GL_INVALID_OPERATION);
 	}
 
-	if(width + xoffset > image->getWidth() || height + yoffset > image->getHeight() || depth + zoffset > image->getDepth())
-	{
-		return error(GL_INVALID_VALUE);
-	}
-
-	if(format != image->getFormat())
-	{
-		return error(GL_INVALID_OPERATION);
-	}
-
 	if(pixels && (imageSize > 0)) // imageSize's correlation to width and height is already validated with egl::ComputeCompressedSize() at the API level
 	{
 		image->loadCompressedData(xoffset, yoffset, zoffset, width, height, depth, imageSize, pixels);
@@ -773,28 +748,31 @@
 		return error(GL_INVALID_VALUE);
 	}
 
-	egl::Image *renderTarget = source->getRenderTarget(0);
-
-	if(!renderTarget)
+	if(width > 0 && height > 0)
 	{
-		ERR("Failed to retrieve the render target.");
-		return error(GL_OUT_OF_MEMORY);
+		egl::Image *renderTarget = source->getRenderTarget(0);
+
+		if(!renderTarget)
+		{
+			ERR("Failed to retrieve the render target.");
+			return error(GL_OUT_OF_MEMORY);
+		}
+
+		Renderbuffer* renderbuffer = source->getReadColorbuffer();
+
+		if(!renderbuffer)
+		{
+			ERR("Failed to retrieve the source colorbuffer.");
+			return;
+		}
+
+		sw::SliceRect sourceRect(x, y, x + width, y + height, 0);
+		sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());
+
+		copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, zoffset, image[level]);
+
+		renderTarget->release();
 	}
-
-	Renderbuffer* renderbuffer = source->getReadColorbuffer();
-
-	if(!renderbuffer)
-	{
-		ERR("Failed to retrieve the source colorbuffer.");
-		return;
-	}
-
-	sw::SliceRect sourceRect(x, y, x + width, y + height, 0);
-	sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());
-
-	copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, zoffset, image[level]);
-
-	renderTarget->release();
 }
 
 void Texture2D::setSharedImage(egl::Image *sharedImage)
@@ -1415,28 +1393,31 @@
 		return error(GL_INVALID_VALUE);
 	}
 
-	egl::Image *renderTarget = source->getRenderTarget(0);
-
-	if(!renderTarget)
+	if(width > 0 && height > 0)
 	{
-		ERR("Failed to retrieve the render target.");
-		return error(GL_OUT_OF_MEMORY);
+		egl::Image *renderTarget = source->getRenderTarget(0);
+
+		if(!renderTarget)
+		{
+			ERR("Failed to retrieve the render target.");
+			return error(GL_OUT_OF_MEMORY);
+		}
+
+		Renderbuffer* renderbuffer = source->getReadColorbuffer();
+
+		if(!renderbuffer)
+		{
+			ERR("Failed to retrieve the source colorbuffer.");
+			return;
+		}
+
+		sw::SliceRect sourceRect(x, y, x + width, y + height, 0);
+		sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());
+
+		copy(renderTarget, sourceRect, image[face][level]->getFormat(), xoffset, yoffset, zoffset, image[face][level]);
+
+		renderTarget->release();
 	}
-
-	Renderbuffer* renderbuffer = source->getReadColorbuffer();
-
-	if(!renderbuffer)
-	{
-		ERR("Failed to retrieve the source colorbuffer.");
-		return;
-	}
-
-	sw::SliceRect sourceRect(x, y, x + width, y + height, 0);
-	sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());
-
-	copy(renderTarget, sourceRect, image[face][level]->getFormat(), xoffset, yoffset, zoffset, image[face][level]);
-
-	renderTarget->release();
 }
 
 void TextureCubeMap::generateMipmaps()
@@ -1758,28 +1739,31 @@
 		return error(GL_INVALID_VALUE);
 	}
 
-	egl::Image *renderTarget = source->getRenderTarget(0);
-
-	if(!renderTarget)
+	if(width > 0 && height > 0)
 	{
-		ERR("Failed to retrieve the render target.");
-		return error(GL_OUT_OF_MEMORY);
+		egl::Image *renderTarget = source->getRenderTarget(0);
+
+		if(!renderTarget)
+		{
+			ERR("Failed to retrieve the render target.");
+			return error(GL_OUT_OF_MEMORY);
+		}
+
+		Renderbuffer* renderbuffer = source->getReadColorbuffer();
+
+		if(!renderbuffer)
+		{
+			ERR("Failed to retrieve the source colorbuffer.");
+			return;
+		}
+
+		sw::SliceRect sourceRect = {x, y, x + width, y + height, 0};
+		sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());
+
+		copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, zoffset, image[level]);
+
+		renderTarget->release();
 	}
-
-	Renderbuffer* renderbuffer = source->getReadColorbuffer();
-
-	if(!renderbuffer)
-	{
-		ERR("Failed to retrieve the source colorbuffer.");
-		return;
-	}
-
-	sw::SliceRect sourceRect = {x, y, x + width, y + height, 0};
-	sourceRect.clip(0, 0, renderbuffer->getWidth(), renderbuffer->getHeight());
-
-	copy(renderTarget, sourceRect, image[level]->getFormat(), xoffset, yoffset, zoffset, image[level]);
-
-	renderTarget->release();
 }
 
 void Texture3D::setSharedImage(egl::Image *sharedImage)
diff --git a/src/OpenGL/libGLESv2/libGLESv2.cpp b/src/OpenGL/libGLESv2/libGLESv2.cpp
index 3d7aa42..08abb33 100644
--- a/src/OpenGL/libGLESv2/libGLESv2.cpp
+++ b/src/OpenGL/libGLESv2/libGLESv2.cpp
@@ -902,7 +902,6 @@
 			case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
 			case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
 				{
-
 					GLenum validationError = context->getPixels(&data, texture->getType(target, level), imageSize);
 					if(validationError != GL_NONE)
 					{
@@ -941,12 +940,6 @@
 		return error(GL_INVALID_VALUE);
 	}
 
-	GLenum validationError = ValidateCompressedFormat(format, egl::getClientVersion(), true);
-	if(validationError != GL_NONE)
-	{
-		return error(validationError);
-	}
-
 	if(imageSize != egl::ComputeCompressedSize(width, height, format))
 	{
 		return error(GL_INVALID_VALUE);
@@ -956,11 +949,6 @@
 
 	if(context)
 	{
-		if(imageSize != egl::ComputeCompressedSize(width, height, format))
-		{
-			return error(GL_INVALID_VALUE);
-		}
-
 		if(xoffset % 4 != 0 || yoffset % 4 != 0)
 		{
 			// We wait to check the offsets until this point, because the multiple-of-four restriction does not exist unless DXT1 textures are supported
@@ -973,41 +961,37 @@
 		{
 			es2::Texture2D *texture = context->getTexture2D();
 
-			GLenum validationError = ValidateSubImageParams(true, width, height, xoffset, yoffset, target, level, sizedInternalFormat, texture);
-
-			if(validationError == GL_NONE)
-			{
-				validationError = context->getPixels(&data, texture->getType(target, level), imageSize);
-			}
-
-			if(validationError == GL_NONE)
-			{
-				texture->subImageCompressed(level, xoffset, yoffset, width, height, sizedInternalFormat, imageSize, data);
-			}
-			else
+			GLenum validationError = ValidateSubImageParams(true, false, target, level, xoffset, yoffset, width, height, format, GL_NONE, texture, context->getClientVersion());
+			if(validationError != GL_NONE)
 			{
 				return error(validationError);
 			}
+
+			validationError = context->getPixels(&data, texture->getType(target, level), imageSize);
+			if(validationError != GL_NONE)
+			{
+				return error(validationError);
+			}
+
+			texture->subImageCompressed(level, xoffset, yoffset, width, height, sizedInternalFormat, imageSize, data);
 		}
 		else if(es2::IsCubemapTextureTarget(target))
 		{
 			es2::TextureCubeMap *texture = context->getTextureCubeMap();
 
-			GLenum validationError = ValidateSubImageParams(true, width, height, xoffset, yoffset, target, level, sizedInternalFormat, texture);
-
-			if(validationError == GL_NONE)
-			{
-				validationError = context->getPixels(&data, texture->getType(target, level), imageSize);
-			}
-
-			if(validationError == GL_NONE)
-			{
-				texture->subImageCompressed(target, level, xoffset, yoffset, width, height, sizedInternalFormat, imageSize, data);
-			}
-			else
+			GLenum validationError = ValidateSubImageParams(true, false, target, level, xoffset, yoffset, width, height, format, GL_NONE, texture, context->getClientVersion());
+			if(validationError != GL_NONE)
 			{
 				return error(validationError);
 			}
+
+			validationError = context->getPixels(&data, texture->getType(target, level), imageSize);
+			if(validationError != GL_NONE)
+			{
+				return error(validationError);
+			}
+
+			texture->subImageCompressed(target, level, xoffset, yoffset, width, height, sizedInternalFormat, imageSize, data);
 		}
 		else UNREACHABLE(target);
 	}
@@ -1136,11 +1120,6 @@
 		return error(GL_INVALID_VALUE);
 	}
 
-	if(width == 0 || height == 0)
-	{
-		return;
-	}
-
 	es2::Context *context = es2::getContext();
 
 	if(context)
@@ -1171,7 +1150,7 @@
 		}
 		else UNREACHABLE(target);
 
-		GLenum validationError = ValidateSubImageParams(false, width, height, xoffset, yoffset, target, level, GL_NONE, texture);
+		GLenum validationError = ValidateSubImageParams(false, true, target, level, xoffset, yoffset, width, height, GL_NONE, GL_NONE, texture, context->getClientVersion());
 		if(validationError != GL_NONE)
 		{
 			return error(validationError);
@@ -4759,7 +4738,7 @@
 
 		GLint clientVersion = context->getClientVersion();
 
-		if(IsColorRenderable(internalformat, clientVersion, false))
+		if(IsColorRenderable(internalformat, clientVersion))
 		{
 			context->setRenderbufferStorage(new es2::Colorbuffer(width, height, internalformat, samples));
 		}
@@ -5102,9 +5081,10 @@
 			return error(validationError);
 		}
 
-		if(!ValidateTextureFormatType(format, type, internalformat, egl::getClientVersion()))
+		validationError = ValidateTextureFormatType(format, type, internalformat, context->getClientVersion());
+		if(validationError != GL_NONE)
 		{
-			return;
+			return error(validationError);
 		}
 
 		if(border != 0)
@@ -5142,7 +5122,7 @@
 			return error(GL_INVALID_ENUM);
 		}
 
-		GLenum sizedInternalFormat = GetSizedInternalFormat(format, type);
+		GLenum sizedInternalFormat = GetSizedInternalFormat(internalformat, type);
 
 		validationError = context->getPixels(&data, type, context->getRequiredBufferSize(width, height, 1, sizedInternalFormat, type));
 		if(validationError != GL_NONE)
@@ -5501,57 +5481,47 @@
 		return error(GL_INVALID_VALUE);
 	}
 
-	if(!ValidateTextureFormatType(format, type, format, egl::getClientVersion()))
-	{
-		return;
-	}
-
-	if(width == 0 || height == 0)
-	{
-		return;
-	}
-
 	es2::Context *context = es2::getContext();
 
 	if(context)
 	{
 		GLenum sizedInternalFormat = GetSizedInternalFormat(format, type);
 
-		GLenum validationError = context->getPixels(&data, type, context->getRequiredBufferSize(width, height, 1, sizedInternalFormat, type));
-		if(validationError != GL_NONE)
-		{
-			return error(validationError);
-		}
-
 		if(target == GL_TEXTURE_2D)
 		{
 			es2::Texture2D *texture = context->getTexture2D();
 
-			validationError = ValidateSubImageParams(false, width, height, xoffset, yoffset, target, level, sizedInternalFormat, texture);
-
-			if(validationError == GL_NONE)
-			{
-				texture->subImage(context, level, xoffset, yoffset, width, height, sizedInternalFormat, type, context->getUnpackInfo(), data);
-			}
-			else
+			GLenum validationError = ValidateSubImageParams(false, false, target, level, xoffset, yoffset, width, height, format, type, texture, context->getClientVersion());
+			if(validationError != GL_NONE)
 			{
 				return error(validationError);
 			}
+
+			validationError = context->getPixels(&data, type, context->getRequiredBufferSize(width, height, 1, sizedInternalFormat, type));
+			if(validationError != GL_NONE)
+			{
+				return error(validationError);
+			}
+
+			texture->subImage(context, level, xoffset, yoffset, width, height, sizedInternalFormat, type, context->getUnpackInfo(), data);
 		}
 		else if(es2::IsCubemapTextureTarget(target))
 		{
 			es2::TextureCubeMap *texture = context->getTextureCubeMap();
 
-			validationError = ValidateSubImageParams(false, width, height, xoffset, yoffset, target, level, sizedInternalFormat, texture);
-
-			if(validationError == GL_NONE)
-			{
-				texture->subImage(context, target, level, xoffset, yoffset, width, height, sizedInternalFormat, type, context->getUnpackInfo(), data);
-			}
-			else
+			GLenum validationError = ValidateSubImageParams(false, false, target, level, xoffset, yoffset, width, height, format, type, texture, context->getClientVersion());
+			if(validationError != GL_NONE)
 			{
 				return error(validationError);
 			}
+
+			validationError = context->getPixels(&data, type, context->getRequiredBufferSize(width, height, 1, sizedInternalFormat, type));
+			if(validationError != GL_NONE)
+			{
+				return error(validationError);
+			}
+
+			texture->subImage(context, target, level, xoffset, yoffset, width, height, sizedInternalFormat, type, context->getUnpackInfo(), data);
 		}
 		else UNREACHABLE(target);
 	}
@@ -6349,9 +6319,15 @@
 		return error(GL_INVALID_ENUM);
 	}
 
-	if(!ValidateTextureFormatType(format, type, internalformat, egl::getClientVersion()))
+	if(internalformat != (GLint)format)
 	{
-		return;
+		return error(GL_INVALID_OPERATION);
+	}
+
+	GLenum validationError = ValidateTextureFormatType(format, type, internalformat, egl::getClientVersion());
+	if(validationError != GL_NONE)
+	{
+		return error(validationError);
 	}
 
 	if((level < 0) || (level >= es2::IMPLEMENTATION_MAX_TEXTURE_LEVELS))
@@ -6430,21 +6406,19 @@
 
 		GLenum sizedInternalFormat = GetSizedInternalFormat(format, type);
 
-		GLenum validationError = ValidateSubImageParams(false, width, height, depth, xoffset, yoffset, zoffset, target, level, sizedInternalFormat, texture);
-
-		if(validationError == GL_NONE)
-		{
-			validationError = context->getPixels(&data, type, context->getRequiredBufferSize(width, height, depth, sizedInternalFormat, type));
-		}
-
-		if(validationError == GL_NONE)
-		{
-			texture->subImage(context, level, xoffset, yoffset, zoffset, width, height, depth, sizedInternalFormat, type, context->getUnpackInfo(), data);
-		}
-		else
+		GLenum validationError = ValidateSubImageParams(false, false, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, texture, context->getClientVersion());
+		if(validationError != GL_NONE)
 		{
 			return error(validationError);
 		}
+
+		validationError = context->getPixels(&data, type, context->getRequiredBufferSize(width, height, depth, sizedInternalFormat, type));
+		if(validationError != GL_NONE)
+		{
+			return error(validationError);
+		}
+
+		texture->subImage(context, level, xoffset, yoffset, zoffset, width, height, depth, sizedInternalFormat, type, context->getUnpackInfo(), data);
 	}
 }
 
@@ -6487,8 +6461,7 @@
 
 		es2::Texture3D *texture = context->getTexture3D();
 
-		GLenum validationError = ValidateSubImageParams(false, width, height, 1, xoffset, yoffset, zoffset, target, level, GL_NONE, texture);
-
+		GLenum validationError = ValidateSubImageParams(false, true, target, level, xoffset, yoffset, zoffset, width, height, 1, GL_NONE, GL_NONE, texture, context->getClientVersion());
 		if(validationError != GL_NONE)
 		{
 			return error(validationError);
@@ -6616,7 +6589,6 @@
 		}
 
 		GLenum validationError = context->getPixels(&data, texture->getType(target, level), imageSize);
-
 		if(validationError != GL_NONE)
 		{
 			return error(validationError);
diff --git a/src/OpenGL/libGLESv2/libGLESv3.cpp b/src/OpenGL/libGLESv2/libGLESv3.cpp
index 2d646ec..0e006c3 100644
--- a/src/OpenGL/libGLESv2/libGLESv3.cpp
+++ b/src/OpenGL/libGLESv2/libGLESv3.cpp
@@ -729,21 +729,19 @@
 
 		GLenum sizedInternalFormat = GetSizedInternalFormat(format, type);
 
-		GLenum validationError = ValidateSubImageParams(false, width, height, depth, xoffset, yoffset, zoffset, target, level, sizedInternalFormat, texture);
-		if(validationError == GL_NONE)
-		{
-			GLenum validationError = context->getPixels(&data, type, context->getRequiredBufferSize(width, height, depth, sizedInternalFormat, type));
-			if(validationError != GL_NONE)
-			{
-				return error(validationError);
-			}
-
-			texture->subImage(context, level, xoffset, yoffset, zoffset, width, height, depth, sizedInternalFormat, type, context->getUnpackInfo(), data);
-		}
-		else
+		GLenum validationError = ValidateSubImageParams(false, false, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, texture, context->getClientVersion());
+		if(validationError != GL_NONE)
 		{
 			return error(validationError);
 		}
+
+		validationError = context->getPixels(&data, type, context->getRequiredBufferSize(width, height, depth, sizedInternalFormat, type));
+		if(validationError != GL_NONE)
+		{
+			return error(validationError);
+		}
+
+		texture->subImage(context, level, xoffset, yoffset, zoffset, width, height, depth, sizedInternalFormat, type, context->getUnpackInfo(), data);
 	}
 }
 
@@ -793,7 +791,7 @@
 		GLenum colorbufferFormat = source->getFormat();
 		es2::Texture3D *texture = (target == GL_TEXTURE_3D) ? context->getTexture3D() : context->getTexture2DArray();
 
-		GLenum validationError = ValidateSubImageParams(false, width, height, 1, xoffset, yoffset, zoffset, target, level, GL_NONE, texture);
+		GLenum validationError = ValidateSubImageParams(false, true, target, level, xoffset, yoffset, zoffset, width, height, 1, GL_NONE, GL_NONE, texture, context->getClientVersion());
 		if(validationError != GL_NONE)
 		{
 			return error(validationError);
@@ -935,7 +933,7 @@
 			return error(GL_INVALID_OPERATION);
 		}
 
-		if(((width  % 4) != 0) || ((height % 4) != 0) ||
+		if(((width % 4) != 0) || ((height % 4) != 0) ||
 		   ((xoffset % 4) != 0) || ((yoffset % 4) != 0))
 		{
 			return error(GL_INVALID_OPERATION);
@@ -967,7 +965,8 @@
 		if(is_ETC2_EAC)
 		{
 			if(((width + xoffset) != texture->getWidth(target, level)) ||
-			   ((height + yoffset) != texture->getHeight(target, level)))
+			   ((height + yoffset) != texture->getHeight(target, level)) ||
+			   ((depth + zoffset) != texture->getDepth(target, level)))
 			{
 				return error(GL_INVALID_OPERATION);
 			}
@@ -4085,7 +4084,7 @@
 		return;
 	}
 
-	if(!IsColorRenderable(internalformat, egl::getClientVersion(), false) &&
+	if(!IsColorRenderable(internalformat, egl::getClientVersion()) &&
 	   !IsDepthRenderable(internalformat, egl::getClientVersion()) &&
 	   !IsStencilRenderable(internalformat, egl::getClientVersion()))
 	{
diff --git a/src/OpenGL/libGLESv2/utilities.cpp b/src/OpenGL/libGLESv2/utilities.cpp
index 0d86291..1cc650c 100644
--- a/src/OpenGL/libGLESv2/utilities.cpp
+++ b/src/OpenGL/libGLESv2/utilities.cpp
@@ -520,31 +520,40 @@
 		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:
 		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:
 		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:
-#if(ASTC_SUPPORT)
-			return ((clientVersion >= 3) && ()) ? (expectCompressedFormats ? GL_NONE : GL_INVALID_OPERATION) : GL_INVALID_ENUM;
-#else
-			return GL_INVALID_ENUM;
-#endif
+			#if(ASTC_SUPPORT)
+				return ((clientVersion >= 3) && ()) ? (expectCompressedFormats ? GL_NONE : GL_INVALID_OPERATION) : GL_INVALID_ENUM;
+			#else
+				return GL_INVALID_ENUM;
+			#endif
 		default:
 			return expectCompressedFormats ? GL_INVALID_ENUM : GL_NONE; // Not compressed format
 		}
 	}
 
-	GLenum ValidateSubImageParams(bool compressed, GLsizei width, GLsizei height, GLint xoffset, GLint yoffset, GLenum target, GLint level, GLenum sizedInternalFormat, Texture *texture)
+	GLenum ValidateSubImageParams(bool compressed, bool copy, GLenum target, GLint level, GLint xoffset, GLint yoffset,
+	                              GLsizei width, GLsizei height, GLenum format, GLenum type, Texture *texture, GLint clientVersion)
 	{
 		if(!texture)
 		{
 			return GL_INVALID_OPERATION;
 		}
 
-		if(compressed != texture->isCompressed(target, level))
-		{
-			return GL_INVALID_OPERATION;
-		}
+		GLenum sizedInternalFormat = texture->getFormat(target, level);
 
-		if(sizedInternalFormat != GL_NONE && sizedInternalFormat != texture->getFormat(target, level))
+		if(compressed)
 		{
-			return GL_INVALID_OPERATION;
+			if(format != sizedInternalFormat)
+			{
+				return GL_INVALID_OPERATION;
+			}
+		}
+		else if(!copy)   // CopyTexSubImage doesn't have format/type parameters.
+		{
+			GLenum validationError = ValidateTextureFormatType(format, type, sizedInternalFormat, clientVersion);
+			if(validationError != GL_NONE)
+			{
+				return validationError;
+			}
 		}
 
 		if(compressed)
@@ -565,7 +574,8 @@
 		return GL_NONE;
 	}
 
-	GLenum ValidateSubImageParams(bool compressed, GLsizei width, GLsizei height, GLsizei depth, GLint xoffset, GLint yoffset, GLint zoffset, GLenum target, GLint level, GLenum sizedInternalFormat, Texture *texture)
+	GLenum ValidateSubImageParams(bool compressed, bool copy, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
+	                              GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, Texture *texture, GLint clientVersion)
 	{
 		if(!texture)
 		{
@@ -577,9 +587,15 @@
 			return GL_INVALID_OPERATION;
 		}
 
-		if(sizedInternalFormat != GL_NONE && sizedInternalFormat != GetSizedInternalFormat(texture->getFormat(target, level), texture->getType(target, level)))
+		if(!copy)
 		{
-			return GL_INVALID_OPERATION;
+			GLenum sizedInternalFormat = texture->getFormat(target, level);
+
+			GLenum validationError = ValidateTextureFormatType(format, type, sizedInternalFormat, clientVersion);
+			if(validationError != GL_NONE)
+			{
+				return validationError;
+			}
 		}
 
 		if(compressed)
@@ -761,7 +777,7 @@
 		return target == GL_TEXTURE_2D || IsCubemapTextureTarget(target) || target == GL_TEXTURE_3D || target == GL_TEXTURE_2D_ARRAY;
 	}
 
-	bool ValidateTextureFormatType(GLenum format, GLenum type, GLint internalformat, GLint clientVersion)
+	GLenum ValidateTextureFormatType(GLenum format, GLenum type, GLint internalformat, GLint clientVersion)
 	{
 		switch(type)
 		{
@@ -785,11 +801,11 @@
 		case GL_FLOAT_32_UNSIGNED_INT_24_8_REV:
 			if(clientVersion < 3)
 			{
-				return error(GL_INVALID_ENUM, false);
+				return GL_INVALID_ENUM;
 			}
 			break;
 		default:
-			return error(GL_INVALID_ENUM, false);
+			return GL_INVALID_ENUM;
 		}
 
 		switch(format)
@@ -811,22 +827,42 @@
 		case GL_RGBA_INTEGER:
 			if(clientVersion < 3)
 			{
-				return error(GL_INVALID_ENUM, false);
+				return GL_INVALID_ENUM;
 			}
 			break;
 		default:
-			return error(GL_INVALID_ENUM, false);
+			return GL_INVALID_ENUM;
 		}
 
 		if((GLenum)internalformat != format)
 		{
-			if(clientVersion < 3)
-			{
-				return error(GL_INVALID_OPERATION, false);
-			}
-
 			switch(internalformat)
 			{
+			// Unsized internal formats:
+			case GL_ALPHA:
+			case GL_RGB:
+			case GL_RGBA:
+			case GL_LUMINANCE:
+			case GL_LUMINANCE_ALPHA:
+			case GL_BGRA_EXT:          // GL_EXT_texture_format_BGRA8888
+			case GL_DEPTH_STENCIL:     // GL_OES_packed_depth_stencil (GL_DEPTH_STENCIL_OES)
+			case GL_DEPTH_COMPONENT:   // GL_OES_depth_texture
+			case GL_RED:               // = GL_RED_EXT in GL_EXT_texture_rg
+			case GL_RG:                // = GL_RG_EXT in GL_EXT_texture_rg
+				break;
+			case GL_RED_INTEGER:
+			case GL_RG_INTEGER:
+			case GL_RGB_INTEGER:
+			case GL_RGBA_INTEGER:
+				if(clientVersion < 3)
+				{
+					return GL_INVALID_ENUM;
+				}
+				break;
+			// Sized internal formats:
+			case GL_ALPHA8_EXT:
+			case GL_LUMINANCE8_ALPHA8_EXT:
+			case GL_LUMINANCE8_EXT:
 			case GL_R8:
 			case GL_R8UI:
 			case GL_R8I:
@@ -885,7 +921,7 @@
 			case GL_RGB9_E5:
 				break;
 			default:
-				return error(GL_INVALID_ENUM, false);
+				return GL_INVALID_ENUM;
 			}
 		}
 
@@ -906,7 +942,7 @@
 			case GL_UNSIGNED_INT_2_10_10_10_REV: VALIDATE_INTERNALFORMAT(GL_RGB10_A2, GL_RGB5_A1)
 			case GL_HALF_FLOAT:                  VALIDATE_INTERNALFORMAT(GL_RGBA16F)
 			case GL_FLOAT:                       VALIDATE_INTERNALFORMAT(GL_RGBA32F, GL_RGBA16F)
-			default:                             return error(GL_INVALID_OPERATION, false);
+			default:                             return GL_INVALID_OPERATION;
 			}
 			break;
 		case GL_RGBA_INTEGER:
@@ -919,7 +955,7 @@
 			case GL_UNSIGNED_INT:                VALIDATE_INTERNALFORMAT(GL_RGBA32UI)
 			case GL_INT:                         VALIDATE_INTERNALFORMAT(GL_RGBA32I)
 			case GL_UNSIGNED_INT_2_10_10_10_REV: VALIDATE_INTERNALFORMAT(GL_RGB10_A2UI)
-			default:                             return error(GL_INVALID_OPERATION, false);
+			default:                             return GL_INVALID_OPERATION;
 			}
 			break;
 		case GL_RGB:
@@ -933,7 +969,7 @@
 			case GL_UNSIGNED_INT_5_9_9_9_REV:     VALIDATE_INTERNALFORMAT(GL_RGB9_E5)
 			case GL_HALF_FLOAT:                   VALIDATE_INTERNALFORMAT(GL_RGB16F, GL_R11F_G11F_B10F, GL_RGB9_E5)
 			case GL_FLOAT:                        VALIDATE_INTERNALFORMAT(GL_RGB32F, GL_RGB16F, GL_R11F_G11F_B10F, GL_RGB9_E5)
-			default:                              return error(GL_INVALID_OPERATION, false);
+			default:                              return GL_INVALID_OPERATION;
 			}
 			break;
 		case GL_RGB_INTEGER:
@@ -945,7 +981,7 @@
 			case GL_SHORT:                       VALIDATE_INTERNALFORMAT(GL_RGB16I)
 			case GL_UNSIGNED_INT:                VALIDATE_INTERNALFORMAT(GL_RGB32UI)
 			case GL_INT:                         VALIDATE_INTERNALFORMAT(GL_RGB32I)
-			default:                             return error(GL_INVALID_OPERATION, false);
+			default:                             return GL_INVALID_OPERATION;
 			}
 			break;
 		case GL_RG:
@@ -956,7 +992,7 @@
 			case GL_HALF_FLOAT_OES: break;
 			case GL_HALF_FLOAT:    VALIDATE_INTERNALFORMAT(GL_RG16F)
 			case GL_FLOAT:         VALIDATE_INTERNALFORMAT(GL_RG32F, GL_RG16F)
-			default:               return error(GL_INVALID_OPERATION, false);
+			default:               return GL_INVALID_OPERATION;
 			}
 			break;
 		case GL_RG_INTEGER:
@@ -968,7 +1004,7 @@
 			case GL_SHORT:          VALIDATE_INTERNALFORMAT(GL_RG16I)
 			case GL_UNSIGNED_INT:   VALIDATE_INTERNALFORMAT(GL_RG32UI)
 			case GL_INT:            VALIDATE_INTERNALFORMAT(GL_RG32I)
-			default:                return error(GL_INVALID_OPERATION, false);
+			default:                return GL_INVALID_OPERATION;
 			}
 			break;
 		case GL_RED:
@@ -979,7 +1015,7 @@
 			case GL_HALF_FLOAT_OES: break;
 			case GL_HALF_FLOAT:    VALIDATE_INTERNALFORMAT(GL_R16F)
 			case GL_FLOAT:         VALIDATE_INTERNALFORMAT(GL_R32F, GL_R16F)
-			default:               return error(GL_INVALID_OPERATION, false);
+			default:               return GL_INVALID_OPERATION;
 			}
 			break;
 		case GL_RED_INTEGER:
@@ -991,7 +1027,7 @@
 			case GL_SHORT:          VALIDATE_INTERNALFORMAT(GL_R16I)
 			case GL_UNSIGNED_INT:   VALIDATE_INTERNALFORMAT(GL_R32UI)
 			case GL_INT:            VALIDATE_INTERNALFORMAT(GL_R32I)
-			default:                return error(GL_INVALID_OPERATION, false);
+			default:                return GL_INVALID_OPERATION;
 			}
 			break;
 		case GL_DEPTH_COMPONENT:
@@ -1000,7 +1036,7 @@
 			case GL_UNSIGNED_SHORT: VALIDATE_INTERNALFORMAT(GL_DEPTH_COMPONENT16)
 			case GL_UNSIGNED_INT:   VALIDATE_INTERNALFORMAT(GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT16)
 			case GL_FLOAT:          VALIDATE_INTERNALFORMAT(GL_DEPTH_COMPONENT32F)
-			default:                return error(GL_INVALID_OPERATION, false);
+			default:                return GL_INVALID_OPERATION;
 			}
 			break;
 		case GL_DEPTH_STENCIL:
@@ -1008,41 +1044,61 @@
 			{
 			case GL_UNSIGNED_INT_24_8:              VALIDATE_INTERNALFORMAT(GL_DEPTH24_STENCIL8)
 			case GL_FLOAT_32_UNSIGNED_INT_24_8_REV: VALIDATE_INTERNALFORMAT(GL_DEPTH32F_STENCIL8)
-			default:                                return error(GL_INVALID_OPERATION, false);
+			default:                                return GL_INVALID_OPERATION;
 			}
 			break;
 		case GL_LUMINANCE_ALPHA:
-		case GL_LUMINANCE:
-		case GL_ALPHA:
 			switch(type)
 			{
-			case GL_UNSIGNED_BYTE:
+			case GL_UNSIGNED_BYTE:  VALIDATE_INTERNALFORMAT(GL_LUMINANCE8_ALPHA8_EXT)
 			case GL_HALF_FLOAT_OES:
 			case GL_FLOAT:
 				break;
 			default:
-				return error(GL_INVALID_OPERATION, false);
+				return GL_INVALID_OPERATION;
+			}
+			break;
+		case GL_LUMINANCE:
+			switch(type)
+			{
+			case GL_UNSIGNED_BYTE:  VALIDATE_INTERNALFORMAT(GL_LUMINANCE8_EXT)
+			case GL_HALF_FLOAT_OES:
+			case GL_FLOAT:
+				break;
+			default:
+				return GL_INVALID_OPERATION;
+			}
+			break;
+		case GL_ALPHA:
+			switch(type)
+			{
+			case GL_UNSIGNED_BYTE:  VALIDATE_INTERNALFORMAT(GL_ALPHA8_EXT)
+			case GL_HALF_FLOAT_OES:
+			case GL_FLOAT:
+				break;
+			default:
+				return GL_INVALID_OPERATION;
 			}
 			break;
 		case GL_BGRA_EXT:
 			if(type != GL_UNSIGNED_BYTE)
 			{
-				return error(GL_INVALID_OPERATION, false);
+				return GL_INVALID_OPERATION;
 			}
 			break;
 		default:
 			UNREACHABLE(format);
-			return error(GL_INVALID_ENUM, false);
+			return GL_INVALID_ENUM;
 		}
 
 		#undef VALIDATE_INTERNALFORMAT
 
 		if((GLenum)internalformat != format && !validSizedInternalformat)
 		{
-			return error(GL_INVALID_OPERATION, false);
+			return GL_INVALID_OPERATION;
 		}
 
-		return true;
+		return GL_NONE;
 	}
 
 	GLsizei GetTypeSize(GLenum type)
@@ -1077,22 +1133,17 @@
 		return 1;
 	}
 
-	bool IsColorRenderable(GLenum internalformat, GLint clientVersion, bool isTexture)
+	bool IsColorRenderable(GLint internalformat, GLint clientVersion)
 	{
 		switch(internalformat)
 		{
-		case GL_RED_EXT:
-		case GL_RG_EXT:
-		case GL_RGB:
-		case GL_RGBA:
-			return isTexture;
 		case GL_RGBA4:
 		case GL_RGB5_A1:
 		case GL_RGB565:
-		case GL_R8_EXT:
-		case GL_RG8_EXT:
-		case GL_RGB8_OES:
-		case GL_RGBA8_OES:
+		case GL_R8:
+		case GL_RG8:
+		case GL_RGB8:
+		case GL_RGBA8:
 		case GL_R16F:
 		case GL_RG16F:
 		case GL_RGB16F:
@@ -1148,25 +1199,7 @@
 		return false;
 	}
 
-	bool IsMipmappable(GLenum internalformat, sw::Format internalFormat, GLint clientVersion)
-	{
-		if(sw::Surface::isNonNormalizedInteger(internalFormat))
-		{
-			return false;
-		}
-
-		switch(internalformat)
-		{
-			case GL_ALPHA8_EXT:
-			case GL_LUMINANCE8_EXT:
-			case GL_LUMINANCE8_ALPHA8_EXT:
-				return true;
-			default:
-				return IsColorRenderable(internalformat, clientVersion, true);
-		}
-	}
-
-	bool IsDepthRenderable(GLenum internalformat, GLint clientVersion)
+	bool IsDepthRenderable(GLint internalformat, GLint clientVersion)
 	{
 		switch(internalformat)
 		{
@@ -1220,6 +1253,10 @@
 		case GL_RG32F:
 		case GL_RGB32F:
 		case GL_RGBA32F:
+		case GL_R8_SNORM:
+		case GL_RG8_SNORM:
+		case GL_RGB8_SNORM:
+		case GL_RGBA8_SNORM:
 			return false;
 		default:
 			UNIMPLEMENTED();
@@ -1228,7 +1265,7 @@
 		return false;
 	}
 
-	bool IsStencilRenderable(GLenum internalformat, GLint clientVersion)
+	bool IsStencilRenderable(GLint internalformat, GLint clientVersion)
 	{
 		switch(internalformat)
 		{
@@ -1282,6 +1319,10 @@
 		case GL_DEPTH_COMPONENT24:
 		case GL_DEPTH_COMPONENT32_OES:
 		case GL_DEPTH_COMPONENT32F:
+		case GL_R8_SNORM:
+		case GL_RG8_SNORM:
+		case GL_RGB8_SNORM:
+		case GL_RGBA8_SNORM:
 			return false;
 		default:
 			UNIMPLEMENTED();
@@ -1290,6 +1331,24 @@
 		return false;
 	}
 
+	bool IsMipmappable(GLint internalformat, sw::Format format, GLint clientVersion)
+	{
+		if(sw::Surface::isNonNormalizedInteger(format))
+		{
+			return false;
+		}
+
+		switch(internalformat)
+		{
+		case GL_ALPHA8_EXT:
+		case GL_LUMINANCE8_EXT:
+		case GL_LUMINANCE8_ALPHA8_EXT:
+			return true;
+		default:
+			return IsColorRenderable(internalformat, clientVersion);
+		}
+	}
+
 	std::string ParseUniformName(const std::string &name, unsigned int *outSubscript)
 	{
 		// Strip any trailing array operator and retrieve the subscript
diff --git a/src/OpenGL/libGLESv2/utilities.h b/src/OpenGL/libGLESv2/utilities.h
index 6e18252..7240999 100644
--- a/src/OpenGL/libGLESv2/utilities.h
+++ b/src/OpenGL/libGLESv2/utilities.h
@@ -45,21 +45,23 @@
 	bool IsCompressed(GLenum format, GLint clientVersion);
 	GLenum GetSizedInternalFormat(GLenum internalFormat, GLenum type);
 	GLenum ValidateCompressedFormat(GLenum format, GLint clientVersion, bool expectCompressedFormats);
-	GLenum ValidateSubImageParams(bool compressed, GLsizei width, GLsizei height, GLint xoffset, GLint yoffset, GLenum target, GLint level, GLenum sizedInternalFormat, Texture *texture);
-	GLenum ValidateSubImageParams(bool compressed, GLsizei width, GLsizei height, GLsizei depth, GLint xoffset, GLint yoffset, GLint zoffset, GLenum target, GLint level, GLenum sizedInternalFormat, Texture *texture);
+	GLenum ValidateSubImageParams(bool compressed, bool copy, GLenum target, GLint level, GLint xoffset, GLint yoffset,
+	                              GLsizei width, GLsizei height, GLenum format, GLenum type, Texture *texture, GLint clientVersion);
+	GLenum ValidateSubImageParams(bool compressed, bool copy, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
+	                              GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, Texture *texture, GLint clientVersion);
 	bool IsValidReadPixelsFormatType(const Framebuffer *framebuffer, GLenum format, GLenum type, GLint clientVersion);
 	bool IsDepthTexture(GLenum format);
 	bool IsStencilTexture(GLenum format);
 	bool IsCubemapTextureTarget(GLenum target);
 	int CubeFaceIndex(GLenum cubeTarget);
 	bool IsTextureTarget(GLenum target);
-	bool ValidateTextureFormatType(GLenum format, GLenum type, GLint internalformat, GLint clientVersion);
+	GLenum ValidateTextureFormatType(GLenum format, GLenum type, GLint internalformat, GLint clientVersion);
 	GLsizei GetTypeSize(GLenum type);
 
-	bool IsColorRenderable(GLenum internalformat, GLint clientVersion, bool isTexture);
-	bool IsMipmappable(GLenum format, sw::Format internalFormat, GLint clientVersion);
-	bool IsDepthRenderable(GLenum internalformat, GLint clientVersion);
-	bool IsStencilRenderable(GLenum internalformat, GLint clientVersion);
+	bool IsColorRenderable(GLint internalformat, GLint clientVersion);
+	bool IsDepthRenderable(GLint internalformat, GLint clientVersion);
+	bool IsStencilRenderable(GLint internalformat, GLint clientVersion);
+	bool IsMipmappable(GLint internalformat, sw::Format format, GLint clientVersion);
 
 	// Parse the base uniform name and array index.  Returns the base name of the uniform. outSubscript is
 	// set to GL_INVALID_INDEX if the provided name is not an array or the array index is invalid.