diff --git a/eeschema/connection_graph.cpp b/eeschema/connection_graph.cpp index 5648bc5fb7..33d610bafb 100644 --- a/eeschema/connection_graph.cpp +++ b/eeschema/connection_graph.cpp @@ -696,31 +696,8 @@ void CONNECTION_GRAPH::updateItemConnectivity( const SCH_SHEET_PATH& aSheet, } -// TODO(JE) This won't give the same subgraph IDs (and eventually net/graph codes) -// to the same subgraph necessarily if it runs over and over again on the same -// sheet. We need: -// -// a) a cache of net/bus codes, like used before -// b) to persist the CONNECTION_GRAPH globally so the cache is persistent, -// c) some way of trying to avoid changing net names. so we should keep track -// of the previous driver of a net, and if it comes down to choosing between -// equally-prioritized drivers, choose the one that already exists as a driver -// on some portion of the items. - - -void CONNECTION_GRAPH::buildConnectionGraph() +void CONNECTION_GRAPH::buildItemSubGraphs() { - // Recache all bus aliases for later use - wxCHECK_RET( m_schematic, wxT( "Connection graph cannot be built without schematic pointer" ) ); - - SCH_SHEET_LIST all_sheets = m_schematic->GetSheets(); - - for( unsigned i = 0; i < all_sheets.size(); i++ ) - { - for( const auto& alias : all_sheets[i].LastScreen()->GetBusAliases() ) - m_bus_alias_cache[ alias->GetName() ] = alias; - } - // Build subgraphs from items (on a per-sheet basis) for( SCH_ITEM* item : m_items ) @@ -742,21 +719,25 @@ void CONNECTION_GRAPH::buildConnectionGraph() connection->SetSubgraphCode( subgraph->m_code ); m_item_to_subgraph_map[item] = subgraph; - std::list members; + std::list memberlist; auto get_items = [&]( SCH_ITEM* aItem ) -> bool { SCH_CONNECTION* conn = aItem->GetOrInitConnection( sheet, this ); + bool unique = !( aItem->GetFlags() & CANDIDATE ); - return ( conn->SubgraphCode() == 0 ); + aItem->SetFlags( CANDIDATE ); + + return ( unique && conn && ( conn->SubgraphCode() == 0 ) ); }; + std::copy_if( item->ConnectedItems( sheet ).begin(), item->ConnectedItems( sheet ).end(), - std::back_inserter( members ), get_items ); + std::back_inserter( memberlist ), get_items ); - for( SCH_ITEM* connected_item : members ) + for( SCH_ITEM* connected_item : memberlist ) { if( connected_item->Type() == SCH_NO_CONNECT_T ) subgraph->m_no_connect = connected_item; @@ -770,24 +751,26 @@ void CONNECTION_GRAPH::buildConnectionGraph() connected_conn->SetSubgraphCode( subgraph->m_code ); m_item_to_subgraph_map[connected_item] = subgraph; subgraph->AddItem( connected_item ); + SCH_ITEM_SET citemset = connected_item->ConnectedItems( sheet ); - std::copy_if( connected_item->ConnectedItems( sheet ).begin(), - connected_item->ConnectedItems( sheet ).end(), - std::back_inserter( members ), get_items ); + std::copy_if( citemset.begin(), citemset.end(), + std::back_inserter( memberlist ), get_items ); } } + for( SCH_ITEM* connected_item : memberlist ) + connected_item->ClearFlags( CANDIDATE ); + subgraph->m_dirty = true; m_subgraphs.push_back( subgraph ); } } } - /** - * TODO(JE): Net codes are non-deterministic. Fortunately, they are also not really used for - * anything. We should consider removing them entirely and just using net names everywhere. - */ +} +void CONNECTION_GRAPH::resolveAllDrivers() +{ // 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) @@ -867,7 +850,11 @@ void CONNECTION_GRAPH::buildConnectionGraph() { return candidate->m_driver; } ); +} + +void CONNECTION_GRAPH::collectAllDriverValues() +{ // Check for subgraphs with the same net name but only weak drivers. // For example, two wires that are both connected to hierarchical // sheet pins that happen to have the same name, but are not the same. @@ -922,7 +909,11 @@ void CONNECTION_GRAPH::buildConnectionGraph() } } } +} + +void CONNECTION_GRAPH::generateInvisiblePinSubGraphs() +{ // Generate subgraphs for invisible power pins. These will be merged with other subgraphs // on the same sheet in the next loop. @@ -942,7 +933,7 @@ void CONNECTION_GRAPH::buildConnectionGraph() SCH_CONNECTION* connection = pin->GetOrInitConnection( sheet, this ); // If this pin already has a subgraph, don't need to process - if( connection->SubgraphCode() > 0 ) + if( !connection || connection->SubgraphCode() > 0 ) continue; connection->SetName( pin->GetShownName() ); @@ -979,7 +970,11 @@ void CONNECTION_GRAPH::buildConnectionGraph() connection->SetSubgraphCode( subgraph->m_code ); } +} + +void CONNECTION_GRAPH::processSubGraphs() +{ // Here we do all the local (sheet) processing of each subgraph, including assigning net // codes, merging subgraphs together that use label connections, etc. @@ -1306,6 +1301,49 @@ void CONNECTION_GRAPH::buildConnectionGraph() subgraph->m_driver_connection->Name() ); } +} + + +// TODO(JE) This won't give the same subgraph IDs (and eventually net/graph codes) +// to the same subgraph necessarily if it runs over and over again on the same +// sheet. We need: +// +// a) a cache of net/bus codes, like used before +// b) to persist the CONNECTION_GRAPH globally so the cache is persistent, +// c) some way of trying to avoid changing net names. so we should keep track +// of the previous driver of a net, and if it comes down to choosing between +// equally-prioritized drivers, choose the one that already exists as a driver +// on some portion of the items. + + +void CONNECTION_GRAPH::buildConnectionGraph() +{ + // Recache all bus aliases for later use + wxCHECK_RET( m_schematic, wxT( "Connection graph cannot be built without schematic pointer" ) ); + + SCH_SHEET_LIST all_sheets = m_schematic->GetSheets(); + + for( unsigned i = 0; i < all_sheets.size(); i++ ) + { + for( const auto& alias : all_sheets[i].LastScreen()->GetBusAliases() ) + m_bus_alias_cache[ alias->GetName() ] = alias; + } + + buildItemSubGraphs(); + + /** + * TODO(JE): Net codes are non-deterministic. Fortunately, they are also not really used for + * anything. We should consider removing them entirely and just using net names everywhere. + */ + + resolveAllDrivers(); + + collectAllDriverValues(); + + generateInvisiblePinSubGraphs(); + + processSubGraphs(); + // Absorbed subgraphs should no longer be considered alg::delete_if( m_driver_subgraphs, [&]( const CONNECTION_SUBGRAPH* candidate ) -> bool { @@ -1328,7 +1366,13 @@ void CONNECTION_GRAPH::buildConnectionGraph() m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph ); // Update item connections at this point so that neighbor propagation works - nextSubgraph.store( 0 ); + std::atomic nextSubgraph( 0 ); + + // We don't want to spin up a new thread for fewer than 8 nets (overhead costs) + size_t parallelThreadCount = std::min( std::thread::hardware_concurrency(), + ( m_subgraphs.size() + 3 ) / 4 ); + + std::vector> returns( parallelThreadCount ); auto preliminaryUpdateTask = [&]() -> size_t diff --git a/eeschema/connection_graph.h b/eeschema/connection_graph.h index f8ac81e5eb..e3be9469c0 100644 --- a/eeschema/connection_graph.h +++ b/eeschema/connection_graph.h @@ -370,6 +370,33 @@ private: */ void buildConnectionGraph(); + /** + * Generates individual item subgraphs on a per-sheet basis + */ + void buildItemSubGraphs(); + + /** + * Finds all subgraphs in the connection graph and calls ResolveDrivers() in + * parallel + */ + void resolveAllDrivers(); + + /** + * Maps the driver values for each subgraph + */ + void collectAllDriverValues(); + + /** + * Iterate through the invisible power pins to collect the global labels + * as drivers + */ + void generateInvisiblePinSubGraphs(); + + /** + * Process all subgraphs to assign netcodes and merge subgraphs based on labels + */ + void processSubGraphs(); + /** * Helper to assign a new net code to a connection * diff --git a/eeschema/sch_item.cpp b/eeschema/sch_item.cpp index 4d20307fdc..b9aef26e5b 100644 --- a/eeschema/sch_item.cpp +++ b/eeschema/sch_item.cpp @@ -182,11 +182,13 @@ SCH_ITEM_SET& SCH_ITEM::ConnectedItems( const SCH_SHEET_PATH& aSheet ) void SCH_ITEM::AddConnectionTo( const SCH_SHEET_PATH& aSheet, SCH_ITEM* aItem ) { - // The vector elements are small, so reserve 1k at a time to prevent re-allocations - if( m_connected_items[ aSheet ].capacity() == 0 ) - m_connected_items[ aSheet ].reserve( 1024 ); + SCH_ITEM_SET& set = m_connected_items[ aSheet ]; - m_connected_items[ aSheet ].emplace_back( aItem ); + // The vector elements are small, so reserve 1k at a time to prevent re-allocations + if( set.size() == set.capacity() ) + set.reserve( set.size() + 4096 ); + + set.emplace_back( aItem ); } @@ -216,6 +218,9 @@ SCH_CONNECTION* SCH_ITEM::InitializeConnection( const SCH_SHEET_PATH& aSheet, SCH_CONNECTION* SCH_ITEM::GetOrInitConnection( const SCH_SHEET_PATH& aSheet, CONNECTION_GRAPH* aGraph ) { + if( !IsConnectable() ) + return nullptr; + SetConnectivityDirty( false ); SCH_CONNECTION* connection = Connection( &aSheet ); diff --git a/eeschema/sch_text.cpp b/eeschema/sch_text.cpp index 164a367caf..bcb59c4411 100644 --- a/eeschema/sch_text.cpp +++ b/eeschema/sch_text.cpp @@ -861,15 +861,13 @@ bool SCH_LABEL::IsType( const KICAD_T aScanTypes[] ) const wxCHECK_MSG( Schematic(), false, wxT( "No parent SCHEMATIC set for SCH_LABEL!" ) ); - SCH_SHEET_PATH current = Schematic()->CurrentSheet(); + const SCH_ITEM_SET& item_set = m_connected_items.at( Schematic()->CurrentSheet() ); for( const KICAD_T* p = aScanTypes; *p != EOT; ++p ) { if( *p == SCH_LABEL_LOCATE_WIRE_T ) { - wxASSERT( m_connected_items.count( current ) ); - - for( SCH_ITEM* connection : m_connected_items.at( current ) ) + for( SCH_ITEM* connection : item_set ) { if( connection->IsType( wireTypes ) ) return true; @@ -877,9 +875,7 @@ bool SCH_LABEL::IsType( const KICAD_T aScanTypes[] ) const } else if ( *p == SCH_LABEL_LOCATE_BUS_T ) { - wxASSERT( m_connected_items.count( current ) ); - - for( SCH_ITEM* connection : m_connected_items.at( current ) ) + for( SCH_ITEM* connection : item_set ) { if( connection->IsType( busTypes ) ) return true;