Improve GAL panel refresh logic.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/15041
This commit is contained in:
Alex Shvartzkop 2023-07-01 19:30:19 +03:00
parent 55b31030c0
commit 7b5e725b98
5 changed files with 44 additions and 30 deletions

View File

@ -62,9 +62,11 @@ EDA_DRAW_PANEL_GAL::EDA_DRAW_PANEL_GAL( wxWindow* aParentWindow, wxWindowID aWin
m_MouseCapturedLost( false ), m_MouseCapturedLost( false ),
m_parent( aParentWindow ), m_parent( aParentWindow ),
m_edaFrame( nullptr ), m_edaFrame( nullptr ),
m_lastRepaint( 0 ), m_lastRepaintStart( 0 ),
m_lastRepaintEnd( 0 ),
m_drawing( false ), m_drawing( false ),
m_drawingEnabled( false ), m_drawingEnabled( false ),
m_needIdleRefresh( false ),
m_gal( nullptr ), m_gal( nullptr ),
m_view( nullptr ), m_view( nullptr ),
m_painter( nullptr ), m_painter( nullptr ),
@ -150,7 +152,7 @@ EDA_DRAW_PANEL_GAL::EDA_DRAW_PANEL_GAL( wxWindow* aParentWindow, wxWindowID aWin
Connect( eventType, wxEventHandler( EDA_DRAW_PANEL_GAL::OnEvent ), nullptr, Connect( eventType, wxEventHandler( EDA_DRAW_PANEL_GAL::OnEvent ), nullptr,
m_eventDispatcher ); m_eventDispatcher );
// Set up timer that prevents too frequent redraw commands // Set up timer to detect when drawing starts
m_refreshTimer.SetOwner( this ); m_refreshTimer.SetOwner( this );
Connect( m_refreshTimer.GetId(), wxEVT_TIMER, Connect( m_refreshTimer.GetId(), wxEVT_TIMER,
wxTimerEventHandler( EDA_DRAW_PANEL_GAL::onRefreshTimer ), nullptr, this ); wxTimerEventHandler( EDA_DRAW_PANEL_GAL::onRefreshTimer ), nullptr, this );
@ -204,7 +206,7 @@ bool EDA_DRAW_PANEL_GAL::DoRePaint()
if( m_drawing ) if( m_drawing )
return false; return false;
m_lastRepaint = wxGetLocalTimeMillis(); m_lastRepaintStart = wxGetLocalTimeMillis();
// Repaint the canvas, and fix scrollbar cursors // Repaint the canvas, and fix scrollbar cursors
// Usually called by a OnPaint event, but because it does not use a wxPaintDC, // Usually called by a OnPaint event, but because it does not use a wxPaintDC,
@ -334,6 +336,8 @@ bool EDA_DRAW_PANEL_GAL::DoRePaint()
); );
} }
m_lastRepaintEnd = wxGetLocalTimeMillis();
return true; return true;
} }
@ -378,26 +382,8 @@ void EDA_DRAW_PANEL_GAL::onSize( wxSizeEvent& aEvent )
void EDA_DRAW_PANEL_GAL::Refresh( bool aEraseBackground, const wxRect* aRect ) void EDA_DRAW_PANEL_GAL::Refresh( bool aEraseBackground, const wxRect* aRect )
{ {
wxLongLong t = wxGetLocalTimeMillis(); if( !DoRePaint() )
wxLongLong delta = t - m_lastRepaint; m_needIdleRefresh = true;
int minRefreshPeriod = 13; // 77 FPS limit when v-sync not available.
if( m_gal && m_gal->IsInitialized() && m_gal->GetSwapInterval() != 0 )
minRefreshPeriod = 3;
// If it has been too long since the last frame (possible depending on platform timer latency),
// just do a refresh. Otherwise, start the refresh timer if it hasn't already been started.
// This ensures that we will render often enough but not too often.
if( delta >= minRefreshPeriod )
{
if( !DoRePaint() )
m_refreshTimer.Start( minRefreshPeriod, true );
}
else if( !m_refreshTimer.IsRunning() )
{
m_refreshTimer.Start( ( minRefreshPeriod - delta ).ToLong(), true );
}
} }
@ -424,7 +410,10 @@ void EDA_DRAW_PANEL_GAL::StopDrawing()
{ {
m_refreshTimer.Stop(); m_refreshTimer.Stop();
m_drawingEnabled = false; m_drawingEnabled = false;
Disconnect( wxEVT_PAINT, wxPaintEventHandler( EDA_DRAW_PANEL_GAL::onPaint ), nullptr, this ); Disconnect( wxEVT_PAINT, wxPaintEventHandler( EDA_DRAW_PANEL_GAL::onPaint ), nullptr, this );
Disconnect( wxEVT_IDLE, wxIdleEventHandler( EDA_DRAW_PANEL_GAL::onIdle ), nullptr, this );
} }
@ -570,7 +559,16 @@ void EDA_DRAW_PANEL_GAL::OnEvent( wxEvent& aEvent )
else else
m_eventDispatcher->DispatchWxEvent( aEvent ); m_eventDispatcher->DispatchWxEvent( aEvent );
Refresh(); // Give events time to process, based on last render duration
wxLongLong endDelta = wxGetLocalTimeMillis() - m_lastRepaintEnd;
long long timeLimit = ( m_lastRepaintEnd - m_lastRepaintStart ).GetValue() / 5;
timeLimit = std::clamp( timeLimit, 3LL, 150LL );
if( endDelta > timeLimit )
Refresh();
else
m_needIdleRefresh = true;
} }
@ -599,6 +597,18 @@ void EDA_DRAW_PANEL_GAL::onLostFocus( wxFocusEvent& aEvent )
} }
void EDA_DRAW_PANEL_GAL::onIdle( wxIdleEvent& aEvent )
{
if( m_needIdleRefresh )
{
m_needIdleRefresh = false;
Refresh();
}
aEvent.Skip();
}
void EDA_DRAW_PANEL_GAL::onRefreshTimer( wxTimerEvent& aEvent ) void EDA_DRAW_PANEL_GAL::onRefreshTimer( wxTimerEvent& aEvent )
{ {
if( !m_drawingEnabled ) if( !m_drawingEnabled )
@ -607,6 +617,9 @@ void EDA_DRAW_PANEL_GAL::onRefreshTimer( wxTimerEvent& aEvent )
{ {
Connect( wxEVT_PAINT, wxPaintEventHandler( EDA_DRAW_PANEL_GAL::onPaint ), nullptr, Connect( wxEVT_PAINT, wxPaintEventHandler( EDA_DRAW_PANEL_GAL::onPaint ), nullptr,
this ); this );
Connect( wxEVT_IDLE, wxIdleEventHandler( EDA_DRAW_PANEL_GAL::onIdle ), nullptr, this );
m_drawingEnabled = true; m_drawingEnabled = true;
} }
else else

View File

@ -333,7 +333,7 @@ void EDA_DRAW_FRAME::CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVars
if( m_canvasType != GetCanvas()->GetBackend() ) if( m_canvasType != GetCanvas()->GetBackend() )
{ {
// Try to switch (will automatically fallback if necessary) // Try to switch (will automatically fallback if necessary)
GetCanvas()->SwitchBackend( m_canvasType ); SwitchCanvas( m_canvasType );
EDA_DRAW_PANEL_GAL::GAL_TYPE newGAL = GetCanvas()->GetBackend(); EDA_DRAW_PANEL_GAL::GAL_TYPE newGAL = GetCanvas()->GetBackend();
bool success = newGAL == m_canvasType; bool success = newGAL == m_canvasType;

View File

@ -1020,9 +1020,6 @@ bool TOOL_MANAGER::ProcessEvent( const TOOL_EVENT& aEvent )
if( m_view && m_view->IsDirty() ) if( m_view && m_view->IsDirty() )
{ {
if( GetToolHolder() )
GetToolHolder()->RefreshCanvas();
#if defined( __WXMAC__ ) #if defined( __WXMAC__ )
wxTheApp->ProcessPendingEvents(); // required for updating brightening behind a popup menu wxTheApp->ProcessPendingEvents(); // required for updating brightening behind a popup menu
#endif #endif

View File

@ -405,7 +405,6 @@ void WX_VIEW_CONTROLS::onWheel( wxMouseEvent& aEvent )
// Do not skip this event, otherwise wxWidgets will fire // Do not skip this event, otherwise wxWidgets will fire
// 3 wxEVT_SCROLLWIN_LINEUP or wxEVT_SCROLLWIN_LINEDOWN (normal wxWidgets behavior) // 3 wxEVT_SCROLLWIN_LINEUP or wxEVT_SCROLLWIN_LINEDOWN (normal wxWidgets behavior)
// and we do not want that. // and we do not want that.
m_parentPanel->Refresh();
} }

View File

@ -254,13 +254,15 @@ protected:
void onSize( wxSizeEvent& aEvent ); void onSize( wxSizeEvent& aEvent );
void onEnter( wxMouseEvent& aEvent ); void onEnter( wxMouseEvent& aEvent );
void onLostFocus( wxFocusEvent& aEvent ); void onLostFocus( wxFocusEvent& aEvent );
void onIdle( wxIdleEvent& aEvent );
void onRefreshTimer( wxTimerEvent& aEvent ); void onRefreshTimer( wxTimerEvent& aEvent );
void onShowTimer( wxTimerEvent& aEvent ); void onShowTimer( wxTimerEvent& aEvent );
wxWindow* m_parent; ///< Pointer to the parent window wxWindow* m_parent; ///< Pointer to the parent window
EDA_DRAW_FRAME* m_edaFrame; ///< Parent EDA_DRAW_FRAME (if available) EDA_DRAW_FRAME* m_edaFrame; ///< Parent EDA_DRAW_FRAME (if available)
wxLongLong m_lastRepaint; ///< Timestamp of the last repaint start wxLongLong m_lastRepaintStart; ///< Timestamp of the last repaint start
wxLongLong m_lastRepaintEnd; ///< Timestamp of the last repaint end
wxTimer m_refreshTimer; ///< Timer to prevent too-frequent refreshing wxTimer m_refreshTimer; ///< Timer to prevent too-frequent refreshing
std::mutex m_refreshMutex; ///< Blocks multiple calls to the draw std::mutex m_refreshMutex; ///< Blocks multiple calls to the draw
@ -271,6 +273,9 @@ protected:
/// Flag that determines if VIEW may use GAL for redrawing the screen. /// Flag that determines if VIEW may use GAL for redrawing the screen.
bool m_drawingEnabled; bool m_drawingEnabled;
/// True when canvas needs to be refreshed from idle handler
bool m_needIdleRefresh;
/// Timer used to execute OnShow() when the window finally appears on the screen. /// Timer used to execute OnShow() when the window finally appears on the screen.
wxTimer m_onShowTimer; wxTimer m_onShowTimer;