Skip to content

Commit

Permalink
Added workaround for Qt OpenGL problem on OS X.
Browse files Browse the repository at this point in the history
This would manifest itself as "invalid framebuffer" errors leaking into the IECoreGL and PyOpenGL code and being reported there, causing various forms of havoc.

Fixes GafferHQ#404.
  • Loading branch information
johnhaddon committed Jan 21, 2014
1 parent b8598ab commit 55c99bf
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 26 deletions.
60 changes: 48 additions & 12 deletions python/GafferUI/GLWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def __init__( self, bufferOptions = set(), **kw ) :
format.setVersion( 2, 1 )

graphicsView = _GLGraphicsView( format )
self.__graphicsScene = _GLGraphicsScene( graphicsView, Gaffer.WeakMethod( self._draw ) )
self.__graphicsScene = _GLGraphicsScene( graphicsView, Gaffer.WeakMethod( self.__draw ) )
graphicsView.setScene( self.__graphicsScene )

GafferUI.Widget.__init__( self, graphicsView, **kw )
Expand All @@ -104,13 +104,15 @@ def addOverlay( self, overlay ) :
item = self.__graphicsScene.addWidget( self.__overlay._qtWidget() )

## Called whenever the widget is resized. May be reimplemented by derived
# classes if necessary.
# classes if necessary. The appropriate OpenGL context will already be current
# when this is called.
def _resize( self, size ) :

GL.glViewport( 0, 0, size.x, size.y )

## Derived classes must override this to draw their contents using
# OpenGL calls.
# OpenGL calls. The appropriate OpenGL context will already be current
# when this is called.
def _draw( self ) :

pass
Expand All @@ -121,10 +123,49 @@ def _redraw( self ) :
self._glWidget().update()

## May be used by derived classes to get access to the internal
# QGLWidget - this can be useful for making the correct context current.
# QGLWidget. Note that _makeCurrent() should be used in preference
# to _glWidget().makeCurrent(), for the reasons stated in the
# documentation for that method.
def _glWidget( self ) :

return self._qtWidget().viewport()

## May be used by derived classes to make the OpenGL context
# for this widget current. Returns True if the operation was
# successful and False if not. In an ideal world, the return
# value would always be True, but it appears that there are
# Qt/Mac bugs which cause it not to be from time to time -
# typically for newly created Widgets. If False is returned,
# no OpenGL operations should be undertaken subsequently by
# the caller.
def _makeCurrent( self ) :

self._qtWidget().viewport().makeCurrent()
return self.__framebufferValid()

def __framebufferValid( self ) :

import OpenGL.GL.framebufferobjects
return GL.framebufferobjects.glCheckFramebufferStatus( GL.framebufferobjects.GL_FRAMEBUFFER ) == GL.framebufferobjects.GL_FRAMEBUFFER_COMPLETE

def __draw( self ) :

# Qt sometimes enters our GraphicsScene.drawBackground() method
# with a GL error flag still set. We unset it here so it won't
# trigger our own error checking.
while GL.glGetError() :
pass

if not self.__framebufferValid() :
return

# we need to call the init method after a GL context has been
# created, and this seems like the only place that is guaranteed.
# calling it here does mean we call init() way more than needed,
# but it's safe.
IECoreGL.init( True )

self._draw()

class _GLGraphicsView( QtGui.QGraphicsView ) :

Expand All @@ -145,8 +186,9 @@ def resizeEvent( self, event ) :

if self.scene() is not None :
self.scene().setSceneRect( 0, 0, event.size().width(), event.size().height() )
self.viewport().makeCurrent()
GafferUI.Widget._owner( self )._resize( IECore.V2i( event.size().width(), event.size().height() ) )
owner = GafferUI.Widget._owner( self )
owner._makeCurrent()
owner._resize( IECore.V2i( event.size().width(), event.size().height() ) )

def keyPressEvent( self, event ) :

Expand Down Expand Up @@ -305,12 +347,6 @@ def addWidget( self, widget ) :
return item

def drawBackground( self, painter, rect ) :

# we need to call the init method after a GL context has been
# created, and this seems like the only place that is guaranteed.
# calling it here does mean we call init() way more than needed,
# but it's safe.
IECoreGL.init( True )

self.__backgroundDrawFunction()

Expand Down
55 changes: 41 additions & 14 deletions python/GafferUI/GadgetWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,22 +157,30 @@ def __buttonPress( self, widget, event ) :
if focusItem.widget().focusWidget() is not None :
focusItem.widget().focusWidget().clearFocus()

self._glWidget().makeCurrent()
if not self._makeCurrent() :
return False

return self.__viewportGadget.buttonPressSignal()( self.__viewportGadget, event )

def __buttonRelease( self, widget, event ) :

self._glWidget().makeCurrent()
if not self._makeCurrent() :
return False

return self.__viewportGadget.buttonReleaseSignal()( self.__viewportGadget, event )

def __buttonDoubleClick( self, widget, event ) :

self._glWidget().makeCurrent()
if not self._makeCurrent() :
return False

return self.__viewportGadget.buttonDoubleClickSignal()( self.__viewportGadget, event )

def __mouseMove( self, widget, event ) :

self._glWidget().makeCurrent()
if not self._makeCurrent() :
return False

self.__viewportGadget.mouseMoveSignal()( self.__viewportGadget, event )

# we always return false so that any overlay items will get appropriate
Expand All @@ -182,32 +190,44 @@ def __mouseMove( self, widget, event ) :

def __dragBegin( self, widget, event ) :

self._glWidget().makeCurrent()
if not self._makeCurrent() :
return False

return self.__viewportGadget.dragBeginSignal()( self.__viewportGadget, event )

def __dragEnter( self, widget, event ) :

self._glWidget().makeCurrent()
if not self._makeCurrent() :
return False

return self.__viewportGadget.dragEnterSignal()( self.__viewportGadget, event )

def __dragMove( self, widget, event ) :

self._glWidget().makeCurrent()
if not self._makeCurrent() :
return False

return self.__viewportGadget.dragMoveSignal()( self.__viewportGadget, event )

def __dragLeave( self, widget, event ) :

self._glWidget().makeCurrent()
if not self._makeCurrent() :
return False

return self.__viewportGadget.dragLeaveSignal()( self.__viewportGadget, event )

def __drop( self, widget, event ) :

self._glWidget().makeCurrent()
if not self._makeCurrent() :
return False

return self.__viewportGadget.dropSignal()( self.__viewportGadget, event )

def __dragEnd( self, widget, event ) :

self._glWidget().makeCurrent()
if not self._makeCurrent() :
return False

return self.__viewportGadget.dragEndSignal()( self.__viewportGadget, event )

def __keyPress( self, widget, event ) :
Expand All @@ -218,7 +238,9 @@ def __keyPress( self, widget, event ) :
if self._qtWidget().scene().focusItem().widget().focusWidget() is not None :
return False

self._glWidget().makeCurrent()
if not self._makeCurrent() :
return False

return self.__viewportGadget.keyPressSignal()( self.__viewportGadget, event )

def __keyRelease( self, widget, event ) :
Expand All @@ -227,12 +249,16 @@ def __keyRelease( self, widget, event ) :
if self._qtWidget().scene().focusItem().widget().focusWidget() is not None :
return False

self._glWidget().makeCurrent()
if not self._makeCurrent() :
return False

return self.__viewportGadget.keyReleaseSignal()( self.__viewportGadget, event )

def __wheel( self, widget, event ) :

self._glWidget().makeCurrent()
if not self._makeCurrent() :
return False

return self.__viewportGadget.wheelSignal()( self.__viewportGadget, event )

## Used to make the tooltips dependent on which gadget is under the mouse
Expand All @@ -249,7 +275,8 @@ def eventFilter( self, qObject, qEvent ) :
widget = GafferUI.Widget._owner( qObject )
assert( isinstance( widget, GadgetWidget ) )

widget._glWidget().makeCurrent()
if not widget._makeCurrent() :
return False

toolTip = widget.getViewportGadget().getToolTip(
IECore.LineSegment3f(
Expand Down

0 comments on commit 55c99bf

Please sign in to comment.