Bulletproof canvas switching.

This commit is contained in:
Maciej Suminski 2015-08-21 10:33:36 +02:00
parent a33fc223ae
commit 0386a4df94
7 changed files with 140 additions and 70 deletions

View File

@ -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() ) );

View File

@ -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 );
}

View File

@ -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;

View File

@ -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;

View File

@ -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<VECTOR2D>& aPointList ) = 0;
virtual void DrawPolyline( std::deque<VECTOR2D>& 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<VECTOR2D>& aPointList ) = 0;
virtual void DrawPolygon( const std::deque<VECTOR2D>& 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;

View File

@ -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;

View File

@ -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();
}