mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 02:03:12 +02:00
Optimize zone-zone clearance checks
Improve the CREEPAGE_GRAPH:::GeneratePaths to skip unused checks. Handle zone-zone paralellism better Fixes https://gitlab.com/kicad/code/kicad/-/issues/21353
This commit is contained in:
parent
4c03ab8ebb
commit
faeaee824a
@ -2245,7 +2245,7 @@ void CREEPAGE_GRAPH::Addshape( const SHAPE& aShape, std::shared_ptr<GRAPH_NODE>&
|
||||
}
|
||||
}
|
||||
|
||||
void CREEPAGE_GRAPH::GeneratePaths( double aMaxWeight, PCB_LAYER_ID aLayer, bool aClearance )
|
||||
void CREEPAGE_GRAPH::GeneratePaths( double aMaxWeight, PCB_LAYER_ID aLayer )
|
||||
{
|
||||
std::vector<std::shared_ptr<GRAPH_NODE>> 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
|
||||
{
|
||||
for( size_t ii = i; ii < j; ii++ )
|
||||
{
|
||||
std::shared_ptr<GRAPH_NODE> gn1 = nodes[ii];
|
||||
// Build parent -> net -> nodes mapping for efficient filtering
|
||||
std::unordered_map<const BOARD_ITEM*, std::unordered_map<int, std::vector<std::shared_ptr<GRAPH_NODE>>>> parent_net_groups;
|
||||
std::vector<const BOARD_ITEM*> parent_keys;
|
||||
|
||||
for( size_t jj = ii + 1; jj < nodes.size(); jj++ )
|
||||
for( const auto& gn : nodes )
|
||||
{
|
||||
std::shared_ptr<GRAPH_NODE> gn2 = nodes[jj];
|
||||
const BOARD_ITEM* parent = gn->m_parent->GetParent();
|
||||
|
||||
if( gn1->m_parent->GetParent() == gn2->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<GRAPH_NODE> node1, node2;
|
||||
};
|
||||
std::vector<WorkItem> 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 )
|
||||
{
|
||||
for( const auto& [net2, nodes2] : group2_nets )
|
||||
{
|
||||
// Skip if same net and both nets have only conductive nodes
|
||||
if( net1 == net2 )
|
||||
{
|
||||
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( all_conductive_1 && all_conductive_2 )
|
||||
continue;
|
||||
}
|
||||
|
||||
if( ( gn1->m_net == gn2->m_net ) && ( gn1->m_parent->IsConductive() )
|
||||
&& ( gn2->m_parent->IsConductive() ) )
|
||||
continue;
|
||||
// Add all node pairs from these net groups
|
||||
for( const auto& gn1 : nodes1 )
|
||||
for( const auto& gn2 : nodes2 )
|
||||
work_items.push_back( { gn1, gn2 } );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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<const BOARD_ITEM*> IgnoreForTest;
|
||||
IgnoreForTest.push_back( gn1->m_parent->GetParent() );
|
||||
IgnoreForTest.push_back( gn2->m_parent->GetParent() );
|
||||
std::vector<const BOARD_ITEM*> 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<GRAPH_NODE>* connect1 = &gn1;
|
||||
std::shared_ptr<GRAPH_NODE>* connect2 = &gn2;
|
||||
std::shared_ptr<GRAPH_NODE> gnt1 = nullptr;
|
||||
std::shared_ptr<GRAPH_NODE> gnt2 = nullptr;
|
||||
std::shared_ptr<GRAPH_NODE> connect1 = gn1, connect2 = gn2;
|
||||
std::lock_guard<std::mutex> lock( nodes_lock );
|
||||
|
||||
// Handle non-point node1
|
||||
if( gn1->m_parent->GetType() != CREEP_SHAPE::TYPE::POINT )
|
||||
{
|
||||
gnt1 = AddNode( GRAPH_NODE::POINT, gn1->m_parent, pc.a1 );
|
||||
auto gnt1 = AddNode( GRAPH_NODE::POINT, gn1->m_parent, pc.a1 );
|
||||
gnt1->m_connectDirectly = false;
|
||||
connect1 = gnt1;
|
||||
|
||||
if( gn1->m_parent->IsConductive() )
|
||||
{
|
||||
std::shared_ptr<GRAPH_CONNECTION> gc = AddConnection( gn1, gnt1 );
|
||||
|
||||
if( gc )
|
||||
if( auto gc = AddConnection( gn1, gnt1 ) )
|
||||
gc->m_path.m_show = false;
|
||||
}
|
||||
connect1 = &gnt1;
|
||||
}
|
||||
|
||||
// Handle non-point node2
|
||||
if( gn2->m_parent->GetType() != CREEP_SHAPE::TYPE::POINT )
|
||||
{
|
||||
gnt2 = AddNode( GRAPH_NODE::POINT, gn2->m_parent, pc.a2 );
|
||||
auto gnt2 = AddNode( GRAPH_NODE::POINT, gn2->m_parent, pc.a2 );
|
||||
gnt2->m_connectDirectly = false;
|
||||
connect2 = gnt2;
|
||||
|
||||
if( gn2->m_parent->IsConductive() )
|
||||
{
|
||||
std::shared_ptr<GRAPH_CONNECTION> gc = AddConnection( gn2, gnt2 );
|
||||
|
||||
if( gc )
|
||||
if( auto gc = AddConnection( gn2, gnt2 ) )
|
||||
gc->m_path.m_show = false;
|
||||
}
|
||||
connect2 = &gnt2;
|
||||
}
|
||||
|
||||
AddConnection( *connect1, *connect2, pc );
|
||||
AddConnection( connect1, connect2, pc );
|
||||
}
|
||||
}
|
||||
} // for jj
|
||||
} // for ii
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// 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() );
|
||||
// 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<bool>& 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<std::shared_ptr<GRAPH_CONNECTION>> toRemove;
|
||||
|
@ -69,7 +69,7 @@ struct PATH_CONNECTION
|
||||
bool isValid( const BOARD& aBoard, PCB_LAYER_ID aLayer,
|
||||
const std::vector<BOARD_ITEM*>& aBoardEdges,
|
||||
const std::vector<const BOARD_ITEM*>& aIgnoreForTest, SHAPE_POLY_SET* aOutline,
|
||||
const std::pair<bool, bool>& aTestLocalConcavity, int aMinGrooveWidth )
|
||||
const std::pair<bool, bool>& 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<GRAPH_NODE>& aFrom, std::shared_ptr<GRAPH_NODE>& aTo,
|
||||
std::vector<std::shared_ptr<GRAPH_CONNECTION>>& aResult );
|
||||
|
||||
void GeneratePaths( double aMaxWeight, PCB_LAYER_ID aLayer, bool aClearance );
|
||||
void GeneratePaths( double aMaxWeight, PCB_LAYER_ID aLayer );
|
||||
|
||||
std::shared_ptr<GRAPH_NODE> AddNetElements( int aNetCode, PCB_LAYER_ID aLayer, int aMaxCreepage );
|
||||
|
||||
|
@ -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;
|
||||
|
@ -1203,17 +1203,38 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZonesToZones()
|
||||
std::vector<std::map<PCB_LAYER_ID, std::vector<SEG>>> 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<int, int, VECTOR2I, int, DRC_CONSTRAINT, PCB_LAYER_ID>;
|
||||
|
||||
std::vector<std::future<REPORT_DATA>> futures;
|
||||
thread_pool& tp = GetKiCadThreadPool();
|
||||
std::atomic<size_t> done( 1 );
|
||||
std::atomic<size_t> done( 0 );
|
||||
size_t count = 0;
|
||||
|
||||
auto checkZones =
|
||||
[this, testClearance, testIntersects, &poly_segments, &done]
|
||||
( int zoneA_idx, int zoneB_idx, bool sameNet, PCB_LAYER_ID layer ) -> REPORT_DATA
|
||||
auto reportZoneZoneViolation = [this]( ZONE* zoneA, ZONE* zoneB, VECTOR2I& pt, int actual,
|
||||
const DRC_CONSTRAINT& constraint, PCB_LAYER_ID layer ) -> void
|
||||
{
|
||||
std::shared_ptr<DRC_ITEM> 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 );
|
||||
}
|
||||
};
|
||||
|
||||
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];
|
||||
@ -1225,7 +1246,8 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZonesToZones()
|
||||
if( zoneA->Outline()->Collide( zoneB->Outline(), 0, &actual, &pt ) )
|
||||
{
|
||||
done.fetch_add( 1 );
|
||||
return std::make_tuple( zoneA_idx, zoneB_idx, pt, 0, DRC_CONSTRAINT(), layer );
|
||||
reportZoneZoneViolation( zoneA, zoneB, pt, actual, DRC_CONSTRAINT(), layer );
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if( !sameNet && testClearance )
|
||||
@ -1243,23 +1265,12 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZonesToZones()
|
||||
// Iterate through all the segments in zoneA
|
||||
for( SEG& refSegment : refSegments )
|
||||
{
|
||||
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 )
|
||||
if( refSegment.B.x < testSegment.A.x )
|
||||
break;
|
||||
|
||||
int64_t dist_sq = 0;
|
||||
@ -1270,7 +1281,8 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZonesToZones()
|
||||
if( actual < clearance )
|
||||
{
|
||||
done.fetch_add( 1 );
|
||||
return std::make_tuple( zoneA_idx, zoneB_idx, pt, actual, constraint, layer );
|
||||
reportZoneZoneViolation( zoneA, zoneB, pt, actual, constraint, layer );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1278,7 +1290,6 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZonesToZones()
|
||||
}
|
||||
|
||||
done.fetch_add( 1 );
|
||||
return std::make_tuple( -1, -1, VECTOR2I(), 0, DRC_CONSTRAINT(), F_Cu );
|
||||
};
|
||||
|
||||
|
||||
@ -1312,8 +1323,6 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZonesToZones()
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, int>> 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<REPORT_DATA>& task : futures )
|
||||
{
|
||||
if( !task.valid() )
|
||||
continue;
|
||||
|
||||
std::future_status result;
|
||||
|
||||
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<DRC_ITEM> 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 );
|
||||
}
|
||||
}
|
||||
|
||||
if( tp.wait_for_tasks_duration( std::chrono::milliseconds( 250 ) ) )
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
@ -137,7 +137,7 @@ int DRC_TEST_PROVIDER_CREEPAGE::testCreepage( CREEPAGE_GRAPH& aGraph, int aNetCo
|
||||
std::shared_ptr<GRAPH_NODE> NetB = aGraph.AddNetElements( aNetCodeB, aLayer, creepageValue );
|
||||
|
||||
|
||||
aGraph.GeneratePaths( creepageValue, aLayer, false );
|
||||
aGraph.GeneratePaths( creepageValue, aLayer );
|
||||
|
||||
std::vector<std::shared_ptr<GRAPH_NODE>> 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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user