diff --git a/common/advanced_config.cpp b/common/advanced_config.cpp index 8908652a3c..121a2d225a 100644 --- a/common/advanced_config.cpp +++ b/common/advanced_config.cpp @@ -58,6 +58,13 @@ namespace AC_KEYS */ static const wxChar EnableSvgImport[] = wxT( "EnableSvgImport" ); +/** + * Testing mode for new connectivity algorithm. Setting this to on will cause all modifications + * to the netlist to be recalculated on the fly. This may be slower than the standard process + * at the moment + */ +static const wxChar RealtimeConnectivity[] = wxT( "RealtimeConnectivity" ); + /** * Allow legacy canvas to be shown in GTK3. Legacy canvas is generally pretty * broken, but this avoids code in an ifdef where it could become broken @@ -183,6 +190,9 @@ void ADVANCED_CFG::loadSettings( wxConfigBase& aCfg ) configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::AllowLegacyCanvasInGtk3, &m_allowLegacyCanvasInGtk3, false ) ); + configParams.push_back( + new PARAM_CFG_BOOL( true, AC_KEYS::RealtimeConnectivity, &m_realTimeConnectivity, false ) ); + wxConfigLoadSetups( &aCfg, configParams ); dumpCfg( configParams ); @@ -200,4 +210,4 @@ bool ADVANCED_CFG::AllowLegacyCanvas() const #endif return allow; -} \ No newline at end of file +} diff --git a/eeschema/connection_graph.cpp b/eeschema/connection_graph.cpp index e57e122a66..3e40ad7f7e 100644 --- a/eeschema/connection_graph.cpp +++ b/eeschema/connection_graph.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -308,23 +309,56 @@ void CONNECTION_GRAPH::Recalculate( SCH_SHEET_LIST aSheetList, bool aUncondition if( aUnconditional ) Reset(); - for( const auto& sheet : aSheetList ) - { - std::vector items; + std::map> sheets; - for( auto item = sheet.LastScreen()->GetDrawItems(); - item; item = item->Next() ) + for( auto sheet : aSheetList ) + { + if( auto list = sheet.LastDrawList() ) + sheets[list].push_back( sheet ); + } + + size_t parallelThreadCount = std::min( std::thread::hardware_concurrency(), + ( sheets.size() + 1 ) / 2 ); + std::atomic nextSheet( 0 ); + std::vector> returns( parallelThreadCount ); + + auto update_connectivity = [&]() -> size_t + { + for( size_t sheetId = nextSheet++; sheetId < sheets.size(); sheetId = nextSheet++ ) { - if( item->IsConnectable() && - ( aUnconditional || item->IsConnectivityDirty() ) ) + auto sheet = sheets.begin(); + std::advance( sheet, sheetId ); + std::vector items; + + for( auto item = sheet->first; item; item = item->Next() ) { - items.push_back( item ); + if( item->IsConnectable() && ( aUnconditional || item->IsConnectivityDirty() ) ) + items.push_back( item ); } + + auto it = sheet->second.begin(); + updateItemConnectivity( *it, items ); + + for( ++it; it != sheet->second.end(); ++it ) + initializeSheetConnections( *it, items ); } - updateItemConnectivity( sheet, items ); + return 1; + }; + + if( parallelThreadCount == 1 ) + update_connectivity(); + else + { + for( size_t ii = 0; ii < parallelThreadCount; ++ii ) + returns[ii] = std::async( std::launch::async, update_connectivity ); + + // Finalize the threads + for( size_t ii = 0; ii < parallelThreadCount; ++ii ) + returns[ii].wait(); } + phase1.Stop(); wxLogTrace( "CONN_PROFILE", "UpdateItemConnectivity() %0.4f ms", phase1.msecs() ); @@ -341,10 +375,50 @@ void CONNECTION_GRAPH::Recalculate( SCH_SHEET_LIST aSheetList, bool aUncondition } -void CONNECTION_GRAPH::updateItemConnectivity( SCH_SHEET_PATH aSheet, - std::vector aItemList ) +void CONNECTION_GRAPH::initializeSheetConnections( SCH_SHEET_PATH aSheet, + std::vector& aItemList ) { - std::unordered_map< wxPoint, std::vector > connection_map; + for( auto item : aItemList ) + { + if( item->Type() == SCH_SHEET_T ) + { + for( auto& pin : static_cast( item )->GetPins() ) + pin.InitializeConnection( aSheet ); + } + else if( item->Type() != SCH_COMPONENT_T ) + { + auto conn = item->InitializeConnection( aSheet ); + + // Set bus/net property here so that the propagation code uses it + switch( item->Type() ) + { + case SCH_LINE_T: + conn->SetType( ( item->GetLayer() == LAYER_BUS ) ? + CONNECTION_BUS : CONNECTION_NET ); + break; + + case SCH_BUS_BUS_ENTRY_T: + conn->SetType( CONNECTION_BUS ); + break; + + case SCH_PIN_T: + case SCH_BUS_WIRE_ENTRY_T: + conn->SetType( CONNECTION_NET ); + break; + + default: + break; + } + + } + } +} + + +void CONNECTION_GRAPH::updateItemConnectivity( SCH_SHEET_PATH aSheet, + std::vector& aItemList ) +{ + std::unordered_multimap< wxPoint, SCH_ITEM* > connection_map; for( auto item : aItemList ) { @@ -356,24 +430,17 @@ void CONNECTION_GRAPH::updateItemConnectivity( SCH_SHEET_PATH aSheet, { for( auto& pin : static_cast( item )->GetPins() ) { - if( !pin.Connection( aSheet ) ) - { - pin.InitializeConnection( aSheet ); - } - + pin.InitializeConnection( aSheet ); pin.ConnectedItems().clear(); - pin.Connection( aSheet )->Reset(); - connection_map[ pin.GetTextPos() ].push_back( &pin ); - m_items.insert( &pin ); + connection_map.emplace( pin.GetTextPos(), &pin ); + InsertItem( &pin ); } } else if( item->Type() == SCH_COMPONENT_T ) { auto component = static_cast( item ); - component->UpdatePins( &aSheet ); - for( auto& it : component->GetPinMap() ) { SCH_PIN* pin = &it.second; @@ -388,15 +455,15 @@ void CONNECTION_GRAPH::updateItemConnectivity( SCH_SHEET_PATH aSheet, // Invisible power pins need to be post-processed later if( pin->IsPowerConnection() && !pin->IsVisible() ) - m_invisible_power_pins.push_back( pin ); + InsertPin( pin ); - connection_map[ pos ].push_back( pin ); - m_items.insert( pin ); + connection_map.emplace( pos, pin ); + InsertItem( pin ); } } else { - m_items.insert( item ); + InsertItem( item ); auto conn = item->InitializeConnection( aSheet ); // Set bus/net property here so that the propagation code uses it @@ -421,22 +488,23 @@ void CONNECTION_GRAPH::updateItemConnectivity( SCH_SHEET_PATH aSheet, } for( auto point : points ) - { - connection_map[ point ].push_back( item ); - } + connection_map.emplace( point, item ); } item->SetConnectivityDirty( false ); } - for( const auto& it : connection_map ) + auto primary_it = connection_map.begin(); + for( auto it = connection_map.begin(); it != connection_map.end(); it = primary_it ) { - auto connection_vec = it.second; + auto range = connection_map.equal_range( it->first ); + auto count = std::distance( range.first, range.second ); + primary_it = range.second; SCH_ITEM* junction = nullptr; - for( auto primary_it = connection_vec.begin(); primary_it != connection_vec.end(); primary_it++ ) + for( primary_it = range.first; primary_it != range.second; ++primary_it ) { - auto connected_item = *primary_it; + auto connected_item = primary_it->second; // Look for junctions. For points that have a junction, we want all // items to connect to the junction but not to each other. @@ -458,10 +526,10 @@ void CONNECTION_GRAPH::updateItemConnectivity( SCH_SHEET_PATH aSheet, // entry itself, this means that either the bus entry is not // connected to anything graphically, or that it is connected to // a segment at some point other than at one of the endpoints. - if( connection_vec.size() == 1 ) + if( count == 1 ) { auto screen = aSheet.LastScreen(); - auto bus = screen->GetBus( it.first ); + auto bus = screen->GetBus( primary_it->first ); if( bus ) { @@ -474,42 +542,41 @@ void CONNECTION_GRAPH::updateItemConnectivity( SCH_SHEET_PATH aSheet, // Bus-to-bus entries are treated just like bus wires if( connected_item->Type() == SCH_BUS_BUS_ENTRY_T ) { - if( connection_vec.size() < 2 ) + if( count < 2 ) { auto screen = aSheet.LastScreen(); - auto bus = screen->GetBus( it.first ); + auto bus = screen->GetBus( primary_it->first ); if( bus ) { auto bus_entry = static_cast( connected_item ); - if( it.first == bus_entry->GetPosition() ) + if( primary_it->first == bus_entry->GetPosition() ) bus_entry->m_connected_bus_items[0] = bus; else bus_entry->m_connected_bus_items[1] = bus; - bus_entry->ConnectedItems().insert( bus ); - bus->ConnectedItems().insert( bus_entry ); + bus_entry->AddConnectionTo( bus ); + bus->AddConnectionTo( bus_entry ); } } } - for( auto test_it = primary_it + 1; test_it != connection_vec.end(); test_it++ ) + for( auto test_it = std::next( primary_it ); test_it != range.second; ++test_it ) { - auto test_item = *test_it; + auto test_item = test_it->second; if( !junction && test_item->Type() == SCH_JUNCTION_T ) { junction = test_item; } - if( connected_item != test_item && - connected_item != junction && + if( connected_item != junction && connected_item->ConnectionPropagatesTo( test_item ) && test_item->ConnectionPropagatesTo( connected_item ) ) { - connected_item->ConnectedItems().insert( test_item ); - test_item->ConnectedItems().insert( connected_item ); + connected_item->AddConnectionTo( test_item ); + test_item->AddConnectionTo( connected_item ); } // Set up the link between the bus entry net and the bus @@ -646,7 +713,7 @@ void CONNECTION_GRAPH::buildConnectionGraph() // Resolve drivers for subgraphs and propagate connectivity info - // We don't want to spin up a new thread for fewer than 8 nets (overhead costs) + // We don't want to spin up a new thread for fewer than 4 nets (overhead costs) size_t parallelThreadCount = std::min( std::thread::hardware_concurrency(), ( m_subgraphs.size() + 3 ) / 4 ); diff --git a/eeschema/connection_graph.h b/eeschema/connection_graph.h index dc62030cab..09bd0f422d 100644 --- a/eeschema/connection_graph.h +++ b/eeschema/connection_graph.h @@ -35,10 +35,6 @@ // #define CONNECTIVITY_DEBUG #endif -// Uncomment this line to enable real-time connectivity updates -// TODO(JE) re-enable this once performance concerns are sorted out -// #define CONNECTIVITY_REAL_TIME - class SCH_PIN; class SCH_EDIT_FRAME; @@ -198,12 +194,29 @@ public: // TODO(JE) firm up API and move to private std::map > m_net_code_to_subgraphs_map; + inline void InsertItem( SCH_ITEM* aItem ) + { + std::lock_guard lock( m_item_mutex ); + m_items.insert( aItem ); + } + + inline void InsertPin( SCH_PIN* aPin ) + { + std::lock_guard lock( m_power_pin_mutex ); + m_invisible_power_pins.push_back( aPin ); + } + + private: + std::mutex m_item_mutex; + std::unordered_set m_items; std::vector m_subgraphs; + std::mutex m_power_pin_mutex; + std::vector m_invisible_power_pins; std::unordered_map> m_bus_alias_cache; @@ -223,8 +236,6 @@ private: int m_last_subgraph_code; - std::mutex m_item_mutex; - // Needed for m_UserUnits for now; maybe refactor later SCH_EDIT_FRAME* m_frame; @@ -256,7 +267,20 @@ private: * @param aItemList is a list of items to consider */ void updateItemConnectivity( SCH_SHEET_PATH aSheet, - std::vector aItemList ); + std::vector& aItemList ); + + /** + * This is a shortcut function to only update the connections for duplicate sheets + * Since shared sheets have the same layout, we don't need to re-create the connection + * map, only add the sheet path to the item connection. + * + * N.B. This must be run sequentially after the first shared sheet + * + * @param aSheet path to the sheet with item list + * @param aItemList vector of items + */ + void initializeSheetConnections( SCH_SHEET_PATH aSheet, + std::vector& aItemList ); /** * Generates the connection graph (after all item connectivity has been updated) diff --git a/eeschema/sch_connection.cpp b/eeschema/sch_connection.cpp index bf2acf5e68..4592471a6f 100644 --- a/eeschema/sch_connection.cpp +++ b/eeschema/sch_connection.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -281,7 +282,8 @@ wxString SCH_CONNECTION::Name( bool aIgnoreSheet ) const void SCH_CONNECTION::AppendInfoToMsgPanel( MSG_PANEL_ITEMS& aList ) const { -#ifdef CONNECTIVITY_REAL_TIME + if( !ADVANCED_CFG::GetCfg().m_realTimeConnectivity ) + return; wxString msg, group_name; std::vector group_members; @@ -319,14 +321,14 @@ void SCH_CONNECTION::AppendInfoToMsgPanel( MSG_PANEL_ITEMS& aList ) const } } } - -#endif } void SCH_CONNECTION::AppendDebugInfoToMsgPanel( MSG_PANEL_ITEMS& aList ) const { -#ifdef CONNECTIVITY_REAL_TIME + if( !ADVANCED_CFG::GetCfg().m_realTimeConnectivity ) + return; + wxString msg; AppendInfoToMsgPanel( aList ); @@ -345,7 +347,6 @@ void SCH_CONNECTION::AppendDebugInfoToMsgPanel( MSG_PANEL_ITEMS& aList ) const msg.Printf( "%s at %p", Parent()->GetSelectMenuText( MILLIMETRES ), Parent() ); aList.push_back( MSG_PANEL_ITEM( _( "Attached To" ), msg, RED ) ); -#endif } diff --git a/eeschema/sch_edit_frame.cpp b/eeschema/sch_edit_frame.cpp index b3049a239e..6090bb3f0a 100644 --- a/eeschema/sch_edit_frame.cpp +++ b/eeschema/sch_edit_frame.cpp @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -794,9 +795,9 @@ void SCH_EDIT_FRAME::OnModify() m_foundItems.SetForceSearch(); -#ifdef CONNECTIVITY_REAL_TIME - RecalculateConnections(); -#endif + + if( ADVANCED_CFG::GetCfg().m_realTimeConnectivity ) + RecalculateConnections(); m_canvas->Refresh(); } diff --git a/eeschema/schedit.cpp b/eeschema/schedit.cpp index c02929785a..794e2c4871 100644 --- a/eeschema/schedit.cpp +++ b/eeschema/schedit.cpp @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -563,9 +564,8 @@ void SCH_EDIT_FRAME::OnSelectTool( wxCommandEvent& aEvent ) case ID_HIGHLIGHT: // TODO(JE) remove once real-time connectivity is a given - #ifndef CONNECTIVITY_REAL_TIME - RecalculateConnections(); - #endif + if( ADVANCED_CFG::GetCfg().m_realTimeConnectivity ) + RecalculateConnections(); SetToolID( ID_HIGHLIGHT, wxCURSOR_HAND, _("Highlight specific net") ); break; diff --git a/include/advanced_config.h b/include/advanced_config.h index a69669ebd6..86c532a31e 100644 --- a/include/advanced_config.h +++ b/include/advanced_config.h @@ -73,6 +73,11 @@ public: */ bool m_enableSvgImport; + /** + * Do real-time connectivity + */ + bool m_realTimeConnectivity; + /** * Helper to determine if legacy canvas is allowed (according to platform * and config)