Bug 749678 - 1/5 - Switch GLContext to use mozilla::tls for current context - r=jrmuizel
authorBenoit Jacob <bjacob@mozilla.com>
Tue, 08 May 2012 09:47:34 -0400
changeset 95762 16fe445890798ca8f8ed79211ca19c4e04ff141d
parent 95761 9f87dbd4d39ce0874c90e8c124e101d65cc93e36
child 95763 eaba5dd27bbd4450a6b66b4c8e436871c4409988
push id1439
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 20:19:22 +0000
treeherdermozilla-aurora@ea74834dccd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs749678
milestone15.0a1
Bug 749678 - 1/5 - Switch GLContext to use mozilla::tls for current context - r=jrmuizel See comment above class GLContextTLSStorage
gfx/gl/GLContext.cpp
gfx/gl/GLContext.h
gfx/thebes/gfxPlatform.cpp
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -59,19 +59,18 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/Util.h" // for DebugOnly
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace gl {
 
-#ifdef DEBUG
-PRUintn GLContext::sCurrentGLContextTLS = -1;
-#endif
+tls::key GLContextTLSStorage::sTLSKey;
+bool GLContextTLSStorage::sTLSKeyAlreadyCreated = false;
 
 PRUint32 GLContext::sDebugMode = 0;
 
 // define this here since it's global to GLContextProvider, not any
 // specific implementation
 const ContextFormat ContextFormat::BasicRGBA32Format(ContextFormat::BasicRGBA32);
 
 #define MAX_SYMBOL_LENGTH 128
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -62,16 +62,17 @@
 #include "nsISupportsImpl.h"
 #include "prlink.h"
 
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "nsRegion.h"
 #include "nsAutoPtr.h"
 #include "nsThreadUtils.h"
+#include "thread_helper.h"
 
 typedef char realGLboolean;
 
 #include "GLContextSymbols.h"
 
 #include "mozilla/mozalloc.h"
 
 namespace mozilla {
@@ -504,16 +505,81 @@ struct THEBES_API ContextFormat
     int green, minGreen;
     int blue, minBlue;
     int alpha, minAlpha;
     int samples;
 
     int colorBits() const { return red + green + blue; }
 };
 
+/*
+ * This is a helper class to do the little bit of TLS storage that we need
+ * to allow GLContext to keep track of the current GLContext for a given thread.
+ *
+ * This is mostly an optimization to avoid calling MakeCurrent on an
+ * already-current context,which depending on OpenGL libraries/drivers can be
+ * very expensive. An earlier optimization consisted in calling
+ * getCurrentContext to check if the context was already current, but
+ * even that was shown to be very slow at least on Mac and on Linux NVIDIA,
+ * see bug 749678.
+ *
+ * In a general setting, we would have to do a TLS lookup on every MakeCurrent
+ * call. But in GLContext, we currently assume that we only ever make GL calls
+ * on a given GLContext in the same thread that created it (the "owning thread").
+ * That assumption allows us to avoid doing a TLS lookup on every MakeCurrent
+ * call. It's checked by assertions in MOZ_GL_DEBUG mode.
+ *
+ * The way this works is that inside each GLContext, we store a pointer to the
+ * TLS pointer to the current context for this thread. This pointer-to-pointer
+ * (mStorage->mCurrentGLContext) is set during GL context creation: that's where
+ * we rely on the assumption that all GL calls on a given context are made on
+ * the same thread that created that context.
+ */
+class GLContextTLSStorage
+{
+    struct Storage
+    {
+        GLContext *mCurrentGLContext;
+
+        NS_INLINE_DECL_REFCOUNTING(Storage)
+        Storage() : mCurrentGLContext(nsnull) {}
+    };
+
+    nsRefPtr<Storage> mStorage;
+    static tls::key sTLSKey;
+    static bool sTLSKeyAlreadyCreated;
+
+public:
+
+    GLContextTLSStorage() {
+        if (!sTLSKeyAlreadyCreated) {
+            tls::create(&sTLSKey);
+            sTLSKeyAlreadyCreated = true;
+        }
+
+        mStorage = tls::get<Storage>(sTLSKey);
+
+        if (!mStorage) {
+            mStorage = new Storage;
+            tls::set<Storage>(sTLSKey, mStorage);
+        }
+    }
+
+    ~GLContextTLSStorage() {
+    }
+
+    GLContext *CurrentGLContext() const {
+        return mStorage->mCurrentGLContext;
+    }
+
+    void SetCurrentGLContext(GLContext *c) {
+        mStorage->mCurrentGLContext = c;
+    }
+};
+
 class GLContext
     : public GLLibraryLoader
 {
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GLContext)
 public:
     GLContext(const ContextFormat& aFormat,
               bool aIsOffscreen = false,
               GLContext *aSharedContext = nsnull)
@@ -555,26 +621,28 @@ public:
         , mGLError(LOCAL_GL_NO_ERROR)
 #endif
     {
         mUserData.Init();
         mOwningThread = NS_GetCurrentThread();
     }
 
     virtual ~GLContext() {
-        NS_ASSERTION(IsDestroyed(), "GLContext implementation must call MarkDestroyed in destructor!");
+        NS_ABORT_IF_FALSE(IsDestroyed(), "GLContext implementation must call MarkDestroyed in destructor!");
 #ifdef DEBUG
         if (mSharedContext) {
             GLContext *tip = mSharedContext;
             while (tip->mSharedContext)
                 tip = tip->mSharedContext;
             tip->SharedContextDestroyed(this);
             tip->ReportOutstandingNames();
         }
 #endif
+        if (this == CurrentGLContext())
+            SetCurrentGLContext(nsnull);
     }
 
     enum ContextFlags {
         ContextFlagsNone = 0x0,
         ContextFlagsGlobal = 0x1
     };
 
     enum GLContextType {
@@ -585,27 +653,28 @@ public:
         ContextTypeEGL,
         ContextTypeOSMesa
     };
 
     virtual GLContextType GetContextType() { return ContextTypeUnknown; }
 
     virtual bool MakeCurrentImpl(bool aForce = false) = 0;
 
-#ifdef DEBUG
-    static void StaticInit() {
-        PR_NewThreadPrivateIndex(&sCurrentGLContextTLS, NULL);
-    }
-#endif
-
     bool MakeCurrent(bool aForce = false) {
-#ifdef DEBUG
-        PR_SetThreadPrivate(sCurrentGLContextTLS, this);
-#endif
-        return MakeCurrentImpl(aForce);
+        if (!aForce &&
+            this == CurrentGLContext())
+        {
+            return true;
+        }
+
+        bool success = MakeCurrentImpl(aForce);
+        if (success) {
+            SetCurrentGLContext(this);
+        }
+        return success;
     }
 
     bool IsContextLost() { return mContextLost; }
 
     virtual bool SetupLookupFunction() = 0;
 
     virtual void WindowDestroyed() {}
 
@@ -1082,16 +1151,26 @@ public:
 
     GLuint SwapUserReadFBO(GLuint name) {
         GLuint prev = GetUserBoundReadFBO();
         BindUserReadFBO(name);
         return prev;
     }
 
 private:
+
+    GLContext *CurrentGLContext() const {
+        return mTLSStorage.CurrentGLContext();
+    }
+
+    void SetCurrentGLContext(GLContext *c) {
+        mTLSStorage.SetCurrentGLContext(c);
+    }
+
+
     bool mOffscreenFBOsDirty;
 
     void GetShaderPrecisionFormatNonES2(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) {
         switch (precisiontype) {
             case LOCAL_GL_LOW_FLOAT:
             case LOCAL_GL_MEDIUM_FLOAT:
             case LOCAL_GL_HIGH_FLOAT:
                 // Assume IEEE 754 precision
@@ -1607,33 +1686,26 @@ protected:
     ContextFormat mCreationFormat;
     nsRefPtr<GLContext> mSharedContext;
 
     // The thread on which this context was created.
     nsCOMPtr<nsIThread> mOwningThread;
 
     GLContextSymbols mSymbols;
 
-#ifdef DEBUG
-    // GLDebugMode will check that we don't send call
-    // to a GLContext that isn't current on the current
-    // thread.
-    // Store the current context when binding to thread local
-    // storage to support DebugMode on an arbitrary thread.
-    static PRUintn sCurrentGLContextTLS;
-#endif
-
     void UpdateActualFormat();
     ContextFormat mActualFormat;
 
     gfxIntSize mOffscreenSize;
     gfxIntSize mOffscreenActualSize;
     GLuint mOffscreenTexture;
     bool mFlipped;
 
+    GLContextTLSStorage mTLSStorage;
+
     // lazy-initialized things
     GLuint mBlitProgram, mBlitFramebuffer;
     void UseBlitProgram();
     void SetBlitFramebufferForDestTexture(GLuint aTexture);
 
     // Helper to create/resize an offscreen FBO,
     // for offscreen implementations that use FBOs.
     // Note that it does -not- clear the resized buffers.
@@ -1800,26 +1872,22 @@ public:
 
 protected:
     GLenum mGLError;
 
 public:
 
     void BeforeGLCall(const char* glFunction) {
         if (DebugMode()) {
-            GLContext *currentGLContext = NULL;
-
-            currentGLContext = (GLContext*)PR_GetThreadPrivate(sCurrentGLContextTLS);
-
             if (DebugMode() & DebugTrace)
                 printf_stderr("[gl:%p] > %s\n", this, glFunction);
-            if (this != currentGLContext) {
+            if (this != CurrentGLContext()) {
                 printf_stderr("Fatal: %s called on non-current context %p. "
                               "The current context for this thread is %p.\n",
-                               glFunction, this, currentGLContext);
+                               glFunction, this, CurrentGLContext());
                 NS_ABORT();
             }
         }
     }
 
     void AfterGLCall(const char* glFunction) {
         if (DebugMode()) {
             // calling fFinish() immediately after every GL call makes sure that if this GL command crashes,
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -296,20 +296,16 @@ gfxPlatform::Init()
 #elif defined(XP_OS2)
     gPlatform = new gfxOS2Platform;
 #elif defined(ANDROID)
     gPlatform = new gfxAndroidPlatform;
 #else
     #error "No gfxPlatform implementation available"
 #endif
 
-#ifdef DEBUG
-    mozilla::gl::GLContext::StaticInit();
-#endif
-
     nsresult rv;
 
 #if defined(XP_MACOSX) || defined(XP_WIN) || defined(ANDROID) // temporary, until this is implemented on others
     rv = gfxPlatformFontList::Init();
     if (NS_FAILED(rv)) {
         NS_RUNTIMEABORT("Could not initialize gfxPlatformFontList");
     }
 #endif