Bug 603134 - Sometimes windows on OS X don't refresh until you move them. r=jrmuizel,josh a=b
authorJoe Drew <joe@drew.ca>
Thu, 03 Feb 2011 16:12:33 -0500
changeset 61878 b22b865abf30f0381173a6c61092274ce9cdaa61
parent 61877 c6993d965d0a373f8e0f1545e3144c761131728d
child 61879 344de3f6033f1b849a44568fd7ed2188d9e632ac
push id18529
push userjdrew@mozilla.com
push dateThu, 03 Feb 2011 21:18:25 +0000
treeherdermozilla-central@b22b865abf30 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel, josh, b
bugs603134
milestone2.0b12pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 603134 - Sometimes windows on OS X don't refresh until you move them. r=jrmuizel,josh a=b OS X has a bug that causes new OpenGL-using windows to only draw once or twice after some amount of uptime. However, if you move those windows, they will redraw happily for a very long time (perhaps forever). We can similarly convince OS X to start redrawing those windows by disassociating our view from the OpenGL context, then reassociating it, on our first draw.
widget/src/cocoa/nsChildView.h
widget/src/cocoa/nsChildView.mm
--- a/widget/src/cocoa/nsChildView.h
+++ b/widget/src/cocoa/nsChildView.h
@@ -200,16 +200,18 @@ extern "C" long TSMProcessRawKeyEvent(Ev
   enum {
     eGestureState_None,
     eGestureState_StartGesture,
     eGestureState_MagnifyGesture,
     eGestureState_RotateGesture
   } mGestureState;
   float mCumulativeMagnification;
   float mCumulativeRotation;
+
+  BOOL mDidForceRefreshOpenGL;
 }
 
 // class initialization
 + (void)initialize;
 
 // these are sent to the first responder when the window key status changes
 - (void)viewsWindowDidBecomeKey;
 - (void)viewsWindowDidResignKey;
--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -148,16 +148,17 @@ PRUint32 gLastModifierState = 0;
 PRBool gUserCancelledDrag = PR_FALSE;
 
 PRUint32 nsChildView::sLastInputEventCount = 0;
 
 @interface ChildView(Private)
 
 // sets up our view, attaching it to its owning gecko view
 - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild;
+- (void)forceRefreshOpenGL;
 
 // sends gecko an ime composition event
 - (void) sendCompositionEvent:(PRInt32)aEventType;
 
 // sends gecko an ime text event
 - (void) sendTextEvent:(PRUnichar*) aBuffer 
                        attributedString:(NSAttributedString*) aString
                        selectedRange:(NSRange)selRange
@@ -2248,16 +2249,22 @@ NSEvent* gLastDragMouseDownEvent = nil;
     mPluginComplexTextInputRequested = NO;
 
     mIgnoreNextKeyUpEvent = NO;
 
     mGestureState = eGestureState_None;
     mCumulativeMagnification = 0.0;
     mCumulativeRotation = 0.0;
 
+    // We can't call forceRefreshOpenGL here because, in order to work around
+    // the bug, it seems we need to have a draw already happening. Therefore,
+    // we call it in drawRect:inContext:, when we know that a draw is in
+    // progress.
+    mDidForceRefreshOpenGL = NO;
+
     [self setFocusRingType:NSFocusRingTypeNone];
   }
   
   // register for things we'll take from other applications
   PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView initWithFrame: registering drag types\n"));
   [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
                                                           NSStringPboardType,
                                                           NSHTMLPboardType,
@@ -2294,16 +2301,33 @@ NSEvent* gLastDragMouseDownEvent = nil;
                                                name:NSViewGlobalFrameDidChangeNotification
                                              object:self];
 
   return self;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
+// Work around bug 603134.
+// OS X has a bug that causes new OpenGL windows to only paint once or twice,
+// then stop painting altogether. By clearing the drawable from the GL context,
+// and then resetting the view to ourselves, we convince OS X to start updating
+// again.
+// This can cause a flash in new windows - bug 631339 - but it's very hard to
+// fix that while maintaining this workaround.
+- (void)forceRefreshOpenGL
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+  [mGLContext clearDrawable];
+  [mGLContext setView:self];
+
+  NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
 - (void)dealloc
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   [mGLContext release];
   [mPendingDirtyRects release];
   [mLastMouseDownEvent release];
   [mClickThroughMouseDownEvent release];
@@ -2751,16 +2775,24 @@ NSEvent* gLastDragMouseDownEvent = nil;
   if (mGeckoChild->GetLayerManager(nsnull)->GetBackendType() == LayerManager::LAYERS_OPENGL) {
     LayerManagerOGL *manager = static_cast<LayerManagerOGL*>(mGeckoChild->GetLayerManager(nsnull));
     manager->SetClippingRegion(paintEvent.region); 
     if (!mGLContext) {
       mGLContext = (NSOpenGLContext *)manager->gl()->GetNativeData(mozilla::gl::GLContext::NativeGLContext);
       [mGLContext retain];
     }
     mGeckoChild->DispatchWindowEvent(paintEvent);
+
+    // Force OpenGL to refresh the very first time we draw. This works around a
+    // Mac OS X bug that stops windows updating on OS X when we use OpenGL.
+    if (!mDidForceRefreshOpenGL) {
+      [self performSelector:@selector(forceRefreshOpenGL) withObject:nil afterDelay:0];
+      mDidForceRefreshOpenGL = YES;
+    }
+
     return;
   }
 
   // Create Cairo objects.
   NSSize bufferSize = [self bounds].size;
   nsRefPtr<gfxQuartzSurface> targetSurface =
     new gfxQuartzSurface(aContext, gfxSize(bufferSize.width, bufferSize.height));
   targetSurface->SetAllowUseAsSource(PR_FALSE);