WIP: hardening FlatRegion against min_coherence toggles (IHP PDK optimization)

This commit is contained in:
Matthias Koefferlein 2026-07-22 22:30:09 +02:00
parent 7ed80e6dd8
commit 51f39d71e2
5 changed files with 213 additions and 29 deletions

View File

@ -1349,7 +1349,7 @@ AsIfFlatRegion::run_single_polygon_check (db::edge_relation_type rel, db::Coord
}
RegionDelegate *
AsIfFlatRegion::merged (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge) const
AsIfFlatRegion::merged (bool min_coh, unsigned int min_wc, bool join_properties_on_merge) const
{
if (empty ()) {
@ -1366,8 +1366,8 @@ AsIfFlatRegion::merged (bool min_coherence, unsigned int min_wc, bool join_prope
} else {
std::unique_ptr<FlatRegion> new_region (new FlatRegion (true));
merge_polygons_to (new_region->raw_polygons (), min_coherence, min_wc, join_properties_on_merge);
std::unique_ptr<FlatRegion> new_region (new FlatRegion (true, 0, 0, min_coh));
merge_polygons_to (new_region->raw_polygons (), min_coh, min_wc, join_properties_on_merge);
return new_region.release ();

View File

@ -45,34 +45,43 @@ FlatRegion::FlatRegion (const FlatRegion &other)
m_area_ratio (other.m_area_ratio), m_max_vertex_count (other.m_max_vertex_count)
{
init ();
set_min_coherence (other.min_coherence ());
m_is_merged = other.m_is_merged;
m_is_merged_min_coherence = other.m_is_merged_min_coherence;
m_merged_polygons_valid = other.m_merged_polygons_valid;
m_merged_polygons_min_coherence = other.m_merged_polygons_min_coherence;
}
FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged, double area_ratio, size_t max_vertex_count)
FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged, double area_ratio, size_t max_vertex_count, bool min_coh)
: MutableRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false)),
m_area_ratio (area_ratio), m_max_vertex_count (max_vertex_count)
{
init ();
set_min_coherence (min_coh);
m_is_merged = is_merged;
m_is_merged_min_coherence = min_coh;
}
FlatRegion::FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged, double area_ratio, size_t max_vertex_count)
FlatRegion::FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged, double area_ratio, size_t max_vertex_count, bool min_coh)
: MutableRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false)),
m_area_ratio (area_ratio), m_max_vertex_count (max_vertex_count)
{
init ();
set_min_coherence (min_coh);
m_is_merged = is_merged;
m_is_merged_min_coherence = min_coh;
transform_generic (trans);
set_merged_semantics (merged_semantics);
}
FlatRegion::FlatRegion (bool is_merged, double area_ratio, size_t max_vertex_count)
FlatRegion::FlatRegion (bool is_merged, double area_ratio, size_t max_vertex_count, bool min_coh)
: MutableRegion (), mp_polygons (new db::Shapes (false)), mp_merged_polygons (new db::Shapes (false)),
m_area_ratio (area_ratio), m_max_vertex_count (max_vertex_count)
{
init ();
set_min_coherence (min_coh);
m_is_merged = is_merged;
m_is_merged_min_coherence = min_coh;
}
FlatRegion::~FlatRegion ()
@ -83,6 +92,7 @@ FlatRegion::~FlatRegion ()
void FlatRegion::set_is_merged (bool m)
{
m_is_merged = m;
m_is_merged_min_coherence = min_coherence ();
}
void FlatRegion::invalidate_cache ()
@ -95,7 +105,9 @@ void FlatRegion::invalidate_cache ()
void FlatRegion::init ()
{
m_is_merged = false;
m_is_merged_min_coherence = false;
m_merged_polygons_valid = false;
m_merged_polygons_min_coherence = false;
}
void FlatRegion::merged_semantics_changed ()
@ -112,9 +124,7 @@ void FlatRegion::join_properties_on_merge_changed ()
void FlatRegion::min_coherence_changed ()
{
m_is_merged = false;
mp_merged_polygons->clear ();
m_merged_polygons_valid = false;
// merged status is tracked in separate variables and is validated on inquiry
}
void FlatRegion::reserve (size_t n)
@ -122,12 +132,25 @@ void FlatRegion::reserve (size_t n)
mp_polygons->reserve (db::Polygon::tag (), n);
}
bool
FlatRegion::merged_polygons_valid () const
{
return m_merged_polygons_valid && m_merged_polygons_min_coherence == min_coherence ();
}
bool
FlatRegion::merged_polygons_available () const
{
return is_merged () || merged_polygons_valid ();
}
void
FlatRegion::ensure_merged_polygons_valid () const
{
if (! m_merged_polygons_valid) {
if (! merged_polygons_valid ()) {
merge_polygons_to (*mp_merged_polygons, min_coherence (), 0, join_properties_on_merge ());
m_merged_polygons_valid = true;
m_merged_polygons_min_coherence = min_coherence ();
}
}
@ -140,7 +163,9 @@ FlatRegion::ensure_unmerged_polygons_valid () const
mp_merged_polygons.reset (new db::Shapes (*mp_polygons));
m_merged_polygons_valid = true;
m_merged_polygons_min_coherence = m_is_merged_min_coherence;
m_is_merged = false;
m_is_merged_min_coherence = min_coherence ();
break_polygons (*mp_polygons, m_max_vertex_count, m_area_ratio);
}
@ -152,7 +177,7 @@ RegionIteratorDelegate *FlatRegion::begin () const
RegionIteratorDelegate *FlatRegion::begin_merged () const
{
if (! merged_semantics () || m_is_merged) {
if (! merged_semantics () || is_merged ()) {
return begin ();
} else {
ensure_merged_polygons_valid ();
@ -173,7 +198,7 @@ std::pair<db::RecursiveShapeIterator, db::ICplxTrans> FlatRegion::begin_iter ()
std::pair<db::RecursiveShapeIterator, db::ICplxTrans> FlatRegion::begin_merged_iter () const
{
if (! merged_semantics () || m_is_merged) {
if (! merged_semantics () || is_merged ()) {
return begin_iter ();
} else {
ensure_merged_polygons_valid ();
@ -204,7 +229,7 @@ size_t FlatRegion::hier_count () const
bool FlatRegion::is_merged () const
{
return m_is_merged;
return m_is_merged && m_is_merged_min_coherence == min_coherence ();
}
Box FlatRegion::compute_bbox () const
@ -246,6 +271,7 @@ RegionDelegate *FlatRegion::filter_in_place (const PolygonFilterBase &filter)
mp_merged_polygons->clear ();
invalidate_cache ();
m_is_merged = filter.requires_raw_input () ? false : merged_semantics ();
m_is_merged_min_coherence = min_coherence ();
return this;
}
@ -276,6 +302,7 @@ RegionDelegate *FlatRegion::process_in_place (const PolygonProcessorBase &filter
mp_merged_polygons->clear ();
invalidate_cache ();
m_is_merged = filter.result_is_merged () && merged_semantics ();
m_is_merged_min_coherence = min_coherence ();
if (filter.result_must_not_be_merged ()) {
set_merged_semantics (false);
@ -286,14 +313,15 @@ RegionDelegate *FlatRegion::process_in_place (const PolygonProcessorBase &filter
RegionDelegate *FlatRegion::merged_in_place ()
{
if (! m_is_merged) {
if (! is_merged ()) {
if (m_merged_polygons_valid) {
if (merged_polygons_valid ()) {
db::Shapes &merged_polygons = *mp_merged_polygons;
mp_polygons->swap (merged_polygons);
merged_polygons.clear ();
m_is_merged = true;
m_is_merged_min_coherence = min_coherence ();
return this;
} else {
@ -305,7 +333,7 @@ RegionDelegate *FlatRegion::merged_in_place ()
}
}
RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge)
RegionDelegate *FlatRegion::merged_in_place (bool min_coh, unsigned int min_wc, bool join_properties_on_merge)
{
if (empty ()) {
@ -322,9 +350,11 @@ RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int mi
} else {
invalidate_cache ();
merge_polygons_to (*mp_polygons, min_coherence, min_wc, join_properties_on_merge);
merge_polygons_to (*mp_polygons, min_coh, min_wc, join_properties_on_merge);
set_min_coherence (min_coh);
m_is_merged = true;
m_is_merged_min_coherence = min_coh;
}
@ -333,19 +363,35 @@ RegionDelegate *FlatRegion::merged_in_place (bool min_coherence, unsigned int mi
RegionDelegate *FlatRegion::merged () const
{
if (! m_is_merged) {
if (! is_merged ()) {
if (m_merged_polygons_valid) {
return new FlatRegion (*mp_merged_polygons, true);
} else {
return AsIfFlatRegion::merged (min_coherence (), 0, join_properties_on_merge ());
if (! merged_polygons_valid ()) {
ensure_merged_polygons_valid ();
}
std::unique_ptr<FlatRegion> new_region (new FlatRegion (*mp_merged_polygons, true, 0, 0, min_coherence ()));
new_region->set_max_vertex_count (m_max_vertex_count);
new_region->set_area_ratio (m_area_ratio);
return new_region.release ();
} else {
return clone ();
}
}
RegionDelegate *FlatRegion::merged (bool min_coh, unsigned int min_wc, bool jp) const
{
std::unique_ptr<FlatRegion> new_region (new FlatRegion (true, 0, 0, min_coh));
merge_polygons_to (new_region->raw_polygons (), min_coh, min_wc, jp);
new_region->set_max_vertex_count (m_max_vertex_count);
new_region->set_area_ratio (m_area_ratio);
return new_region.release ();
}
RegionDelegate *FlatRegion::add (const Region &other) const
{
std::unique_ptr<FlatRegion> new_region (new FlatRegion (*this));

View File

@ -52,9 +52,9 @@ public:
typedef polygon_layer_wp_type::iterator polygon_iterator_wp_type;
FlatRegion (double area_ratio = 0.0, size_t max_vertex_count = 0);
FlatRegion (const db::Shapes &polygons, bool is_merged = false, double area_ratio = 0.0, size_t max_vertex_count = 0);
FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false, double area_ratio = 0.0, size_t max_vertex_count = 0);
FlatRegion (bool is_merged, double area_ratio = 0.0, size_t max_vertex_count = 0);
FlatRegion (const db::Shapes &polygons, bool is_merged = false, double area_ratio = 0.0, size_t max_vertex_count = 0, bool min_coh = false);
FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false, double area_ratio = 0.0, size_t max_vertex_count = 0, bool min_coh = false);
FlatRegion (bool is_merged, double area_ratio = 0.0, size_t max_vertex_count = 0, bool min_coh = false);
FlatRegion (const FlatRegion &other);
@ -85,10 +85,9 @@ public:
virtual RegionDelegate *merged_in_place ();
virtual RegionDelegate *merged_in_place (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge);
virtual RegionDelegate *merged () const;
virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge) const
{
return db::AsIfFlatRegion::merged (min_coherence, min_wc, join_properties_on_merge);
}
virtual RegionDelegate *merged (bool min_coherence, unsigned int min_wc, bool join_properties_on_merge) const;
bool merged_polygons_available () const;
virtual RegionDelegate *process_in_place (const PolygonProcessorBase &filter);
virtual RegionDelegate *filter_in_place (const PolygonFilterBase &filter);
@ -138,6 +137,7 @@ protected:
virtual Box compute_bbox () const;
void invalidate_cache ();
void set_is_merged (bool m);
bool merged_polygons_valid () const;
private:
friend class AsIfFlatRegion;
@ -146,9 +146,11 @@ private:
FlatRegion &operator= (const FlatRegion &other);
mutable bool m_is_merged;
mutable bool m_is_merged_min_coherence;
mutable tl::copy_on_write_ptr<db::Shapes> mp_polygons;
mutable tl::copy_on_write_ptr<db::Shapes> mp_merged_polygons;
mutable bool m_merged_polygons_valid;
mutable bool m_merged_polygons_min_coherence;
double m_area_ratio;
size_t m_max_vertex_count;
@ -156,6 +158,16 @@ private:
void ensure_merged_polygons_valid () const;
void ensure_unmerged_polygons_valid () const;
void set_area_ratio (double ar)
{
m_area_ratio = ar;
}
void set_max_vertex_count (size_t mc)
{
m_max_vertex_count = mc;
}
template <class Trans>
void transform_generic (const Trans &trans)
{

View File

@ -2033,6 +2033,131 @@ TEST(44_SizeWithProperties)
db::compare_layouts (_this, target, tl::testdata () + "/algo/flat_region_au44.gds");
}
TEST(45_min_coherence_and_merge)
{
// NOTE: with an explicit area ratio and max vertex count we can play with unmerged input
db::Region r1 (new db::FlatRegion (3.0, 32));
r1.insert (db::Box (0, 0, 1000, 2000));
r1.insert (db::Box (1000, 2000, 10000, 3000));
r1.insert (db::Box (9000, 3000, 10000, 10000));
EXPECT_EQ (r1.min_coherence (), false);
EXPECT_EQ (r1.is_merged (), false);
EXPECT_EQ (dynamic_cast<const db::FlatRegion *> (r1.delegate ())->merged_polygons_available (), false);
EXPECT_EQ (r1.to_string (), "(0,0;0,2000;1000,2000;1000,0);(1000,2000;1000,3000;10000,3000;10000,2000);(9000,3000;9000,10000;10000,10000;10000,3000)");
// forces merged status
r1.sized (1);
EXPECT_EQ (r1.min_coherence (), false);
EXPECT_EQ (r1.is_merged (), false);
EXPECT_EQ (dynamic_cast<const db::FlatRegion *> (r1.delegate ())->merged_polygons_available (), true);
db::Region r2 = r1.merged ();
EXPECT_EQ (r2.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)");
EXPECT_EQ (r2.min_coherence (), false);
EXPECT_EQ (r1.min_coherence (), false);
EXPECT_EQ (r1.is_merged (), false);
EXPECT_EQ (dynamic_cast<const db::FlatRegion *> (r1.delegate ())->merged_polygons_available (), true);
r1.set_min_coherence (true);
// valid_merged_polygons is invalidated
EXPECT_EQ (r1.min_coherence (), true);
EXPECT_EQ (r1.is_merged (), false);
EXPECT_EQ (dynamic_cast<const db::FlatRegion *> (r1.delegate ())->merged_polygons_available (), false);
r1.set_min_coherence (false);
// setting min_coherence to false will bring the validation back
EXPECT_EQ (r1.min_coherence (), false);
EXPECT_EQ (r1.is_merged (), false);
EXPECT_EQ (dynamic_cast<const db::FlatRegion *> (r1.delegate ())->merged_polygons_available (), true);
r1.set_min_coherence (true);
// valid_merged_polygons is invalidated again by setting min_coherence to true
EXPECT_EQ (r1.min_coherence (), true);
EXPECT_EQ (r1.is_merged (), false);
EXPECT_EQ (dynamic_cast<const db::FlatRegion *> (r1.delegate ())->merged_polygons_available (), false);
// inserting a box into the region will reset the merged valid flag and not bring it back by resetting min_coherence
db::Region r3 = r1;
r3.insert (db::Box (0, 0, 100, 100));
EXPECT_EQ (r3.min_coherence (), true);
r3.set_min_coherence (false);
EXPECT_EQ (r3.is_merged (), false);
EXPECT_EQ (dynamic_cast<const db::FlatRegion *> (r3.delegate ())->merged_polygons_available (), false);
// creates a intrinsically merged copy and tests if the min_coherence
// flag is inherited
r3 = r1.merged ();
EXPECT_EQ (r3.to_string (), "(0,0;0,2000;1000,2000;1000,0);(1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000)");
EXPECT_EQ (r3.is_merged (), true);
EXPECT_EQ (r3.min_coherence (), true);
// merged polygons are validated against min_coherence = true during r1.merged() before, so
// resetting min_coherence to false will invalidat them
r1.set_min_coherence (false);
EXPECT_EQ (r1.min_coherence (), false);
EXPECT_EQ (r1.is_merged (), false);
EXPECT_EQ (dynamic_cast<const db::FlatRegion *> (r1.delegate ())->merged_polygons_available (), false);
// creates a intrinsically merged copy and tests if the min_coherence
// flag is inherited
r3 = r1.merged ();
EXPECT_EQ (r3.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)");
EXPECT_EQ (r3.is_merged (), true);
EXPECT_EQ (r3.min_coherence (), false);
// r2 is an intrinsically merged region (with min coherence false)
EXPECT_EQ (r2.min_coherence (), false);
EXPECT_EQ (r2.is_merged (), true);
EXPECT_EQ (r2.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)");
// after changing min_coherence, the merged status is no longer true
r2.set_min_coherence (true);
EXPECT_EQ (r2.min_coherence (), true);
EXPECT_EQ (r2.is_merged (), false);
EXPECT_EQ (r2.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)");
// read-only experiments with r2
// inserting a box into the region will reset the merged state
// and not bring it back by resetting min_coherence
r3 = r2;
r3.insert (db::Box (0, 0, 100, 100));
r3.set_min_coherence (false);
EXPECT_EQ (r3.is_merged (), false);
EXPECT_EQ (r3.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0);(0,0;0,100;100,100;100,0)");
// unmerge after resetting min coherence
r3 = r2;
r3.set_min_coherence (false);
EXPECT_EQ (r3.is_merged (), true);
r3.begin_unmerged ();
EXPECT_EQ (r3.is_merged (), false);
EXPECT_EQ (r3.to_string (), "(1000,2000;1000,3000;10000,3000;10000,2000);(9000,3000;9000,10000;10000,10000;10000,3000);(0,0;0,2000;1000,2000;1000,0)");
// r2 still remembers it is merged, even though it does not say so and
// will unmerge
r3 = r2;
EXPECT_EQ (r3.is_merged (), false);
// unmerge
r3.begin_unmerged ();
EXPECT_EQ (r3.is_merged (), false);
EXPECT_EQ (r3.to_string (), "(1000,2000;1000,3000;10000,3000;10000,2000);(9000,3000;9000,10000;10000,10000;10000,3000);(0,0;0,2000;1000,2000;1000,0)");
// r2 merged state it can be restored by resetting min_coherence to false
r2.set_min_coherence (false);
EXPECT_EQ (r2.min_coherence (), false);
EXPECT_EQ (r2.is_merged (), true);
EXPECT_EQ (r2.to_string (), "(0,0;0,2000;1000,2000;1000,3000;9000,3000;9000,10000;10000,10000;10000,2000;1000,2000;1000,0)");
}
TEST(100_Issue1275)
{
db::Point pts[] = {

View File

@ -3400,6 +3400,7 @@ TEST(min_coherence_and_merge)
// inserting a box into the region will reset the merged valid flag and not bring it back by resetting min_coherence
db::Region r3 = r1;
EXPECT_EQ (r3.min_coherence (), true);
r3.insert (db::Box (0, 0, 100, 100));
r3.set_min_coherence (false);
EXPECT_EQ (r3.is_merged (), false);