From 51f39d71e2aed66171af3ac9a8c912efb5213fd0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 22 Jul 2026 22:30:09 +0200 Subject: [PATCH] WIP: hardening FlatRegion against min_coherence toggles (IHP PDK optimization) --- src/db/db/dbAsIfFlatRegion.cc | 6 +- src/db/db/dbFlatRegion.cc | 84 ++++++++++---- src/db/db/dbFlatRegion.h | 26 +++-- src/db/unit_tests/dbAsIfFlatRegionTests.cc | 125 +++++++++++++++++++++ src/db/unit_tests/dbDeepRegionTests.cc | 1 + 5 files changed, 213 insertions(+), 29 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 88f174d0e..da5ff23fc 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -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 new_region (new FlatRegion (true)); - merge_polygons_to (new_region->raw_polygons (), min_coherence, min_wc, join_properties_on_merge); + std::unique_ptr 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 (); diff --git a/src/db/db/dbFlatRegion.cc b/src/db/db/dbFlatRegion.cc index 720f376de..f32d1d988 100644 --- a/src/db/db/dbFlatRegion.cc +++ b/src/db/db/dbFlatRegion.cc @@ -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 FlatRegion::begin_iter () std::pair 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 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 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 new_region (new FlatRegion (*this)); diff --git a/src/db/db/dbFlatRegion.h b/src/db/db/dbFlatRegion.h index 5610eb061..70d4249b0 100644 --- a/src/db/db/dbFlatRegion.h +++ b/src/db/db/dbFlatRegion.h @@ -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 mp_polygons; mutable tl::copy_on_write_ptr 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 void transform_generic (const Trans &trans) { diff --git a/src/db/unit_tests/dbAsIfFlatRegionTests.cc b/src/db/unit_tests/dbAsIfFlatRegionTests.cc index 3377a2ab4..c23e0c388 100644 --- a/src/db/unit_tests/dbAsIfFlatRegionTests.cc +++ b/src/db/unit_tests/dbAsIfFlatRegionTests.cc @@ -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 (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 (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 (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 (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 (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 (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 (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 (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[] = { diff --git a/src/db/unit_tests/dbDeepRegionTests.cc b/src/db/unit_tests/dbDeepRegionTests.cc index 32e831239..0a0ff2a66 100644 --- a/src/db/unit_tests/dbDeepRegionTests.cc +++ b/src/db/unit_tests/dbDeepRegionTests.cc @@ -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);