diff --git a/pcbnew/drc/drc_creepage_utils.cpp b/pcbnew/drc/drc_creepage_utils.cpp index 5e0e62cbff..a326c6bcab 100644 --- a/pcbnew/drc/drc_creepage_utils.cpp +++ b/pcbnew/drc/drc_creepage_utils.cpp @@ -2245,7 +2245,7 @@ void CREEPAGE_GRAPH::Addshape( const SHAPE& aShape, std::shared_ptr& } } -void CREEPAGE_GRAPH::GeneratePaths( double aMaxWeight, PCB_LAYER_ID aLayer, bool aClearance ) +void CREEPAGE_GRAPH::GeneratePaths( double aMaxWeight, PCB_LAYER_ID aLayer ) { std::vector> nodes; std::mutex nodes_lock; @@ -2265,102 +2265,134 @@ void CREEPAGE_GRAPH::GeneratePaths( double aMaxWeight, PCB_LAYER_ID aLayer, bool && gn1->m_net < gn2->m_net ); } ); - auto processNodes = [&]( size_t i, size_t j ) -> bool + // Build parent -> net -> nodes mapping for efficient filtering + std::unordered_map>>> parent_net_groups; + std::vector parent_keys; + + for( const auto& gn : nodes ) { - for( size_t ii = i; ii < j; ii++ ) + const BOARD_ITEM* parent = gn->m_parent->GetParent(); + + if( parent_net_groups[parent].empty() ) + parent_keys.push_back( parent ); + + parent_net_groups[parent][gn->m_net].push_back( gn ); + } + + // Generate work items: compare nodes between different parents only + struct WorkItem { + std::shared_ptr node1, node2; + }; + std::vector work_items; + + for( size_t i = 0; i < parent_keys.size(); ++i ) + { + for( size_t j = i + 1; j < parent_keys.size(); ++j ) + { + const auto& group1_nets = parent_net_groups[parent_keys[i]]; + const auto& group2_nets = parent_net_groups[parent_keys[j]]; + + for( const auto& [net1, nodes1] : group1_nets ) { - std::shared_ptr gn1 = nodes[ii]; - - for( size_t jj = ii + 1; jj < nodes.size(); jj++ ) + for( const auto& [net2, nodes2] : group2_nets ) { - std::shared_ptr gn2 = nodes[jj]; - - if( gn1->m_parent->GetParent() == gn2->m_parent->GetParent() ) - continue; - - if( ( gn1->m_net == gn2->m_net ) && ( gn1->m_parent->IsConductive() ) - && ( gn2->m_parent->IsConductive() ) ) - continue; - - for( PATH_CONNECTION pc : GetPaths( gn1->m_parent, gn2->m_parent, aMaxWeight ) ) + // Skip if same net and both nets have only conductive nodes + if( net1 == net2 ) { - std::vector IgnoreForTest; - IgnoreForTest.push_back( gn1->m_parent->GetParent() ); - IgnoreForTest.push_back( gn2->m_parent->GetParent() ); + bool all_conductive_1 = std::all_of( nodes1.begin(), nodes1.end(), + []( const auto& n ) { return n->m_parent->IsConductive(); } ); + bool all_conductive_2 = std::all_of( nodes2.begin(), nodes2.end(), + []( const auto& n ) { return n->m_parent->IsConductive(); } ); - if( !pc.isValid( m_board, aLayer, m_boardEdge, IgnoreForTest, m_boardOutline, - { false, true }, m_minGrooveWidth ) ) + if( all_conductive_1 && all_conductive_2 ) continue; - - std::shared_ptr* connect1 = &gn1; - std::shared_ptr* connect2 = &gn2; - std::shared_ptr gnt1 = nullptr; - std::shared_ptr gnt2 = nullptr; - std::lock_guard lock( nodes_lock ); - - if( gn1->m_parent->GetType() != CREEP_SHAPE::TYPE::POINT ) - { - gnt1 = AddNode( GRAPH_NODE::POINT, gn1->m_parent, pc.a1 ); - gnt1->m_connectDirectly = false; - - if( gn1->m_parent->IsConductive() ) - { - std::shared_ptr gc = AddConnection( gn1, gnt1 ); - - if( gc ) - gc->m_path.m_show = false; - } - connect1 = &gnt1; - } - - if( gn2->m_parent->GetType() != CREEP_SHAPE::TYPE::POINT ) - { - gnt2 = AddNode( GRAPH_NODE::POINT, gn2->m_parent, pc.a2 ); - gnt2->m_connectDirectly = false; - - if( gn2->m_parent->IsConductive() ) - { - std::shared_ptr gc = AddConnection( gn2, gnt2 ); - - if( gc ) - gc->m_path.m_show = false; - } - connect2 = &gnt2; - } - - AddConnection( *connect1, *connect2, pc ); } - } // for jj - } // for ii - return true; - }; + // Add all node pairs from these net groups + for( const auto& gn1 : nodes1 ) + for( const auto& gn2 : nodes2 ) + work_items.push_back( { gn1, gn2 } ); + } + } + } + } - // Running in the clearance test, we are already in a parallelized loop, so parallelizing again - // will run into deadlock as all threads start waiting - if( aClearance ) - processNodes( 0, nodes.size() ); + auto processWorkItems = [&]( size_t start_idx, size_t end_idx ) -> bool + { + for( size_t idx = start_idx; idx < end_idx; ++idx ) + { + auto [gn1, gn2] = work_items[idx]; + + for( PATH_CONNECTION pc : GetPaths( gn1->m_parent, gn2->m_parent, aMaxWeight ) ) + { + std::vector IgnoreForTest = { + gn1->m_parent->GetParent(), gn2->m_parent->GetParent() + }; + + if( !pc.isValid( m_board, aLayer, m_boardEdge, IgnoreForTest, m_boardOutline, + { false, true }, m_minGrooveWidth ) ) + continue; + + std::shared_ptr connect1 = gn1, connect2 = gn2; + std::lock_guard lock( nodes_lock ); + + // Handle non-point node1 + if( gn1->m_parent->GetType() != CREEP_SHAPE::TYPE::POINT ) + { + auto gnt1 = AddNode( GRAPH_NODE::POINT, gn1->m_parent, pc.a1 ); + gnt1->m_connectDirectly = false; + connect1 = gnt1; + + if( gn1->m_parent->IsConductive() ) + { + if( auto gc = AddConnection( gn1, gnt1 ) ) + gc->m_path.m_show = false; + } + } + + // Handle non-point node2 + if( gn2->m_parent->GetType() != CREEP_SHAPE::TYPE::POINT ) + { + auto gnt2 = AddNode( GRAPH_NODE::POINT, gn2->m_parent, pc.a2 ); + gnt2->m_connectDirectly = false; + connect2 = gnt2; + + if( gn2->m_parent->IsConductive() ) + { + if( auto gc = AddConnection( gn2, gnt2 ) ) + gc->m_path.m_show = false; + } + } + + AddConnection( connect1, connect2, pc ); + } + } + return true; + }; + + // If the number of tasks is high enough, this indicates that the calling process + // has already parallelized the work, so we can process all items in one go. + if( tp.get_tasks_total() >= tp.get_thread_count() - 4 ) + { + processWorkItems( 0, work_items.size() ); + } else { - auto ret = tp.parallelize_loop( nodes.size(), processNodes ); + auto ret = tp.parallelize_loop( work_items.size(), processWorkItems ); for( size_t ii = 0; ii < ret.size(); ii++ ) { std::future& r = ret[ii]; - if( r.valid() ) - { - std::future_status status = r.wait_for( std::chrono::seconds( 0 ) ); + if( !r.valid() ) + continue; - while( status != std::future_status::ready ) - { - status = r.wait_for( std::chrono::milliseconds( 100 ) ); - } - } + while( r.wait_for( std::chrono::milliseconds( 100 ) ) != std::future_status::ready ){} } } } + void CREEPAGE_GRAPH::Trim( double aWeightLimit ) { std::vector> toRemove; diff --git a/pcbnew/drc/drc_creepage_utils.h b/pcbnew/drc/drc_creepage_utils.h index 42cd1a312f..d7aea4bf6d 100644 --- a/pcbnew/drc/drc_creepage_utils.h +++ b/pcbnew/drc/drc_creepage_utils.h @@ -69,7 +69,7 @@ struct PATH_CONNECTION bool isValid( const BOARD& aBoard, PCB_LAYER_ID aLayer, const std::vector& aBoardEdges, const std::vector& aIgnoreForTest, SHAPE_POLY_SET* aOutline, - const std::pair& aTestLocalConcavity, int aMinGrooveWidth ) + const std::pair& aTestLocalConcavity, int aMinGrooveWidth ) const { if( !aOutline ) return true; // We keep the segment if there is a problem @@ -760,7 +760,7 @@ public: double Solve( std::shared_ptr& aFrom, std::shared_ptr& aTo, std::vector>& aResult ); - void GeneratePaths( double aMaxWeight, PCB_LAYER_ID aLayer, bool aClearance ); + void GeneratePaths( double aMaxWeight, PCB_LAYER_ID aLayer ); std::shared_ptr AddNetElements( int aNetCode, PCB_LAYER_ID aLayer, int aMaxCreepage ); diff --git a/pcbnew/drc/drc_test_provider_clearance_base.cpp b/pcbnew/drc/drc_test_provider_clearance_base.cpp index 9254e7a759..4e76f039db 100644 --- a/pcbnew/drc/drc_test_provider_clearance_base.cpp +++ b/pcbnew/drc/drc_test_provider_clearance_base.cpp @@ -112,7 +112,7 @@ void DRC_TEST_PROVIDER_CLEARANCE_BASE::ReportAndShowPathCuToCu( std::shared_ptr< graph.Addshape( *( aItem1->GetEffectiveShape( layer ) ), NetA, nullptr ); graph.Addshape( *( aItem2->GetEffectiveShape( layer ) ), NetB, nullptr ); - graph.GeneratePaths( aDistance * 2, layer, true ); + graph.GeneratePaths( aDistance * 2, layer ); double minValue = aDistance * 2; GRAPH_CONNECTION* minGc = nullptr; diff --git a/pcbnew/drc/drc_test_provider_copper_clearance.cpp b/pcbnew/drc/drc_test_provider_copper_clearance.cpp index f0c7ac0b6f..75b29dee2d 100644 --- a/pcbnew/drc/drc_test_provider_copper_clearance.cpp +++ b/pcbnew/drc/drc_test_provider_copper_clearance.cpp @@ -1203,83 +1203,94 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZonesToZones() std::vector>> poly_segments; poly_segments.resize( m_board->m_DRCCopperZones.size() ); - // Contains the index for zoneA, zoneB, the conflict point, the actual clearance, the - // constraint, and the layer - using REPORT_DATA = std::tuple; + thread_pool& tp = GetKiCadThreadPool(); + std::atomic done( 0 ); + size_t count = 0; - std::vector> futures; - thread_pool& tp = GetKiCadThreadPool(); - std::atomic done( 1 ); + auto reportZoneZoneViolation = [this]( ZONE* zoneA, ZONE* zoneB, VECTOR2I& pt, int actual, + const DRC_CONSTRAINT& constraint, PCB_LAYER_ID layer ) -> void + { + std::shared_ptr drce; - auto checkZones = - [this, testClearance, testIntersects, &poly_segments, &done] - ( int zoneA_idx, int zoneB_idx, bool sameNet, PCB_LAYER_ID layer ) -> REPORT_DATA + if( constraint.IsNull() ) + { + drce = DRC_ITEM::Create( DRCE_ZONES_INTERSECT ); + wxString msg = _( "(intersecting zones must have distinct priorities)" ); + drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg ); + drce->SetItems( zoneA, zoneB ); + reportViolation( drce, pt, layer ); + } + else + { + drce = DRC_ITEM::Create( DRCE_CLEARANCE ); + wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ), constraint.GetName(), + constraint.GetValue().Min(), std::max( actual, 0 ) ); + + drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg ); + drce->SetItems( zoneA, zoneB ); + drce->SetViolatingRule( constraint.GetParentRule() ); + ReportAndShowPathCuToCu( drce, pt, layer, zoneA, zoneB, layer, actual ); + } + }; + + auto checkZones = [this, testClearance, testIntersects, reportZoneZoneViolation, &poly_segments, + &done]( int zoneA_idx, int zoneB_idx, bool sameNet, PCB_LAYER_ID layer ) -> void + { + ZONE* zoneA = m_board->m_DRCCopperZones[zoneA_idx]; + ZONE* zoneB = m_board->m_DRCCopperZones[zoneB_idx]; + int actual = 0; + VECTOR2I pt; + + if( sameNet && testIntersects ) + { + if( zoneA->Outline()->Collide( zoneB->Outline(), 0, &actual, &pt ) ) { - ZONE* zoneA = m_board->m_DRCCopperZones[zoneA_idx]; - ZONE* zoneB = m_board->m_DRCCopperZones[zoneB_idx]; - int actual = 0; - VECTOR2I pt; + done.fetch_add( 1 ); + reportZoneZoneViolation( zoneA, zoneB, pt, actual, DRC_CONSTRAINT(), layer ); + return; + } + } + else if( !sameNet && testClearance ) + { + DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, zoneA, zoneB, layer ); + int clearance = constraint.GetValue().Min(); - if( sameNet && testIntersects ) + if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 ) + { + std::map conflictPoints; + + std::vector& refSegments = poly_segments[zoneA_idx][layer]; + std::vector& testSegments = poly_segments[zoneB_idx][layer]; + + // Iterate through all the segments in zoneA + for( SEG& refSegment : refSegments ) { - if( zoneA->Outline()->Collide( zoneB->Outline(), 0, &actual, &pt ) ) + // Iterate through all the segments in zoneB + for( SEG& testSegment : testSegments ) { - done.fetch_add( 1 ); - return std::make_tuple( zoneA_idx, zoneB_idx, pt, 0, DRC_CONSTRAINT(), layer ); - } - } - else if( !sameNet && testClearance ) - { - DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, zoneA, zoneB, layer ); - int clearance = constraint.GetValue().Min(); + // We have ensured that the 'A' segment starts before the 'B' segment, so if the + // 'A' segment ends before the 'B' segment starts, we can skip to the next 'A' + if( refSegment.B.x < testSegment.A.x ) + break; - if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 ) - { - std::map conflictPoints; + int64_t dist_sq = 0; + VECTOR2I other_pt; + refSegment.NearestPoints( testSegment, pt, other_pt, dist_sq ); + actual = std::floor( std::sqrt( dist_sq ) + 0.5 ); - std::vector& refSegments = poly_segments[zoneA_idx][layer]; - std::vector& testSegments = poly_segments[zoneB_idx][layer]; - - // Iterate through all the segments in zoneA - for( SEG& refSegment : refSegments ) + if( actual < clearance ) { - int ax1 = refSegment.A.x; - int ay1 = refSegment.A.y; - int ax2 = refSegment.B.x; - int ay2 = refSegment.B.y; - - // Iterate through all the segments in zoneB - for( SEG& testSegment : testSegments ) - { - // Build test segment - int bx1 = testSegment.A.x; - int by1 = testSegment.A.y; - int bx2 = testSegment.B.x; - int by2 = testSegment.B.y; - - // We have ensured that the 'A' segment starts before the 'B' segment, so if the - // 'A' segment ends before the 'B' segment starts, we can skip to the next 'A' - if( ax2 < bx1 ) - break; - - int64_t dist_sq = 0; - VECTOR2I other_pt; - refSegment.NearestPoints( testSegment, pt, other_pt, dist_sq ); - actual = std::floor( std::sqrt( dist_sq ) + 0.5 ); - - if( actual < clearance ) - { - done.fetch_add( 1 ); - return std::make_tuple( zoneA_idx, zoneB_idx, pt, actual, constraint, layer ); - } - } + done.fetch_add( 1 ); + reportZoneZoneViolation( zoneA, zoneB, pt, actual, constraint, layer ); + return; } } } + } + } - done.fetch_add( 1 ); - return std::make_tuple( -1, -1, VECTOR2I(), 0, DRC_CONSTRAINT(), F_Cu ); - }; + done.fetch_add( 1 ); + }; for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, m_board->GetCopperLayerCount() ) ) @@ -1312,8 +1323,6 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZonesToZones() } } - std::vector> zonePairs; - for( size_t ia = 0; ia < m_board->m_DRCCopperZones.size(); ia++ ) { ZONE* zoneA = m_board->m_DRCCopperZones[ia]; @@ -1356,75 +1365,24 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZonesToZones() if( !polyA->BBoxFromCaches().Intersects( polyB->BBoxFromCaches() ) ) continue; - futures.push_back( tp.submit( checkZones, ia, ia2, sameNet, layer ) ); + count++; + tp.push_task( checkZones, ia, ia2, sameNet, layer ); } } } - size_t count = futures.size(); - - for( std::future& task : futures ) + while( true ) { - if( !task.valid() ) - continue; + reportProgress( done, count ); - std::future_status result; + if( m_drcEngine->IsCancelled() ) + break; - while( true ) - { - result = task.wait_for( std::chrono::milliseconds( 250 ) ); - - reportProgress( done, count ); - - if( m_drcEngine->IsCancelled() ) - break; - - if( result == std::future_status::ready ) - { - REPORT_DATA data = task.get(); - int zoneA_idx = std::get<0>( data ); - int zoneB_idx = std::get<1>( data ); - VECTOR2I pt = std::get<2>( data ); - int actual = std::get<3>( data ); - DRC_CONSTRAINT constraint = std::get<4>( data ); - PCB_LAYER_ID layer = std::get<5>( data ); - - if( zoneA_idx >= 0 ) - { - ZONE* zoneA = m_board->m_DRCCopperZones[zoneA_idx]; - ZONE* zoneB = m_board->m_DRCCopperZones[zoneB_idx]; - - std::shared_ptr drce; - - if( constraint.IsNull() ) - { - drce = DRC_ITEM::Create( DRCE_ZONES_INTERSECT ); - wxString msg = _( "(intersecting zones must have distinct priorities)" ); - drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg ); - drce->SetItems( zoneA, zoneB ); - reportViolation( drce, pt, layer ); - } - else - { - drce = DRC_ITEM::Create( DRCE_CLEARANCE ); - wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ), - constraint.GetName(), - constraint.GetValue().Min(), - std::max( actual, 0 ) ); - - drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg ); - drce->SetItems( zoneA, zoneB ); - drce->SetViolatingRule( constraint.GetParentRule() ); - ReportAndShowPathCuToCu( drce, pt, layer, zoneA, zoneB, layer, actual ); - } - } - - break; - } - } + if( tp.wait_for_tasks_duration( std::chrono::milliseconds( 250 ) ) ) + break; } -} +} namespace detail { diff --git a/pcbnew/drc/drc_test_provider_creepage.cpp b/pcbnew/drc/drc_test_provider_creepage.cpp index 2aaac7be8b..4efb1b6a7f 100644 --- a/pcbnew/drc/drc_test_provider_creepage.cpp +++ b/pcbnew/drc/drc_test_provider_creepage.cpp @@ -137,7 +137,7 @@ int DRC_TEST_PROVIDER_CREEPAGE::testCreepage( CREEPAGE_GRAPH& aGraph, int aNetCo std::shared_ptr NetB = aGraph.AddNetElements( aNetCodeB, aLayer, creepageValue ); - aGraph.GeneratePaths( creepageValue, aLayer, false ); + aGraph.GeneratePaths( creepageValue, aLayer ); std::vector> temp_nodes; @@ -347,7 +347,7 @@ int DRC_TEST_PROVIDER_CREEPAGE::testCreepage() graph.RemoveDuplicatedShapes(); graph.TransformCreepShapesToNodes( graph.m_shapeCollection ); - graph.GeneratePaths( maxConstraint, Edge_Cuts, false ); + graph.GeneratePaths( maxConstraint, Edge_Cuts ); int beNodeSize = graph.m_nodes.size(); int beConnectionsSize = graph.m_connections.size();