Texture rectangle related fixes and associated tests

Imported texture rectangle tests from angle and fixed all
texture rectangle related failures:
- Rectangle textures can be rendered to
- Rectangle textures only support level 0
- Rectangle textures can't be compressed
- glTexStorage2D can no longer create a texture larger
  than the maximum size allowed

Change-Id: I089291c94aad79e244782a8d56dd224c7510d237
Reviewed-on: https://swiftshader-review.googlesource.com/16908
Reviewed-by: Alexis Hétu <sugoi@google.com>
Tested-by: Alexis Hétu <sugoi@google.com>
diff --git a/src/OpenGL/libGLESv2/Renderbuffer.cpp b/src/OpenGL/libGLESv2/Renderbuffer.cpp
index 2e24b56..48a61e9 100644
--- a/src/OpenGL/libGLESv2/Renderbuffer.cpp
+++ b/src/OpenGL/libGLESv2/Renderbuffer.cpp
@@ -22,6 +22,8 @@
 #include "Texture.h"
 #include "utilities.h"
 
+#include "compiler/Compiler.h"
+
 namespace es2
 {
 RenderbufferInterface::RenderbufferInterface()
@@ -132,6 +134,69 @@
 	return 0;   // Core OpenGL ES 3.0 does not support multisample textures.
 }
 
+///// RenderbufferTexture2DRect Implementation ////////
+
+RenderbufferTexture2DRect::RenderbufferTexture2DRect(Texture2DRect *texture)
+{
+	mTexture2DRect = texture;
+}
+
+RenderbufferTexture2DRect::~RenderbufferTexture2DRect()
+{
+	mTexture2DRect = NULL;
+}
+
+// Textures need to maintain their own reference count for references via
+// Renderbuffers acting as proxies. Here, we notify the texture of a reference.
+void RenderbufferTexture2DRect::addProxyRef(const Renderbuffer *proxy)
+{
+	mTexture2DRect->addProxyRef(proxy);
+}
+
+void RenderbufferTexture2DRect::releaseProxy(const Renderbuffer *proxy)
+{
+	mTexture2DRect->releaseProxy(proxy);
+}
+
+// Increments refcount on image.
+// caller must release() the returned image
+egl::Image *RenderbufferTexture2DRect::getRenderTarget()
+{
+	return mTexture2DRect->getRenderTarget(GL_TEXTURE_RECTANGLE_ARB, 0);
+}
+
+// Increments refcount on image.
+// caller must release() the returned image
+egl::Image *RenderbufferTexture2DRect::createSharedImage()
+{
+	return mTexture2DRect->createSharedImage(GL_TEXTURE_RECTANGLE_ARB, 0);
+}
+
+bool RenderbufferTexture2DRect::isShared() const
+{
+	return mTexture2DRect->isShared(GL_TEXTURE_RECTANGLE_ARB, 0);
+}
+
+GLsizei RenderbufferTexture2DRect::getWidth() const
+{
+	return mTexture2DRect->getWidth(GL_TEXTURE_RECTANGLE_ARB, 0);
+}
+
+GLsizei RenderbufferTexture2DRect::getHeight() const
+{
+	return mTexture2DRect->getHeight(GL_TEXTURE_RECTANGLE_ARB, 0);
+}
+
+GLint RenderbufferTexture2DRect::getFormat() const
+{
+	return mTexture2DRect->getFormat(GL_TEXTURE_RECTANGLE_ARB, 0);
+}
+
+GLsizei RenderbufferTexture2DRect::getSamples() const
+{
+	return 0;   // Core OpenGL ES 3.0 does not support multisample textures.
+}
+
 ///// RenderbufferTexture3D Implementation ////////
 
 RenderbufferTexture3D::RenderbufferTexture3D(Texture3D *texture, GLint level) : mLevel(level)
diff --git a/src/OpenGL/libGLESv2/Renderbuffer.h b/src/OpenGL/libGLESv2/Renderbuffer.h
index 7a48fc8..fa7123b 100644
--- a/src/OpenGL/libGLESv2/Renderbuffer.h
+++ b/src/OpenGL/libGLESv2/Renderbuffer.h
@@ -31,6 +31,7 @@
 class Texture2D;
 class Texture3D;
 class TextureCubeMap;
+class Texture2DRect;
 class Renderbuffer;
 class Colorbuffer;
 class DepthStencilbuffer;
@@ -93,6 +94,29 @@
 	GLint mLevel;
 };
 
+class RenderbufferTexture2DRect : public RenderbufferInterface
+{
+public:
+	RenderbufferTexture2DRect(Texture2DRect *texture);
+
+	~RenderbufferTexture2DRect() override;
+
+	void addProxyRef(const Renderbuffer *proxy) override;
+	void releaseProxy(const Renderbuffer *proxy) override;
+
+	egl::Image *getRenderTarget() override;
+	egl::Image *createSharedImage() override;
+	bool isShared() const override;
+
+	GLsizei getWidth() const override;
+	GLsizei getHeight() const override;
+	GLint getFormat() const override;
+	GLsizei getSamples() const override;
+
+private:
+	gl::BindingPointer<Texture2DRect> mTexture2DRect;
+};
+
 class RenderbufferTexture3D : public RenderbufferInterface
 {
 public:
diff --git a/src/OpenGL/libGLESv2/Texture.cpp b/src/OpenGL/libGLESv2/Texture.cpp
index 1e3e51f..5ab0a8f 100644
--- a/src/OpenGL/libGLESv2/Texture.cpp
+++ b/src/OpenGL/libGLESv2/Texture.cpp
@@ -75,7 +75,7 @@
 	case GL_LINEAR_MIPMAP_NEAREST:
 	case GL_NEAREST_MIPMAP_LINEAR:
 	case GL_LINEAR_MIPMAP_LINEAR:
-		if(getTarget() == GL_TEXTURE_EXTERNAL_OES)
+		if((getTarget() == GL_TEXTURE_EXTERNAL_OES) || (getTarget() == GL_TEXTURE_RECTANGLE_ARB))
 		{
 			return false;
 		}
@@ -110,7 +110,7 @@
 	{
 	case GL_REPEAT:
 	case GL_MIRRORED_REPEAT:
-		if(getTarget() == GL_TEXTURE_EXTERNAL_OES)
+		if((getTarget() == GL_TEXTURE_EXTERNAL_OES) || (getTarget() == GL_TEXTURE_RECTANGLE_ARB))
 		{
 			return false;
 		}
@@ -130,7 +130,7 @@
 	{
 	case GL_REPEAT:
 	case GL_MIRRORED_REPEAT:
-		if(getTarget() == GL_TEXTURE_EXTERNAL_OES)
+		if((getTarget() == GL_TEXTURE_EXTERNAL_OES) || (getTarget() == GL_TEXTURE_RECTANGLE_ARB))
 		{
 			return false;
 		}
@@ -150,7 +150,7 @@
 	{
 	case GL_REPEAT:
 	case GL_MIRRORED_REPEAT:
-		if(getTarget() == GL_TEXTURE_EXTERNAL_OES)
+		if((getTarget() == GL_TEXTURE_EXTERNAL_OES) || (getTarget() == GL_TEXTURE_RECTANGLE_ARB))
 		{
 			return false;
 		}
@@ -844,6 +844,11 @@
 
 Texture2DRect::Texture2DRect(GLuint name) : Texture2D(name)
 {
+	mMinFilter = GL_LINEAR;
+	mMagFilter = GL_LINEAR;
+	mWrapS = GL_CLAMP_TO_EDGE;
+	mWrapT = GL_CLAMP_TO_EDGE;
+	mWrapR = GL_CLAMP_TO_EDGE;
 }
 
 GLenum Texture2DRect::getTarget() const
@@ -851,6 +856,21 @@
 	return GL_TEXTURE_RECTANGLE_ARB;
 }
 
+Renderbuffer *Texture2DRect::getRenderbuffer(GLenum target, GLint level)
+{
+	if((target != getTarget()) || (level != 0))
+	{
+		return error(GL_INVALID_OPERATION, (Renderbuffer*)nullptr);
+	}
+
+	if(!mColorbufferProxy)
+	{
+		mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture2DRect(this));
+	}
+
+	return mColorbufferProxy;
+}
+
 TextureCubeMap::TextureCubeMap(GLuint name) : Texture(name)
 {
 	for(int f = 0; f < 6; f++)
diff --git a/src/OpenGL/libGLESv2/Texture.h b/src/OpenGL/libGLESv2/Texture.h
index 13b3a56..54ca5f9 100644
--- a/src/OpenGL/libGLESv2/Texture.h
+++ b/src/OpenGL/libGLESv2/Texture.h
@@ -205,6 +205,8 @@
 	explicit Texture2DRect(GLuint name);
 
 	GLenum getTarget() const override;
+
+	Renderbuffer *getRenderbuffer(GLenum target, GLint level) override;
 };
 
 class TextureCubeMap : public Texture
diff --git a/src/OpenGL/libGLESv2/libGLESv2.cpp b/src/OpenGL/libGLESv2/libGLESv2.cpp
index 507f989..93f8e4c 100644
--- a/src/OpenGL/libGLESv2/libGLESv2.cpp
+++ b/src/OpenGL/libGLESv2/libGLESv2.cpp
@@ -761,7 +761,6 @@
 		switch(target)
 		{
 		case GL_TEXTURE_2D:
-		case GL_TEXTURE_RECTANGLE_ARB:
 			if(width > (es2::IMPLEMENTATION_MAX_TEXTURE_SIZE >> level) ||
 			   height > (es2::IMPLEMENTATION_MAX_TEXTURE_SIZE >> level))
 			{
@@ -785,6 +784,7 @@
 				return error(GL_INVALID_VALUE);
 			}
 			break;
+		case GL_TEXTURE_RECTANGLE_ARB: // Rectangle textures cannot be compressed
 		default:
 			return error(GL_INVALID_ENUM);
 		}
@@ -920,8 +920,13 @@
 	{
 		switch(target)
 		{
-		case GL_TEXTURE_2D:
 		case GL_TEXTURE_RECTANGLE_ARB:
+			if(level != 0)
+			{
+				return error(GL_INVALID_VALUE);
+			}
+			// Fall through
+		case GL_TEXTURE_2D:
 			if(width > (es2::IMPLEMENTATION_MAX_TEXTURE_SIZE >> level) ||
 			   height > (es2::IMPLEMENTATION_MAX_TEXTURE_SIZE >> level))
 			{
@@ -2038,7 +2043,7 @@
 				return error(GL_INVALID_ENUM);
 			}
 
-			if((level != 0) && (clientVersion < 3))
+			if((level != 0) && ((clientVersion < 3) || (textarget == GL_TEXTURE_RECTANGLE_ARB)))
 			{
 				return error(GL_INVALID_VALUE);
 			}
@@ -4970,8 +4975,13 @@
 
 		switch(target)
 		{
-		case GL_TEXTURE_2D:
 		case GL_TEXTURE_RECTANGLE_ARB:
+			if(level != 0)
+			{
+				return error(GL_INVALID_VALUE); // Defining level other than 0 is not allowed
+			}
+			// Fall through
+		case GL_TEXTURE_2D:
 			if(width > (es2::IMPLEMENTATION_MAX_TEXTURE_SIZE >> level) ||
 			   height > (es2::IMPLEMENTATION_MAX_TEXTURE_SIZE >> level))
 			{
@@ -5266,6 +5276,10 @@
 			}
 			break;
 		case GL_TEXTURE_BASE_LEVEL:
+			if((texture->getTarget() == GL_TEXTURE_RECTANGLE_ARB) && (param != 0))
+			{
+				return error(GL_INVALID_OPERATION); // Base level has to be 0
+			}
 			if(clientVersion < 3 || !texture->setBaseLevel(param))
 			{
 				return error(GL_INVALID_VALUE);
diff --git a/src/OpenGL/libGLESv2/libGLESv3.cpp b/src/OpenGL/libGLESv2/libGLESv3.cpp
index 5006161..a13000e 100644
--- a/src/OpenGL/libGLESv2/libGLESv3.cpp
+++ b/src/OpenGL/libGLESv2/libGLESv3.cpp
@@ -3594,7 +3594,7 @@
 	TRACE("(GLenum target = 0x%X, GLsizei levels = %d, GLenum internalformat = 0x%X, GLsizei width = %d, GLsizei height = %d)",
 	      target, levels, internalformat, width, height);
 
-	if(width < 1 || height < 1 || levels < 1)
+	if(width < 1 || height < 1 || levels < 1 || ((target == GL_TEXTURE_RECTANGLE_ARB) && (levels != 1)))
 	{
 		return error(GL_INVALID_VALUE);
 	}
@@ -3604,7 +3604,8 @@
 		return error(GL_INVALID_OPERATION);
 	}
 
-	if(!IsSizedInternalFormat(internalformat) && !IsCompressed(internalformat, egl::getClientVersion()))
+	bool isCompressed = IsCompressed(internalformat, egl::getClientVersion());
+	if(!IsSizedInternalFormat(internalformat) && !isCompressed)
 	{
 		return error(GL_INVALID_ENUM);
 	}
@@ -3615,9 +3616,19 @@
 	{
 		switch(target)
 		{
-		case GL_TEXTURE_2D:
 		case GL_TEXTURE_RECTANGLE_ARB:
+			if(isCompressed) // Rectangle textures cannot be compressed
 			{
+				return error(GL_INVALID_ENUM);
+			}
+		case GL_TEXTURE_2D:
+			{
+				if((width > es2::IMPLEMENTATION_MAX_TEXTURE_SIZE) ||
+				   (height > es2::IMPLEMENTATION_MAX_TEXTURE_SIZE))
+				{
+					return error(GL_INVALID_VALUE);
+				}
+
 				es2::Texture2D *texture = context->getTexture2D(target);
 				if(!texture || texture->name == 0 || texture->getImmutableFormat() == GL_TRUE)
 				{
@@ -3635,6 +3646,12 @@
 			break;
 		case GL_TEXTURE_CUBE_MAP:
 			{
+				if((width > es2::IMPLEMENTATION_MAX_CUBE_MAP_TEXTURE_SIZE) ||
+				   (height > es2::IMPLEMENTATION_MAX_CUBE_MAP_TEXTURE_SIZE))
+				{
+					return error(GL_INVALID_VALUE);
+				}
+
 				es2::TextureCubeMap *texture = context->getTextureCubeMap();
 				if(!texture || texture->name == 0 || texture->getImmutableFormat())
 				{
diff --git a/tests/unittests/unittests.cpp b/tests/unittests/unittests.cpp
index 8861424..9fcb19b 100644
--- a/tests/unittests/unittests.cpp
+++ b/tests/unittests/unittests.cpp
@@ -17,6 +17,8 @@
 
 #include <EGL/egl.h>
 #include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES3/gl3.h>
 
 #if defined(_WIN32)
 #include <Windows.h>
@@ -37,109 +39,179 @@
 			EXPECT_NE((HMODULE)NULL, libGLESv2);
 		#endif
 	}
+
+	void compareColor(unsigned char referenceColor[4])
+	{
+		unsigned char color[4] = { 0 };
+		glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &color);
+		EXPECT_EQ(color[0], referenceColor[0]);
+		EXPECT_EQ(color[1], referenceColor[1]);
+		EXPECT_EQ(color[2], referenceColor[2]);
+		EXPECT_EQ(color[3], referenceColor[3]);
+	}
+
+	void Initialize(int version, bool withChecks)
+	{
+		EXPECT_EQ(EGL_SUCCESS, eglGetError());
+
+		display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+		if(withChecks)
+		{
+			EXPECT_EQ(EGL_SUCCESS, eglGetError());
+			EXPECT_NE(EGL_NO_DISPLAY, display);
+
+			eglQueryString(display, EGL_VENDOR);
+			EXPECT_EQ(EGL_NOT_INITIALIZED, eglGetError());
+		}
+
+		EGLint major;
+		EGLint minor;
+		EGLBoolean initialized = eglInitialize(display, &major, &minor);
+
+		if(withChecks)
+		{
+			EXPECT_EQ(EGL_SUCCESS, eglGetError());
+			EXPECT_EQ((EGLBoolean)EGL_TRUE, initialized);
+			EXPECT_EQ(1, major);
+			EXPECT_EQ(4, minor);
+
+			const char *eglVendor = eglQueryString(display, EGL_VENDOR);
+			EXPECT_EQ(EGL_SUCCESS, eglGetError());
+			EXPECT_STREQ("Google Inc.", eglVendor);
+
+			const char *eglVersion = eglQueryString(display, EGL_VERSION);
+			EXPECT_EQ(EGL_SUCCESS, eglGetError());
+			EXPECT_THAT(eglVersion, testing::HasSubstr("1.4 SwiftShader "));
+		}
+
+		eglBindAPI(EGL_OPENGL_ES_API);
+		EXPECT_EQ(EGL_SUCCESS, eglGetError());
+
+		const EGLint configAttributes[] =
+		{
+			EGL_SURFACE_TYPE,		EGL_PBUFFER_BIT,
+			EGL_RENDERABLE_TYPE,	EGL_OPENGL_ES2_BIT,
+			EGL_ALPHA_SIZE,			8,
+			EGL_NONE
+		};
+
+		EGLConfig config;
+		EGLint num_config = -1;
+		EGLBoolean success = eglChooseConfig(display, configAttributes, &config, 1, &num_config);
+		EXPECT_EQ(EGL_SUCCESS, eglGetError());
+		EXPECT_EQ(num_config, 1);
+		EXPECT_EQ((EGLBoolean)EGL_TRUE, success);
+
+		if(withChecks)
+		{
+			EGLint conformant = 0;
+			eglGetConfigAttrib(display, config, EGL_CONFORMANT, &conformant);
+			EXPECT_EQ(EGL_SUCCESS, eglGetError());
+			EXPECT_TRUE(conformant & EGL_OPENGL_ES2_BIT);
+
+			EGLint renderableType = 0;
+			eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType);
+			EXPECT_EQ(EGL_SUCCESS, eglGetError());
+			EXPECT_TRUE(renderableType & EGL_OPENGL_ES2_BIT);
+
+			EGLint surfaceType = 0;
+			eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &surfaceType);
+			EXPECT_EQ(EGL_SUCCESS, eglGetError());
+			EXPECT_TRUE(surfaceType & EGL_WINDOW_BIT);
+		}
+
+		EGLint surfaceAttributes[] =
+		{
+			EGL_WIDTH, 1920,
+			EGL_HEIGHT, 1080,
+			EGL_NONE
+		};
+
+		surface = eglCreatePbufferSurface(display, config, surfaceAttributes);
+		EXPECT_EQ(EGL_SUCCESS, eglGetError());
+		EXPECT_NE(EGL_NO_SURFACE, surface);
+
+		EGLint contextAttributes[] =
+		{
+			EGL_CONTEXT_CLIENT_VERSION, version,
+			EGL_NONE
+		};
+
+		context = eglCreateContext(display, config, NULL, contextAttributes);
+		EXPECT_EQ(EGL_SUCCESS, eglGetError());
+		EXPECT_NE(EGL_NO_CONTEXT, context);
+
+		success = eglMakeCurrent(display, surface, surface, context);
+		EXPECT_EQ(EGL_SUCCESS, eglGetError());
+		EXPECT_EQ((EGLBoolean)EGL_TRUE, success);
+
+		if(withChecks)
+		{
+			EGLDisplay currentDisplay = eglGetCurrentDisplay();
+			EXPECT_EQ(EGL_SUCCESS, eglGetError());
+			EXPECT_EQ(display, currentDisplay);
+
+			EGLSurface currentDrawSurface = eglGetCurrentSurface(EGL_DRAW);
+			EXPECT_EQ(EGL_SUCCESS, eglGetError());
+			EXPECT_EQ(surface, currentDrawSurface);
+
+			EGLSurface currentReadSurface = eglGetCurrentSurface(EGL_READ);
+			EXPECT_EQ(EGL_SUCCESS, eglGetError());
+			EXPECT_EQ(surface, currentReadSurface);
+
+			EGLContext currentContext = eglGetCurrentContext();
+			EXPECT_EQ(EGL_SUCCESS, eglGetError());
+			EXPECT_EQ(context, currentContext);
+		}
+
+		EXPECT_EQ((GLenum)GL_NO_ERROR, glGetError());
+	}
+
+	void Uninitialize()
+	{
+		EGLBoolean success = eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+		EXPECT_EQ(EGL_SUCCESS, eglGetError());
+		EXPECT_EQ((EGLBoolean)EGL_TRUE, success);
+
+		EGLDisplay currentDisplay = eglGetCurrentDisplay();
+		EXPECT_EQ(EGL_SUCCESS, eglGetError());
+		EXPECT_EQ(EGL_NO_DISPLAY, currentDisplay);
+
+		EGLSurface currentDrawSurface = eglGetCurrentSurface(EGL_DRAW);
+		EXPECT_EQ(EGL_SUCCESS, eglGetError());
+		EXPECT_EQ(EGL_NO_SURFACE, currentDrawSurface);
+
+		EGLSurface currentReadSurface = eglGetCurrentSurface(EGL_READ);
+		EXPECT_EQ(EGL_SUCCESS, eglGetError());
+		EXPECT_EQ(EGL_NO_SURFACE, currentReadSurface);
+
+		EGLContext currentContext = eglGetCurrentContext();
+		EXPECT_EQ(EGL_SUCCESS, eglGetError());
+		EXPECT_EQ(EGL_NO_CONTEXT, currentContext);
+
+		success = eglDestroyContext(display, context);
+		EXPECT_EQ(EGL_SUCCESS, eglGetError());
+		EXPECT_EQ((EGLBoolean)EGL_TRUE, success);
+
+		success = eglDestroySurface(display, surface);
+		EXPECT_EQ(EGL_SUCCESS, eglGetError());
+		EXPECT_EQ((EGLBoolean)EGL_TRUE, success);
+
+		success = eglTerminate(display);
+		EXPECT_EQ(EGL_SUCCESS, eglGetError());
+		EXPECT_EQ((EGLBoolean)EGL_TRUE, success);
+	}
+
+private:
+	EGLDisplay display;
+	EGLSurface surface;
+	EGLContext context;
 };
 
 TEST_F(SwiftShaderTest, Initalization)
 {
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-
-	EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_NE(EGL_NO_DISPLAY, display);
-
-	eglQueryString(display, EGL_VENDOR);
-	EXPECT_EQ(EGL_NOT_INITIALIZED, eglGetError());
-
-	EGLint major;
-	EGLint minor;
-	EGLBoolean initialized = eglInitialize(display, &major, &minor);
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_EQ((EGLBoolean)EGL_TRUE, initialized);
-	EXPECT_EQ(1, major);
-	EXPECT_EQ(4, minor);
-
-	const char *eglVendor = eglQueryString(display, EGL_VENDOR);
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_STREQ("Google Inc.", eglVendor);
-
-	const char *eglVersion = eglQueryString(display, EGL_VERSION);
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_THAT(eglVersion, testing::HasSubstr("1.4 SwiftShader "));
-
-	eglBindAPI(EGL_OPENGL_ES_API);
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-
-	const EGLint configAttributes[] =
-	{
-		EGL_SURFACE_TYPE,		EGL_PBUFFER_BIT,
-		EGL_RENDERABLE_TYPE,	EGL_OPENGL_ES2_BIT,
-		EGL_NONE
-	};
-
-	EGLConfig config;
-	EGLint num_config = -1;
-	EGLBoolean success = eglChooseConfig(display, configAttributes, &config, 1, &num_config);
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_EQ(num_config, 1);
-	EXPECT_EQ((EGLBoolean)EGL_TRUE, success);
-
-	EGLint conformant = 0;
-	eglGetConfigAttrib(display, config, EGL_CONFORMANT, &conformant);
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_TRUE(conformant & EGL_OPENGL_ES2_BIT);
-
-	EGLint renderableType = 0;
-	eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType);
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_TRUE(renderableType & EGL_OPENGL_ES2_BIT);
-
-	EGLint surfaceType = 0;
-	eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &surfaceType);
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_TRUE(surfaceType & EGL_WINDOW_BIT);
-
-	EGLint surfaceAttributes[] =
-	{
-		EGL_WIDTH, 1920,
-		EGL_HEIGHT, 1080,
-		EGL_NONE
-	};
-
-	EGLSurface surface = eglCreatePbufferSurface(display, config, surfaceAttributes);
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_NE(EGL_NO_SURFACE, surface);
-
-	EGLint contextAttributes[] =
-	{
-		EGL_CONTEXT_CLIENT_VERSION, 2,
-		EGL_NONE
-	};
-
-	EGLContext context = eglCreateContext(display, config, NULL, contextAttributes);
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_NE(EGL_NO_CONTEXT, context);
-
-	success = eglMakeCurrent(display, surface, surface, context);
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_EQ((EGLBoolean)EGL_TRUE, success);
-
-	EGLDisplay currentDisplay = eglGetCurrentDisplay();
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_EQ(display, currentDisplay);
-
-	EGLSurface currentDrawSurface = eglGetCurrentSurface(EGL_DRAW);
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_EQ(surface, currentDrawSurface);
-
-	EGLSurface currentReadSurface = eglGetCurrentSurface(EGL_READ);
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_EQ(surface, currentReadSurface);
-
-	EGLContext currentContext = eglGetCurrentContext();
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_EQ(context, currentContext);
-
-	EXPECT_EQ((GLenum)GL_NO_ERROR, glGetError());
+	Initialize(2, true);
 
 	const GLubyte *glVendor = glGetString(GL_VENDOR);
 	EXPECT_EQ((GLenum)GL_NO_ERROR, glGetError());
@@ -153,35 +225,422 @@
 	EXPECT_EQ((GLenum)GL_NO_ERROR, glGetError());
 	EXPECT_THAT((const char*)glVersion, testing::HasSubstr("OpenGL ES 2.0 SwiftShader "));
 
-	success = eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_EQ((EGLBoolean)EGL_TRUE, success);
+	Uninitialize();
+}
 
-	currentDisplay = eglGetCurrentDisplay();
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_EQ(EGL_NO_DISPLAY, currentDisplay);
+// Note: GL_ARB_texture_rectangle is part of gl2extchromium.h in the Chromium repo
+// GL_ARB_texture_rectangle
+#ifndef GL_ARB_texture_rectangle
+#define GL_ARB_texture_rectangle 1
 
-	currentDrawSurface = eglGetCurrentSurface(EGL_DRAW);
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_EQ(EGL_NO_SURFACE, currentDrawSurface);
+#ifndef GL_SAMPLER_2D_RECT_ARB
+#define GL_SAMPLER_2D_RECT_ARB 0x8B63
+#endif
 
-	currentReadSurface = eglGetCurrentSurface(EGL_READ);
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_EQ(EGL_NO_SURFACE, currentReadSurface);
+#ifndef GL_TEXTURE_BINDING_RECTANGLE_ARB
+#define GL_TEXTURE_BINDING_RECTANGLE_ARB 0x84F6
+#endif
 
-	currentContext = eglGetCurrentContext();
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_EQ(EGL_NO_CONTEXT, currentContext);
+#ifndef GL_TEXTURE_RECTANGLE_ARB
+#define GL_TEXTURE_RECTANGLE_ARB 0x84F5
+#endif
 
-	success = eglDestroyContext(display, context);
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_EQ((EGLBoolean)EGL_TRUE, success);
+#ifndef GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB
+#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8
+#endif
 
-	success = eglDestroySurface(display, surface);
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_EQ((EGLBoolean)EGL_TRUE, success);
+#endif  // GL_ARB_texture_rectangle
 
-	success = eglTerminate(display);
-	EXPECT_EQ(EGL_SUCCESS, eglGetError());
-	EXPECT_EQ((EGLBoolean)EGL_TRUE, success);
+// Test using TexImage2D to define a rectangle texture
+
+TEST_F(SwiftShaderTest, TextureRectangle_TexImage2D)
+{
+	Initialize(2, false);
+
+	GLuint tex = 1;
+	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex);
+
+	// Defining level 0 is allowed
+	glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	// Defining level other than 0 is not allowed
+	glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 1, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+	EXPECT_EQ(GL_INVALID_VALUE, glGetError());
+
+	GLint maxSize = 0;
+	glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &maxSize);
+
+	// Defining a texture of the max size is allowed
+	{
+		glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, maxSize, maxSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+		GLenum error = glGetError();
+		ASSERT_TRUE(error == GL_NO_ERROR || error == GL_OUT_OF_MEMORY);
+	}
+
+	// Defining a texture larger than the max size is disallowed
+	glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, maxSize + 1, maxSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+	EXPECT_EQ(GL_INVALID_VALUE, glGetError());
+	glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, maxSize, maxSize + 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+	EXPECT_EQ(GL_INVALID_VALUE, glGetError());
+
+	Uninitialize();
+}
+
+// Test using CompressedTexImage2D cannot be used on a retangle texture
+TEST_F(SwiftShaderTest, TextureRectangle_CompressedTexImage2DDisallowed)
+{
+	Initialize(2, false);
+
+	const char data[128] = { 0 };
+
+	// Control case: 2D texture
+	{
+		GLuint tex = 1;
+		glBindTexture(GL_TEXTURE_2D, tex);
+		glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 16, 16, 0, 128, data);
+		EXPECT_EQ(GL_NONE, glGetError());
+	}
+
+	// Rectangle textures cannot be compressed
+	{
+		GLuint tex = 2;
+		glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex);
+		glCompressedTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 16, 16, 0, 128, data);
+		EXPECT_EQ(GL_INVALID_ENUM, glGetError());
+	}
+
+	Uninitialize();
+}
+
+// Test using TexStorage2D to define a rectangle texture (ES3)
+TEST_F(SwiftShaderTest, TextureRectangle_TexStorage2D)
+{
+	Initialize(3, false);
+
+	// Defining one level is allowed
+	{
+		GLuint tex = 1;
+		glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex);
+		glTexStorage2D(GL_TEXTURE_RECTANGLE_ARB, 1, GL_RGBA8UI, 16, 16);
+		EXPECT_EQ(GL_NONE, glGetError());
+	}
+
+	// Having more than one level is not allowed
+	{
+		GLuint tex = 2;
+		glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex);
+		// Use 5 levels because the EXT_texture_storage extension requires a mip chain all the way
+		// to a 1x1 mip.
+		glTexStorage2D(GL_TEXTURE_RECTANGLE_ARB, 5, GL_RGBA8UI, 16, 16);
+		EXPECT_EQ(GL_INVALID_VALUE, glGetError());
+	}
+
+	GLint maxSize = 0;
+	glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &maxSize);
+
+	// Defining a texture of the max size is allowed but still allow for OOM
+	{
+		GLuint tex = 3;
+		glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex);
+		glTexStorage2D(GL_TEXTURE_RECTANGLE_ARB, 1, GL_RGBA8UI, maxSize, maxSize);
+		GLenum error = glGetError();
+		ASSERT_TRUE(error == GL_NO_ERROR || error == GL_OUT_OF_MEMORY);
+	}
+
+	// Defining a texture larger than the max size is disallowed
+	{
+		GLuint tex = 4;
+		glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex);
+		glTexStorage2D(GL_TEXTURE_RECTANGLE_ARB, 1, GL_RGBA8UI, maxSize + 1, maxSize);
+		EXPECT_EQ(GL_INVALID_VALUE, glGetError());
+		glTexStorage2D(GL_TEXTURE_RECTANGLE_ARB, 1, GL_RGBA8UI, maxSize, maxSize + 1);
+		EXPECT_EQ(GL_INVALID_VALUE, glGetError());
+	}
+
+	// Compressed formats are disallowed
+	GLuint tex = 5;
+	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex);
+	glTexStorage2D(GL_TEXTURE_RECTANGLE_ARB, 1, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 16, 16);
+	EXPECT_EQ(GL_INVALID_ENUM, glGetError());
+
+	Uninitialize();
+}
+
+// Test validation of disallowed texture parameters
+TEST_F(SwiftShaderTest, TextureRectangle_TexParameterRestriction)
+{
+	Initialize(3, false);
+
+	GLuint tex = 1;
+	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex);
+
+	// Only wrap mode CLAMP_TO_EDGE is supported
+	// Wrap S
+	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+	EXPECT_EQ(GL_NONE, glGetError());
+	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_REPEAT);
+	EXPECT_EQ(GL_INVALID_ENUM, glGetError());
+	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
+	EXPECT_EQ(GL_INVALID_ENUM, glGetError());
+
+	// Wrap T
+	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+	EXPECT_EQ(GL_NONE, glGetError());
+	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_REPEAT);
+	EXPECT_EQ(GL_INVALID_ENUM, glGetError());
+	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
+	EXPECT_EQ(GL_INVALID_ENUM, glGetError());
+
+	// Min filter has to be nearest or linear
+	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+	EXPECT_EQ(GL_NONE, glGetError());
+	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+	EXPECT_EQ(GL_NONE, glGetError());
+	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
+	EXPECT_EQ(GL_INVALID_ENUM, glGetError());
+	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
+	EXPECT_EQ(GL_INVALID_ENUM, glGetError());
+	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
+	EXPECT_EQ(GL_INVALID_ENUM, glGetError());
+	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+	EXPECT_EQ(GL_INVALID_ENUM, glGetError());
+
+	// Base level has to be 0
+	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_BASE_LEVEL, 0);
+	EXPECT_EQ(GL_NONE, glGetError());
+	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_BASE_LEVEL, 1);
+	EXPECT_EQ(GL_INVALID_OPERATION, glGetError());
+
+	Uninitialize();
+}
+
+// Test validation of "level" in FramebufferTexture2D
+TEST_F(SwiftShaderTest, TextureRectangle_FramebufferTexture2DLevel)
+{
+	Initialize(3, false);
+
+	GLuint tex = 1;
+	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex);
+	glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	GLuint fbo = 1;
+	glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+	// Using level 0 of a rectangle texture is valid.
+	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, tex, 0);
+	EXPECT_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	// Setting level != 0 is invalid
+	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, tex, 1);
+	EXPECT_EQ(GL_INVALID_VALUE, glGetError());
+
+	Uninitialize();
+}
+
+// Test sampling from a rectangle texture
+TEST_F(SwiftShaderTest, TextureRectangle_SamplingFromRectangle)
+{
+	Initialize(3, false);
+
+	GLuint tex = 1;
+	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex);
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	unsigned char green[4] = { 0, 255, 0, 255 };
+	glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, green);
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	const std::string vs =
+		"attribute vec4 position;\n"
+		"void main()\n"
+		"{\n"
+		"    gl_Position = vec4(position.xy, 0.0, 1.0);\n"
+		"}\n";
+
+	const std::string fs =
+		"#extension GL_ARB_texture_rectangle : require\n"
+		"precision mediump float;\n"
+		"uniform sampler2DRect tex;\n"
+		"void main()\n"
+		"{\n"
+		"    gl_FragColor = texture2DRect(tex, vec2(0, 0));\n"
+		"}\n";
+
+	GLuint program = glCreateProgram();
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	GLuint vsShader = glCreateShader(GL_VERTEX_SHADER);
+	const char* vsSource[1] = { vs.c_str() };
+	glShaderSource(vsShader, 1, vsSource, nullptr);
+	glCompileShader(vsShader);
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	GLuint fsShader = glCreateShader(GL_FRAGMENT_SHADER);
+	const char* fsSource[1] = { fs.c_str() };
+	glShaderSource(fsShader, 1, fsSource, nullptr);
+	glCompileShader(fsShader);
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	glAttachShader(program, vsShader);
+	glAttachShader(program, fsShader);
+	glLinkProgram(program);
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	glUseProgram(program);
+	GLint location = glGetUniformLocation(program, "tex");
+	ASSERT_NE(-1, location);
+	glUniform1i(location, 0);
+
+	glClearColor(0.0, 0.0, 0.0, 0.0);
+	glClear(GL_COLOR_BUFFER_BIT);
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	GLint prevProgram = 0;
+	glGetIntegerv(GL_CURRENT_PROGRAM, &prevProgram);
+
+	glUseProgram(program);
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	GLint posLoc = glGetAttribLocation(program, "position");
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	float vertices[18] = { -1.0f,  1.0f, 0.5f,
+						   -1.0f, -1.0f, 0.5f,
+							1.0f, -1.0f, 0.5f,
+						   -1.0f,  1.0f, 0.5f,
+							1.0f, -1.0f, 0.5f,
+							1.0f,  1.0f, 0.5f };
+	glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, vertices);
+	glEnableVertexAttribArray(posLoc);
+	glDrawArrays(GL_TRIANGLES, 0, 6);
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	glVertexAttribPointer(posLoc, 4, GL_FLOAT, GL_FALSE, 0, nullptr);
+	glDisableVertexAttribArray(posLoc);
+	glUseProgram(prevProgram);
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	compareColor(green);
+
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	Uninitialize();
+}
+
+// Test attaching a rectangle texture and rendering to it.
+TEST_F(SwiftShaderTest, TextureRectangle_RenderToRectangle)
+{
+	Initialize(3, false);
+
+	GLuint tex = 1;
+	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex);
+	unsigned char black[4] = { 0, 0, 0, 255 };
+	glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, black);
+
+	GLuint fbo = 1;
+	glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, tex, 0);
+	EXPECT_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	// Clearing a texture is just as good as checking we can render to it, right?
+	glClearColor(0.0, 1.0, 0.0, 1.0);
+	glClear(GL_COLOR_BUFFER_BIT);
+
+	unsigned char green[4] = { 0, 255, 0, 255 };
+	compareColor(green);
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	Uninitialize();
+}
+
+TEST_F(SwiftShaderTest, TextureRectangle_DefaultSamplerParameters)
+{
+	Initialize(3, false);
+
+	GLuint tex = 1;
+	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex);
+
+	GLint minFilter = 0;
+	glGetTexParameteriv(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, &minFilter);
+	EXPECT_EQ(GL_LINEAR, minFilter);
+
+	GLint wrapS = 0;
+	glGetTexParameteriv(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, &wrapS);
+	EXPECT_EQ(GL_CLAMP_TO_EDGE, wrapS);
+
+	GLint wrapT = 0;
+	glGetTexParameteriv(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, &wrapT);
+	EXPECT_EQ(GL_CLAMP_TO_EDGE, wrapT);
+
+	Uninitialize();
+}
+
+// Test glCopyTexImage with rectangle textures (ES3)
+TEST_F(SwiftShaderTest, TextureRectangle_CopyTexImage)
+{
+	Initialize(3, false);
+
+	GLuint tex = 1;
+	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex);
+
+	glBindFramebuffer(GL_FRAMEBUFFER, 0);
+	glClearColor(0, 1, 0, 1);
+	glClear(GL_COLOR_BUFFER_BIT);
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	// Error case: level != 0
+	glCopyTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 1, GL_RGBA8, 0, 0, 1, 1, 0);
+	EXPECT_EQ(GL_INVALID_VALUE, glGetError());
+
+	// level = 0 works and defines the texture.
+	glCopyTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, 0, 0, 1, 1, 0);
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	GLuint fbo = 1;
+	glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, tex, 0);
+
+	unsigned char green[4] = { 0, 255, 0, 255 };
+	compareColor(green);
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	Uninitialize();
+}
+
+// Test glCopyTexSubImage with rectangle textures (ES3)
+TEST_F(SwiftShaderTest, TextureRectangle_CopyTexSubImage)
+{
+	Initialize(3, false);
+
+	GLuint tex = 1;
+	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, tex);
+	unsigned char black[4] = { 0, 0, 0, 255 };
+	glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, black);
+
+	glBindFramebuffer(GL_FRAMEBUFFER, 0);
+	glClearColor(0, 1, 0, 1);
+	glClear(GL_COLOR_BUFFER_BIT);
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	// Error case: level != 0
+	glCopyTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 1, 0, 0, 0, 0, 1, 1);
+	EXPECT_EQ(GL_INVALID_VALUE, glGetError());
+
+	// level = 0 works and defines the texture.
+	glCopyTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 0, 0, 0, 0, 1, 1);
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	GLuint fbo = 1;
+	glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, tex, 0);
+
+	unsigned char green[4] = { 0, 255, 0, 255 };
+	compareColor(green);
+	EXPECT_EQ(GL_NONE, glGetError());
+
+	Uninitialize();
 }