Implement GL_OES_fbo_render_mipmap.

This enables binding any texture mipmap level as a framebuffer
attachment.

Bug swiftshader:104

Change-Id: I3d4ea637ddd38bb62ca1363fe2c69c569eea36e9
Reviewed-on: https://swiftshader-review.googlesource.com/18008
Reviewed-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Lingfeng Yang <lfy@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/OpenGL/libGLES_CM/Framebuffer.cpp b/src/OpenGL/libGLES_CM/Framebuffer.cpp
index 097c6a2..6fcb9df 100644
--- a/src/OpenGL/libGLES_CM/Framebuffer.cpp
+++ b/src/OpenGL/libGLES_CM/Framebuffer.cpp
@@ -39,7 +39,7 @@
 	mStencilbufferPointer = nullptr;
 }
 
-Renderbuffer *Framebuffer::lookupRenderbuffer(GLenum type, GLuint handle) const
+Renderbuffer *Framebuffer::lookupRenderbuffer(GLenum type, GLuint handle, GLint level) const
 {
 	Context *context = getContext();
 	Renderbuffer *buffer = nullptr;
@@ -54,29 +54,29 @@
 	}
 	else if(IsTextureTarget(type))
 	{
-		buffer = context->getTexture(handle)->getRenderbuffer(type);
+		buffer = context->getTexture(handle)->getRenderbuffer(type, level);
 	}
 	else UNREACHABLE(type);
 
 	return buffer;
 }
 
-void Framebuffer::setColorbuffer(GLenum type, GLuint colorbuffer)
+void Framebuffer::setColorbuffer(GLenum type, GLuint colorbuffer, GLint level)
 {
 	mColorbufferType = (colorbuffer != 0) ? type : GL_NONE_OES;
-	mColorbufferPointer = lookupRenderbuffer(type, colorbuffer);
+	mColorbufferPointer = lookupRenderbuffer(type, colorbuffer, level);
 }
 
-void Framebuffer::setDepthbuffer(GLenum type, GLuint depthbuffer)
+void Framebuffer::setDepthbuffer(GLenum type, GLuint depthbuffer, GLint level)
 {
 	mDepthbufferType = (depthbuffer != 0) ? type : GL_NONE_OES;
-	mDepthbufferPointer = lookupRenderbuffer(type, depthbuffer);
+	mDepthbufferPointer = lookupRenderbuffer(type, depthbuffer, level);
 }
 
-void Framebuffer::setStencilbuffer(GLenum type, GLuint stencilbuffer)
+void Framebuffer::setStencilbuffer(GLenum type, GLuint stencilbuffer, GLint level)
 {
 	mStencilbufferType = (stencilbuffer != 0) ? type : GL_NONE_OES;
-	mStencilbufferPointer = lookupRenderbuffer(type, stencilbuffer);
+	mStencilbufferPointer = lookupRenderbuffer(type, stencilbuffer, level);
 }
 
 void Framebuffer::detachTexture(GLuint texture)
diff --git a/src/OpenGL/libGLES_CM/Framebuffer.h b/src/OpenGL/libGLES_CM/Framebuffer.h
index 39f227c..9c44129 100644
--- a/src/OpenGL/libGLES_CM/Framebuffer.h
+++ b/src/OpenGL/libGLES_CM/Framebuffer.h
@@ -38,9 +38,9 @@
 
 	virtual ~Framebuffer();
 
-	void setColorbuffer(GLenum type, GLuint colorbuffer);
-	void setDepthbuffer(GLenum type, GLuint depthbuffer);
-	void setStencilbuffer(GLenum type, GLuint stencilbuffer);
+	void setColorbuffer(GLenum type, GLuint colorbuffer, GLint level = 0);
+	void setDepthbuffer(GLenum type, GLuint depthbuffer, GLint level = 0);
+	void setStencilbuffer(GLenum type, GLuint stencilbuffer, GLint level = 0);
 
 	void detachTexture(GLuint texture);
 	void detachRenderbuffer(GLuint renderbuffer);
@@ -80,7 +80,7 @@
 	gl::BindingPointer<Renderbuffer> mStencilbufferPointer;
 
 private:
-	Renderbuffer *lookupRenderbuffer(GLenum type, GLuint handle) const;
+	Renderbuffer *lookupRenderbuffer(GLenum type, GLuint handle, GLint level) const;
 };
 
 class DefaultFramebuffer : public Framebuffer
diff --git a/src/OpenGL/libGLES_CM/Renderbuffer.cpp b/src/OpenGL/libGLES_CM/Renderbuffer.cpp
index c832443..68094d8 100644
--- a/src/OpenGL/libGLES_CM/Renderbuffer.cpp
+++ b/src/OpenGL/libGLES_CM/Renderbuffer.cpp
@@ -71,7 +71,7 @@
 
 ///// RenderbufferTexture2D Implementation ////////
 
-RenderbufferTexture2D::RenderbufferTexture2D(Texture2D *texture)
+RenderbufferTexture2D::RenderbufferTexture2D(Texture2D *texture, GLint level) : mLevel(level)
 {
 	mTexture2D = texture;
 }
@@ -190,6 +190,11 @@
 	return mInstance->getHeight();
 }
 
+GLint Renderbuffer::getLevel() const
+{
+	return mInstance->getLevel();
+}
+
 GLenum Renderbuffer::getFormat() const
 {
 	return mInstance->getFormat();
@@ -230,6 +235,11 @@
 	return mInstance->getSamples();
 }
 
+void Renderbuffer::setLevel(GLint level)
+{
+	return mInstance->setLevel(level);
+}
+
 void Renderbuffer::setStorage(RenderbufferStorage *newStorage)
 {
 	ASSERT(newStorage);
diff --git a/src/OpenGL/libGLES_CM/Renderbuffer.h b/src/OpenGL/libGLES_CM/Renderbuffer.h
index 4405b93..09a7391 100644
--- a/src/OpenGL/libGLES_CM/Renderbuffer.h
+++ b/src/OpenGL/libGLES_CM/Renderbuffer.h
@@ -48,9 +48,12 @@
 
 	virtual GLsizei getWidth() const = 0;
 	virtual GLsizei getHeight() const = 0;
+	virtual GLint getLevel() const { return 0; }
 	virtual GLint getFormat() const = 0;
 	virtual GLsizei getSamples() const = 0;
 
+	virtual void setLevel(GLint) {}
+
 	GLuint getRedSize() const;
 	GLuint getGreenSize() const;
 	GLuint getBlueSize() const;
@@ -62,24 +65,28 @@
 class RenderbufferTexture2D : public RenderbufferInterface
 {
 public:
-	RenderbufferTexture2D(Texture2D *texture);
+	RenderbufferTexture2D(Texture2D *texture, GLint level);
 
-	virtual ~RenderbufferTexture2D();
+	~RenderbufferTexture2D() override;
 
-	virtual void addProxyRef(const Renderbuffer *proxy);
-    virtual void releaseProxy(const Renderbuffer *proxy);
+	void addProxyRef(const Renderbuffer *proxy) override;
+    void releaseProxy(const Renderbuffer *proxy) override;
 
-	virtual egl::Image *getRenderTarget();
-    virtual egl::Image *createSharedImage();
-    virtual bool isShared() const;
+	egl::Image *getRenderTarget() override;
+    egl::Image *createSharedImage() override;
+    bool isShared() const override;
 
-	virtual GLsizei getWidth() const;
-	virtual GLsizei getHeight() const;
-	virtual GLint getFormat() const;
-	virtual GLsizei getSamples() const;
+	GLsizei getWidth() const override;
+	GLsizei getHeight() const override;
+	GLint getLevel() const override { return mLevel; }
+	GLint getFormat() const override;
+	GLsizei getSamples() const override;
+
+	void setLevel(GLint level) override { mLevel = level; }
 
 private:
 	gl::BindingPointer<Texture2D> mTexture2D;
+	GLint mLevel;
 };
 
 // A class derived from RenderbufferStorage is created whenever glRenderbufferStorage
@@ -90,16 +97,16 @@
 public:
 	RenderbufferStorage();
 
-	virtual ~RenderbufferStorage() = 0;
+	~RenderbufferStorage() override = 0;
 
-	virtual egl::Image *getRenderTarget() = 0;
-    virtual egl::Image *createSharedImage() = 0;
-    virtual bool isShared() const = 0;
+	egl::Image *getRenderTarget() override = 0;
+    egl::Image *createSharedImage() override = 0;
+    bool isShared() const override = 0;
 
-	virtual GLsizei getWidth() const;
-	virtual GLsizei getHeight() const;
-	virtual GLint getFormat() const;
-	virtual GLsizei getSamples() const;
+	GLsizei getWidth() const override;
+	GLsizei getHeight() const override;
+	GLint getFormat() const override;
+	GLsizei getSamples() const override;
 
 protected:
 	GLsizei mWidth;
@@ -131,6 +138,7 @@
 
 	GLsizei getWidth() const;
 	GLsizei getHeight() const;
+	GLint getLevel() const;
 	GLenum getFormat() const;
 	GLuint getRedSize() const;
 	GLuint getGreenSize() const;
@@ -140,6 +148,7 @@
 	GLuint getStencilSize() const;
 	GLsizei getSamples() const;
 
+	void setLevel(GLint level);
 	void setStorage(RenderbufferStorage *newStorage);
 
 private:
@@ -152,11 +161,11 @@
 	explicit Colorbuffer(egl::Image *renderTarget);
 	Colorbuffer(GLsizei width, GLsizei height, GLenum internalformat, GLsizei samples);
 
-	virtual ~Colorbuffer();
+	~Colorbuffer() override;
 
-	virtual egl::Image *getRenderTarget();
-    virtual egl::Image *createSharedImage();
-    virtual bool isShared() const;
+	egl::Image *getRenderTarget() override;
+    egl::Image *createSharedImage() override;
+    bool isShared() const override;
 
 private:
 	egl::Image *mRenderTarget;
@@ -170,9 +179,9 @@
 
 	~DepthStencilbuffer();
 
-	virtual egl::Image *getRenderTarget();
-    virtual egl::Image *createSharedImage();
-    virtual bool isShared() const;
+	egl::Image *getRenderTarget() override;
+    egl::Image *createSharedImage() override;
+    bool isShared() const override;
 
 protected:
 	egl::Image *mDepthStencil;
@@ -184,7 +193,7 @@
 	explicit Depthbuffer(egl::Image *depthStencil);
 	Depthbuffer(GLsizei width, GLsizei height, GLenum internalformat, GLsizei samples);
 
-	virtual ~Depthbuffer();
+	~Depthbuffer() override;
 };
 
 class Stencilbuffer : public DepthStencilbuffer
@@ -193,7 +202,7 @@
 	explicit Stencilbuffer(egl::Image *depthStencil);
 	Stencilbuffer(GLsizei width, GLsizei height, GLsizei samples);
 
-	virtual ~Stencilbuffer();
+	~Stencilbuffer() override;
 };
 }
 
diff --git a/src/OpenGL/libGLES_CM/Texture.cpp b/src/OpenGL/libGLES_CM/Texture.cpp
index 1043918..4d8b75a 100644
--- a/src/OpenGL/libGLES_CM/Texture.cpp
+++ b/src/OpenGL/libGLES_CM/Texture.cpp
@@ -678,7 +678,7 @@
 	return image[level];
 }
 
-Renderbuffer *Texture2D::getRenderbuffer(GLenum target)
+Renderbuffer *Texture2D::getRenderbuffer(GLenum target, GLint level)
 {
 	if(target != GL_TEXTURE_2D)
 	{
@@ -687,7 +687,11 @@
 
 	if(!mColorbufferProxy)
 	{
-		mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture2D(this));
+		mColorbufferProxy = new Renderbuffer(name, new RenderbufferTexture2D(this, level));
+	}
+	else
+	{
+		mColorbufferProxy->setLevel(level);
 	}
 
 	return mColorbufferProxy;
diff --git a/src/OpenGL/libGLES_CM/Texture.h b/src/OpenGL/libGLES_CM/Texture.h
index 0439039..e287c15 100644
--- a/src/OpenGL/libGLES_CM/Texture.h
+++ b/src/OpenGL/libGLES_CM/Texture.h
@@ -83,7 +83,7 @@
 	virtual bool isCompressed(GLenum target, GLint level) const = 0;
 	virtual bool isDepth(GLenum target, GLint level) const = 0;
 
-	virtual Renderbuffer *getRenderbuffer(GLenum target) = 0;
+	virtual Renderbuffer *getRenderbuffer(GLenum target, GLint level) = 0;
 	virtual egl::Image *getRenderTarget(GLenum target, unsigned int level) = 0;
 	egl::Image *createSharedImage(GLenum target, unsigned int level);
 	virtual bool isShared(GLenum target, unsigned int level) const = 0;
@@ -153,7 +153,7 @@
 	void generateMipmaps() override;
 	void autoGenerateMipmaps() override;
 
-	Renderbuffer *getRenderbuffer(GLenum target) override;
+	Renderbuffer *getRenderbuffer(GLenum target, GLint level) override;
 	egl::Image *getRenderTarget(GLenum target, unsigned int level) override;
 	bool isShared(GLenum target, unsigned int level) const override;
 
diff --git a/src/OpenGL/libGLES_CM/libGLES_CM.cpp b/src/OpenGL/libGLES_CM/libGLES_CM.cpp
index b1f5e80..7e5247c 100644
--- a/src/OpenGL/libGLES_CM/libGLES_CM.cpp
+++ b/src/OpenGL/libGLES_CM/libGLES_CM.cpp
@@ -1507,7 +1507,7 @@
 				return error(GL_INVALID_ENUM);
 			}
 
-			if(level != 0)
+			if((level < 0) || (level >= es1::IMPLEMENTATION_MAX_TEXTURE_LEVELS))
 			{
 				return error(GL_INVALID_VALUE);
 			}
@@ -1528,9 +1528,9 @@
 
 		switch(attachment)
 		{
-		case GL_COLOR_ATTACHMENT0_OES:  framebuffer->setColorbuffer(textarget, texture);   break;
-		case GL_DEPTH_ATTACHMENT_OES:   framebuffer->setDepthbuffer(textarget, texture);   break;
-		case GL_STENCIL_ATTACHMENT_OES: framebuffer->setStencilbuffer(textarget, texture); break;
+		case GL_COLOR_ATTACHMENT0_OES:  framebuffer->setColorbuffer(textarget, texture, level);   break;
+		case GL_DEPTH_ATTACHMENT_OES:   framebuffer->setDepthbuffer(textarget, texture, level);   break;
+		case GL_STENCIL_ATTACHMENT_OES: framebuffer->setStencilbuffer(textarget, texture, level); break;
 		}
 	}
 }
@@ -2082,19 +2082,23 @@
 
 		GLenum attachmentType;
 		GLuint attachmentHandle;
+		Renderbuffer *renderbuffer = nullptr;
 		switch(attachment)
 		{
 		case GL_COLOR_ATTACHMENT0_OES:
 			attachmentType = framebuffer->getColorbufferType();
 			attachmentHandle = framebuffer->getColorbufferName();
+			renderbuffer = framebuffer->getColorbuffer();
 			break;
 		case GL_DEPTH_ATTACHMENT_OES:
 			attachmentType = framebuffer->getDepthbufferType();
 			attachmentHandle = framebuffer->getDepthbufferName();
+			renderbuffer = framebuffer->getDepthbuffer();
 			break;
 		case GL_STENCIL_ATTACHMENT_OES:
 			attachmentType = framebuffer->getStencilbufferType();
 			attachmentHandle = framebuffer->getStencilbufferName();
+			renderbuffer = framebuffer->getStencilbuffer();
 			break;
 		default:
 			return error(GL_INVALID_ENUM);
@@ -2129,7 +2133,7 @@
 		case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_OES:
 			if(attachmentObjectType == GL_TEXTURE)
 			{
-				*params = 0; // FramebufferTexture2D will not allow level to be set to anything else in GL ES 2.0
+				*params = renderbuffer->getLevel();
 			}
 			else
 			{
@@ -2268,6 +2272,7 @@
 			"GL_OES_EGL_image_external "
 			"GL_OES_EGL_sync "
 			"GL_OES_element_index_uint "
+			"GL_OES_fbo_render_mipmap "
 			"GL_OES_framebuffer_object "
 			"GL_OES_packed_depth_stencil "
 			"GL_OES_read_format "
diff --git a/src/OpenGL/libGLESv2/Context.cpp b/src/OpenGL/libGLESv2/Context.cpp
index 5a5a6b3..e257174 100644
--- a/src/OpenGL/libGLESv2/Context.cpp
+++ b/src/OpenGL/libGLESv2/Context.cpp
@@ -4405,6 +4405,7 @@
 		"GL_OES_EGL_image_external",
 		"GL_OES_EGL_sync",
 		"GL_OES_element_index_uint",
+		"GL_OES_fbo_render_mipmap",
 		"GL_OES_framebuffer_object",
 		"GL_OES_packed_depth_stencil",
 		"GL_OES_rgb8_rgba8",
diff --git a/src/OpenGL/libGLESv2/Renderbuffer.cpp b/src/OpenGL/libGLESv2/Renderbuffer.cpp
index a970dcf..fdbb617 100644
--- a/src/OpenGL/libGLESv2/Renderbuffer.cpp
+++ b/src/OpenGL/libGLESv2/Renderbuffer.cpp
@@ -78,7 +78,7 @@
 
 RenderbufferTexture2D::~RenderbufferTexture2D()
 {
-	mTexture2D = NULL;
+	mTexture2D = nullptr;
 }
 
 // Textures need to maintain their own reference count for references via
@@ -441,7 +441,7 @@
 
 void Renderbuffer::setStorage(RenderbufferStorage *newStorage)
 {
-	ASSERT(newStorage != NULL);
+	ASSERT(newStorage);
 
 	delete mInstance;
 	mInstance = newStorage;
diff --git a/src/OpenGL/libGLESv2/libGLESv2.cpp b/src/OpenGL/libGLESv2/libGLESv2.cpp
index 1577569..4be1261 100644
--- a/src/OpenGL/libGLESv2/libGLESv2.cpp
+++ b/src/OpenGL/libGLESv2/libGLESv2.cpp
@@ -6006,11 +6006,6 @@
 				return error(GL_INVALID_OPERATION);
 			}
 
-			if(tex->isCompressed(textarget, level))
-			{
-				return error(GL_INVALID_OPERATION);
-			}
-
 			switch(textarget)
 			{
 			case GL_TEXTURE_3D:
@@ -6023,10 +6018,15 @@
 				return error(GL_INVALID_ENUM);
 			}
 
-			if(level != 0)
+			if((level < 0) || (level >= es2::IMPLEMENTATION_MAX_TEXTURE_LEVELS))
 			{
 				return error(GL_INVALID_VALUE);
 			}
+
+			if(tex->isCompressed(textarget, level))
+			{
+				return error(GL_INVALID_OPERATION);
+			}
 		}
 
 		es2::Framebuffer *framebuffer = nullptr;
@@ -6049,14 +6049,14 @@
 
 		switch(attachment)
 		{
-		case GL_DEPTH_ATTACHMENT:   framebuffer->setDepthbuffer(textarget, texture);   break;
-		case GL_STENCIL_ATTACHMENT: framebuffer->setStencilbuffer(textarget, texture); break;
+		case GL_DEPTH_ATTACHMENT:   framebuffer->setDepthbuffer(textarget, texture, level);   break;
+		case GL_STENCIL_ATTACHMENT: framebuffer->setStencilbuffer(textarget, texture, level); break;
 		default:
 			if((attachment - GL_COLOR_ATTACHMENT0) >= MAX_COLOR_ATTACHMENTS)
 			{
 				return error(GL_INVALID_ENUM);
 			}
-			framebuffer->setColorbuffer(textarget, texture, attachment - GL_COLOR_ATTACHMENT0);
+			framebuffer->setColorbuffer(textarget, texture, attachment - GL_COLOR_ATTACHMENT0, level);
 			break;
 		}
 	}