From 634154893986dc905dfcc855a4ef11ef4ffcd593 Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Sun, 27 Feb 2022 11:54:12 +0000 Subject: [PATCH] Infobar warning if symbol loading was cancelled. Also makes sure the progress dialog is closed when we're done reading symbols (it used to stay up for much of the symbol editor initialization). Also makes sure that any cancel in the preLoad step is honoured in the sync step. (The preload is done because it is multi-threaded and therefore faster than the single-threaded sync.) Also makes sure that individual threads pay attention to the cancellation, not just the GUI thread. Fixes https://gitlab.com/kicad/code/kicad/issues/8372 (cherry picked from commit 1f16092e29f579a3bd7d20754e9c1fdd51a28c7e) --- common/widgets/progress_reporter_base.cpp | 4 +-- eeschema/CMakeLists.txt | 2 +- eeschema/{getpart.cpp => picksymbol.cpp} | 36 +++++++++++-------- eeschema/symbol_async_loader.cpp | 27 ++++++-------- eeschema/symbol_async_loader.h | 8 +---- eeschema/symbol_editor/symbol_edit_frame.cpp | 25 +++++++++---- eeschema/symbol_editor/symbol_edit_frame.h | 3 +- eeschema/symbol_editor/symbol_editor.cpp | 2 +- .../symbol_editor/symbol_library_manager.cpp | 24 +++++-------- eeschema/symbol_tree_model_adapter.cpp | 26 +++++++------- eeschema/symbol_tree_model_adapter.h | 5 +-- eeschema/symbol_viewer_frame.cpp | 19 ++++++---- pcbnew/footprint_info_impl.cpp | 4 +-- pcbnew/fp_tree_synchronizing_adapter.cpp | 2 +- 14 files changed, 96 insertions(+), 91 deletions(-) rename eeschema/{getpart.cpp => picksymbol.cpp} (88%) diff --git a/common/widgets/progress_reporter_base.cpp b/common/widgets/progress_reporter_base.cpp index a4103846fc..dbb214fdfb 100644 --- a/common/widgets/progress_reporter_base.cpp +++ b/common/widgets/progress_reporter_base.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2017 CERN - * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2021-2022 KiCad Developers, see AUTHORS.txt for contributors. * * Author: Tomasz Wlostowski * @@ -121,7 +121,7 @@ bool PROGRESS_REPORTER_BASE::KeepRefreshing( bool aWait ) return false; } - wxMilliSleep( 20 ); + wxMilliSleep( 33 /* 30 FPS refresh rate */ ); } return true; diff --git a/eeschema/CMakeLists.txt b/eeschema/CMakeLists.txt index 52a11230ba..473fc65b54 100644 --- a/eeschema/CMakeLists.txt +++ b/eeschema/CMakeLists.txt @@ -175,7 +175,7 @@ set( EESCHEMA_SRCS fields_grid_table.cpp files-io.cpp generate_alias_info.cpp - getpart.cpp + picksymbol.cpp hierarch.cpp lib_shape.cpp lib_item.cpp diff --git a/eeschema/getpart.cpp b/eeschema/picksymbol.cpp similarity index 88% rename from eeschema/getpart.cpp rename to eeschema/picksymbol.cpp index 5c9736acaa..cd394e5f95 100644 --- a/eeschema/getpart.cpp +++ b/eeschema/picksymbol.cpp @@ -105,7 +105,10 @@ PICKED_SYMBOL SCH_BASE_FRAME::PickSymbolFromLibTree( const SCHLIB_FILTER* aFilte Pgm().GetSettingsManager().GetAppSettings(); Pgm().GetSettingsManager().GetAppSettings(); - wxObjectDataPtr adapter = SYMBOL_TREE_MODEL_ADAPTER::Create( this, libs ); + wxObjectDataPtr dataPtr + = SYMBOL_TREE_MODEL_ADAPTER::Create( this, libs ); + SYMBOL_TREE_MODEL_ADAPTER* modelAdapter + = static_cast( dataPtr.get() ); bool loaded = false; if( aFilter ) @@ -117,14 +120,14 @@ PICKED_SYMBOL SCH_BASE_FRAME::PickSymbolFromLibTree( const SCHLIB_FILTER* aFilte if( libs->HasLibrary( liblist[ii], true ) ) { loaded = true; - static_cast( adapter.get() )->AddLibrary( liblist[ii] ); + modelAdapter->AddLibrary( liblist[ii] ); } } - adapter->AssignIntrinsicRanks(); + modelAdapter->AssignIntrinsicRanks(); if( aFilter->GetFilterPowerSymbols() ) - adapter->SetFilter( SYMBOL_TREE_MODEL_ADAPTER::SYM_FILTER_POWER ); + modelAdapter->SetFilter( SYMBOL_TREE_MODEL_ADAPTER::SYM_FILTER_POWER ); } std::vector< LIB_TREE_ITEM* > history_list; @@ -138,29 +141,34 @@ PICKED_SYMBOL SCH_BASE_FRAME::PickSymbolFromLibTree( const SCHLIB_FILTER* aFilte history_list.push_back( symbol ); } - adapter->DoAddLibrary( wxT( "-- " ) + _( "Recently Used" ) + " --", wxEmptyString, history_list, - true ); + modelAdapter->DoAddLibrary( wxT( "-- " ) + _( "Recently Used" ) + wxT( " --" ), wxEmptyString, + history_list, true ); if( !aHistoryList.empty() ) - adapter->SetPreselectNode( aHistoryList[0].LibId, aHistoryList[0].Unit ); + modelAdapter->SetPreselectNode( aHistoryList[0].LibId, aHistoryList[0].Unit ); const std::vector< wxString > libNicknames = libs->GetLogicalLibs(); if( !loaded ) - static_cast( adapter.get() )->AddLibraries( libNicknames, - this ); + { + if( !modelAdapter->AddLibraries( libNicknames, this ) ) + { + // loading cancelled by user + return PICKED_SYMBOL(); + } + } if( aHighlight && aHighlight->IsValid() ) - adapter->SetPreselectNode( *aHighlight, /* aUnit */ 0 ); + modelAdapter->SetPreselectNode( *aHighlight, /* aUnit */ 0 ); wxString dialogTitle; - if( adapter->GetFilter() == SYMBOL_TREE_MODEL_ADAPTER::SYM_FILTER_POWER ) - dialogTitle.Printf( _( "Choose Power Symbol (%d items loaded)" ), adapter->GetItemCount() ); + if( modelAdapter->GetFilter() == SYMBOL_TREE_MODEL_ADAPTER::SYM_FILTER_POWER ) + dialogTitle.Printf( _( "Choose Power Symbol (%d items loaded)" ), dataPtr->GetItemCount() ); else - dialogTitle.Printf( _( "Choose Symbol (%d items loaded)" ), adapter->GetItemCount() ); + dialogTitle.Printf( _( "Choose Symbol (%d items loaded)" ), dataPtr->GetItemCount() ); - DIALOG_CHOOSE_SYMBOL dlg( this, dialogTitle, adapter, aConvert, aAllowFields, aShowFootprints, + DIALOG_CHOOSE_SYMBOL dlg( this, dialogTitle, dataPtr, aConvert, aAllowFields, aShowFootprints, aUseLibBrowser ); if( dlg.ShowModal() == wxID_CANCEL ) diff --git a/eeschema/symbol_async_loader.cpp b/eeschema/symbol_async_loader.cpp index fc40552bca..f7eb5a3974 100644 --- a/eeschema/symbol_async_loader.cpp +++ b/eeschema/symbol_async_loader.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2021 Jon Evans - * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2021-2022 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -35,8 +35,7 @@ SYMBOL_ASYNC_LOADER::SYMBOL_ASYNC_LOADER( const std::vector& aNickname m_onlyPowerSymbols( aOnlyPowerSymbols ), m_output( aOutput ), m_reporter( aReporter ), - m_nextLibrary( 0 ), - m_canceled( false ) + m_nextLibrary( 0 ) { wxASSERT( m_table ); m_threadCount = std::max( 1, std::thread::hardware_concurrency() ); @@ -48,7 +47,7 @@ SYMBOL_ASYNC_LOADER::SYMBOL_ASYNC_LOADER( const std::vector& aNickname SYMBOL_ASYNC_LOADER::~SYMBOL_ASYNC_LOADER() { - Abort(); + Join(); } @@ -88,13 +87,6 @@ bool SYMBOL_ASYNC_LOADER::Join() } -void SYMBOL_ASYNC_LOADER::Abort() -{ - m_canceled.store( true ); - Join(); -} - - bool SYMBOL_ASYNC_LOADER::Done() { return m_nextLibrary.load() >= m_nicknames.size(); @@ -110,11 +102,15 @@ std::vector SYMBOL_ASYNC_LOADER::worker() for( size_t libraryIndex = m_nextLibrary++; libraryIndex < m_nicknames.size(); libraryIndex = m_nextLibrary++ ) { - if( m_canceled.load() ) + const wxString& nickname = m_nicknames[libraryIndex]; + + if( m_reporter ) + m_reporter->AdvancePhase( wxString::Format( _( "Loading library %s..." ), nickname ) ); + + if( m_reporter && m_reporter->IsCancelled() ) break; - const wxString& nickname = m_nicknames[libraryIndex]; - LOADED_PAIR pair( nickname, {} ); + LOADED_PAIR pair( nickname, {} ); try { @@ -129,9 +125,6 @@ std::vector SYMBOL_ASYNC_LOADER::worker() std::lock_guard lock( m_errorMutex ); m_errors += msg; } - - if( m_reporter ) - m_reporter->AdvancePhase( wxString::Format( _( "Loading library %s..." ), nickname ) ); } return ret; diff --git a/eeschema/symbol_async_loader.h b/eeschema/symbol_async_loader.h index 7a1257b626..3051366cc8 100644 --- a/eeschema/symbol_async_loader.h +++ b/eeschema/symbol_async_loader.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2021 Jon Evans - * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2021-2022 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -62,11 +62,6 @@ public: */ bool Join(); - /** - * Cancels a load in-progress - */ - void Abort(); - ///< Return true if loading is done bool Done(); @@ -97,7 +92,6 @@ private: size_t m_threadCount; std::atomic m_nextLibrary; - std::atomic_bool m_canceled; wxString m_errors; std::mutex m_errorMutex; diff --git a/eeschema/symbol_editor/symbol_edit_frame.cpp b/eeschema/symbol_editor/symbol_edit_frame.cpp index 93c2f77d4e..4e191d207f 100644 --- a/eeschema/symbol_editor/symbol_edit_frame.cpp +++ b/eeschema/symbol_editor/symbol_edit_frame.cpp @@ -127,13 +127,20 @@ SYMBOL_EDIT_FRAME::SYMBOL_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : LoadSettings( m_settings ); m_libMgr = new SYMBOL_LIBRARY_MANAGER( *this ); + bool loadingCancelled = false; - // Preload libraries before using SyncLibraries the first time, as the preload is threaded - WX_PROGRESS_REPORTER reporter( this, _( "Loading Symbol Libraries" ), - m_libMgr->GetLibraryCount(), true ); - m_libMgr->Preload( reporter ); + { + // Preload libraries before using SyncLibraries the first time, as the preload is + // multi-threaded + WX_PROGRESS_REPORTER reporter( this, _( "Loading Symbol Libraries" ), + m_libMgr->GetLibraryCount(), true ); + m_libMgr->Preload( reporter ); - SyncLibraries( false ); + loadingCancelled = reporter.IsCancelled(); + wxSafeYield(); + } + + SyncLibraries( false, loadingCancelled ); m_treePane = new SYMBOL_TREE_PANE( this, m_libMgr ); resolveCanvasType(); @@ -235,6 +242,9 @@ SYMBOL_EDIT_FRAME::SYMBOL_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : // Ensure the window is on top Raise(); + + if( loadingCancelled ) + ShowInfoBarWarning( _( "Symbol library loading was cancelled by user." ) ); } @@ -922,7 +932,8 @@ wxString SYMBOL_EDIT_FRAME::getTargetLib() const } -void SYMBOL_EDIT_FRAME::SyncLibraries( bool aShowProgress, const wxString& aForceRefresh ) +void SYMBOL_EDIT_FRAME::SyncLibraries( bool aShowProgress, bool aPreloadCancelled, + const wxString& aForceRefresh ) { LIB_ID selected; @@ -941,7 +952,7 @@ void SYMBOL_EDIT_FRAME::SyncLibraries( bool aShowProgress, const wxString& aForc libName ) ); } ); } - else + else if( !aPreloadCancelled ) { m_libMgr->Sync( aForceRefresh, [&]( int progress, int max, const wxString& libName ) diff --git a/eeschema/symbol_editor/symbol_edit_frame.h b/eeschema/symbol_editor/symbol_edit_frame.h index 5f6d7f8bf9..c9a20b5e4c 100644 --- a/eeschema/symbol_editor/symbol_edit_frame.h +++ b/eeschema/symbol_editor/symbol_edit_frame.h @@ -296,7 +296,8 @@ public: * Synchronize the library manager to the symbol library table, and then the symbol tree * to the library manager. Optionally displays a progress dialog. */ - void SyncLibraries( bool aShowProgress, const wxString& aForceRefresh = wxEmptyString ); + void SyncLibraries( bool aShowProgress, bool aPreloadCancelled = false, + const wxString& aForceRefresh = wxEmptyString ); /** * Filter, sort, and redisplay the library tree. diff --git a/eeschema/symbol_editor/symbol_editor.cpp b/eeschema/symbol_editor/symbol_editor.cpp index 3755dd8c9e..3ddb1c8641 100644 --- a/eeschema/symbol_editor/symbol_editor.cpp +++ b/eeschema/symbol_editor/symbol_editor.cpp @@ -1119,7 +1119,7 @@ bool SYMBOL_EDIT_FRAME::saveLibrary( const wxString& aLibrary, bool aNewFile ) if( resyncLibTree ) { FreezeLibraryTree(); - SyncLibraries( true, forceRefresh ); + SyncLibraries( true, false, forceRefresh ); ThawLibraryTree(); } } diff --git a/eeschema/symbol_editor/symbol_library_manager.cpp b/eeschema/symbol_editor/symbol_library_manager.cpp index 07a728bede..2afa0025c5 100644 --- a/eeschema/symbol_editor/symbol_library_manager.cpp +++ b/eeschema/symbol_editor/symbol_library_manager.cpp @@ -74,8 +74,6 @@ void SYMBOL_LIBRARY_MANAGER::Sync( const wxString& aForceRefresh, void SYMBOL_LIBRARY_MANAGER::Preload( PROGRESS_REPORTER& aReporter ) { - const int progressIntervalMillis = 60; - SYMBOL_ASYNC_LOADER loader( symTable()->GetLogicalLibs(), symTable(), false, nullptr, &aReporter ); @@ -85,19 +83,13 @@ void SYMBOL_LIBRARY_MANAGER::Preload( PROGRESS_REPORTER& aReporter ) while( !loader.Done() ) { - aReporter.KeepRefreshing(); + if( !aReporter.KeepRefreshing() ) + break; - wxMilliSleep( progressIntervalMillis ); + wxMilliSleep( 33 /* 30 FPS refresh rate */ ); } - if( aReporter.IsCancelled() ) - { - loader.Abort(); - } - else - { - loader.Join(); - } + loader.Join(); if( !loader.GetErrors().IsEmpty() ) { @@ -116,7 +108,7 @@ void SYMBOL_LIBRARY_MANAGER::Preload( PROGRESS_REPORTER& aReporter ) bool SYMBOL_LIBRARY_MANAGER::HasModifications() const { - for( const auto& lib : m_libs ) + for( const std::pair& lib : m_libs ) { if( lib.second.IsModified() ) return true; @@ -130,7 +122,7 @@ int SYMBOL_LIBRARY_MANAGER::GetHash() const { int hash = symTable()->GetModifyHash(); - for( const auto& lib : m_libs ) + for( const std::pair& lib : m_libs ) hash += lib.second.GetHash(); return hash; @@ -144,7 +136,7 @@ int SYMBOL_LIBRARY_MANAGER::GetLibraryHash( const wxString& aLibrary ) const if( libBufIt != m_libs.end() ) return libBufIt->second.GetHash(); - auto row = GetLibrary( aLibrary ); + SYMBOL_LIB_TABLE_ROW* row = GetLibrary( aLibrary ); // return -1 if library does not exist or 0 if not modified return row ? std::hash{}( aLibrary.ToStdString() + @@ -156,7 +148,7 @@ wxArrayString SYMBOL_LIBRARY_MANAGER::GetLibraryNames() const { wxArrayString res; - for( const auto& libName : symTable()->GetLogicalLibs() ) + for( const wxString& libName : symTable()->GetLogicalLibs() ) res.Add( libName ); return res; diff --git a/eeschema/symbol_tree_model_adapter.cpp b/eeschema/symbol_tree_model_adapter.cpp index d0361b5280..87f0650d6d 100644 --- a/eeschema/symbol_tree_model_adapter.cpp +++ b/eeschema/symbol_tree_model_adapter.cpp @@ -3,7 +3,7 @@ * * Copyright (C) 2017 Chris Pavlina * Copyright (C) 2014 Henner Zeller - * Copyright (C) 2014-2021 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2014-2022 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -35,7 +35,7 @@ bool SYMBOL_TREE_MODEL_ADAPTER::m_show_progress = true; -#define PROGRESS_INTERVAL_MILLIS 66 +#define PROGRESS_INTERVAL_MILLIS 33 // 30 FPS refresh rate wxObjectDataPtr @@ -56,7 +56,7 @@ SYMBOL_TREE_MODEL_ADAPTER::~SYMBOL_TREE_MODEL_ADAPTER() {} -void SYMBOL_TREE_MODEL_ADAPTER::AddLibraries( const std::vector& aNicknames, +bool SYMBOL_TREE_MODEL_ADAPTER::AddLibraries( const std::vector& aNicknames, wxWindow* aParent ) { std::unique_ptr prg = nullptr; @@ -82,20 +82,18 @@ void SYMBOL_TREE_MODEL_ADAPTER::AddLibraries( const std::vector& aNick while( !loader.Done() ) { - if( prg ) - prg->KeepRefreshing(); + if( prg && !prg->KeepRefreshing() ) + break; wxMilliSleep( PROGRESS_INTERVAL_MILLIS ); } - if( prg && prg->IsCancelled() ) - { - loader.Abort(); - } - else - { - loader.Join(); - } + loader.Join(); + + bool cancelled = false; + + if( prg ) + cancelled = prg->IsCancelled(); if( !loader.GetErrors().IsEmpty() ) { @@ -134,6 +132,8 @@ void SYMBOL_TREE_MODEL_ADAPTER::AddLibraries( const std::vector& aNick prg.reset(); m_show_progress = false; } + + return !cancelled; } diff --git a/eeschema/symbol_tree_model_adapter.h b/eeschema/symbol_tree_model_adapter.h index 4690d74470..ef19cfbf04 100644 --- a/eeschema/symbol_tree_model_adapter.h +++ b/eeschema/symbol_tree_model_adapter.h @@ -3,7 +3,7 @@ * * Copyright (C) 2017 Chris Pavlina * Copyright (C) 2014 Henner Zeller - * Copyright (C) 2014-2021 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2014-2022 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -49,8 +49,9 @@ public: * * @param aNicknames is the list of library nicknames * @param aParent is the parent window to display the progress dialog + * @return false if loading was cancelled by the user */ - void AddLibraries( const std::vector& aNicknames, wxWindow* aParent ); + bool AddLibraries( const std::vector& aNicknames, wxWindow* aParent ); void AddLibrary( wxString const& aLibNickname ); diff --git a/eeschema/symbol_viewer_frame.cpp b/eeschema/symbol_viewer_frame.cpp index 5406218987..3028990991 100644 --- a/eeschema/symbol_viewer_frame.cpp +++ b/eeschema/symbol_viewer_frame.cpp @@ -835,11 +835,16 @@ void SYMBOL_VIEWER_FRAME::OnSelectSymbol( wxCommandEvent& aEvent ) // Container doing search-as-you-type. SYMBOL_LIB_TABLE* libs = Prj().SchSymbolLibTable(); - wxObjectDataPtr adapter = - SYMBOL_TREE_MODEL_ADAPTER::Create( this, libs ); + wxObjectDataPtr dataPtr + = SYMBOL_TREE_MODEL_ADAPTER::Create( this, libs ); + SYMBOL_TREE_MODEL_ADAPTER* modelAdapter + = static_cast( dataPtr.get() ); - const auto libNicknames = libs->GetLogicalLibs(); - static_cast( adapter.get() )->AddLibraries( libNicknames, this ); + if( !modelAdapter->AddLibraries( libs->GetLogicalLibs(), this ) ) + { + // loading cancelled by user + return; + } LIB_SYMBOL* current = GetSelectedSymbol(); LIB_ID id; @@ -848,13 +853,13 @@ void SYMBOL_VIEWER_FRAME::OnSelectSymbol( wxCommandEvent& aEvent ) if( current ) { id = current->GetLibId(); - adapter->SetPreselectNode( id, unit ); + modelAdapter->SetPreselectNode( id, unit ); } wxString dialogTitle; - dialogTitle.Printf( _( "Choose Symbol (%d items loaded)" ), adapter->GetItemCount() ); + dialogTitle.Printf( _( "Choose Symbol (%d items loaded)" ), modelAdapter->GetItemCount() ); - DIALOG_CHOOSE_SYMBOL dlg( this, dialogTitle, adapter, m_convert, false, false, false ); + DIALOG_CHOOSE_SYMBOL dlg( this, dialogTitle, dataPtr, m_convert, false, false, false ); if( dlg.ShowQuasiModal() == wxID_CANCEL ) return; diff --git a/pcbnew/footprint_info_impl.cpp b/pcbnew/footprint_info_impl.cpp index bfae62a4aa..f350ffcf62 100644 --- a/pcbnew/footprint_info_impl.cpp +++ b/pcbnew/footprint_info_impl.cpp @@ -143,7 +143,7 @@ bool FOOTPRINT_LIST_IMPL::ReadFootprintFiles( FP_LIB_TABLE* aTable, const wxStri if( m_progress_reporter && !m_progress_reporter->KeepRefreshing() ) m_cancelled = true; - wxMilliSleep( 20 ); + wxMilliSleep( 33 /* 30 FPS refresh rate */); } if( m_cancelled ) @@ -326,7 +326,7 @@ bool FOOTPRINT_LIST_IMPL::joinWorkers() if( m_progress_reporter && !m_progress_reporter->KeepRefreshing() ) m_cancelled = true; - wxMilliSleep( 30 ); + wxMilliSleep( 33 /* 30 FPS refresh rate */ ); } for( auto& thr : threads ) diff --git a/pcbnew/fp_tree_synchronizing_adapter.cpp b/pcbnew/fp_tree_synchronizing_adapter.cpp index 6d689301fc..09de73ee12 100644 --- a/pcbnew/fp_tree_synchronizing_adapter.cpp +++ b/pcbnew/fp_tree_synchronizing_adapter.cpp @@ -64,7 +64,7 @@ bool FP_TREE_SYNCHRONIZING_ADAPTER::IsContainer( const wxDataViewItem& aItem ) c } -#define PROGRESS_INTERVAL_MILLIS 66 +#define PROGRESS_INTERVAL_MILLIS 33 // 30 FPS refresh rate void FP_TREE_SYNCHRONIZING_ADAPTER::Sync() {