From 82ebc247b82ba5a34817d3a75a99ec08b95804cd Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Sat, 18 Jun 2022 19:47:11 +0100 Subject: [PATCH] More performance enhancements for DRC. --- libs/kimath/src/geometry/shape_collisions.cpp | 12 +- libs/kimath/src/geometry/shape_rect.cpp | 10 +- pcbnew/connectivity/connectivity_algo.cpp | 28 +- pcbnew/connectivity/connectivity_algo.h | 3 +- pcbnew/connectivity/connectivity_data.cpp | 30 ++- pcbnew/connectivity/connectivity_data.h | 3 +- pcbnew/drc/drc_test_provider_connectivity.cpp | 2 +- pcbnew/drc/drc_test_provider_solder_mask.cpp | 10 +- .../drc_test_provider_zone_connections.cpp | 241 ++++++++++-------- pcbnew/footprint.cpp | 16 +- pcbnew/zone.cpp | 12 +- 11 files changed, 213 insertions(+), 154 deletions(-) diff --git a/libs/kimath/src/geometry/shape_collisions.cpp b/libs/kimath/src/geometry/shape_collisions.cpp index d04ddbde35..4a34931b7d 100644 --- a/libs/kimath/src/geometry/shape_collisions.cpp +++ b/libs/kimath/src/geometry/shape_collisions.cpp @@ -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 ); + } } diff --git a/libs/kimath/src/geometry/shape_rect.cpp b/libs/kimath/src/geometry/shape_rect.cpp index 3f609914d6..d2f9664316 100644 --- a/libs/kimath/src/geometry/shape_rect.cpp +++ b/libs/kimath/src/geometry/shape_rect.cpp @@ -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 * * This program is free software; you can redistribute it and/or @@ -25,10 +26,11 @@ #include -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; diff --git a/pcbnew/connectivity/connectivity_algo.cpp b/pcbnew/connectivity/connectivity_algo.cpp index fd8109afda..e52fafd65f 100644 --- a/pcbnew/connectivity/connectivity_algo.cpp +++ b/pcbnew/connectivity/connectivity_algo.cpp @@ -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& aZones ) +void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( std::vector& 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 ); diff --git a/pcbnew/connectivity/connectivity_algo.h b/pcbnew/connectivity/connectivity_algo.h index 1e7a540f71..1d098c8d09 100644 --- a/pcbnew/connectivity/connectivity_algo.h +++ b/pcbnew/connectivity/connectivity_algo.h @@ -224,7 +224,8 @@ public: * * @param: aZones is the set of zones to search for islands. */ - void FindIsolatedCopperIslands( std::vector& aZones ); + void FindIsolatedCopperIslands( std::vector& aZones, + bool aConnectivityAlreadyRebuilt ); const CLUSTERS& GetClusters(); diff --git a/pcbnew/connectivity/connectivity_data.cpp b/pcbnew/connectivity/connectivity_data.cpp index dfae84c101..4460c9b58f 100644 --- a/pcbnew/connectivity/connectivity_data.cpp +++ b/pcbnew/connectivity/connectivity_data.cpp @@ -41,6 +41,7 @@ #include #include #include +#include CONNECTIVITY_DATA::CONNECTIVITY_DATA() { @@ -312,9 +313,10 @@ void CONNECTIVITY_DATA::FindIsolatedCopperIslands( ZONE* aZone, std::vector #endif } -void CONNECTIVITY_DATA::FindIsolatedCopperIslands( std::vector& aZones ) +void CONNECTIVITY_DATA::FindIsolatedCopperIslands( std::vector& 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( item ); + DRC_RTREE* rtree = nullptr; + bool hitStart = false; + bool hitEnd = false; if( item->GetFlags() & IS_DELETED ) continue; - std::shared_ptr 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 = item->GetEffectiveShape( layer ); + + hitStart = shape->Collide( aTrack->GetStart(), accuracy ); + hitEnd = shape->Collide( aTrack->GetEnd(), accuracy ); + } if( hitStart && hitEnd ) { diff --git a/pcbnew/connectivity/connectivity_data.h b/pcbnew/connectivity/connectivity_data.h index 3ef5548c93..978068fe71 100644 --- a/pcbnew/connectivity/connectivity_data.h +++ b/pcbnew/connectivity/connectivity_data.h @@ -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& aIslands ); - void FindIsolatedCopperIslands( std::vector& aZones ); + void FindIsolatedCopperIslands( std::vector& aZones, + bool aConnectivityAlreadyRebuilt = false ); /** * Function RecalculateRatsnest() diff --git a/pcbnew/drc/drc_test_provider_connectivity.cpp b/pcbnew/drc/drc_test_provider_connectivity.cpp index 74ecce5ea0..21b17cb9dc 100644 --- a/pcbnew/drc/drc_test_provider_connectivity.cpp +++ b/pcbnew/drc/drc_test_provider_connectivity.cpp @@ -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; diff --git a/pcbnew/drc/drc_test_provider_solder_mask.cpp b/pcbnew/drc/drc_test_provider_solder_mask.cpp index b1e8401211..5f55bfd254 100644 --- a/pcbnew/drc/drc_test_provider_solder_mask.cpp +++ b/pcbnew/drc/drc_test_provider_solder_mask.cpp @@ -94,7 +94,6 @@ private: std::unique_ptr m_tesselatedTree; std::unique_ptr m_itemTree; - std::vector m_copperZones; std::map< std::tuple, 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(); m_itemTree = std::make_unique(); - 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; diff --git a/pcbnew/drc/drc_test_provider_zone_connections.cpp b/pcbnew/drc/drc_test_provider_zone_connections.cpp index 8f4db972ea..0b6f1d34b3 100644 --- a/pcbnew/drc/drc_test_provider_zone_connections.cpp +++ b/pcbnew/drc/drc_test_provider_zone_connections.cpp @@ -21,6 +21,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#include +#include #include #include #include @@ -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 = board->GetConnectivity(); + DRC_CONSTRAINT constraint; + + const std::shared_ptr& 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 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 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 = board->GetConnectivity(); DRC_CONSTRAINT constraint; - std::vector zones; if( !reportPhase( _( "Checking thermal reliefs..." ) ) ) return false; // DRC cancelled - for( ZONE* zone : board->Zones() ) - zones.push_back( zone ); + std::vector< std::pair > 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 next( 0 ); + std::atomic done( 1 ); + std::atomic threads_finished( 0 ); + size_t parallelThreadCount = std::max( 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& 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 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 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(); diff --git a/pcbnew/footprint.cpp b/pcbnew/footprint.cpp index a98acf94bf..4637e49e4f 100644 --- a/pcbnew/footprint.cpp +++ b/pcbnew/footprint.cpp @@ -2343,13 +2343,17 @@ void FOOTPRINT::CheckOverlappingPads( const std::functionGetEffectiveShape().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 ); + } } } } diff --git a/pcbnew/zone.cpp b/pcbnew/zone.cpp index 46cca8de32..5e23464c25 100644 --- a/pcbnew/zone.cpp +++ b/pcbnew/zone.cpp @@ -1284,18 +1284,10 @@ double FP_ZONE::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const std::shared_ptr ZONE::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const { - std::shared_ptr shape; - if( m_FilledPolysList.find( aLayer ) == m_FilledPolysList.end() ) - { - shape = std::make_shared(); - } + return std::make_shared(); else - { - shape.reset( m_FilledPolysList.at( aLayer )->Clone() ); - } - - return shape; + return m_FilledPolysList.at( aLayer ); }