Fix reference counting of texture images.

- Only unbind (orphan and release) images when the texture destructs.
- Let images hold just one reference to the parent texture.
- Check if textures and images only reference each other and garbage collect.

Bug 26851951

Change-Id: I2b0bcc283bf545d948e91288c531eac7cc14d122
Reviewed-on: https://swiftshader-review.googlesource.com/4711
Tested-by: Nicolas Capens <capn@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <capn@google.com>
diff --git a/src/Common/Version.h b/src/Common/Version.h
index 7268708..ec9107a 100644
--- a/src/Common/Version.h
+++ b/src/Common/Version.h
@@ -1,7 +1,7 @@
 #define MAJOR_VERSION 3
 #define MINOR_VERSION 2
 #define BUILD_VERSION 10
-#define BUILD_REVISION 47661
+#define BUILD_REVISION 47663
 
 #define STRINGIFY(x) #x
 #define MACRO_STRINGIFY(x) STRINGIFY(x)
diff --git a/src/OpenGL/common/Image.cpp b/src/OpenGL/common/Image.cpp
index 73a8656..1396a52 100644
--- a/src/OpenGL/common/Image.cpp
+++ b/src/OpenGL/common/Image.cpp
@@ -1132,27 +1132,29 @@
 
 	Image::~Image()
 	{
-		ASSERT(!shared);
-	}
-
-	void Image::addRef()
-	{
 		if(parentTexture)
 		{
-			return parentTexture->addRef();
+			parentTexture->release();
 		}
 
-		Object::addRef();
+		ASSERT(!shared);
 	}
 
 	void Image::release()
 	{
-		if(parentTexture)
-		{
-			return parentTexture->release();
-		}
+		int refs = dereference();
 
-		Object::release();
+		if(refs > 0)
+		{
+			if(parentTexture)
+			{
+				parentTexture->sweep();
+			}
+		}
+		else
+		{
+			delete this;
+		}
 	}
 
 	void Image::unbind(const egl::Texture *parent)
diff --git a/src/OpenGL/common/Image.hpp b/src/OpenGL/common/Image.hpp
index a019133..632e360 100644
--- a/src/OpenGL/common/Image.hpp
+++ b/src/OpenGL/common/Image.hpp
@@ -42,6 +42,7 @@
 	{

 		shared = false;

 		Object::addRef();

+		parentTexture->addRef();

 	}

 

 	// 3D texture image

@@ -52,6 +53,7 @@
 	{

 		shared = false;

 		Object::addRef();

+		parentTexture->addRef();

 	}

 

 	// Native EGL image

@@ -146,7 +148,6 @@
 	void loadImageData(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const UnpackInfo& unpackInfo, const void *input);

 	void loadCompressedData(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei imageSize, const void *pixels);

 

-	void addRef() override;

 	void release() override;

 	void unbind(const Texture *parent);   // Break parent ownership and release

 	bool isChildOf(const Texture *parent) const;

diff --git a/src/OpenGL/libEGL/Texture.hpp b/src/OpenGL/libEGL/Texture.hpp
index 89f40bf..3805d97 100644
--- a/src/OpenGL/libEGL/Texture.hpp
+++ b/src/OpenGL/libEGL/Texture.hpp
@@ -17,6 +17,22 @@
 

 	virtual void releaseTexImage() = 0;

 	virtual sw::Resource *getResource() const = 0;

+

+	virtual void sweep() = 0;   // Garbage collect if no external references

+

+	void release() override

+	{

+		int refs = dereference();

+

+		if(refs > 0)

+		{

+			sweep();

+		}

+		else

+		{

+			delete this;

+		}

+	}

 };

 }

 

diff --git a/src/OpenGL/libGLES_CM/Texture.cpp b/src/OpenGL/libGLES_CM/Texture.cpp
index 9ee8989..bd3da9c 100644
--- a/src/OpenGL/libGLES_CM/Texture.cpp
+++ b/src/OpenGL/libGLES_CM/Texture.cpp
@@ -387,6 +387,29 @@
 	}

 }

 

+void Texture2D::sweep()

+{

+	int imageCount = 0;

+

+	for(int i = 0; i < MIPMAP_LEVELS; i++)

+	{

+		if(image[i] && image[i]->isChildOf(this))

+		{

+			if(!image[i]->hasSingleReference())

+			{

+				return;

+			}

+

+			imageCount++;

+		}

+	}

+

+	if(imageCount == referenceCount)

+	{

+		destroy();

+	}

+}

+

 GLenum Texture2D::getTarget() const

 {

     return GL_TEXTURE_2D;

@@ -439,7 +462,7 @@
 {

 	if(image[level])

 	{

-		image[level]->unbind(this);

+		image[level]->release();

 	}

 

 	image[level] = new egl::Image(this, width, height, format, type);

@@ -477,7 +500,7 @@
 	{

 		if(image[level])

 		{

-			image[level]->unbind(this);

+			image[level]->release();

 			image[level] = nullptr;

 		}

 	}

@@ -494,7 +517,7 @@
 	{

 		if(image[level])

 		{

-			image[level]->unbind(this);

+			image[level]->release();

 			image[level] = nullptr;

 		}

 	}

@@ -504,7 +527,7 @@
 {

 	if(image[level])

 	{

-		image[level]->unbind(this);

+		image[level]->release();

 	}

 

 	image[level] = new egl::Image(this, width, height, format, GL_UNSIGNED_BYTE);

@@ -539,7 +562,7 @@
 

 	if(image[level])

 	{

-		image[level]->unbind(this);

+		image[level]->release();

 	}

 

 	image[level] = new egl::Image(this, width, height, format, GL_UNSIGNED_BYTE);

@@ -594,7 +617,7 @@
 

     if(image[0])

     {

-        image[0]->unbind(this);

+        image[0]->release();

     }

 

     image[0] = sharedImage;

@@ -689,7 +712,7 @@
     {

 		if(image[i])

 		{

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

+			image[i]->release();

 		}

 

 		image[i] = new egl::Image(this, std::max(image[0]->getWidth() >> i, 1), std::max(image[0]->getHeight() >> i, 1), image[0]->getFormat(), image[0]->getType());

diff --git a/src/OpenGL/libGLES_CM/Texture.h b/src/OpenGL/libGLES_CM/Texture.h
index 75b483b..4a49ddb 100644
--- a/src/OpenGL/libGLES_CM/Texture.h
+++ b/src/OpenGL/libGLES_CM/Texture.h
@@ -129,6 +129,7 @@
 

 	void addProxyRef(const Renderbuffer *proxy) override;

     void releaseProxy(const Renderbuffer *proxy) override;

+	void sweep() override;

 

     virtual GLenum getTarget() const;

 

diff --git a/src/OpenGL/libGLESv2/Texture.cpp b/src/OpenGL/libGLESv2/Texture.cpp
index 68ef704..c7877fa 100644
--- a/src/OpenGL/libGLESv2/Texture.cpp
+++ b/src/OpenGL/libGLESv2/Texture.cpp
@@ -558,6 +558,29 @@
 	}

 }

 

+void Texture2D::sweep()

+{

+	int imageCount = 0;

+

+	for(int i = 0; i < MIPMAP_LEVELS; i++)

+	{

+		if(image[i] && image[i]->isChildOf(this))

+		{

+			if(!image[i]->hasSingleReference())

+			{

+				return;

+			}

+

+			imageCount++;

+		}

+	}

+

+	if(imageCount == referenceCount)

+	{

+		destroy();

+	}

+}

+

 GLenum Texture2D::getTarget() const

 {

     return GL_TEXTURE_2D;

@@ -610,7 +633,7 @@
 {

 	if(image[level])

 	{

-		image[level]->unbind(this);

+		image[level]->release();

 	}

 

 	image[level] = new egl::Image(this, width, height, format, type);

@@ -648,7 +671,7 @@
 	{

 		if(image[level])

 		{

-			image[level]->unbind(this);

+			image[level]->release();

 			image[level] = nullptr;

 		}

 	}

@@ -665,7 +688,7 @@
 	{

 		if(image[level])

 		{

-			image[level]->unbind(this);

+			image[level]->release();

 			image[level] = nullptr;

 		}

 	}

@@ -675,7 +698,7 @@
 {

 	if(image[level])

 	{

-		image[level]->unbind(this);

+		image[level]->release();

 	}

 

 	GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);

@@ -711,7 +734,7 @@
 

 	if(image[level])

 	{

-		image[level]->unbind(this);

+		image[level]->release();

 	}

 

 	GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);

@@ -788,7 +811,7 @@
 

     if(image[0])

     {

-        image[0]->unbind(this);

+        image[0]->release();

     }

 

     image[0] = sharedImage;

@@ -883,7 +906,7 @@
     {

 		if(image[i])

 		{

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

+			image[i]->release();

 		}

 

 		image[i] = new egl::Image(this, std::max(image[0]->getWidth() >> i, 1), std::max(image[0]->getHeight() >> i, 1), image[0]->getFormat(), image[0]->getType());

@@ -1024,6 +1047,32 @@
     }

 }

 

+void TextureCubeMap::sweep()

+{

+	int imageCount = 0;

+

+	for(int f = 0; f < 6; f++)

+	{

+		for(int i = 0; i < MIPMAP_LEVELS; i++)

+		{

+			if(image[f][i] && image[f][i]->isChildOf(this))

+			{

+				if(!image[f][i]->hasSingleReference())

+				{

+					return;

+				}

+

+				imageCount++;

+			}

+		}

+	}

+

+	if(imageCount == referenceCount)

+	{

+		destroy();

+	}

+}

+

 GLenum TextureCubeMap::getTarget() const

 {

     return GL_TEXTURE_CUBE_MAP;

@@ -1078,7 +1127,7 @@
 

 	if(image[face][level])

 	{

-		image[face][level]->unbind(this);

+		image[face][level]->release();

 	}

 

 	GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);

@@ -1220,7 +1269,7 @@
 

 	if(image[face][level])

 	{

-		image[face][level]->unbind(this);

+		image[face][level]->release();

 	}

 

 	image[face][level] = new egl::Image(this, width, height, format, type);

@@ -1247,7 +1296,7 @@
 

 	if(image[face][level])

 	{

-		image[face][level]->unbind(this);

+		image[face][level]->release();

 	}

 

 	GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);

@@ -1342,7 +1391,7 @@
 		{

 			if(image[f][i])

 			{

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

+				image[f][i]->release();

 			}

 

 			image[f][i] = new egl::Image(this, std::max(image[0][0]->getWidth() >> i, 1), std::max(image[0][0]->getHeight() >> i, 1), image[0][0]->getFormat(), image[0][0]->getType());

@@ -1462,6 +1511,29 @@
 	}

 }

 

+void Texture3D::sweep()

+{

+	int imageCount = 0;

+

+	for(int i = 0; i < MIPMAP_LEVELS; i++)

+	{

+		if(image[i] && image[i]->isChildOf(this))

+		{

+			if(!image[i]->hasSingleReference())

+			{

+				return;

+			}

+

+			imageCount++;

+		}

+	}

+

+	if(imageCount == referenceCount)

+	{

+		destroy();

+	}

+}

+

 GLenum Texture3D::getTarget() const

 {

 	return GL_TEXTURE_3D_OES;

@@ -1520,7 +1592,7 @@
 {

 	if(image[level])

 	{

-		image[level]->unbind(this);

+		image[level]->release();

 	}

 

 	image[level] = new egl::Image(this, width, height, depth, format, type);

@@ -1554,7 +1626,7 @@
 	{

 		if(image[level])

 		{

-			image[level]->unbind(this);

+			image[level]->release();

 			image[level] = nullptr;

 		}

 	}

@@ -1571,7 +1643,7 @@
 	{

 		if(image[level])

 		{

-			image[level]->unbind(this);

+			image[level]->release();

 			image[level] = nullptr;

 		}

 	}

@@ -1581,7 +1653,7 @@
 {

 	if(image[level])

 	{

-		image[level]->unbind(this);

+		image[level]->release();

 	}

 

 	GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);

@@ -1617,7 +1689,7 @@
 

 	if(image[level])

 	{

-		image[level]->unbind(this);

+		image[level]->release();

 	}

 

 	GLenum sizedInternalFormat = GetSizedInternalFormat(format, GL_UNSIGNED_BYTE);

@@ -1691,7 +1763,7 @@
 

 	if(image[0])

 	{

-		image[0]->unbind(this);

+		image[0]->release();

 	}

 

 	image[0] = sharedImage;

@@ -1796,7 +1868,7 @@
 	{

 		if(image[i])

 		{

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

+			image[i]->release();

 		}

 

 		image[i] = new egl::Image(this, std::max(image[0]->getWidth() >> i, 1), std::max(image[0]->getHeight() >> i, 1), std::max(image[0]->getDepth() >> i, 1), image[0]->getFormat(), image[0]->getType());

@@ -1888,7 +1960,7 @@
 	{

 		if(image[i])

 		{

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

+			image[i]->release();

 		}

 

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

diff --git a/src/OpenGL/libGLESv2/Texture.h b/src/OpenGL/libGLESv2/Texture.h
index 30dc822..28d759c 100644
--- a/src/OpenGL/libGLESv2/Texture.h
+++ b/src/OpenGL/libGLESv2/Texture.h
@@ -157,6 +157,7 @@
 

 	void addProxyRef(const Renderbuffer *proxy) override;

     void releaseProxy(const Renderbuffer *proxy) override;

+	void sweep() override;

 

     virtual GLenum getTarget() const;

 

@@ -215,6 +216,7 @@
 

 	void addProxyRef(const Renderbuffer *proxy) override;

     void releaseProxy(const Renderbuffer *proxy) override;

+	void sweep() override;

 

     virtual GLenum getTarget() const;

 

@@ -274,6 +276,7 @@
 

 	void addProxyRef(const Renderbuffer *proxy) override;

     void releaseProxy(const Renderbuffer *proxy) override;

+	void sweep() override;

 

 	virtual GLenum getTarget() const;