Performance enhancements for connectivity.

1) Generate SHAPE_POLY_SET triangulation by outline so they can be
shared between connectivity system and other clients.
2) Don't add items to connectivity when reading board; we're going
to do a total rebuild anyway.
3) Use multithreading when caching triangulation.
This commit is contained in:
Jeff Young 2022-02-15 12:17:31 +00:00
parent 98b9c6e2a1
commit 5c9e718407
10 changed files with 122 additions and 79 deletions

View File

@ -125,7 +125,7 @@ public:
TRIANGULATED_POLYGON* parent;
};
TRIANGULATED_POLYGON();
TRIANGULATED_POLYGON( int aSourceOutline );
TRIANGULATED_POLYGON( const TRIANGULATED_POLYGON& aOther );
~TRIANGULATED_POLYGON();
@ -154,6 +154,8 @@ public:
size_t GetTriangleCount() const { return m_triangles.size(); }
int GetSourceOutlineIndex() const { return m_sourceOutline; }
std::deque<TRI>& Triangles() { return m_triangles; }
const std::deque<TRI>& Triangles() const { return m_triangles; }
@ -169,6 +171,7 @@ public:
}
private:
int m_sourceOutline;
std::deque<TRI> m_triangles;
std::deque<VECTOR2I> m_vertices;
};

View File

@ -2246,8 +2246,7 @@ bool SHAPE_POLY_SET::IsTriangulationUpToDate() const
}
static void partitionPolyIntoRegularCellGrid( const SHAPE_POLY_SET& aPoly, int aSize,
SHAPE_POLY_SET& aOut )
static SHAPE_POLY_SET partitionPolyIntoRegularCellGrid( const SHAPE_POLY_SET& aPoly, int aSize )
{
BOX2I bb = aPoly.BBox();
@ -2255,7 +2254,7 @@ static void partitionPolyIntoRegularCellGrid( const SHAPE_POLY_SET& aPoly, int a
double h = bb.GetHeight();
if( w == 0.0 || h == 0.0 )
return;
return aPoly;
int n_cells_x, n_cells_y;
@ -2306,13 +2305,13 @@ static void partitionPolyIntoRegularCellGrid( const SHAPE_POLY_SET& aPoly, int a
ps1.Fracture( SHAPE_POLY_SET::PM_FAST );
ps2.Fracture( SHAPE_POLY_SET::PM_FAST );
aOut = ps1;
for( int i = 0; i < ps2.OutlineCount(); i++ )
aOut.AddOutline( ps2.COutline( i ) );
ps1.AddOutline( ps2.COutline( i ) );
if( !aOut.OutlineCount() )
aOut = aPoly;
if( ps1.OutlineCount() )
return ps1;
else
return aPoly;
}
@ -2338,46 +2337,60 @@ void SHAPE_POLY_SET::CacheTriangulation( bool aPartition )
if( !recalculate )
return;
SHAPE_POLY_SET tmpSet;
if( aPartition )
auto triangulate =
[]( SHAPE_POLY_SET& polySet, int forOutline,
std::vector<std::unique_ptr<TRIANGULATED_POLYGON>>& dest )
{
// This partitions into regularly-sized grids (1cm in Pcbnew)
SHAPE_POLY_SET flattened( *this );
flattened.ClearArcs();
partitionPolyIntoRegularCellGrid( flattened, 1e7, tmpSet );
}
else
bool triangulationValid = false;
while( polySet.OutlineCount() > 0 )
{
tmpSet = *this;
if( !dest.empty() && dest.back()->GetTriangleCount() == 0 )
dest.erase( dest.end() - 1 );
if( tmpSet.HasHoles() )
tmpSet.Fracture( PM_FAST );
}
m_triangulatedPolys.clear();
m_triangulationValid = false;
while( tmpSet.OutlineCount() > 0 )
{
if( !m_triangulatedPolys.empty() && m_triangulatedPolys.back()->GetTriangleCount() == 0 )
m_triangulatedPolys.erase( m_triangulatedPolys.end() - 1 );
m_triangulatedPolys.push_back( std::make_unique<TRIANGULATED_POLYGON>() );
PolygonTriangulation tess( *m_triangulatedPolys.back() );
dest.push_back( std::make_unique<TRIANGULATED_POLYGON>( forOutline ) );
PolygonTriangulation tess( *dest.back() );
// If the tessellation fails, we re-fracture the polygon, which will
// first simplify the system before fracturing and removing the holes
// This may result in multiple, disjoint polygons.
if( !tess.TesselatePolygon( tmpSet.Polygon( 0 ).front() ) )
if( !tess.TesselatePolygon( polySet.Polygon( 0 ).front() ) )
{
tmpSet.Fracture( PM_FAST );
m_triangulationValid = false;
polySet.Fracture( PM_FAST );
triangulationValid = false;
continue;
}
tmpSet.DeletePolygon( 0 );
polySet.DeletePolygon( 0 );
triangulationValid = true;
}
return triangulationValid;
};
m_triangulatedPolys.clear();
m_triangulationValid = true;
if( aPartition )
{
for( int ii = 0; ii < OutlineCount(); ++ii )
{
// This partitions into regularly-sized grids (1cm in Pcbnew)
SHAPE_POLY_SET flattened( Outline( ii ) );
flattened.ClearArcs();
SHAPE_POLY_SET partitions = partitionPolyIntoRegularCellGrid( flattened, 1e7 );
m_triangulationValid &= triangulate( partitions, ii, m_triangulatedPolys );
}
}
else
{
SHAPE_POLY_SET tmpSet( *this );
if( tmpSet.HasHoles() )
tmpSet.Fracture( PM_FAST );
m_triangulationValid = triangulate( tmpSet, -1, m_triangulatedPolys );
}
if( m_triangulationValid )
@ -2496,6 +2509,7 @@ void SHAPE_POLY_SET::TRIANGULATED_POLYGON::AddTriangle( int a, int b, int c )
SHAPE_POLY_SET::TRIANGULATED_POLYGON::TRIANGULATED_POLYGON( const TRIANGULATED_POLYGON& aOther )
{
m_sourceOutline = aOther.m_sourceOutline;
m_vertices = aOther.m_vertices;
m_triangles = aOther.m_triangles;
@ -2506,6 +2520,7 @@ SHAPE_POLY_SET::TRIANGULATED_POLYGON::TRIANGULATED_POLYGON( const TRIANGULATED_P
SHAPE_POLY_SET::TRIANGULATED_POLYGON& SHAPE_POLY_SET::TRIANGULATED_POLYGON::operator=( const TRIANGULATED_POLYGON& aOther )
{
m_sourceOutline = aOther.m_sourceOutline;
m_vertices = aOther.m_vertices;
m_triangles = aOther.m_triangles;
@ -2516,7 +2531,8 @@ SHAPE_POLY_SET::TRIANGULATED_POLYGON& SHAPE_POLY_SET::TRIANGULATED_POLYGON::oper
}
SHAPE_POLY_SET::TRIANGULATED_POLYGON::TRIANGULATED_POLYGON()
SHAPE_POLY_SET::TRIANGULATED_POLYGON::TRIANGULATED_POLYGON( int aSourceOutline ) :
m_sourceOutline( aSourceOutline )
{
}

View File

@ -26,6 +26,7 @@
*/
#include <iterator>
#include <thread>
#include <drc/drc_rtree.h>
#include <pcb_base_frame.h>
#include <board_design_settings.h>
@ -55,6 +56,7 @@
#include <tool/selection_conditions.h>
#include <convert_shape_list_to_polygon.h>
#include <wx/log.h>
#include <progress_reporter.h>
// This is an odd place for this, but CvPcb won't link if it's in board_item.cpp like I first
// tried it.
@ -619,6 +621,44 @@ void BOARD::SetZoneSettings( const ZONE_SETTINGS& aSettings )
}
void BOARD::CacheTriangulation( PROGRESS_REPORTER* aReporter )
{
if( aReporter )
aReporter->Report( _( "Tessellating copper zones..." ) );
std::atomic<size_t> next( 0 );
std::atomic<size_t> count_done( 0 );
size_t parallelThreadCount = std::max<size_t>( std::thread::hardware_concurrency(), 2 );
for( size_t ii = 0; ii < parallelThreadCount; ++ii )
{
std::thread t = std::thread(
[ this, &count_done, &next ]( )
{
for( size_t i = next.fetch_add( 1 ); i < m_zones.size(); i = next.fetch_add( 1 ) )
m_zones[i]->CacheTriangulation();
count_done++;
} );
t.detach();
}
// Finalize the triangulation threads
while( count_done < parallelThreadCount )
{
if( aReporter && m_zones.size() )
{
aReporter->SetCurrentProgress( (double) count_done / (double) m_zones.size() );
aReporter->KeepRefreshing();
}
std::this_thread::sleep_for( std::chrono::milliseconds( 30 ) );
}
}
void BOARD::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode )
{
if( aBoardItem == nullptr )
@ -704,11 +744,13 @@ void BOARD::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode )
aBoardItem->SetParent( this );
aBoardItem->ClearEditFlags();
m_connectivity->Add( aBoardItem );
if( aMode != ADD_MODE::BULK_INSERT && aMode != ADD_MODE::BULK_APPEND )
{
m_connectivity->Add( aBoardItem );
InvokeListeners( &BOARD_LISTENER::OnBoardItemAdded, *this, aBoardItem );
}
}
void BOARD::FinalizeBulkAdd( std::vector<BOARD_ITEM*>& aNewItems )

View File

@ -308,6 +308,8 @@ public:
*/
void FinalizeBulkRemove( std::vector<BOARD_ITEM*>& aRemovedItems );
void CacheTriangulation( PROGRESS_REPORTER* aReporter = nullptr );
/**
* Get the first footprint on the board or nullptr.
*

View File

@ -438,9 +438,6 @@ void CN_CONNECTIVITY_ALGO::Build( BOARD* aBoard, PROGRESS_REPORTER* aReporter )
delta = std::max( delta, size / 10 );
if( aReporter )
aReporter->KeepRefreshing( false );
for( ZONE* zone : aBoard->Zones() )
{
Add( zone );

View File

@ -86,11 +86,16 @@ bool CONNECTIVITY_DATA::Update( BOARD_ITEM* aItem )
void CONNECTIVITY_DATA::Build( BOARD* aBoard, PROGRESS_REPORTER* aReporter )
{
aBoard->CacheTriangulation( aReporter );
std::unique_lock<KISPINLOCK> lock( m_lock, std::try_to_lock );
if( !lock )
return;
if( aReporter )
aReporter->Report( _( "Updating nets..." ) );
m_connAlgo.reset( new CN_CONNECTIVITY_ALGO );
m_connAlgo->Build( aBoard, aReporter );

View File

@ -292,12 +292,19 @@ public:
{
const SHAPE_POLY_SET& fill = aParent->GetFilledPolysList( aLayer );
m_triangulatedSubpoly = SHAPE_POLY_SET( fill.COutline( aSubpolyIndex ) );
m_triangulatedSubpoly.CacheTriangulation();
if( !fill.IsTriangulationUpToDate() )
const_cast<SHAPE_POLY_SET&>( fill ).CacheTriangulation();
for( size_t ii = 0; ii < m_triangulatedSubpoly.TriangulatedPolyCount(); ++ii )
m_triangulatedPoly = fill;
for( unsigned int ii = 0; ii < m_triangulatedPoly.TriangulatedPolyCount(); ++ii )
{
for( auto& tri : m_triangulatedSubpoly.TriangulatedPolygon( ii )->Triangles() )
const auto* triangleSet = m_triangulatedPoly.TriangulatedPolygon( ii );
if( triangleSet->GetSourceOutlineIndex() != aSubpolyIndex )
continue;
for( const SHAPE_POLY_SET::TRIANGULATED_POLYGON::TRI& tri : triangleSet->Triangles() )
{
BOX2I bbox = tri.BBox();
const int mmin[2] = { bbox.GetX(), bbox.GetY() };
@ -369,7 +376,7 @@ private:
std::vector<VECTOR2I> m_testOutlinePoints;
int m_subpolyIndex;
PCB_LAYER_ID m_layer;
SHAPE_POLY_SET m_triangulatedSubpoly;
SHAPE_POLY_SET m_triangulatedPoly;
RTree<const SHAPE*, int, 2, double> m_rTree;
};

View File

@ -929,7 +929,6 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
std::vector<ZONE*> toFill;
// Rebuild list of nets (full ratsnest rebuild)
progressReporter.Report( _( "Updating nets" ) );
GetBoard()->BuildConnectivity( &progressReporter );
// Load project settings after setting up board; some of them depend on the nets list
@ -970,7 +969,6 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
if( filler.Fill( toFill ) )
{
commit.Push( _( "Convert Zone(s)" ) );
progressReporter.Report( _( "Updating nets" ) );
GetBoard()->BuildConnectivity( &progressReporter );
}
}

View File

@ -182,23 +182,7 @@ void PCB_DRAW_PANEL_GAL::DisplayBoard( BOARD* aBoard, PROGRESS_REPORTER* aReport
{
m_view->Clear();
auto zones = aBoard->Zones();
std::atomic<size_t> next( 0 );
std::atomic<size_t> count_done( 0 );
size_t parallelThreadCount = std::max<size_t>( std::thread::hardware_concurrency(), 2 );
for( size_t ii = 0; ii < parallelThreadCount; ++ii )
{
std::thread t = std::thread( [ &count_done, &next, &zones ]( )
{
for( size_t i = next.fetch_add( 1 ); i < zones.size(); i = next.fetch_add( 1 ) )
zones[i]->CacheTriangulation();
count_done++;
} );
t.detach();
}
aBoard->CacheTriangulation();
if( m_drawingSheet )
m_drawingSheet->SetFileName( TO_UTF8( aBoard->GetFileName() ) );
@ -219,15 +203,6 @@ void PCB_DRAW_PANEL_GAL::DisplayBoard( BOARD* aBoard, PROGRESS_REPORTER* aReport
for( PCB_MARKER* marker : aBoard->Markers() )
m_view->Add( marker );
// Finalize the triangulation threads
while( count_done < parallelThreadCount )
{
if( aReporter )
aReporter->KeepRefreshing();
std::this_thread::sleep_for( std::chrono::milliseconds( 30 ) );
}
// Load zones
for( ZONE* zone : aBoard->Zones() )
m_view->Add( zone );

View File

@ -157,7 +157,6 @@ void ZONE_FILLER_TOOL::FillAllZones( wxWindow* aCaller, PROGRESS_REPORTER* aRepo
if( filler.Fill( toFill ) )
{
reporter->AdvancePhase();
reporter->Report( _( "Updating nets..." ) );
board()->GetConnectivity()->Build( board(), reporter.get() );
commit.Push( _( "Fill Zone(s)" ), true, true, false );
@ -215,7 +214,6 @@ int ZONE_FILLER_TOOL::ZoneFill( const TOOL_EVENT& aEvent )
if( filler.Fill( toFill ) )
{
reporter->AdvancePhase();
reporter->Report( _( "Updating nets..." ) );
board()->GetConnectivity()->Build( board(), reporter.get() );
commit.Push( _( "Fill Zone(s)" ), true, true, false );