Bug 705904 - [1/6] - Kill nsRefPtrHashtables of WebGL objects, allow unreferenced objects to be freed, fix about:memory reporting of deleted objects - r=jgilbert
authorBenoit Jacob <bjacob@mozilla.com>
Sun, 04 Dec 2011 14:15:43 -0500
changeset 82010 a9f0158d9494d608d3bce49c8fed487c89dfd4e3
parent 82009 3a7aa2475d3cd9b5024501ebc6d4cc0cca42461b
child 82011 eaf41f64aad7d983a7b1d0a01195c4231cc5f13b
push idunknown
push userunknown
push dateunknown
reviewersjgilbert
bugs705904
milestone11.0a1
Bug 705904 - [1/6] - Kill nsRefPtrHashtables of WebGL objects, allow unreferenced objects to be freed, fix about:memory reporting of deleted objects - r=jgilbert This patch takes care of WebGLTexture, and introduces the WebGLFastArray helper (see comment).
content/canvas/src/WebGLContext.cpp
content/canvas/src/WebGLContext.h
content/canvas/src/WebGLContextGL.cpp
content/canvas/src/WebGLContextValidate.cpp
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -232,17 +232,16 @@ WebGLContext::WebGLContext()
     mWebGLError = LOCAL_GL_NO_ERROR;
     mPixelStoreFlipY = false;
     mPixelStorePremultiplyAlpha = false;
     mPixelStoreColorspaceConversion = BROWSER_DEFAULT_WEBGL;
 
     mShaderValidation = true;
 
     mMapBuffers.Init();
-    mMapTextures.Init();
     mMapPrograms.Init();
     mMapShaders.Init();
     mMapFramebuffers.Init();
     mMapRenderbuffers.Init();
 
     mBlackTexturesAreInitialized = false;
     mFakeBlackStatus = DoNotNeedFakeBlack;
 
@@ -343,17 +342,18 @@ WebGLContext::DestroyResourcesAndContext
     mBoundArrayBuffer = nsnull;
     mBoundElementArrayBuffer = nsnull;
     mCurrentProgram = nsnull;
     mBoundFramebuffer = nsnull;
     mBoundRenderbuffer = nsnull;
 
     mAttribBuffers.Clear();
 
-    DeleteWebGLObjectsHashTable(mMapTextures);
+    while (mTextures.Length())
+        mTextures.Last()->DeleteOnce();
     DeleteWebGLObjectsHashTable(mMapRenderbuffers);
     DeleteWebGLObjectsHashTable(mMapFramebuffers);
     DeleteWebGLObjectsHashTable(mMapBuffers);
     DeleteWebGLObjectsHashTable(mMapShaders);
     DeleteWebGLObjectsHashTable(mMapPrograms);
 
     if (mBlackTexturesAreInitialized) {
         gl->fDeleteTextures(1, &mBlackTexture2D);
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -360,16 +360,86 @@ private:
         mRawPtr = newPtr;
         ReleasePtr(oldPtr);
     }
 
 protected:
     T *mRawPtr;
 };
 
+typedef PRUint64 WebGLMonotonicHandle;
+
+/* WebGLFastArray offers a fast array for the use case where all what one needs is to append
+ * and remove elements. Removal is fast because the array is always kept sorted with respect
+ * to "monotonic handles". Appending an element returns such a "monotonic handle" which the
+ * user needs to keep for future use for when it will want to remove the element.
+ */
+template<typename ElementType>
+class WebGLFastArray
+{
+    struct Entry {
+        ElementType mElement;
+        WebGLMonotonicHandle mMonotonicHandle;
+
+        Entry(ElementType elem, WebGLMonotonicHandle monotonicHandle)
+            : mElement(elem), mMonotonicHandle(monotonicHandle)
+        {}
+
+        struct Comparator {
+            bool Equals(const Entry& a, const Entry& b) const {
+                return a.mMonotonicHandle == b.mMonotonicHandle;
+            }
+            bool LessThan(const Entry& a, const Entry& b) const {
+                return a.mMonotonicHandle < b.mMonotonicHandle;
+            }
+        };
+    };
+
+public:
+    WebGLFastArray()
+        : mCurrentMonotonicHandle(0) // CheckedInt already does it, this is just defensive coding
+    {}
+
+    ElementType operator[](size_t index) const {
+        return mArray[index].mElement;
+    }
+
+    size_t Length() const {
+        return mArray.Length();
+    }
+
+    ElementType Last() const {
+        return operator[](Length() - 1);
+    }
+
+    WebGLMonotonicHandle AppendElement(ElementType elem)
+    {
+        WebGLMonotonicHandle monotonicHandle = NextMonotonicHandle();
+        mArray.AppendElement(Entry(elem, monotonicHandle));
+        return monotonicHandle;
+    }
+
+    void RemoveElement(WebGLMonotonicHandle monotonicHandle)
+    {
+        mArray.RemoveElementSorted(Entry(ElementType(), monotonicHandle),
+                                   typename Entry::Comparator());
+    }
+
+private:
+    WebGLMonotonicHandle NextMonotonicHandle() {
+        ++mCurrentMonotonicHandle;
+        if (!mCurrentMonotonicHandle.valid())
+            NS_RUNTIMEABORT("ran out of monotonic ids!");
+        return mCurrentMonotonicHandle.value();
+    }
+
+    nsTArray<Entry> mArray;
+    CheckedInt<WebGLMonotonicHandle> mCurrentMonotonicHandle;
+};
+
 struct WebGLContextOptions {
     // these are defaults
     WebGLContextOptions()
         : alpha(true), depth(true), stencil(false),
           premultipliedAlpha(true), antialias(true),
           preserveDrawingBuffer(false)
     { }
 
@@ -573,16 +643,19 @@ protected:
     bool mInvalidated;
     bool mResetLayer;
     bool mVerbose;
     bool mOptionsFrozen;
     bool mMinCapability;
     bool mDisableExtensions;
     bool mHasRobustness;
 
+    template<typename WebGLObjectType>
+    void DeleteWebGLObjectsArray(nsTArray<WebGLObjectType>& array);
+
     WebGLuint mActiveTexture;
     WebGLenum mWebGLError;
 
     // whether shader validation is supported
     bool mShaderValidation;
 
     // some GL constants
     PRInt32 mGLMaxVertexAttribs;
@@ -747,17 +820,17 @@ protected:
     WebGLRefPtr<WebGLProgram> mCurrentProgram;
 
     PRUint32 mMaxFramebufferColorAttachments;
 
     WebGLRefPtr<WebGLFramebuffer> mBoundFramebuffer;
     WebGLRefPtr<WebGLRenderbuffer> mBoundRenderbuffer;
 
     // lookup tables for GL name -> object wrapper
-    nsRefPtrHashtable<nsUint32HashKey, WebGLTexture> mMapTextures;
+    WebGLFastArray<WebGLTexture*> mTextures;
     nsRefPtrHashtable<nsUint32HashKey, WebGLBuffer> mMapBuffers;
     nsRefPtrHashtable<nsUint32HashKey, WebGLProgram> mMapPrograms;
     nsRefPtrHashtable<nsUint32HashKey, WebGLShader> mMapShaders;
     nsRefPtrHashtable<nsUint32HashKey, WebGLFramebuffer> mMapFramebuffers;
     nsRefPtrHashtable<nsUint32HashKey, WebGLRenderbuffer> mMapRenderbuffers;
 
     // PixelStore parameters
     PRUint32 mPixelStorePackAlignment, mPixelStoreUnpackAlignment, mPixelStoreColorspaceConversion;
@@ -1060,26 +1133,28 @@ public:
         , mWrapT(LOCAL_GL_REPEAT)
         , mFacesCount(0)
         , mMaxLevelWithCustomImages(0)
         , mHaveGeneratedMipmap(false)
         , mFakeBlackStatus(DoNotNeedFakeBlack)
     {
         mContext->MakeContextCurrent();
         mContext->gl->fGenTextures(1, &mGLName);
+        mMonotonicHandle = mContext->mTextures.AppendElement(this);
     }
 
     ~WebGLTexture() {
         DeleteOnce();
     }
 
     void Delete() {
         mImageInfos.Clear();
         mContext->MakeContextCurrent();
         mContext->gl->fDeleteTextures(1, &mGLName);
+        mContext->mTextures.RemoveElement(mMonotonicHandle);
     }
 
     bool HasEverBeenBound() { return mHasEverBeenBound; }
     void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; }
     WebGLuint GLName() { return mGLName; }
     GLenum Target() const { return mTarget; }
 
     NS_DECL_ISUPPORTS
@@ -1152,16 +1227,18 @@ public:
                ImageInfoAt(level, face).mIsDefined;
     }
 
     static size_t FaceForTarget(WebGLenum target) {
         return target == LOCAL_GL_TEXTURE_2D ? 0 : target - LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
     }
 
     PRInt64 MemoryUsage() const {
+        if (IsDeleted())
+            return 0;
         PRInt64 result = 0;
         for(size_t face = 0; face < mFacesCount; face++) {
             if (mHaveGeneratedMipmap) {
                 // Each mipmap level is 1/4 the size of the previous level
                 // 1 + x + x^2 + ... = 1/(1-x)
                 // for x = 1/4, we get 1/(1-1/4) = 4/3
                 result += ImageInfoAt(0, face).MemoryUsage() * 4 / 3;
             } else {
@@ -1178,16 +1255,18 @@ protected:
     WebGLenum mMinFilter, mMagFilter, mWrapS, mWrapT;
 
     size_t mFacesCount, mMaxLevelWithCustomImages;
     nsTArray<ImageInfo> mImageInfos;
 
     bool mHaveGeneratedMipmap;
     FakeBlackStatus mFakeBlackStatus;
 
+    WebGLMonotonicHandle mMonotonicHandle;
+
     void EnsureMaxLevelWithCustomImagesAtLeast(size_t aMaxLevelWithCustomImages) {
         mMaxLevelWithCustomImages = NS_MAX(mMaxLevelWithCustomImages, aMaxLevelWithCustomImages);
         mImageInfos.EnsureLengthAtLeast((mMaxLevelWithCustomImages + 1) * mFacesCount);
     }
 
     bool CheckFloatTextureFilterParams() const {
         // Without OES_texture_float_linear, only NEAREST and NEAREST_MIMPAMP_NEAREST are supported
         return (mMagFilter == LOCAL_GL_NEAREST) &&
@@ -2385,43 +2464,33 @@ class WebGLMemoryReporter
         ContextsArrayType & contexts = Contexts();
         contexts.RemoveElement(c);
         if (contexts.IsEmpty()) {
             delete sUniqueInstance;
             sUniqueInstance = nsnull;
         }
     }
 
-    static PLDHashOperator TextureMemoryUsageFunction(const PRUint32&, WebGLTexture *aValue, void *aData)
-    {
-        PRInt64 *result = (PRInt64*) aData;
-        *result += aValue->MemoryUsage();
-        return PL_DHASH_NEXT;
-    }
-
     static PRInt64 GetTextureMemoryUsed() {
         const ContextsArrayType & contexts = Contexts();
         PRInt64 result = 0;
-        for(size_t i = 0; i < contexts.Length(); ++i) {
-            PRInt64 textureMemoryUsageForThisContext = 0;
-            contexts[i]->mMapTextures.EnumerateRead(TextureMemoryUsageFunction, &textureMemoryUsageForThisContext);
-            result += textureMemoryUsageForThisContext;
-        }
+        for(size_t i = 0; i < contexts.Length(); ++i)
+            for (size_t t = 0; t < contexts[i]->mTextures.Length(); ++t)
+              result += contexts[i]->mTextures[t]->MemoryUsage();
         return result;
     }
-    
+
     static PRInt64 GetTextureCount() {
         const ContextsArrayType & contexts = Contexts();
         PRInt64 result = 0;
-        for(size_t i = 0; i < contexts.Length(); ++i) {
-            result += contexts[i]->mMapTextures.Count();
-        }
+        for(size_t i = 0; i < contexts.Length(); ++i)
+            result += contexts[i]->mTextures.Length();
         return result;
     }
-    
+
     static PLDHashOperator BufferMemoryUsageFunction(const PRUint32&, WebGLBuffer *aValue, void *aData)
     {
         PRInt64 *result = (PRInt64*) aData;
         *result += aValue->ByteLength();
         return PL_DHASH_NEXT;
     }
 
     static PRInt64 GetBufferMemoryUsed() {
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -1231,17 +1231,16 @@ WebGLContext::DeleteTexture(nsIWebGLText
         {
             ActiveTexture(LOCAL_GL_TEXTURE0 + i);
             BindTexture(tex->Target(), nsnull);
         }
     }
     ActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture);
 
     tex->RequestDelete();
-    mMapTextures.Remove(texname);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 WebGLContext::DeleteProgram(nsIWebGLProgram *pobj)
 {
     if (mContextLost)
@@ -2546,17 +2545,16 @@ WebGLContext::CreateTexture(nsIWebGLText
         return NS_OK;
 
     *retval = nsnull;
 
     MakeContextCurrent();
 
     WebGLTexture *globj = new WebGLTexture(this);
     NS_ADDREF(*retval = globj);
-    mMapTextures.Put(globj->GLName(), globj);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 WebGLContext::GetError(WebGLenum *_retval)
 {
     if (!mContextLost) {
--- a/content/canvas/src/WebGLContextValidate.cpp
+++ b/content/canvas/src/WebGLContextValidate.cpp
@@ -520,17 +520,16 @@ WebGLContext::InitAndValidateGL()
 
     mBoundArrayBuffer = nsnull;
     mBoundElementArrayBuffer = nsnull;
     mCurrentProgram = nsnull;
 
     mBoundFramebuffer = nsnull;
     mBoundRenderbuffer = nsnull;
 
-    mMapTextures.Clear();
     mMapBuffers.Clear();
     mMapPrograms.Clear();
     mMapShaders.Clear();
     mMapFramebuffers.Clear();
     mMapRenderbuffers.Clear();
 
     MakeContextCurrent();