More performance enhancements for DRC.

This commit is contained in:
Jeff Young 2022-06-18 19:47:11 +01:00
parent 5da817649b
commit 82ebc247b8
11 changed files with 213 additions and 154 deletions

View File

@ -476,7 +476,17 @@ static inline bool Collide( const SHAPE_LINE_CHAIN_BASE& aA, const SHAPE_SEGMENT
static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_RECT& aB, int aClearance,
int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
{
return Collide( aA.Outline(), aB.Outline(), aClearance, aActual, aLocation, aMTV );
if( aClearance || aActual || aLocation || aMTV )
{
return Collide( aA.Outline(), aB.Outline(), aClearance, aActual, aLocation, aMTV );
}
else
{
BOX2I bboxa = aA.BBox();
BOX2I bboxb = aB.BBox();
return bboxa.Intersects( bboxb );
}
}

View File

@ -2,6 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2015 CERN
* Copyright (C) 2015-2022 KiCad Developers, see AUTHORS.txt for contributors.
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* This program is free software; you can redistribute it and/or
@ -25,10 +26,11 @@
#include <geometry/shape_rect.h>
bool SHAPE_RECT::Collide( const SEG& aSeg, int aClearance, int* aActual,
VECTOR2I* aLocation ) const
bool SHAPE_RECT::Collide( const SEG& aSeg, int aClearance, int* aActual, VECTOR2I* aLocation ) const
{
if( BBox( 0 ).Contains( aSeg.A ) )
BOX2I bbox( BBox() );
if( bbox.Contains( aSeg.A ) )
{
if( aLocation )
*aLocation = aSeg.A;
@ -39,7 +41,7 @@ bool SHAPE_RECT::Collide( const SEG& aSeg, int aClearance, int* aActual,
return true;
}
if( BBox( 0 ).Contains( aSeg.B ) )
if( bbox.Contains( aSeg.B ) )
{
if( aLocation )
*aLocation = aSeg.B;

View File

@ -664,25 +664,29 @@ void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( ZONE* aZone, PCB_LAYER_ID
wxLogTrace( wxT( "CN" ), wxT( "Found %u isolated islands\n" ), (unsigned) aIslands.size() );
}
void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones )
void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones,
bool aConnectivityAlreadyRebuilt )
{
int delta = 10; // Number of additions between 2 calls to the progress bar
int ii = 0;
for( CN_ZONE_ISOLATED_ISLAND_LIST& z : aZones )
if( !aConnectivityAlreadyRebuilt )
{
Remove( z.m_zone );
Add( z.m_zone );
ii++;
if( m_progressReporter && ( ii % delta ) == 0 )
for( CN_ZONE_ISOLATED_ISLAND_LIST& z : aZones )
{
m_progressReporter->SetCurrentProgress( (double) ii / (double) aZones.size() );
m_progressReporter->KeepRefreshing( false );
}
Remove( z.m_zone );
Add( z.m_zone );
ii++;
if( m_progressReporter && m_progressReporter->IsCancelled() )
return;
if( m_progressReporter && ( ii % delta ) == 0 )
{
m_progressReporter->SetCurrentProgress( (double) ii / (double) aZones.size() );
m_progressReporter->KeepRefreshing( false );
}
if( m_progressReporter && m_progressReporter->IsCancelled() )
return;
}
}
m_connClusters = SearchClusters( CSM_CONNECTIVITY_CHECK );

View File

@ -224,7 +224,8 @@ public:
*
* @param: aZones is the set of zones to search for islands.
*/
void FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones );
void FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones,
bool aConnectivityAlreadyRebuilt );
const CLUSTERS& GetClusters();

View File

@ -41,6 +41,7 @@
#include <ratsnest/ratsnest_data.h>
#include <progress_reporter.h>
#include <trigo.h>
#include <drc/drc_rtree.h>
CONNECTIVITY_DATA::CONNECTIVITY_DATA()
{
@ -312,9 +313,10 @@ void CONNECTIVITY_DATA::FindIsolatedCopperIslands( ZONE* aZone, std::vector<int>
#endif
}
void CONNECTIVITY_DATA::FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones )
void CONNECTIVITY_DATA::FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones,
bool aConnectivityAlreadyRebuilt )
{
m_connAlgo->FindIsolatedCopperIslands( aZones );
m_connAlgo->FindIsolatedCopperIslands( aZones, aConnectivityAlreadyRebuilt );
}
@ -740,14 +742,32 @@ bool CONNECTIVITY_DATA::TestTrackEndpointDangling( PCB_TRACK* aTrack, VECTOR2I*
for( CN_ITEM* connected : citem->ConnectedItems() )
{
BOARD_CONNECTED_ITEM* item = connected->Parent();
ZONE* zone = dynamic_cast<ZONE*>( item );
DRC_RTREE* rtree = nullptr;
bool hitStart = false;
bool hitEnd = false;
if( item->GetFlags() & IS_DELETED )
continue;
std::shared_ptr<SHAPE> shape = item->GetEffectiveShape( layer );
if( zone )
rtree = zone->GetBoard()->m_CopperZoneRTreeCache[ zone ].get();
bool hitStart = shape->Collide( aTrack->GetStart(), accuracy );
bool hitEnd = shape->Collide( aTrack->GetEnd(), accuracy );
if( rtree )
{
SHAPE_CIRCLE start( aTrack->GetStart(), accuracy );
SHAPE_CIRCLE end( aTrack->GetEnd(), accuracy );
hitStart = rtree->QueryColliding( start.BBox(), &start, layer );
hitEnd = rtree->QueryColliding( end.BBox(), &end, layer );
}
else
{
std::shared_ptr<SHAPE> shape = item->GetEffectiveShape( layer );
hitStart = shape->Collide( aTrack->GetStart(), accuracy );
hitEnd = shape->Collide( aTrack->GetEnd(), accuracy );
}
if( hitStart && hitEnd )
{

View File

@ -185,7 +185,8 @@ public:
* @param aIslands list of islands that have no connections (outline indices in the polygon set)
*/
void FindIsolatedCopperIslands( ZONE* aZone, std::vector<int>& aIslands );
void FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones );
void FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones,
bool aConnectivityAlreadyRebuilt = false );
/**
* Function RecalculateRatsnest()

View File

@ -85,7 +85,7 @@ bool DRC_TEST_PROVIDER_CONNECTIVITY::Run()
// Rebuild just in case. This really needs to be reliable.
connectivity->Clear();
connectivity->Build( board, m_drcEngine->GetProgressReporter() );
connectivity->FindIsolatedCopperIslands( islandsList );
connectivity->FindIsolatedCopperIslands( islandsList, true );
int delta = 100; // This is the number of tests between 2 calls to the progress bar
int ii = 0;

View File

@ -94,7 +94,6 @@ private:
std::unique_ptr<DRC_RTREE> m_tesselatedTree;
std::unique_ptr<DRC_RTREE> m_itemTree;
std::vector<ZONE*> m_copperZones;
std::map< std::tuple<BOARD_ITEM*, BOARD_ITEM*, PCB_LAYER_ID>, int> m_checkedPairs;
@ -121,9 +120,6 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::addItemToRTrees( BOARD_ITEM* item )
SHAPE_POLY_SET::PM_FAST );
}
}
if( zone->IsOnCopperLayer() && !zone->GetIsRuleArea() )
m_copperZones.push_back( zone );
}
else if( item->Type() == PCB_PAD_T )
{
@ -188,10 +184,6 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::buildRTrees()
m_tesselatedTree = std::make_unique<DRC_RTREE>();
m_itemTree = std::make_unique<DRC_RTREE>();
m_copperZones.clear();
// Unlikely to be correct, but better than starting at 0
m_copperZones.reserve( m_board->Zones().size() );
forEachGeometryItem( s_allBasicItems, layers,
[&]( BOARD_ITEM* item ) -> bool
@ -495,7 +487,7 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::testMaskItemAgainstZones( BOARD_ITEM* aItem,
PCB_LAYER_ID aMaskLayer,
PCB_LAYER_ID aTargetLayer )
{
for( ZONE* zone : m_copperZones )
for( ZONE* zone : m_board->m_DRCCopperZones )
{
if( !zone->GetLayerSet().test( aTargetLayer ) )
continue;

View File

@ -21,6 +21,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <atomic>
#include <thread>
#include <board.h>
#include <board_design_settings.h>
#include <connectivity/connectivity_data.h>
@ -65,132 +67,163 @@ public:
{
return wxT( "Checks thermal reliefs for a sufficient number of connecting spokes" );
}
private:
void testZoneLayer( ZONE* aZone, PCB_LAYER_ID aLayer );
};
bool DRC_TEST_PROVIDER_ZONE_CONNECTIONS::Run()
{
const int delta = 5; // This is the number of tests between 2 calls to the progress bar
int ii = 0;
void DRC_TEST_PROVIDER_ZONE_CONNECTIONS::testZoneLayer( ZONE* aZone, PCB_LAYER_ID aLayer )
{
BOARD* board = m_drcEngine->GetBoard();
BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
std::shared_ptr<CONNECTIVITY_DATA> connectivity = board->GetConnectivity();
DRC_CONSTRAINT constraint;
const std::shared_ptr<SHAPE_POLY_SET>& zoneFill = aZone->GetFilledPolysList( aLayer );
for( FOOTPRINT* footprint : board->Footprints() )
{
for( PAD* pad : footprint->Pads() )
{
if( m_drcEngine->IsErrorLimitExceeded( DRCE_STARVED_THERMAL ) )
return;
if( m_drcEngine->IsCancelled() )
return;
// Quick tests for "connected":
//
if( !pad->FlashLayer( aLayer ) )
continue;
if( pad->GetNetCode() != aZone->GetNetCode() || pad->GetNetCode() <= 0 )
continue;
EDA_RECT item_boundingbox = pad->GetBoundingBox();
if( !item_boundingbox.Intersects( aZone->GetCachedBoundingBox() ) )
continue;
// If those passed, do a thorough test:
//
constraint = bds.m_DRCEngine->EvalZoneConnection( pad, aZone, aLayer );
ZONE_CONNECTION conn = constraint.m_ZoneConnection;
if( conn != ZONE_CONNECTION::THERMAL )
continue;
constraint = bds.m_DRCEngine->EvalRules( MIN_RESOLVED_SPOKES_CONSTRAINT, pad, aZone,
aLayer );
int minCount = constraint.m_Value.Min();
if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE || minCount <= 0 )
continue;
SHAPE_POLY_SET padPoly;
pad->TransformShapeWithClearanceToPolygon( padPoly, aLayer, 0, ARC_LOW_DEF,
ERROR_OUTSIDE );
SHAPE_LINE_CHAIN& padOutline = padPoly.Outline( 0 );
std::vector<SHAPE_LINE_CHAIN::INTERSECTION> intersections;
int spokes = 0;
for( int jj = 0; jj < zoneFill->OutlineCount(); ++jj )
padOutline.Intersect( zoneFill->Outline( jj ), intersections, true );
spokes += intersections.size() / 2;
if( spokes <= 0 )
continue;
// Now we know we're connected, so see if there are any other manual spokes
// added:
//
for( PCB_TRACK* track : connectivity->GetConnectedTracks( pad ) )
{
if( padOutline.PointInside( track->GetStart() ) )
{
if( aZone->GetFilledPolysList( aLayer )->Collide( track->GetEnd() ) )
spokes++;
}
else if( padOutline.PointInside( track->GetEnd() ) )
{
if( aZone->GetFilledPolysList( aLayer )->Collide( track->GetStart() ) )
spokes++;
}
}
// And finally report it if there aren't enough:
//
if( spokes < minCount )
{
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_STARVED_THERMAL );
wxString msg;
msg.Printf( _( "(%s min spoke count %d; actual %d)" ),
constraint.GetName(),
minCount,
spokes );
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
drce->SetItems( aZone, pad );
drce->SetViolatingRule( constraint.GetParentRule() );
reportViolation( drce, pad->GetPosition(), UNDEFINED_LAYER );
}
}
}
}
bool DRC_TEST_PROVIDER_ZONE_CONNECTIONS::Run()
{
BOARD* board = m_drcEngine->GetBoard();
BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
std::shared_ptr<CONNECTIVITY_DATA> connectivity = board->GetConnectivity();
DRC_CONSTRAINT constraint;
std::vector<ZONE*> zones;
if( !reportPhase( _( "Checking thermal reliefs..." ) ) )
return false; // DRC cancelled
for( ZONE* zone : board->Zones() )
zones.push_back( zone );
std::vector< std::pair<ZONE*, PCB_LAYER_ID> > zoneLayers;
for( FOOTPRINT* footprint : board->Footprints() )
for( ZONE* zone : board->m_DRCCopperZones )
{
for( ZONE* zone : footprint->Zones() )
zones.push_back( zone );
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
zoneLayers.push_back( { zone, layer } );
}
for( ZONE* zone : zones )
int zoneLayerCount = zoneLayers.size();
std::atomic<size_t> next( 0 );
std::atomic<size_t> done( 1 );
std::atomic<size_t> threads_finished( 0 );
size_t parallelThreadCount = std::max<size_t>( std::thread::hardware_concurrency(), 2 );
for( size_t ii = 0; ii < parallelThreadCount; ++ii )
{
if( !reportProgress( ii++, zones.size(), delta ) )
return false;
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
{
const std::shared_ptr<SHAPE_POLY_SET>& zoneFill = zone->GetFilledPolysList( layer );
for( FOOTPRINT* footprint : board->Footprints() )
{
for( PAD* pad : footprint->Pads() )
std::thread t = std::thread(
[&]( )
{
if( m_drcEngine->IsErrorLimitExceeded( DRCE_STARVED_THERMAL ) )
return true;
if( m_drcEngine->IsCancelled() )
return false;
// Quick tests for "connected":
//
if( !pad->FlashLayer( layer ) )
continue;
if( pad->GetNetCode() != zone->GetNetCode() || pad->GetNetCode() <= 0 )
continue;
EDA_RECT item_boundingbox = pad->GetBoundingBox();
if( !item_boundingbox.Intersects( zone->GetCachedBoundingBox() ) )
continue;
// If those passed, do a thorough test:
//
constraint = bds.m_DRCEngine->EvalZoneConnection( pad, zone, layer );
ZONE_CONNECTION conn = constraint.m_ZoneConnection;
if( conn != ZONE_CONNECTION::THERMAL )
continue;
constraint = bds.m_DRCEngine->EvalRules( MIN_RESOLVED_SPOKES_CONSTRAINT,
pad, zone, layer );
int minCount = constraint.m_Value.Min();
if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE || minCount <= 0 )
continue;
SHAPE_POLY_SET padPoly;
pad->TransformShapeWithClearanceToPolygon( padPoly, layer, 0, ARC_LOW_DEF,
ERROR_OUTSIDE );
SHAPE_LINE_CHAIN& padOutline = padPoly.Outline( 0 );
std::vector<SHAPE_LINE_CHAIN::INTERSECTION> intersections;
int spokes = 0;
for( int jj = 0; jj < zoneFill->OutlineCount(); ++jj )
padOutline.Intersect( zoneFill->Outline( jj ), intersections, true );
spokes += intersections.size() / 2;
if( spokes <= 0 )
continue;
// Now we know we're connected, so see if there are any other manual spokes
// added:
//
for( PCB_TRACK* track : connectivity->GetConnectedTracks( pad ) )
for( int i = next.fetch_add( 1 ); i < zoneLayerCount; i = next.fetch_add( 1 ) )
{
if( padOutline.PointInside( track->GetStart() ) )
{
if( zone->GetFilledPolysList( layer )->Collide( track->GetEnd() ) )
spokes++;
}
else if( padOutline.PointInside( track->GetEnd() ) )
{
if( zone->GetFilledPolysList( layer )->Collide( track->GetStart() ) )
spokes++;
}
testZoneLayer( zoneLayers[i].first, zoneLayers[i].second );
done.fetch_add( 1 );
if( m_drcEngine->IsCancelled() )
break;
}
// And finally report it if there aren't enough:
//
if( spokes < minCount )
{
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_STARVED_THERMAL );
wxString msg;
threads_finished.fetch_add( 1 );
} );
msg.Printf( _( "(%s min spoke count %d; actual %d)" ),
constraint.GetName(),
minCount,
spokes );
t.detach();
}
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
drce->SetItems( zone, pad );
drce->SetViolatingRule( constraint.GetParentRule() );
reportViolation( drce, pad->GetPosition(), UNDEFINED_LAYER );
}
}
}
}
while( threads_finished < parallelThreadCount )
{
m_drcEngine->ReportProgress( (double) done / (double) zoneLayerCount );
std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
}
return !m_drcEngine->IsCancelled();

View File

@ -2343,13 +2343,17 @@ void FOOTPRINT::CheckOverlappingPads( const std::function<void( const PAD*, cons
{
checkedPairs[ { a, b } ] = 1;
VECTOR2I pos;
SHAPE* padShape = pad->GetEffectiveShape().get();
SHAPE* otherShape = other->GetEffectiveShape().get();
if( padShape->Collide( otherShape, 0, nullptr, &pos ) )
if( pad->GetBoundingBox().Intersects( other->GetBoundingBox() ) )
{
(aErrorHandler)( pad, other, pos );
VECTOR2I pos;
SHAPE* padShape = pad->GetEffectiveShape().get();
SHAPE* otherShape = other->GetEffectiveShape().get();
if( padShape->Collide( otherShape, 0, nullptr, &pos ) )
{
(aErrorHandler)( pad, other, pos );
}
}
}
}

View File

@ -1284,18 +1284,10 @@ double FP_ZONE::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
std::shared_ptr<SHAPE> ZONE::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
{
std::shared_ptr<SHAPE> shape;
if( m_FilledPolysList.find( aLayer ) == m_FilledPolysList.end() )
{
shape = std::make_shared<SHAPE_NULL>();
}
return std::make_shared<SHAPE_NULL>();
else
{
shape.reset( m_FilledPolysList.at( aLayer )->Clone() );
}
return shape;
return m_FilledPolysList.at( aLayer );
}