Add clearance cache to improve clearance outline rendering performance
When displaying pad and track clearance outlines, GetOwnClearance() was called repeatedly during rendering, re-evaluating DRC rules on every paint refresh. With complex DRC rules, this caused significant slowdown. Add a lazy-evaluated cache in DRC_ENGINE keyed by (UUID, layer) that stores clearance values. The cache is invalidated when DRC rules change (in InitEngine) or properties change that could affect clearance (net, layer, pad type)
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
|
||||
#include <wx/log.h>
|
||||
|
||||
#include <drc/drc_engine.h>
|
||||
#include <drc/drc_rtree.h>
|
||||
#include <board_design_settings.h>
|
||||
#include <board_commit.h>
|
||||
@@ -1090,6 +1091,13 @@ void BOARD::SetDesignSettings( const BOARD_DESIGN_SETTINGS& aSettings )
|
||||
}
|
||||
|
||||
|
||||
void BOARD::InvalidateClearanceCache( const KIID& aUuid )
|
||||
{
|
||||
if( m_designSettings && m_designSettings->m_DRCEngine )
|
||||
m_designSettings->m_DRCEngine->InvalidateClearanceCache( aUuid );
|
||||
}
|
||||
|
||||
|
||||
int BOARD::GetMaxClearanceValue() const
|
||||
{
|
||||
if( !m_maxClearanceValue.has_value() )
|
||||
|
||||
@@ -778,6 +778,15 @@ public:
|
||||
BOARD_DESIGN_SETTINGS& GetDesignSettings() const;
|
||||
void SetDesignSettings( const BOARD_DESIGN_SETTINGS& aSettings );
|
||||
|
||||
/**
|
||||
* Invalidate the clearance cache for a specific item.
|
||||
*
|
||||
* Called by items when properties that could affect clearance change.
|
||||
*
|
||||
* @param aUuid the UUID of the item to invalidate.
|
||||
*/
|
||||
void InvalidateClearanceCache( const KIID& aUuid );
|
||||
|
||||
BOARD_STACKUP GetStackupOrDefault() const;
|
||||
|
||||
const PAGE_INFO& GetPageSettings() const { return m_paper; }
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <board.h>
|
||||
#include <board_connected_item.h>
|
||||
#include <board_design_settings.h>
|
||||
#include <drc/drc_engine.h>
|
||||
#include <connectivity/connectivity_data.h>
|
||||
#include <lset.h>
|
||||
#include <properties/property_validators.h>
|
||||
@@ -44,6 +45,16 @@ BOARD_CONNECTED_ITEM::BOARD_CONNECTED_ITEM( BOARD_ITEM* aParent, KICAD_T idtype
|
||||
}
|
||||
|
||||
|
||||
void BOARD_CONNECTED_ITEM::SetLayer( PCB_LAYER_ID aLayer )
|
||||
{
|
||||
BOARD_ITEM::SetLayer( aLayer );
|
||||
|
||||
// Invalidate clearance cache since layer can affect clearance rules
|
||||
if( BOARD* board = GetBoard() )
|
||||
board->InvalidateClearanceCache( m_Uuid );
|
||||
}
|
||||
|
||||
|
||||
void BOARD_CONNECTED_ITEM::UnpackNet( const kiapi::board::types::Net& aProto )
|
||||
{
|
||||
if( BOARD* board = GetBoard() )
|
||||
@@ -88,27 +99,20 @@ bool BOARD_CONNECTED_ITEM::SetNetCode( int aNetCode, bool aNoAssert )
|
||||
if( !aNoAssert )
|
||||
wxASSERT( m_netinfo );
|
||||
|
||||
// Invalidate clearance cache since net can affect clearance rules
|
||||
if( board )
|
||||
board->InvalidateClearanceCache( m_Uuid );
|
||||
|
||||
return ( m_netinfo != nullptr );
|
||||
}
|
||||
|
||||
|
||||
int BOARD_CONNECTED_ITEM::GetOwnClearance( PCB_LAYER_ID aLayer, wxString* aSource ) const
|
||||
{
|
||||
DRC_CONSTRAINT constraint;
|
||||
|
||||
if( GetBoard() && GetBoard()->GetDesignSettings().m_DRCEngine )
|
||||
{
|
||||
BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings();
|
||||
|
||||
constraint = bds.m_DRCEngine->EvalRules( CLEARANCE_CONSTRAINT, this, nullptr, aLayer );
|
||||
}
|
||||
|
||||
if( constraint.Value().HasMin() )
|
||||
{
|
||||
if( aSource )
|
||||
*aSource = constraint.GetName();
|
||||
|
||||
return constraint.Value().Min();
|
||||
return bds.m_DRCEngine->GetCachedOwnClearance( this, aLayer, aSource );
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -215,7 +219,7 @@ static struct BOARD_CONNECTED_ITEM_DESC
|
||||
|
||||
// Replace layer property as the properties panel will set a restriction for copper layers
|
||||
// only for BOARD_CONNECTED_ITEM that we don't want to apply to BOARD_ITEM
|
||||
auto layer = new PROPERTY_ENUM<BOARD_CONNECTED_ITEM, PCB_LAYER_ID, BOARD_ITEM>(
|
||||
auto layer = new PROPERTY_ENUM<BOARD_CONNECTED_ITEM, PCB_LAYER_ID>(
|
||||
_HKI( "Layer" ),
|
||||
&BOARD_CONNECTED_ITEM::SetLayer, &BOARD_CONNECTED_ITEM::GetLayer );
|
||||
layer->SetChoices( layerEnum.Choices() );
|
||||
|
||||
@@ -47,6 +47,9 @@ class BOARD_CONNECTED_ITEM : public BOARD_ITEM
|
||||
public:
|
||||
BOARD_CONNECTED_ITEM( BOARD_ITEM* aParent, KICAD_T idtype );
|
||||
|
||||
void SetLayer( PCB_LAYER_ID aLayer ) override;
|
||||
PCB_LAYER_ID GetLayer() const override { return BOARD_ITEM::GetLayer(); }
|
||||
|
||||
// Do not create a copy constructor & operator=.
|
||||
// The ones generated by the compiler are adequate.
|
||||
|
||||
|
||||
@@ -752,6 +752,7 @@ void DRC_ENGINE::InitEngine( const wxFileName& aRulePath )
|
||||
}
|
||||
|
||||
m_constraintMap.clear();
|
||||
m_ownClearanceCache.clear();
|
||||
|
||||
m_board->IncrementTimeStamp(); // Clear board-level caches
|
||||
|
||||
@@ -2221,3 +2222,68 @@ std::vector<BOARD_ITEM*> DRC_ENGINE::GetItemsMatchingCondition( const wxString&
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
|
||||
int DRC_ENGINE::GetCachedOwnClearance( const BOARD_ITEM* aItem, PCB_LAYER_ID aLayer,
|
||||
wxString* aSource )
|
||||
{
|
||||
DRC_OWN_CLEARANCE_CACHE_KEY key{ aItem->m_Uuid, aLayer };
|
||||
|
||||
auto it = m_ownClearanceCache.find( key );
|
||||
|
||||
if( it != m_ownClearanceCache.end() )
|
||||
{
|
||||
// Cache hit. We don't cache the source string since it's rarely requested
|
||||
// and caching it would add complexity.
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// Cache miss - evaluate the constraint
|
||||
DRC_CONSTRAINT_T constraintType = CLEARANCE_CONSTRAINT;
|
||||
|
||||
if( aItem->Type() == PCB_PAD_T )
|
||||
{
|
||||
const PAD* pad = static_cast<const PAD*>( aItem );
|
||||
|
||||
if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
|
||||
constraintType = HOLE_CLEARANCE_CONSTRAINT;
|
||||
}
|
||||
|
||||
DRC_CONSTRAINT constraint = EvalRules( constraintType, aItem, nullptr, aLayer );
|
||||
|
||||
int clearance = 0;
|
||||
|
||||
if( constraint.Value().HasMin() )
|
||||
{
|
||||
clearance = constraint.Value().Min();
|
||||
|
||||
if( aSource )
|
||||
*aSource = constraint.GetName();
|
||||
}
|
||||
|
||||
// Store in cache
|
||||
m_ownClearanceCache[key] = clearance;
|
||||
|
||||
return clearance;
|
||||
}
|
||||
|
||||
|
||||
void DRC_ENGINE::InvalidateClearanceCache( const KIID& aUuid )
|
||||
{
|
||||
// Remove all entries for this item (across all layers)
|
||||
auto it = m_ownClearanceCache.begin();
|
||||
|
||||
while( it != m_ownClearanceCache.end() )
|
||||
{
|
||||
if( it->first.m_uuid == aUuid )
|
||||
it = m_ownClearanceCache.erase( it );
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DRC_ENGINE::ClearClearanceCache()
|
||||
{
|
||||
m_ownClearanceCache.clear();
|
||||
}
|
||||
|
||||
@@ -27,12 +27,46 @@
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <kiid.h>
|
||||
#include <layer_ids.h>
|
||||
#include <units_provider.h>
|
||||
#include <pcb_shape.h>
|
||||
#include <lset.h>
|
||||
#include <drc/drc_rule.h>
|
||||
|
||||
|
||||
/**
|
||||
* Cache key for own clearance lookups, combining item UUID and layer.
|
||||
*/
|
||||
struct DRC_OWN_CLEARANCE_CACHE_KEY
|
||||
{
|
||||
KIID m_uuid;
|
||||
PCB_LAYER_ID m_layer;
|
||||
|
||||
bool operator==( const DRC_OWN_CLEARANCE_CACHE_KEY& aOther ) const
|
||||
{
|
||||
return m_uuid == aOther.m_uuid && m_layer == aOther.m_layer;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct hash<DRC_OWN_CLEARANCE_CACHE_KEY>
|
||||
{
|
||||
std::size_t operator()( const DRC_OWN_CLEARANCE_CACHE_KEY& aKey ) const
|
||||
{
|
||||
std::size_t seed = 0xa82de1c0;
|
||||
seed ^= std::hash<KIID>{}( aKey.m_uuid ) + 0x9e3779b9 + ( seed << 6 ) + ( seed >> 2 );
|
||||
seed ^= std::hash<int>{}( static_cast<int>( aKey.m_layer ) ) + 0x9e3779b9
|
||||
+ ( seed << 6 ) + ( seed >> 2 );
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
class BOARD_COMMIT;
|
||||
class BOARD_DESIGN_SETTINGS;
|
||||
class DRC_TEST_PROVIDER;
|
||||
@@ -162,6 +196,36 @@ public:
|
||||
DRC_CONSTRAINT EvalZoneConnection( const BOARD_ITEM* a, const BOARD_ITEM* b,
|
||||
PCB_LAYER_ID aLayer, REPORTER* aReporter = nullptr );
|
||||
|
||||
/**
|
||||
* Get the cached own clearance for an item on a specific layer.
|
||||
*
|
||||
* This is used by BOARD_CONNECTED_ITEM::GetOwnClearance() to avoid re-evaluating
|
||||
* DRC rules on every paint refresh.
|
||||
*
|
||||
* @param aItem the item to get clearance for.
|
||||
* @param aLayer the layer in question.
|
||||
* @param aSource optionally reports the source as a user-readable string.
|
||||
* @return the clearance in internal units.
|
||||
*/
|
||||
int GetCachedOwnClearance( const BOARD_ITEM* aItem, PCB_LAYER_ID aLayer,
|
||||
wxString* aSource = nullptr );
|
||||
|
||||
/**
|
||||
* Invalidate the clearance cache for a specific item.
|
||||
*
|
||||
* Called when item properties that could affect clearance (net, type, layer) change.
|
||||
*
|
||||
* @param aUuid the UUID of the item to invalidate.
|
||||
*/
|
||||
void InvalidateClearanceCache( const KIID& aUuid );
|
||||
|
||||
/**
|
||||
* Clear the entire clearance cache.
|
||||
*
|
||||
* Called when DRC rules change or board design settings change.
|
||||
*/
|
||||
void ClearClearanceCache();
|
||||
|
||||
void ProcessAssertions( const BOARD_ITEM* a,
|
||||
std::function<void( const DRC_CONSTRAINT* )> aFailureHandler,
|
||||
REPORTER* aReporter = nullptr );
|
||||
@@ -270,4 +334,8 @@ protected:
|
||||
PROGRESS_REPORTER* m_progressReporter;
|
||||
|
||||
std::shared_ptr<KIGFX::VIEW_OVERLAY> m_debugOverlay;
|
||||
|
||||
// Cache for GetOwnClearance lookups to improve rendering performance.
|
||||
// Key is (UUID, layer), value is clearance in internal units.
|
||||
std::unordered_map<DRC_OWN_CLEARANCE_CACHE_KEY, int> m_ownClearanceCache;
|
||||
};
|
||||
|
||||
+17
-21
@@ -1355,6 +1355,10 @@ void PAD::SetAttribute( PAD_ATTRIB aAttribute )
|
||||
SetNetCode( NETINFO_LIST::UNCONNECTED );
|
||||
break;
|
||||
}
|
||||
|
||||
// Invalidate clearance cache since pad type affects constraint evaluation
|
||||
if( BOARD* board = GetBoard() )
|
||||
board->InvalidateClearanceCache( m_Uuid );
|
||||
}
|
||||
|
||||
SetDirty();
|
||||
@@ -1581,29 +1585,21 @@ std::optional<int> PAD::GetClearanceOverrides( wxString* aSource ) const
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetLayerSet( const LSET& aLayers )
|
||||
{
|
||||
m_padStack.SetLayerSet( aLayers );
|
||||
SetDirty();
|
||||
|
||||
// Invalidate clearance cache since layer set can affect clearance rules
|
||||
if( BOARD* board = GetBoard() )
|
||||
board->InvalidateClearanceCache( m_Uuid );
|
||||
}
|
||||
|
||||
|
||||
int PAD::GetOwnClearance( PCB_LAYER_ID aLayer, wxString* aSource ) const
|
||||
{
|
||||
DRC_CONSTRAINT c;
|
||||
|
||||
if( GetBoard() && GetBoard()->GetDesignSettings().m_DRCEngine )
|
||||
{
|
||||
BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings();
|
||||
|
||||
if( GetAttribute() == PAD_ATTRIB::NPTH )
|
||||
c = bds.m_DRCEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, this, nullptr, aLayer );
|
||||
else
|
||||
c = bds.m_DRCEngine->EvalRules( CLEARANCE_CONSTRAINT, this, nullptr, aLayer );
|
||||
}
|
||||
|
||||
if( c.Value().HasMin() )
|
||||
{
|
||||
if( aSource )
|
||||
*aSource = c.GetName();
|
||||
|
||||
return c.Value().Min();
|
||||
}
|
||||
|
||||
return 0;
|
||||
// The NPTH vs regular pad logic is handled in DRC_ENGINE::GetCachedOwnClearance
|
||||
return BOARD_CONNECTED_ITEM::GetOwnClearance( aLayer, aSource );
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -556,7 +556,7 @@ public:
|
||||
m_polyDirty[ERROR_OUTSIDE] = true;
|
||||
}
|
||||
|
||||
void SetLayerSet( const LSET& aLayers ) override { m_padStack.SetLayerSet( aLayers ); SetDirty(); }
|
||||
void SetLayerSet( const LSET& aLayers ) override;
|
||||
LSET GetLayerSet() const override { return m_padStack.LayerSet(); }
|
||||
|
||||
void SetAttribute( PAD_ATTRIB aAttribute );
|
||||
|
||||
@@ -1640,6 +1640,10 @@ void PCB_VIA::SetLayerPair( PCB_LAYER_ID aTopLayer, PCB_LAYER_ID aBottomLayer )
|
||||
Padstack().Drill().start = aTopLayer;
|
||||
Padstack().Drill().end = aBottomLayer;
|
||||
SanitizeLayers();
|
||||
|
||||
// Invalidate clearance cache since layer can affect clearance rules
|
||||
if( BOARD* board = GetBoard() )
|
||||
board->InvalidateClearanceCache( m_Uuid );
|
||||
}
|
||||
|
||||
|
||||
@@ -1651,6 +1655,10 @@ void PCB_VIA::SetTopLayer( PCB_LAYER_ID aLayer )
|
||||
|
||||
Padstack().Drill().start = aLayer;
|
||||
SanitizeLayers();
|
||||
|
||||
// Invalidate clearance cache since layer can affect clearance rules
|
||||
if( BOARD* board = GetBoard() )
|
||||
board->InvalidateClearanceCache( m_Uuid );
|
||||
}
|
||||
|
||||
|
||||
@@ -1662,6 +1670,10 @@ void PCB_VIA::SetBottomLayer( PCB_LAYER_ID aLayer )
|
||||
|
||||
Padstack().Drill().end = aLayer;
|
||||
SanitizeLayers();
|
||||
|
||||
// Invalidate clearance cache since layer can affect clearance rules
|
||||
if( BOARD* board = GetBoard() )
|
||||
board->InvalidateClearanceCache( m_Uuid );
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user