Bug 593342 - Use double buffering on Mac instead of flushing, for greater performance. r=vlad,roc,joe a=b
authorMarkus Stange <mstange@themasta.com>, Joe Drew <joe@drew.ca>, Matt Woodrow <mwoodrow@mozilla.com>
Tue, 21 Sep 2010 14:39:38 -0400
changeset 57545 16b51dc62bbd3716a991c3547daeed7cb74fd73d
parent 57544 326225ef1cab3c1ca818020e2666ec84b97dabd9
child 57546 00a2180e037115ff88591fff00097878aa227078
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersvlad, roc, joe, b
bugs593342
milestone2.0b8pre
Bug 593342 - Use double buffering on Mac instead of flushing, for greater performance. r=vlad,roc,joe a=b
gfx/layers/opengl/ContainerLayerOGL.cpp
gfx/layers/opengl/LayerManagerOGL.cpp
gfx/thebes/GLContextProviderCGL.mm
widget/src/cocoa/nsChildView.mm
--- a/gfx/layers/opengl/ContainerLayerOGL.cpp
+++ b/gfx/layers/opengl/ContainerLayerOGL.cpp
@@ -169,23 +169,30 @@ ContainerRender(Container* aContainer,
 
     const nsIntRect *clipRect = layerToRender->GetLayer()->GetEffectiveClipRect();
     if (clipRect) {
       scissorRect = *clipRect;
     }
 
     if (needsFramebuffer) {
       scissorRect.MoveBy(- visibleRect.TopLeft());
-    }
+    } else {
+      if (!aPreviousFrameBuffer) {
+        /**
+         * glScissor coordinates are oriented with 0,0 being at the bottom left,
+         * the opposite to layout (0,0 at the top left).
+         * All rendering to an FBO is upside-down, making the coordinate systems
+         * match.
+         * When rendering directly to a window (No current or previous FBO),
+         * we need to flip the scissor rect.
+         */
+        aContainer->gl()->FixWindowCoordinateRect(scissorRect,
+                                                  aManager->GetWigetSize().height);
+      }
 
-    if (!needsFramebuffer && aPreviousFrameBuffer) {
-      scissorRect.IntersectRect(scissorRect, cachedScissor);
-    } else if (!needsFramebuffer) {
-      aContainer->gl()->FixWindowCoordinateRect(scissorRect, 
-                                                aManager->GetWigetSize().height);
       scissorRect.IntersectRect(scissorRect, cachedScissor);
     }
 
     /**
      *  We can't clip to a visible region if theres no framebuffer since we might be transformed
      */
     if (needsFramebuffer || clipRect) {
       aContainer->gl()->fScissor(scissorRect.x, 
@@ -237,17 +244,17 @@ ContainerRender(Container* aContainer,
       // 2DRect case, get the multiplier right for a sampler2DRect
       float f[] = { float(visibleRect.width), float(visibleRect.height) };
       rgb->SetUniform(rgb->GetTexCoordMultiplierUniformLocation(),
                       2, f);
     }
 
     DEBUG_GL_ERROR_CHECK(aContainer->gl());
 
-    aManager->BindAndDrawQuad(rgb);
+    aManager->BindAndDrawQuad(rgb, aPreviousFrameBuffer == 0);
 
     DEBUG_GL_ERROR_CHECK(aContainer->gl());
 
     // Clean up resources.  This also unbinds the texture.
     aContainer->gl()->fDeleteTextures(1, &containerSurface);
 
     DEBUG_GL_ERROR_CHECK(aContainer->gl());
   }
--- a/gfx/layers/opengl/LayerManagerOGL.cpp
+++ b/gfx/layers/opengl/LayerManagerOGL.cpp
@@ -574,32 +574,32 @@ LayerManagerOGL::Render()
   mGLContext->fEnable(LOCAL_GL_BLEND);
 
   DEBUG_GL_ERROR_CHECK(mGLContext);
 
   const nsIntRect *clipRect = mRoot->GetClipRect();
 
   if (clipRect) {
     nsIntRect r = *clipRect;
-    if (!mGLContext->IsDoubleBuffered())
+    if (!mGLContext->IsDoubleBuffered() && !mTarget)
       mGLContext->FixWindowCoordinateRect(r, mWidgetSize.height);
     mGLContext->fScissor(r.x, r.y, r.width, r.height);
   } else {
     mGLContext->fScissor(0, 0, width, height);
   }
 
   mGLContext->fEnable(LOCAL_GL_SCISSOR_TEST);
 
   DEBUG_GL_ERROR_CHECK(mGLContext);
 
   mGLContext->fClearColor(0.0, 0.0, 0.0, 0.0);
   mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT);
 
   // Render our layers.
-  RootLayer()->RenderLayer(mGLContext->IsDoubleBuffered() ? 0 : mBackBufferFBO,
+  RootLayer()->RenderLayer(mGLContext->IsDoubleBuffered() && !mTarget ? 0 : mBackBufferFBO,
                            nsIntPoint(0, 0));
 
   DEBUG_GL_ERROR_CHECK(mGLContext);
 
   if (mTarget) {
     CopyToTarget();
     return;
   }
@@ -713,17 +713,17 @@ LayerManagerOGL::SetupPipeline(int aWidt
   // the window's back buffer, so this keeps things looking correct.
   //
   // XXX we could potentially always use the double-buffering view
   // matrix and just change our single-buffer draw code.
   //
   // XXX we keep track of whether the window size changed, so we can
   // skip this update if it hadn't since the last call.
   gfx3DMatrix viewMatrix;
-  if (mGLContext->IsDoubleBuffered()) {
+  if (mGLContext->IsDoubleBuffered() && !mTarget) {
     /* If it's double buffered, we don't have a frontbuffer FBO,
      * so put in a Y-flip in this transform.
      */
     viewMatrix._11 = 2.0f / float(aWidth);
     viewMatrix._22 = -2.0f / float(aHeight);
     viewMatrix._41 = -1.0f;
     viewMatrix._42 = 1.0f;
   } else {
@@ -734,17 +734,17 @@ LayerManagerOGL::SetupPipeline(int aWidt
   }
 
   SetLayerProgramProjectionMatrix(viewMatrix);
 }
 
 void
 LayerManagerOGL::SetupBackBuffer(int aWidth, int aHeight)
 {
-  if (mGLContext->IsDoubleBuffered()) {
+  if (mGLContext->IsDoubleBuffered() && !mTarget) {
     mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
     return;
   }
 
   // Do we have a FBO of the right size already?
   if (mBackBufferSize.width == aWidth &&
       mBackBufferSize.height == aHeight)
   {
@@ -791,38 +791,39 @@ LayerManagerOGL::CopyToTarget()
     NS_ERROR("Widget size too big - integer overflow!");
     return;
   }
 
   nsRefPtr<gfxImageSurface> imageSurface =
     new gfxImageSurface(gfxIntSize(width, height),
                         gfxASurface::ImageFormatARGB32);
 
-#ifdef USE_GLES2
-  // GLES2 promises that binding to any custom FBO will attach 
+  mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER,
+                               mBackBufferFBO);
+#ifndef USE_GLES2
+  // GLES2 promises that binding to any custom FBO will attach
   // to GL_COLOR_ATTACHMENT0 attachment point.
-  mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER,
-                               mGLContext->IsDoubleBuffered() ? 0 : mBackBufferFBO);
-#else
-  mGLContext->fReadBuffer(LOCAL_GL_COLOR_ATTACHMENT0);
+    mGLContext->fReadBuffer(LOCAL_GL_COLOR_ATTACHMENT0);
 #endif
 
   GLenum format = LOCAL_GL_RGBA;
   if (mHasBGRA)
     format = LOCAL_GL_BGRA;
 
   NS_ASSERTION(imageSurface->Stride() == width * 4,
                "Image Surfaces being created with weird stride!");
 
   PRUint32 currentPackAlignment = 0;
   mGLContext->fGetIntegerv(LOCAL_GL_PACK_ALIGNMENT, (GLint*)&currentPackAlignment);
   if (currentPackAlignment != 4) {
     mGLContext->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 4);
   }
 
+  mGLContext->fFinish();
+
   mGLContext->fReadPixels(0, 0,
                           width, height,
                           format,
                           LOCAL_GL_UNSIGNED_BYTE,
                           imageSurface->Data());
 
   if (currentPackAlignment != 4) {
     mGLContext->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, currentPackAlignment);
--- a/gfx/thebes/GLContextProviderCGL.mm
+++ b/gfx/thebes/GLContextProviderCGL.mm
@@ -39,20 +39,23 @@
 #include "nsIWidget.h"
 #include "OpenGL/OpenGL.h"
 #include <OpenGL/gl.h>
 #include <AppKit/NSOpenGL.h>
 #include "gfxASurface.h"
 #include "gfxImageSurface.h"
 #include "gfxQuartzSurface.h"
 #include "gfxPlatform.h"
+#include "prenv.h"
 
 namespace mozilla {
 namespace gl {
 
+static PRBool gUseDoubleBufferedWindows = PR_TRUE;
+
 class CGLLibrary
 {
 public:
     CGLLibrary()
       : mInitialized(PR_FALSE),
         mOGLLibrary(nsnull),
         mPixelFormat(nsnull)
     { }
@@ -64,29 +67,37 @@ public:
         }
         if (!mOGLLibrary) {
             mOGLLibrary = PR_LoadLibrary("/System/Library/Frameworks/OpenGL.framework/OpenGL");
             if (!mOGLLibrary) {
                 NS_WARNING("Couldn't load OpenGL Framework.");
                 return PR_FALSE;
             }
         }
-        
+
+        const char* db = PR_GetEnv("MOZ_CGL_DB");
+        gUseDoubleBufferedWindows = (!db || *db != '0');
+
         mInitialized = PR_TRUE;
         return PR_TRUE;
     }
 
     NSOpenGLPixelFormat *PixelFormat()
     {
         if (mPixelFormat == nsnull) {
             NSOpenGLPixelFormatAttribute attribs[] = {
                 NSOpenGLPFAAccelerated,
+                NSOpenGLPFADoubleBuffer,
                 (NSOpenGLPixelFormatAttribute)nil 
             };
 
+            if (!gUseDoubleBufferedWindows) {
+              attribs[1] = (NSOpenGLPixelFormatAttribute)nil;
+            }
+
             mPixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
         }
 
         return mPixelFormat;
     }
 private:
     PRBool mInitialized;
     PRLibrary *mOGLLibrary;
@@ -160,16 +171,27 @@ public:
         return PR_TRUE;
     }
 
     PRBool SetupLookupFunction()
     {
         return PR_FALSE;
     }
 
+    PRBool IsDoubleBuffered() 
+    { 
+      return gUseDoubleBufferedWindows; 
+    }
+
+    PRBool SwapBuffers()
+    {
+      [mContext flushBuffer];
+      return PR_TRUE;
+    }
+
     PRBool BindTex2DOffscreen(GLContext *aOffscreen);
     void UnbindTex2DOffscreen(GLContext *aOffscreen);
     PRBool ResizeOffscreen(const gfxIntSize& aNewSize);
 
     virtual already_AddRefed<TextureImage>
     CreateBasicTextureImage(GLuint aTexture,
                             const nsIntSize& aSize,
                             GLenum aWrapMode,
--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -2722,19 +2722,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
 
   if (mGeckoChild->GetLayerManager()->GetBackendType() == LayerManager::LAYERS_OPENGL) {
     LayerManagerOGL *manager = static_cast<LayerManagerOGL*>(mGeckoChild->GetLayerManager());
     manager->SetClippingRegion(paintEvent.region); 
     if (!mGLContext) {
       mGLContext = (NSOpenGLContext *)manager->gl()->GetNativeData(mozilla::gl::GLContext::NativeGLContext);
       [mGLContext retain];
     }
-    [mGLContext makeCurrentContext];
     mGeckoChild->DispatchWindowEvent(paintEvent);
-    [mGLContext flushBuffer];
     return;
   }
 
   // Create Cairo objects.
   NSSize bufferSize = [self bounds].size;
   nsRefPtr<gfxQuartzSurface> targetSurface =
     new gfxQuartzSurface(aContext, gfxSize(bufferSize.width, bufferSize.height));