From 0386a4df945a26c1e407acddf2e3104c73a5bd37 Mon Sep 17 00:00:00 2001 From: Maciej Suminski Date: Fri, 21 Aug 2015 10:33:36 +0200 Subject: [PATCH] Bulletproof canvas switching. --- common/draw_frame.cpp | 4 +- common/draw_panel_gal.cpp | 77 +++++++++++++++--------- common/gal/opengl/opengl_gal.cpp | 31 +++++++++- include/class_draw_panel_gal.h | 3 + include/gal/graphics_abstraction_layer.h | 71 +++++++++++----------- include/gal/opengl/opengl_gal.h | 8 +++ pcbnew/pcbframe.cpp | 16 +++-- 7 files changed, 140 insertions(+), 70 deletions(-) diff --git a/common/draw_frame.cpp b/common/draw_frame.cpp index 1b5d3ad08e..0a66865c02 100644 --- a/common/draw_frame.cpp +++ b/common/draw_frame.cpp @@ -1049,7 +1049,7 @@ void EDA_DRAW_FRAME::UseGalCanvas( bool aEnable ) // Display the same view after canvas switching if( aEnable ) { - // Switch to GAL rendering + // Switch to GAL renderer from legacy if( !m_galCanvasActive ) { // Set up viewport @@ -1070,7 +1070,7 @@ void EDA_DRAW_FRAME::UseGalCanvas( bool aEnable ) } else if( m_galCanvasActive ) { - // Switch to standard rendering + // Switch to legacy renderer from GAL double zoomFactor = gal->GetWorldScale() / gal->GetZoomFactor(); // TODO replace it with EDA_DRAW_PANEL_GAL::GetLegacyZoom m_canvas->SetZoom( 1.0 / ( zoomFactor * view->GetScale() ) ); diff --git a/common/draw_panel_gal.cpp b/common/draw_panel_gal.cpp index 7b19d5499f..68b66de2d4 100644 --- a/common/draw_panel_gal.cpp +++ b/common/draw_panel_gal.cpp @@ -103,6 +103,7 @@ EDA_DRAW_PANEL_GAL::EDA_DRAW_PANEL_GAL( wxWindow* aParentWindow, wxWindowID aWin m_refreshTimer.SetOwner( this ); m_pendingRefresh = false; m_drawing = false; + m_drawingEnabled = false; Connect( wxEVT_TIMER, wxTimerEventHandler( EDA_DRAW_PANEL_GAL::onRefreshTimer ), NULL, this ); } @@ -219,21 +220,18 @@ void EDA_DRAW_PANEL_GAL::SetEventDispatcher( TOOL_DISPATCHER* aEventDispatcher ) void EDA_DRAW_PANEL_GAL::StartDrawing() { - m_drawing = false; - m_pendingRefresh = true; - Connect( wxEVT_PAINT, wxPaintEventHandler( EDA_DRAW_PANEL_GAL::onPaint ), NULL, this ); - - wxPaintEvent redrawEvent; - wxPostEvent( this, redrawEvent ); + // Start querying GAL if it is ready + m_refreshTimer.StartOnce( 100 ); } void EDA_DRAW_PANEL_GAL::StopDrawing() { + m_drawingEnabled = false; + Disconnect( wxEVT_PAINT, wxPaintEventHandler( EDA_DRAW_PANEL_GAL::onPaint ), NULL, this ); m_pendingRefresh = false; m_drawing = true; m_refreshTimer.Stop(); - Disconnect( wxEVT_PAINT, wxPaintEventHandler( EDA_DRAW_PANEL_GAL::onPaint ), NULL, this ); } @@ -272,6 +270,8 @@ bool EDA_DRAW_PANEL_GAL::SwitchBackend( GAL_TYPE aGalType ) if( aGalType == m_backend && m_gal != NULL ) return true; + bool result = true; // assume everything will be fine + // Prevent refreshing canvas during backend switch StopDrawing(); @@ -289,35 +289,41 @@ bool EDA_DRAW_PANEL_GAL::SwitchBackend( GAL_TYPE aGalType ) new_gal = new KIGFX::CAIRO_GAL( this, this, this ); break; - case GAL_TYPE_NONE: - return false; - default: assert( false ); - return false; + // warn about unhandled GAL canvas type, but continue with the fallback option + + case GAL_TYPE_NONE: + // KIGFX::GAL is a stub - it actually does cannot display anything, + // but prevents code relying on GAL canvas existence from crashing + new_gal = new KIGFX::GAL(); + break; } - - delete m_gal; - m_gal = new_gal; - - wxSize size = GetClientSize(); - m_gal->ResizeScreen( size.GetX(), size.GetY() ); - - if( m_painter ) - m_painter->SetGAL( m_gal ); - - if( m_view ) - m_view->SetGAL( m_gal ); - - m_backend = aGalType; } catch( std::runtime_error& err ) { + new_gal = new KIGFX::GAL(); + aGalType = GAL_TYPE_NONE; DisplayError( m_parent, wxString( err.what() ) ); - return false; + result = false; } - return true; + assert( new_gal ); + delete m_gal; + m_gal = new_gal; + + wxSize size = GetClientSize(); + m_gal->ResizeScreen( size.GetX(), size.GetY() ); + + if( m_painter ) + m_painter->SetGAL( m_gal ); + + if( m_view ) + m_view->SetGAL( m_gal ); + + m_backend = aGalType; + + return result; } @@ -353,6 +359,23 @@ void EDA_DRAW_PANEL_GAL::onLostFocus( wxFocusEvent& aEvent ) void EDA_DRAW_PANEL_GAL::onRefreshTimer( wxTimerEvent& aEvent ) { + if( !m_drawingEnabled ) + { + if( m_gal->IsInitialized() ) + { + m_drawing = false; + m_pendingRefresh = true; + Connect( wxEVT_PAINT, wxPaintEventHandler( EDA_DRAW_PANEL_GAL::onPaint ), NULL, this ); + m_drawingEnabled = true; + } + else + { + // Try again soon + m_refreshTimer.Start( 100, true ); + return; + } + } + wxPaintEvent redrawEvent; wxPostEvent( this, redrawEvent ); } diff --git a/common/gal/opengl/opengl_gal.cpp b/common/gal/opengl/opengl_gal.cpp index 46681454a2..b1b27537f0 100644 --- a/common/gal/opengl/opengl_gal.cpp +++ b/common/gal/opengl/opengl_gal.cpp @@ -126,6 +126,9 @@ OPENGL_GAL::~OPENGL_GAL() void OPENGL_GAL::BeginDrawing() { + if( !IsShownOnScreen() ) + return; + SetCurrent( *glContext ); clientDC = new wxClientDC( this ); @@ -978,7 +981,10 @@ OPENGL_GAL::OPENGL_TEST::OPENGL_TEST( wxDialog* aParent, OPENGL_GAL* aGal ) : wxDefaultSize, 0, wxT( "GLCanvas" ) ), m_parent( aParent ), m_gal( aGal ), m_tested( false ), m_result( false ) { + m_timeoutTimer.SetOwner( this ); Connect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::OPENGL_TEST::Render ) ); + Connect( wxEVT_TIMER, wxTimerEventHandler( OPENGL_GAL::OPENGL_TEST::OnTimeout ) ); + m_parent->Connect( wxEVT_PAINT, wxPaintEventHandler( OPENGL_GAL::OPENGL_TEST::OnDialogPaint ), NULL, this ); } @@ -986,6 +992,10 @@ void OPENGL_GAL::OPENGL_TEST::Render( wxPaintEvent& WXUNUSED( aEvent ) ) { if( !m_tested ) { + if( !IsShownOnScreen() ) + return; + + m_timeoutTimer.Stop(); m_result = true; // Assume everything is fine, until proven otherwise // One test is enough - close the testing dialog when the test is finished @@ -1055,8 +1065,27 @@ void OPENGL_GAL::OPENGL_TEST::Render( wxPaintEvent& WXUNUSED( aEvent ) ) } -void OPENGL_GAL::OPENGL_TEST::error(const std::string& aError ) +void OPENGL_GAL::OPENGL_TEST::OnTimeout( wxTimerEvent& aEvent ) { + error( "Could not create OpenGL canvas" ); + m_parent->EndModal( wxID_NONE ); +} + + +void OPENGL_GAL::OPENGL_TEST::OnDialogPaint( wxPaintEvent& aEvent ) +{ + // GL canvas may never appear on the screen (e.g. due to missing GL extensions), and the test + // will not be run. Therefore give at most a second to perform the test, otherwise we conclude + // it has failed. + // Also, wxWidgets OnShow event is triggered before a window is shown, therefore here we use + // OnPaint event, which is executed when a window is actually visible. + m_timeoutTimer.StartOnce( 1000 ); +} + + +void OPENGL_GAL::OPENGL_TEST::error( const std::string& aError ) +{ + m_timeoutTimer.Stop(); m_result = false; m_tested = true; m_error = aError; diff --git a/include/class_draw_panel_gal.h b/include/class_draw_panel_gal.h index 11c6cb153d..64a180700f 100644 --- a/include/class_draw_panel_gal.h +++ b/include/class_draw_panel_gal.h @@ -185,6 +185,9 @@ protected: /// True if GAL is currently redrawing the view bool m_drawing; + /// Flag that determines if VIEW may use GAL for redrawing the screen. + bool m_drawingEnabled; + /// Timer responsible for preventing too frequent refresh wxTimer m_refreshTimer; diff --git a/include/gal/graphics_abstraction_layer.h b/include/gal/graphics_abstraction_layer.h index 6c478e8558..b886741e7c 100644 --- a/include/gal/graphics_abstraction_layer.h +++ b/include/gal/graphics_abstraction_layer.h @@ -66,15 +66,18 @@ public: GAL(); virtual ~GAL(); + /// @brief Returns the initalization status for the canvas. + virtual bool IsInitialized() const { return true; } + // --------------- // Drawing methods // --------------- /// @brief Begin the drawing, needs to be called for every new frame. - virtual void BeginDrawing() = 0; + virtual void BeginDrawing() {}; /// @brief End the drawing, needs to be called for every new frame. - virtual void EndDrawing() = 0; + virtual void EndDrawing() {}; /** * @brief Draw a line. @@ -84,7 +87,7 @@ public: * @param aStartPoint is the start point of the line. * @param aEndPoint is the end point of the line. */ - virtual void DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) = 0; + virtual void DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) {}; /** * @brief Draw a rounded segment. @@ -95,14 +98,14 @@ public: * @param aEndPoint is the end point of the segment. * @param aWidth is a width of the segment */ - virtual void DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint, double aWidth ) = 0; + virtual void DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint, double aWidth ) {}; /** * @brief Draw a polyline * * @param aPointList is a list of 2D-Vectors containing the polyline points. */ - virtual void DrawPolyline( std::deque& aPointList ) = 0; + virtual void DrawPolyline( std::deque& aPointList ) {}; /** * @brief Draw a circle using world coordinates. @@ -110,7 +113,7 @@ public: * @param aCenterPoint is the center point of the circle. * @param aRadius is the radius of the circle. */ - virtual void DrawCircle( const VECTOR2D& aCenterPoint, double aRadius ) = 0; + virtual void DrawCircle( const VECTOR2D& aCenterPoint, double aRadius ) {}; /** * @brief Draw an arc. @@ -121,7 +124,7 @@ public: * @param aEndAngle is the end angle of the arc. */ virtual void - DrawArc( const VECTOR2D& aCenterPoint, double aRadius, double aStartAngle, double aEndAngle ) = 0; + DrawArc( const VECTOR2D& aCenterPoint, double aRadius, double aStartAngle, double aEndAngle ) {}; /** * @brief Draw a rectangle. @@ -129,14 +132,14 @@ public: * @param aStartPoint is the start point of the rectangle. * @param aEndPoint is the end point of the rectangle. */ - virtual void DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) = 0; + virtual void DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) {}; /** * @brief Draw a polygon. * * @param aPointList is the list of the polygon points. */ - virtual void DrawPolygon( const std::deque& aPointList ) = 0; + virtual void DrawPolygon( const std::deque& aPointList ) {}; /** * @brief Draw a cubic bezier spline. @@ -147,17 +150,17 @@ public: * @param endPoint is the end point of the spline. */ virtual void DrawCurve( const VECTOR2D& startPoint, const VECTOR2D& controlPointA, - const VECTOR2D& controlPointB, const VECTOR2D& endPoint ) = 0; + const VECTOR2D& controlPointB, const VECTOR2D& endPoint ) {}; // -------------- // Screen methods // -------------- /// @brief Resizes the canvas. - virtual void ResizeScreen( int aWidth, int aHeight ) = 0; + virtual void ResizeScreen( int aWidth, int aHeight ) {}; /// @brief Shows/hides the GAL canvas - virtual bool Show( bool aShow ) = 0; + virtual bool Show( bool aShow ) { return true; }; /// @brief Returns GAL canvas size in pixels const VECTOR2I& GetScreenPixelSize() const @@ -166,13 +169,13 @@ public: } /// @brief Force all remaining objects to be drawn. - virtual void Flush() = 0; + virtual void Flush() {}; /** * @brief Clear the screen. * @param aColor is the color used for clearing. */ - virtual void ClearScreen( const COLOR4D& aColor ) = 0; + virtual void ClearScreen( const COLOR4D& aColor ) {}; // ----------------- // Attribute setting @@ -329,34 +332,34 @@ public: * * @param aTransformation is the ransformation matrix. */ - virtual void Transform( const MATRIX3x3D& aTransformation ) = 0; + virtual void Transform( const MATRIX3x3D& aTransformation ) {}; /** * @brief Rotate the context. * * @param aAngle is the rotation angle in radians. */ - virtual void Rotate( double aAngle ) = 0; + virtual void Rotate( double aAngle ) {}; /** * @brief Translate the context. * * @param aTranslation is the translation vector. */ - virtual void Translate( const VECTOR2D& aTranslation ) = 0; + virtual void Translate( const VECTOR2D& aTranslation ) {}; /** * @brief Scale the context. * * @param aScale is the scale factor for the x- and y-axis. */ - virtual void Scale( const VECTOR2D& aScale ) = 0; + virtual void Scale( const VECTOR2D& aScale ) {}; /// @brief Save the context. - virtual void Save() = 0; + virtual void Save() {}; /// @brief Restore the context. - virtual void Restore() = 0; + virtual void Restore() {}; // -------------------------------------------- // Group methods @@ -370,17 +373,17 @@ public: * * @return the number of the group. */ - virtual int BeginGroup() = 0; + virtual int BeginGroup() { return 0; }; /// @brief End the group. - virtual void EndGroup() = 0; + virtual void EndGroup() {}; /** * @brief Draw the stored group. * * @param aGroupNumber is the group number. */ - virtual void DrawGroup( int aGroupNumber ) = 0; + virtual void DrawGroup( int aGroupNumber ) {}; /** * @brief Changes the color used to draw the group. @@ -388,7 +391,7 @@ public: * @param aGroupNumber is the group number. * @param aNewColor is the new color. */ - virtual void ChangeGroupColor( int aGroupNumber, const COLOR4D& aNewColor ) = 0; + virtual void ChangeGroupColor( int aGroupNumber, const COLOR4D& aNewColor ) {}; /** * @brief Changes the depth (Z-axis position) of the group. @@ -396,19 +399,19 @@ public: * @param aGroupNumber is the group number. * @param aDepth is the new depth. */ - virtual void ChangeGroupDepth( int aGroupNumber, int aDepth ) = 0; + virtual void ChangeGroupDepth( int aGroupNumber, int aDepth ) {}; /** * @brief Delete the group from the memory. * * @param aGroupNumber is the group number. */ - virtual void DeleteGroup( int aGroupNumber ) = 0; + virtual void DeleteGroup( int aGroupNumber ) {}; /** * @brief Delete all data created during caching of graphic items. */ - virtual void ClearCache() = 0; + virtual void ClearCache() {}; // -------------------------------------------------------- // Handling the world <-> screen transformation @@ -581,33 +584,33 @@ public: /** * @brief Save the screen contents. */ - virtual void SaveScreen() = 0; + virtual void SaveScreen() {}; /** * @brief Restore the screen contents. */ - virtual void RestoreScreen() = 0; + virtual void RestoreScreen() {}; /** * @brief Sets the target for rendering. * * @param aTarget is the new target for rendering. */ - virtual void SetTarget( RENDER_TARGET aTarget ) = 0; + virtual void SetTarget( RENDER_TARGET aTarget ) {}; /** * @brief Gets the currently used target for rendering. * * @return The current rendering target. */ - virtual RENDER_TARGET GetTarget() const = 0; + virtual RENDER_TARGET GetTarget() const { return TARGET_CACHED; }; /** * @brief Clears the target for rendering. * * @param aTarget is the target to be cleared. */ - virtual void ClearTarget( RENDER_TARGET aTarget ) = 0; + virtual void ClearTarget( RENDER_TARGET aTarget ) {}; // ------------- // Grid methods @@ -798,7 +801,7 @@ public: * * @param aCursorPosition is the cursor position in screen coordinates. */ - virtual void DrawCursor( const VECTOR2D& aCursorPosition ) = 0; + virtual void DrawCursor( const VECTOR2D& aCursorPosition ) {}; /** * @brief Changes the current depth to deeper, so it is possible to draw objects right beneath @@ -887,7 +890,7 @@ protected: * @param aStartPoint is the start point of the line. * @param aEndPoint is the end point of the line. */ - virtual void drawGridLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) = 0; + virtual void drawGridLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) {}; /// Possible depth range static const int MIN_DEPTH; diff --git a/include/gal/opengl/opengl_gal.h b/include/gal/opengl/opengl_gal.h index bfaafedc74..b587e7023f 100644 --- a/include/gal/opengl/opengl_gal.h +++ b/include/gal/opengl/opengl_gal.h @@ -82,6 +82,9 @@ public: virtual ~OPENGL_GAL(); + /// @copydoc GAL::IsInitialized() + virtual bool IsInitialized() const { return IsShownOnScreen(); } + // --------------- // Drawing methods // --------------- @@ -362,7 +365,11 @@ private: { public: OPENGL_TEST( wxDialog* aParent, OPENGL_GAL* aGal ); + void Render( wxPaintEvent& aEvent ); + void OnTimeout( wxTimerEvent& aEvent ); + void OnDialogPaint( wxPaintEvent& aEvent ); + inline bool IsTested() const { return m_tested; } inline bool IsOk() const { return m_result && m_tested; } inline std::string GetError() const { return m_error; } @@ -375,6 +382,7 @@ private: bool m_tested; bool m_result; std::string m_error; + wxTimer m_timeoutTimer; }; friend class OPENGL_TEST; diff --git a/pcbnew/pcbframe.cpp b/pcbnew/pcbframe.cpp index 50755b0536..c5d5be76c6 100644 --- a/pcbnew/pcbframe.cpp +++ b/pcbnew/pcbframe.cpp @@ -322,7 +322,6 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : m_hasAutoSave = true; m_RecordingMacros = -1; m_microWaveToolBar = NULL; - EDA_DRAW_PANEL_GAL::GAL_TYPE canvasType = LoadCanvasTypeSetting(); m_rotationAngle = 900; @@ -330,8 +329,10 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : m_Macros[i].m_Record.clear(); // Create GAL canvas - SetGalCanvas( new PCB_DRAW_PANEL_GAL( this, -1, wxPoint( 0, 0 ), m_FrameSize, - canvasType == EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE ? EDA_DRAW_PANEL_GAL::GAL_TYPE_CAIRO : canvasType ) ); + EDA_DRAW_PANEL_GAL* galCanvas = new PCB_DRAW_PANEL_GAL( this, -1, wxPoint( 0, 0 ), + m_FrameSize, EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE ); + + SetGalCanvas( galCanvas ); SetBoard( new BOARD() ); @@ -452,15 +453,18 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : m_auimgr.Update(); setupTools(); - enableGALSpecificMenus(); Zoom_Automatique( false ); + EDA_DRAW_PANEL_GAL::GAL_TYPE canvasType = LoadCanvasTypeSetting(); + if( canvasType != EDA_DRAW_PANEL_GAL::GAL_TYPE_NONE ) { - GetGalCanvas()->SwitchBackend( canvasType ); - UseGalCanvas( true ); + if( GetGalCanvas()->SwitchBackend( canvasType ) ) + UseGalCanvas( true ); } + + enableGALSpecificMenus(); }