Implement a more durable zone bounding box caching strategy.

Fixes https://gitlab.com/kicad/code/kicad/issues/10821
This commit is contained in:
Jeff Young 2022-10-01 22:09:38 +01:00
parent 6d298b661a
commit e49de68a59
12 changed files with 55 additions and 37 deletions

View File

@ -232,6 +232,7 @@ void BOARD::IncrementTimeStamp()
m_DRCCopperZones.clear();
m_CopperZoneRTreeCache.clear();
m_CopperItemRTreeCache = std::make_unique<DRC_RTREE>();
m_ZoneBBoxCache.clear();
}
}

View File

@ -1152,6 +1152,8 @@ public:
std::unordered_map<ZONE*, std::unique_ptr<DRC_RTREE>> m_CopperZoneRTreeCache;
std::unique_ptr<DRC_RTREE> m_CopperItemRTreeCache;
mutable std::unordered_map<const ZONE*, BOX2I> m_ZoneBBoxCache;
// ------------ DRC caches -------------
std::vector<ZONE*> m_DRCZones;
std::vector<ZONE*> m_DRCCopperZones;

View File

@ -149,7 +149,7 @@ void BOARD_COMMIT::dirtyIntersectingZones( BOARD_ITEM* item )
continue;
if( ( zone->GetLayerSet() & layers ).any()
&& zone->GetCachedBoundingBox().Intersects( bbox ) )
&& zone->GetBoundingBox().Intersects( bbox ) )
{
zoneFillerTool->DirtyZone( zone );
}

View File

@ -314,7 +314,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testItemAgainstZone( BOARD_ITEM* aItem,
worstCaseBBox.Inflate( m_board->m_DRCMaxClearance );
if( !worstCaseBBox.Intersects( aZone->GetCachedBoundingBox() ) )
if( !worstCaseBBox.Intersects( aZone->GetBoundingBox() ) )
return;
bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );

View File

@ -116,8 +116,8 @@ bool DRC_TEST_PROVIDER_DISALLOW::Run()
ZONE* ruleArea = areaZonePair.first;
ZONE* copperZone = areaZonePair.second;
BOX2I areaBBox = ruleArea->GetCachedBoundingBox();
BOX2I copperBBox = copperZone->GetCachedBoundingBox();
BOX2I areaBBox = ruleArea->GetBoundingBox();
BOX2I copperBBox = copperZone->GetBoundingBox();
bool isInside = false;
if( copperZone->IsFilled() && areaBBox.Intersects( copperBBox ) )

View File

@ -706,7 +706,7 @@ void DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstZones( BOARD_ITEM* aIt
worstCaseBBox.Inflate( m_board->m_DRCMaxClearance );
if( !worstCaseBBox.Intersects( zone->GetCachedBoundingBox() ) )
if( !worstCaseBBox.Intersects( zone->GetBoundingBox() ) )
continue;
bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );

View File

@ -582,7 +582,7 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::testMaskItemAgainstZones( BOARD_ITEM* aItem,
inflatedBBox.Inflate( clearance );
if( !inflatedBBox.Intersects( zone->GetCachedBoundingBox() ) )
if( !inflatedBBox.Intersects( zone->GetBoundingBox() ) )
continue;
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ zone ].get();

View File

@ -101,7 +101,7 @@ void DRC_TEST_PROVIDER_ZONE_CONNECTIONS::testZoneLayer( ZONE* aZone, PCB_LAYER_I
BOX2I item_bbox = pad->GetBoundingBox();
if( !item_bbox.Intersects( aZone->GetCachedBoundingBox() ) )
if( !item_bbox.Intersects( aZone->GetBoundingBox() ) )
continue;
if( !pad->FlashLayer( aLayer ) )

View File

@ -406,7 +406,7 @@ static void intersectsBackCourtyardFunc( LIBEVAL::CONTEXT* aCtx, void* self )
bool collidesWithArea( BOARD_ITEM* aItem, PCB_EXPR_CONTEXT* aCtx, ZONE* aArea )
{
BOARD* board = aArea->GetBoard();
BOX2I areaBBox = aArea->GetCachedBoundingBox();
BOX2I areaBBox = aArea->GetBoundingBox();
std::shared_ptr<SHAPE> shape;
// Collisions include touching, so we need to deflate outline by enough to exclude it.
@ -621,12 +621,7 @@ static void intersectsAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
{
BOARD* board = item->GetBoard();
PCB_LAYER_ID layer = context->GetLayer();
BOX2I itemBBox;
if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
itemBBox = static_cast<ZONE*>( item )->GetCachedBoundingBox();
else
itemBBox = item->GetBoundingBox();
BOX2I itemBBox = item->GetBoundingBox();
if( searchAreas( board, arg->AsString(), context,
[&]( ZONE* aArea )
@ -634,7 +629,7 @@ static void intersectsAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
if( !aArea || aArea == item || aArea->GetParent() == item )
return false;
if( !aArea->GetCachedBoundingBox().Intersects( itemBBox ) )
if( !aArea->GetBoundingBox().Intersects( itemBBox ) )
return false;
std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );
@ -689,12 +684,7 @@ static void enclosedByAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
BOARD* board = item->GetBoard();
int maxError = board->GetDesignSettings().m_MaxError;
PCB_LAYER_ID layer = context->GetLayer();
BOX2I itemBBox;
if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
itemBBox = static_cast<ZONE*>( item )->GetCachedBoundingBox();
else
itemBBox = item->GetBoundingBox();
BOX2I itemBBox = item->GetBoundingBox();
if( searchAreas( board, arg->AsString(), context,
[&]( ZONE* aArea )
@ -702,7 +692,7 @@ static void enclosedByAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
if( !aArea || aArea == item || aArea->GetParent() == item )
return false;
if( !aArea->GetCachedBoundingBox().Intersects( itemBBox ) )
if( !aArea->GetBoundingBox().Intersects( itemBBox ) )
return false;
std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );

View File

@ -308,10 +308,34 @@ bool ZONE::IsOnLayer( PCB_LAYER_ID aLayer ) const
const BOX2I ZONE::GetBoundingBox() const
{
if( const BOARD* parent = GetBoard() )
{
std::unordered_map<const ZONE*, BOX2I>& cache = parent->m_ZoneBBoxCache;
auto cacheIter = cache.find( this );
if( cacheIter != cache.end() )
return cacheIter->second;
BOX2I bbox = m_Poly->BBox();
cache[ this ] = bbox;
return bbox;
}
return m_Poly->BBox();
}
void ZONE::CacheBoundingBox()
{
std::unordered_map<const ZONE*, BOX2I>& cache = GetBoard()->m_ZoneBBoxCache;
auto cacheIter = cache.find( this );
if( cacheIter == cache.end() )
cache[ this ] = m_Poly->BBox();
}
int ZONE::GetThermalReliefGap( PAD* aPad, wxString* aSource ) const
{
if( aPad->GetLocalThermalGapOverride() == 0 )
@ -972,7 +996,7 @@ bool ZONE::IsIsland( PCB_LAYER_ID aLayer, int aPolyIdx ) const
void ZONE::GetInteractingZones( PCB_LAYER_ID aLayer, std::vector<ZONE*>* aZones ) const
{
int epsilon = pcbIUScale.mmToIU( 0.001 );
BOX2I bbox = GetCachedBoundingBox();
BOX2I bbox = GetBoundingBox();
bbox.Inflate( epsilon );
@ -990,7 +1014,7 @@ void ZONE::GetInteractingZones( PCB_LAYER_ID aLayer, std::vector<ZONE*>* aZones
if( candidate->GetNetCode() != GetNetCode() )
continue;
if( !candidate->GetCachedBoundingBox().Intersects( bbox ) )
if( !candidate->GetBoundingBox().Intersects( bbox ) )
continue;
for( auto iter = m_Poly->CIterate(); iter; iter++ )

View File

@ -128,10 +128,10 @@ public:
const BOX2I GetBoundingBox() const override;
/**
* ONLY TO BE USED BY CLIENTS WHICH SET UP THE CACHE!
* Used to preload the zone bounding box cache so we don't have to worry about mutex-locking
* it each time.
*/
const BOX2I GetCachedBoundingBox() const { return m_bboxCache; }
void CacheBoundingBox() { m_bboxCache = GetBoundingBox(); }
void CacheBoundingBox();
/**
* Return any local clearances set in the "classic" (ie: pre-rule) system. These are
@ -869,7 +869,6 @@ protected:
std::map<PCB_LAYER_ID, std::shared_ptr<SHAPE_POLY_SET>> m_FilledPolysList;
/// Temp variables used while filling
BOX2I m_bboxCache;
LSET m_fillFlags;
/// A hash value used in zone filling calculations to see if the filled areas are up to date

View File

@ -216,10 +216,10 @@ bool ZONE_FILLER::Fill( std::vector<ZONE*>& aZones, bool aCheck, wxWindow* aPare
// A higher priority zone is found: if we intersect and it's not filled yet
// then we have to wait.
BOX2I inflatedBBox = aZone->GetCachedBoundingBox();
BOX2I inflatedBBox = aZone->GetBoundingBox();
inflatedBBox.Inflate( m_worstClearance );
if( !inflatedBBox.Intersects( aOtherZone->GetCachedBoundingBox() ) )
if( !inflatedBBox.Intersects( aOtherZone->GetBoundingBox() ) )
return false;
return aZone->Outline()->Collide( aOtherZone->Outline(), m_worstClearance );
@ -619,7 +619,7 @@ void ZONE_FILLER::knockoutThermalReliefs( const ZONE* aZone, PCB_LAYER_ID aLayer
BOX2I padBBox = pad->GetBoundingBox();
padBBox.Inflate( m_worstClearance );
if( !padBBox.Intersects( aZone->GetCachedBoundingBox() ) )
if( !padBBox.Intersects( aZone->GetBoundingBox() ) )
continue;
if( pad->GetNetCode() != aZone->GetNetCode() || pad->GetNetCode() <= 0 )
@ -716,7 +716,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLa
// A small extra clearance to be sure actual track clearances are not smaller than
// requested clearance due to many approximations in calculations, like arc to segment
// approx, rounding issues, etc.
BOX2I zone_boundingbox = aZone->GetCachedBoundingBox();
BOX2I zone_boundingbox = aZone->GetBoundingBox();
int extra_margin = pcbIUScale.mmToIU( ADVANCED_CFG::GetCfg().m_ExtraClearance );
// Items outside the zone bounding box are skipped, so it needs to be inflated by the
@ -958,7 +958,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLa
if( !aKnockout->GetLayerSet().test( aLayer ) )
return;
if( aKnockout->GetCachedBoundingBox().Intersects( zone_boundingbox ) )
if( aKnockout->GetBoundingBox().Intersects( zone_boundingbox ) )
{
if( aKnockout->GetIsRuleArea() )
{
@ -1032,6 +1032,8 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLa
void ZONE_FILLER::subtractHigherPriorityZones( const ZONE* aZone, PCB_LAYER_ID aLayer,
SHAPE_POLY_SET& aRawFill )
{
BOX2I zoneBBox = aZone->GetBoundingBox();
auto knockoutZoneOutline =
[&]( ZONE* aKnockout )
{
@ -1039,7 +1041,7 @@ void ZONE_FILLER::subtractHigherPriorityZones( const ZONE* aZone, PCB_LAYER_ID a
if( !aKnockout->GetLayerSet().test( aLayer ) )
return;
if( aKnockout->GetCachedBoundingBox().Intersects( aZone->GetCachedBoundingBox() ) )
if( aKnockout->GetBoundingBox().Intersects( zoneBBox ) )
{
// Processing of arc shapes in zones is not yet supported because Clipper
// can't do boolean operations on them. The poly outline must be converted to
@ -1310,7 +1312,7 @@ bool ZONE_FILLER::fillNonCopperZone( const ZONE* aZone, PCB_LAYER_ID aLayer,
SHAPE_POLY_SET& aFillPolys )
{
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
BOX2I zone_boundingbox = aZone->GetCachedBoundingBox();
BOX2I zone_boundingbox = aZone->GetBoundingBox();
SHAPE_POLY_SET clearanceHoles;
long ticker = 0;
@ -1428,7 +1430,7 @@ void ZONE_FILLER::buildThermalSpokes( const ZONE* aZone, PCB_LAYER_ID aLayer,
std::deque<SHAPE_LINE_CHAIN>& aSpokesList )
{
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
BOX2I zoneBB = aZone->GetCachedBoundingBox();
BOX2I zoneBB = aZone->GetBoundingBox();
DRC_CONSTRAINT constraint;
zoneBB.Inflate( std::max( bds.GetBiggestClearanceValue(), aZone->GetLocalClearance() ) );
@ -1778,7 +1780,7 @@ bool ZONE_FILLER::addHatchFillTypeOnZone( const ZONE* aZone, PCB_LAYER_ID aLayer
// one of the holes. Effectively this means their copper outline needs to be expanded
// to be at least as wide as the gap so that it is guaranteed to touch at least one
// edge.
BOX2I zone_boundingbox = aZone->GetCachedBoundingBox();
BOX2I zone_boundingbox = aZone->GetBoundingBox();
SHAPE_POLY_SET aprons;
int min_apron_radius = ( aZone->GetHatchGap() * 10 ) / 19;