From 5187ddbfc0f7a6f0caff7caf3690cb19fab851f7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 17 Feb 2024 21:20:24 +0100 Subject: [PATCH 01/14] Do not insert the same point twice into edge set in EdgeProcessor - this improves performance in the case of manifold intersecions in one point. Also: added edge count API --- src/db/db/dbEdgeProcessor.cc | 13 +++++++++++++ src/db/db/dbEdgeProcessor.h | 5 +++++ src/db/db/dbShapeProcessor.cc | 6 ++++++ src/db/db/dbShapeProcessor.h | 5 +++++ 4 files changed, 29 insertions(+) diff --git a/src/db/db/dbEdgeProcessor.cc b/src/db/db/dbEdgeProcessor.cc index 9f1c3f11f..4f0534ae8 100644 --- a/src/db/db/dbEdgeProcessor.cc +++ b/src/db/db/dbEdgeProcessor.cc @@ -251,6 +251,13 @@ struct CutPoints } + // do not insert points twice + for (auto c = cut_points.begin (); c != cut_points.end (); ++c) { + if (*c == p) { + return; + } + } + cut_points.push_back (p); } @@ -1057,6 +1064,12 @@ EdgeProcessor::reserve (size_t n) mp_work_edges->reserve (n); } +size_t +EdgeProcessor::count () const +{ + return mp_work_edges->size (); +} + void EdgeProcessor::insert (const db::Edge &e, EdgeProcessor::property_type p) { diff --git a/src/db/db/dbEdgeProcessor.h b/src/db/db/dbEdgeProcessor.h index a6fb2a680..30de5b35d 100644 --- a/src/db/db/dbEdgeProcessor.h +++ b/src/db/db/dbEdgeProcessor.h @@ -695,6 +695,11 @@ public: */ void reserve (size_t n); + /** + * @brief Reports the number of edges stored in the processor + */ + size_t count () const; + /** * @brief Insert an edge */ diff --git a/src/db/db/dbShapeProcessor.cc b/src/db/db/dbShapeProcessor.cc index 518aaa9e7..974fd469b 100644 --- a/src/db/db/dbShapeProcessor.cc +++ b/src/db/db/dbShapeProcessor.cc @@ -53,6 +53,12 @@ ShapeProcessor::reserve (size_t n) m_processor.reserve (n); } +size_t +ShapeProcessor::count () const +{ + return m_processor.count (); +} + void ShapeProcessor::process (db::EdgeSink &es, EdgeEvaluatorBase &op) { diff --git a/src/db/db/dbShapeProcessor.h b/src/db/db/dbShapeProcessor.h index a7afde10c..333c53b69 100644 --- a/src/db/db/dbShapeProcessor.h +++ b/src/db/db/dbShapeProcessor.h @@ -196,6 +196,11 @@ public: */ void reserve (size_t n); + /** + * @brief Reports the number of edges stored in the processor + */ + size_t count () const; + /** * @brief Sets the base verbosity of the processor (see EdgeProcessor::set_base_verbosity for details) */ From 8947c9992fb534c12e7c1a354552717245d22c4f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 17 Feb 2024 21:20:52 +0100 Subject: [PATCH 02/14] Added configuration options for XOR tool to switch between with merge-before and without. --- .../tools/xor/lay_plugin/layXORToolDialog.cc | 169 ++++++++++-------- 1 file changed, 92 insertions(+), 77 deletions(-) diff --git a/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc b/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc index d78cbc9cf..08558adc4 100644 --- a/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc +++ b/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc @@ -52,6 +52,12 @@ namespace lay { +bool merge_before_bool () +{ + // $KLAYOUT_XOR_MERGE_BEFORE_BOOLEAN + return tl::app_flag ("xor-merge-before-boolean"); +} + std::string cfg_xor_input_mode ("xor-input-mode"); std::string cfg_xor_output_mode ("xor-output-mode"); std::string cfg_xor_nworkers ("xor-num-workers"); @@ -864,107 +870,116 @@ XORWorker::do_perform_tiled (const XORTask *xor_task) if (! mp_job->has_tiles ()) { tl::SelfTimer timer (tl::verbosity () >= 21, "Boolean part"); -#if 0 - // Straightforward implementation - sp.boolean (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la, - mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb, - xor_results_cell.shapes (0), op, true, false, true); -#else - // This implementation is faster when a lot of overlapping shapes are involved - db::Layout merge_helper; - db::Cell &merge_helper_cell = merge_helper.cell (merge_helper.add_cell ()); - merge_helper.insert_layer (0); - merge_helper.insert_layer (1); - if (!la.empty ()) { - sp.merge (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la, - merge_helper_cell.shapes (0), true, 0, false, true); + if (! merge_before_bool ()) { +# + // Straightforward implementation + sp.boolean (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la, + mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb, + xor_results_cell.shapes (0), mp_job->op (), true, false, true); + + } else { + + // This implementation is faster when a lot of overlapping shapes are involved + db::Layout merge_helper; + db::Cell &merge_helper_cell = merge_helper.cell (merge_helper.add_cell ()); + merge_helper.insert_layer (0); + merge_helper.insert_layer (1); + + if (!la.empty ()) { + sp.merge (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la, + merge_helper_cell.shapes (0), true, 0, false, true); + } + if (!lb.empty ()) { + sp.merge (mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb, + merge_helper_cell.shapes (1), true, 0, false, true); + } + sp.boolean (merge_helper, merge_helper_cell, 0, + merge_helper, merge_helper_cell, 1, + xor_results_cell.shapes (0), mp_job->op (), true, false, true); + } - if (!lb.empty ()) { - sp.merge (mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb, - merge_helper_cell.shapes (1), true, 0, false, true); - } - sp.boolean (merge_helper, merge_helper_cell, 0, - merge_helper, merge_helper_cell, 1, - xor_results_cell.shapes (0), mp_job->op (), true, false, true); -#endif } else { tl::SelfTimer timer (tl::verbosity () >= 31, "Boolean part"); size_t n; -#if 0 - // Straightforward implementation - sp.clear (); - - db::CplxTrans dbu_scale_a (mp_job->cva ()->layout ().dbu () / xor_results.dbu ()); - db::CplxTrans dbu_scale_b (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); - - n = 0; - for (db::RecursiveShapeIterator s (mp_job->cva ()->layout (), mp_job->cva ().cell (), la, region_a); ! s.at_end (); ++s, ++n) { - sp.insert (s.shape (), dbu_scale_a * s.trans (), n * 2); - } - - n = 0; - for (db::RecursiveShapeIterator s (mp_job->cvb ()->layout (), mp_job->cvb ().cell (), lb, region_b); ! s.at_end (); ++s, ++n) { - sp.insert (s.shape (), dbu_scale_b * s.trans (), n * 2 + 1); - } - - db::BooleanOp bool_op (mp_job->op ()); - db::ShapeGenerator sg (xor_results_cell.shapes (0), true /*clear shapes*/); - db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); - sp.process (out, mp_job->op ()); -#else - // This implementation is faster when a lot of overlapping shapes are involved - db::Layout merge_helper; - merge_helper.dbu (mp_job->dbu ()); - db::Cell &merge_helper_cell = merge_helper.cell (merge_helper.add_cell ()); - merge_helper.insert_layer (0); - merge_helper.insert_layer (1); - - // This implementation is faster when a lot of overlapping shapes are involved - if (!la.empty ()) { + if (! merge_before_bool ()) { + // Straightforward implementation sp.clear (); - db::CplxTrans dbu_scale (mp_job->cva ()->layout ().dbu () / xor_results.dbu ()); + db::CplxTrans dbu_scale_a (mp_job->cva ()->layout ().dbu () / xor_results.dbu ()); + db::CplxTrans dbu_scale_b (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); n = 0; for (db::RecursiveShapeIterator s (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la, xor_task->region_a ()); ! s.at_end (); ++s, ++n) { - sp.insert (s.shape (), dbu_scale * s.trans (), n); + sp.insert (s.shape (), dbu_scale_a * s.trans (), n * 2); } - db::MergeOp op (0); - db::ShapeGenerator sg (merge_helper_cell.shapes (0), true /*clear shapes*/); - db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); - sp.process (out, op); - - } - - if (!lb.empty ()) { - - sp.clear (); - - db::CplxTrans dbu_scale (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); - n = 0; for (db::RecursiveShapeIterator s (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb, xor_task->region_b ()); ! s.at_end (); ++s, ++n) { - sp.insert (s.shape (), dbu_scale * s.trans (), n); + sp.insert (s.shape (), dbu_scale_b * s.trans (), n * 2 + 1); } - db::MergeOp op (0); - db::ShapeGenerator sg (merge_helper_cell.shapes (1), true /*clear shapes*/); + db::BooleanOp bool_op (mp_job->op ()); + db::ShapeGenerator sg (xor_results_cell.shapes (0), true /*clear shapes*/); db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); - sp.process (out, op); + sp.process (out, bool_op); + + } else { + + // This implementation is faster when a lot of overlapping shapes are involved + db::Layout merge_helper; + merge_helper.dbu (mp_job->dbu ()); + db::Cell &merge_helper_cell = merge_helper.cell (merge_helper.add_cell ()); + merge_helper.insert_layer (0); + merge_helper.insert_layer (1); + + // This implementation is faster when a lot of overlapping shapes are involved + if (!la.empty ()) { + + sp.clear (); + + db::CplxTrans dbu_scale (mp_job->cva ()->layout ().dbu () / xor_results.dbu ()); + + n = 0; + for (db::RecursiveShapeIterator s (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la, xor_task->region_a ()); ! s.at_end (); ++s, ++n) { + sp.insert (s.shape (), dbu_scale * s.trans (), n); + } + + db::MergeOp op (0); + db::ShapeGenerator sg (merge_helper_cell.shapes (0), true /*clear shapes*/); + db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); + sp.process (out, op); + + } + + if (!lb.empty ()) { + + sp.clear (); + + db::CplxTrans dbu_scale (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); + + n = 0; + for (db::RecursiveShapeIterator s (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb, xor_task->region_b ()); ! s.at_end (); ++s, ++n) { + sp.insert (s.shape (), dbu_scale * s.trans (), n); + } + + db::MergeOp op (0); + db::ShapeGenerator sg (merge_helper_cell.shapes (1), true /*clear shapes*/); + db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); + sp.process (out, op); + + } + + sp.boolean (merge_helper, merge_helper_cell, 0, + merge_helper, merge_helper_cell, 1, + xor_results_cell.shapes (0), mp_job->op (), true, false, true); } - sp.boolean (merge_helper, merge_helper_cell, 0, - merge_helper, merge_helper_cell, 1, - xor_results_cell.shapes (0), mp_job->op (), true, false, true); -#endif - } } else if (mp_job->op () == db::BooleanOp::Xor || From 1673c472f2a7d9eb51142c10f3f41ce2a3705f8e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 23 Mar 2024 19:23:21 +0100 Subject: [PATCH 03/14] [consider merging] fixed a linker problem for debug builds --- src/tl/tl/tlOptional.cc | 2 +- src/tl/tl/tlOptional.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tl/tl/tlOptional.cc b/src/tl/tl/tlOptional.cc index ba2774439..d7ee370b2 100644 --- a/src/tl/tl/tlOptional.cc +++ b/src/tl/tl/tlOptional.cc @@ -25,6 +25,6 @@ namespace tl { -extern const nullopt_t nullopt = nullopt_t (); +const nullopt_t nullopt = nullopt_t (); } // namespace tl diff --git a/src/tl/tl/tlOptional.h b/src/tl/tl/tlOptional.h index c30fa6679..3893d6ac0 100644 --- a/src/tl/tl/tlOptional.h +++ b/src/tl/tl/tlOptional.h @@ -34,7 +34,7 @@ namespace tl struct nullopt_t {}; -extern const nullopt_t nullopt; +extern TL_PUBLIC const nullopt_t nullopt; /** * @brief Poor man's partial implementation of C++17's std::optional From b9bdcf6facb65c87b76ebf1dd1cc17c4ccfa58e6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 Mar 2024 19:01:36 +0100 Subject: [PATCH 04/14] Preparations: recursive shape iterator shortcuts if hierarchy traversal, needs testing. --- src/db/db/dbAsIfFlatRegion.cc | 2 + src/db/db/dbFlatRegion.cc | 12 ++- src/db/db/dbFlatRegion.h | 3 +- src/db/db/dbRecursiveShapeIterator.cc | 103 +++++++++++++++---- src/db/db/dbRecursiveShapeIterator.h | 51 +++++++-- src/db/db/dbRegion.cc | 36 ++++++- src/db/db/dbRegion.h | 20 +++- src/db/db/gsiDeclDbRecursiveShapeIterator.cc | 18 +++- src/db/db/gsiDeclDbRegion.cc | 54 ++++++---- src/db/unit_tests/dbRegionTests.cc | 29 ++++++ testdata/ruby/dbRegionTest.rb | 41 ++++++++ 11 files changed, 307 insertions(+), 62 deletions(-) diff --git a/src/db/db/dbAsIfFlatRegion.cc b/src/db/db/dbAsIfFlatRegion.cc index 9ee803c2a..2171c4f37 100644 --- a/src/db/db/dbAsIfFlatRegion.cc +++ b/src/db/db/dbAsIfFlatRegion.cc @@ -219,6 +219,8 @@ AsIfFlatRegion::area (const db::Box &box) const for (RegionIterator p (begin_merged ()); ! p.at_end (); ++p) { if (box.empty () || p->box ().inside (box)) { a += p->area (); + } else if (p->is_box ()) { + a += (p->box () & box).area (); } else { std::vector clipped; clip_poly (*p, box, clipped); diff --git a/src/db/db/dbFlatRegion.cc b/src/db/db/dbFlatRegion.cc index d96bb8a22..0d79dbe3e 100644 --- a/src/db/db/dbFlatRegion.cc +++ b/src/db/db/dbFlatRegion.cc @@ -43,7 +43,6 @@ FlatRegion::FlatRegion (const FlatRegion &other) : MutableRegion (other), mp_polygons (other.mp_polygons), mp_merged_polygons (other.mp_merged_polygons), mp_properties_repository (other.mp_properties_repository) { init (); - m_is_merged = other.m_is_merged; m_merged_polygons_valid = other.m_merged_polygons_valid; } @@ -52,15 +51,22 @@ FlatRegion::FlatRegion (const db::Shapes &polygons, bool is_merged) : MutableRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false)), mp_properties_repository (new db::PropertiesRepository ()) { init (); - m_is_merged = is_merged; } +FlatRegion::FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged) + : MutableRegion (), mp_polygons (new db::Shapes (polygons)), mp_merged_polygons (new db::Shapes (false)), mp_properties_repository (new db::PropertiesRepository ()) +{ + init (); + m_is_merged = is_merged; + transform_generic (trans); + set_merged_semantics (merged_semantics); +} + FlatRegion::FlatRegion (bool is_merged) : MutableRegion (), mp_polygons (new db::Shapes (false)), mp_merged_polygons (new db::Shapes (false)), mp_properties_repository (new db::PropertiesRepository ()) { init (); - m_is_merged = is_merged; } diff --git a/src/db/db/dbFlatRegion.h b/src/db/db/dbFlatRegion.h index d89e53173..55805a8a5 100644 --- a/src/db/db/dbFlatRegion.h +++ b/src/db/db/dbFlatRegion.h @@ -52,7 +52,8 @@ public: typedef polygon_layer_wp_type::iterator polygon_iterator_wp_type; FlatRegion (); - FlatRegion (const db::Shapes &polygons, bool is_merged); + FlatRegion (const db::Shapes &polygons, bool is_merged = false); + FlatRegion (const db::Shapes &polygons, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged = false); FlatRegion (bool is_merged); FlatRegion (const FlatRegion &other); diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index 9a8f6052f..0534ebd3f 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -48,6 +48,8 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI mp_shape_prop_sel = d.mp_shape_prop_sel; m_shape_inv_prop_sel = d.m_shape_inv_prop_sel; m_overlapping = d.m_overlapping; + m_for_merged_input = d.m_for_merged_input; + m_start = d.m_start; m_stop = d.m_stop; @@ -99,6 +101,7 @@ RecursiveShapeIterator::RecursiveShapeIterator () mp_cell = 0; m_current_layer = 0; m_overlapping = false; + m_for_merged_input = false; m_max_depth = std::numeric_limits::max (); // all m_min_depth = 0; m_shape_flags = shape_iterator::All; @@ -116,6 +119,7 @@ RecursiveShapeIterator::RecursiveShapeIterator (const shapes_type &shapes) mp_shapes = &shapes; mp_top_cell = 0; m_overlapping = false; + m_for_merged_input = false; init (); init_region (box_type::world ()); } @@ -127,6 +131,7 @@ RecursiveShapeIterator::RecursiveShapeIterator (const shapes_type &shapes, const mp_shapes = &shapes; mp_top_cell = 0; m_overlapping = overlapping; + m_for_merged_input = false; init (); init_region (region); } @@ -138,11 +143,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const shapes_type &shapes, const mp_shapes = &shapes; mp_top_cell = 0; m_overlapping = overlapping; + m_for_merged_input = false; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const box_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const box_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout, layer) { m_layer = layer; @@ -151,11 +157,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const region_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const region_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout, layer) { m_layer = layer; @@ -164,11 +171,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, bool for_merged_input) : m_box_convert (layout, layer) { m_layer = layer; @@ -177,11 +185,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = false; + m_for_merged_input = for_merged_input; init (); init_region (box_type::world ()); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const box_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const box_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -191,11 +200,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const region_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const region_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -205,11 +215,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -219,11 +230,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = false; + m_for_merged_input = for_merged_input; init (); init_region (box_type::world ()); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const box_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const box_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -233,11 +245,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const region_type ®ion, bool overlapping) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const region_type ®ion, bool overlapping, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -247,11 +260,12 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = overlapping; + m_for_merged_input = for_merged_input; init (); init_region (region); } -RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers) +RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, bool for_merged_input) : m_box_convert (layout) { m_layer = 0; @@ -261,6 +275,7 @@ RecursiveShapeIterator::RecursiveShapeIterator (const layout_type &layout, const mp_shapes = 0; mp_top_cell = &cell; m_overlapping = false; + m_for_merged_input = for_merged_input; init (); init_region (box_type::world ()); } @@ -721,13 +736,9 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const } - if (is_empty) { - + if (is_empty || !down (receiver)) { ++m_inst; new_inst (receiver); - - } else { - down (receiver); } } else { @@ -755,7 +766,7 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const } } -void +bool RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const { tl_assert (mp_layout); @@ -783,6 +794,40 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const new_region = m_trans.inverted () * m_region; new_region &= cell_bbox (cell_index ()); } + + // try some optimization - only consider optimizing by dropping the shape-covered area under certain circumstances: + // - single layer + // - less than 32 shapes to consider + // - total shape bbox in current region covers at least a third of it + // - total area of shapes in current region is at least a third of it + // TODO: the current implementation does not touch the complex search region + + if (m_for_merged_input && (! m_has_layers || m_layers.size () == 1) && ! new_region.empty ()) { + + unsigned int l = m_has_layers ? m_layers.front () : m_layer; + const shapes_type &shapes = m_cells.back ()->shapes (l); + box_type region_in_parent = m_inst->complex_trans (*m_inst_array) * new_region; + + // NOTE: new_region is already in the coordinate system of the child cell + + if (shapes.size () < 32 && + 3 * (shapes.bbox () & region_in_parent).area () > region_in_parent.area ()) { + + region_type shapes_region (shapes); + if (3 * shapes_region.area (region_in_parent) > region_in_parent.area ()) { + + shapes_region.transform (m_inst->complex_trans (*m_inst_array).inverted ()); + + // reduce the search region for less instances to look up + region_type new_complex_region = region_type (new_region) - shapes_region; + new_region = new_complex_region.bbox (); + + } + + } + + } + m_local_region_stack.push_back (new_region); if (! m_local_complex_region_stack.empty ()) { @@ -817,11 +862,25 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const } - if (receiver) { - receiver->enter_cell (this, cell (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ()); - } + // do not descend if the box is empty - new_cell (receiver); + if (m_local_region_stack.back ().empty ()) { + + pop (); + + return false; + + } else { + + if (receiver) { + receiver->enter_cell (this, cell (), m_local_region_stack.back (), m_local_complex_region_stack.empty () ? 0 : &m_local_complex_region_stack.back ()); + } + + new_cell (receiver); + + return true; + + } } void @@ -831,6 +890,12 @@ RecursiveShapeIterator::up (RecursiveShapeReceiver *receiver) const receiver->leave_cell (this, cell ()); } + pop (); +} + +void +RecursiveShapeIterator::pop () const +{ m_shape = shape_iterator (); m_shape_quad_id = 0; diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index 43f913b1a..b8e6a640a 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -122,12 +122,13 @@ public: * @param layer The layer from which to deliver the shapes * @param region The region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const box_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const box_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor @@ -137,13 +138,14 @@ public: * @param layer The layer from which to deliver the shapes * @param region The complex region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. It allows specification of a complex * search region. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const region_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, const region_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor for "world" iteration @@ -153,8 +155,9 @@ public: * @param layout The layout from which to get the cell hierarchy * @param cell The starting cell * @param layer The layer from which to deliver the shapes + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, unsigned int layer, bool for_merged_input = false); /** * @brief Standard constructor with a layer selection @@ -164,12 +167,13 @@ public: * @param layers The layers from which to deliver the shapes * @param region The region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const box_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const box_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor with a layer selection @@ -179,13 +183,14 @@ public: * @param layers The layers from which to deliver the shapes * @param region The complex region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. It allows specification of a complex * search region. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const region_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, const region_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor with a layer selection @@ -195,12 +200,13 @@ public: * @param layers The layers from which to deliver the shapes * @param region The region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const box_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const box_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor with a layer selection @@ -210,13 +216,14 @@ public: * @param layers The layers from which to deliver the shapes * @param region The complex region from which to select the shapes * @param overlapping Specify overlapping mode + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others * * By default the iterator operates in touching mode - i.e. shapes that touch the given region * are returned. By specifying the "overlapping" flag with a true value, the iterator delivers shapes that * overlap the given region by at least one database unit. It allows specification of a complex * search region. */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const region_type ®ion, bool overlapping = false); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, const region_type ®ion, bool overlapping = false, bool for_merged_input = false); /** * @brief Standard constructor for "world" iteration with a layer set @@ -226,8 +233,9 @@ public: * @param layout The layout from which to get the cell hierarchy * @param cell The starting cell * @param layers The layers from which to deliver the shapes + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::vector &layers, bool for_merged_input = false); /** * @brief Standard constructor for "world" iteration with a layer set @@ -237,8 +245,9 @@ public: * @param layout The layout from which to get the cell hierarchy * @param cell The starting cell * @param layers The layers from which to deliver the shapes + * @param for_merged_input Optimize for merged input - drop shapes that are completely covered by others */ - RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers); + RecursiveShapeIterator (const layout_type &layout, const cell_type &cell, const std::set &layers, bool for_merged_input = false); /** * @brief Destructor @@ -427,6 +436,25 @@ public: } } + /** + * @brief Gets a flag indicating whether optimizing for merged input + */ + bool for_merged_input () const + { + return m_for_merged_input; + } + + /** + * @brief Sets a flag indicating whether optimizing for merged input + */ + void set_for_merged_input (bool f) + { + if (m_for_merged_input != f) { + m_for_merged_input = f; + m_needs_reinit = true; + } + } + /** * @brief Sets a global transformation * @@ -812,7 +840,7 @@ private: unsigned int m_shape_flags; const shape_iterator::property_selector *mp_shape_prop_sel; bool m_shape_inv_prop_sel; - bool m_overlapping; + bool m_overlapping, m_for_merged_input; std::set m_start, m_stop; cplx_trans_type m_global_trans; db::PropertiesTranslator m_property_translator; @@ -858,7 +886,8 @@ private: void new_cell (RecursiveShapeReceiver *receiver) const; void new_layer () const; void up (RecursiveShapeReceiver *receiver) const; - void down (RecursiveShapeReceiver *receiver) const; + bool down (RecursiveShapeReceiver *receiver) const; + void pop () const; bool is_outside_complex_region (const db::Box &box) const; diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index 488fbc460..c05f9d338 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -74,14 +74,42 @@ Region &Region::operator= (const Region &other) return *this; } -Region::Region (const RecursiveShapeIterator &si) +Region::Region (const RecursiveShapeIterator &si, bool merged_semantics, bool is_merged) { - mp_delegate = new OriginalLayerRegion (si); + mp_delegate = new OriginalLayerRegion (si, db::ICplxTrans (), merged_semantics, is_merged); } -Region::Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics) +Region::Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged) { - mp_delegate = new OriginalLayerRegion (si, trans, merged_semantics); + mp_delegate = new OriginalLayerRegion (si, trans, merged_semantics, is_merged); +} + +Region::Region (const Shapes &shapes, bool merged_semantics, bool is_merged) +{ + db::FlatRegion *flat_region = new FlatRegion (is_merged); + flat_region->reserve (shapes.size (db::ShapeIterator::Regions)); + + // NOTE: we need to normalize the shapes to polygons because this is what the flat region expects + for (auto s = shapes.begin (db::ShapeIterator::Regions); ! s.at_end (); ++s) { + flat_region->insert (*s); + } + + mp_delegate = flat_region; + mp_delegate->set_merged_semantics (merged_semantics); +} + +Region::Region (const Shapes &shapes, const db::ICplxTrans &trans, bool merged_semantics, bool is_merged) +{ + db::FlatRegion *flat_region = new FlatRegion (is_merged); + flat_region->reserve (shapes.size (db::ShapeIterator::Regions)); + + // NOTE: we need to normalize the shapes to polygons because this is what the flat region expects + for (auto s = shapes.begin (db::ShapeIterator::Regions); ! s.at_end (); ++s) { + flat_region->insert (*s, trans); + } + + mp_delegate = flat_region; + mp_delegate->set_merged_semantics (merged_semantics); } Region::Region (const RecursiveShapeIterator &si, DeepShapeStore &dss, double area_ratio, size_t max_vertex_count) diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 39880b2ca..1329adcc8 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -199,7 +199,7 @@ public: * Creates a region from a recursive shape iterator. This allows feeding a region * from a hierarchy of cells. */ - explicit Region (const RecursiveShapeIterator &si); + explicit Region (const RecursiveShapeIterator &si, bool merged_semantics = true, bool is_merged = false); /** * @brief Constructor from a RecursiveShapeIterator with a transformation @@ -208,7 +208,23 @@ public: * from a hierarchy of cells. The transformation is useful to scale to a specific * DBU for example. */ - explicit Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics = true); + explicit Region (const RecursiveShapeIterator &si, const db::ICplxTrans &trans, bool merged_semantics = true, bool is_merged = false); + + /** + * @brief Constructor from a Shapes container + * + * Creates a region from a shapes container. + */ + explicit Region (const Shapes &si, bool merged_semantics = true, bool is_merged = false); + + /** + * @brief Constructor from a Shapes container with a transformation + * + * Creates a region from a recursive shape iterator. This allows feeding a region + * from a hierarchy of cells. The transformation is useful to scale to a specific + * DBU for example. + */ + explicit Region (const Shapes &si, const db::ICplxTrans &trans, bool merged_semantics = true, bool is_merged = false); /** * @brief Constructor from a RecursiveShapeIterator providing a deep representation diff --git a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc index bc702af0f..50f840eee 100644 --- a/src/db/db/gsiDeclDbRecursiveShapeIterator.cc +++ b/src/db/db/gsiDeclDbRecursiveShapeIterator.cc @@ -476,13 +476,29 @@ Class decl_RecursiveShapeIterator ("db", "RecursiveS "\n" "This method has been introduced in version 0.23.\n" ) + - gsi::method ("overlapping=", &db::RecursiveShapeIterator::set_overlapping, gsi::arg ("region"), + gsi::method ("overlapping=", &db::RecursiveShapeIterator::set_overlapping, gsi::arg ("flag"), "@brief Sets a flag indicating whether overlapping shapes are selected when a region is used\n" "\n" "If this flag is false, shapes touching the search region are returned.\n" "\n" "This method has been introduced in version 0.23.\n" ) + + gsi::method ("for_merged_input?", &db::RecursiveShapeIterator::for_merged_input, + "@brief Gets a flag indicating whether iterator optimizes for merged input\n" + "\n" + "see \\for_merged_input= for details of this attribute.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + + gsi::method ("for_merged_input=", &db::RecursiveShapeIterator::set_for_merged_input, gsi::arg ("flag"), + "@brief Sets a flag indicating whether iterator optimizes for merged input\n" + "\n" + "If this flag is set to true, the iterator is allowed to skip shapes it deems irrelevant " + "because they are covered entirely by other shapes. This allows shortcutting hierarchy traversal in " + "some cases.\n" + "\n" + "This method has been introduced in version 0.29.\n" + ) + gsi::method ("unselect_all_cells", &db::RecursiveShapeIterator::unselect_all_cells, "@brief Unselects all cells.\n" "\n" diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index f978a7c4f..ad941cc27 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -270,15 +270,6 @@ static db::Region *new_path (const db::Path &o) return new db::Region (o); } -static db::Region *new_shapes (const db::Shapes &s) -{ - db::Region *r = new db::Region (); - for (db::Shapes::shape_iterator i = s.begin (db::ShapeIterator::All); !i.at_end (); ++i) { - r->insert (*i); - } - return r; -} - static db::Region *new_texts_as_boxes1 (const db::RecursiveShapeIterator &si, const std::string &pat, bool pattern, db::Coord enl) { return new db::Region (db::Region (si).texts_as_boxes (pat, pattern, enl)); @@ -329,16 +320,26 @@ static db::Region *new_si (const db::RecursiveShapeIterator &si) return new db::Region (si); } -static db::Region *new_sid (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, double area_ratio, size_t max_vertex_count) -{ - return new db::Region (si, dss, area_ratio, max_vertex_count); -} - static db::Region *new_si2 (const db::RecursiveShapeIterator &si, const db::ICplxTrans &trans) { return new db::Region (si, trans); } +static db::Region *new_sis (const db::Shapes &si) +{ + return new db::Region (si); +} + +static db::Region *new_sis2 (const db::Shapes &si, const db::ICplxTrans &trans) +{ + return new db::Region (si, trans); +} + +static db::Region *new_sid (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, double area_ratio, size_t max_vertex_count) +{ + return new db::Region (si, dss, area_ratio, max_vertex_count); +} + static db::Region *new_sid2 (const db::RecursiveShapeIterator &si, db::DeepShapeStore &dss, const db::ICplxTrans &trans, double area_ratio, size_t max_vertex_count) { return new db::Region (si, dss, trans, true, area_ratio, max_vertex_count); @@ -1088,13 +1089,6 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This constructor creates a region from a path.\n" ) + - constructor ("new", &new_shapes, gsi::arg ("shapes"), - "@brief Shapes constructor\n" - "\n" - "This constructor creates a region from a \\Shapes collection.\n" - "\n" - "This constructor has been introduced in version 0.25." - ) + constructor ("new", &new_si, gsi::arg ("shape_iterator"), "@brief Constructor from a hierarchical shape set\n" "\n" @@ -1126,6 +1120,24 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "r = RBA::Region::new(layout.begin_shapes(cell, layer), RBA::ICplxTrans::new(layout.dbu / dbu))\n" "@/code\n" ) + + constructor ("new", &new_sis, gsi::arg ("shapes"), + "@brief Constructor from a shapes container\n" + "\n" + "This constructor creates a region from the shapes container.\n" + "Text objects and edges are not inserted, because they cannot be converted to polygons.\n" + "This method allows feeding the shapes from a hierarchy of cells into the region.\n" + "\n" + "This constructor has been introduced in version 0.25 and extended in version 0.29." + ) + + constructor ("new", &new_sis2, gsi::arg ("shapes"), gsi::arg ("trans"), + "@brief Constructor from a shapes container with a transformation\n" + "\n" + "This constructor creates a region from the shapes container after applying the transformation.\n" + "Text objects and edges are not inserted, because they cannot be converted to polygons.\n" + "This method allows feeding the shapes from a hierarchy of cells into the region.\n" + "\n" + "This constructor variant has been introduced in version 0.29." + ) + constructor ("new", &new_sid, gsi::arg ("shape_iterator"), gsi::arg ("deep_shape_store"), gsi::arg ("area_ratio", 0.0), gsi::arg ("max_vertex_count", size_t (0)), "@brief Constructor for a deep region from a hierarchical shape set\n" "\n" diff --git a/src/db/unit_tests/dbRegionTests.cc b/src/db/unit_tests/dbRegionTests.cc index 554f25956..70ac453b2 100644 --- a/src/db/unit_tests/dbRegionTests.cc +++ b/src/db/unit_tests/dbRegionTests.cc @@ -2537,6 +2537,35 @@ TEST(55_PropertiesFilterFlat) EXPECT_EQ (s->to_string (), "(1,2;1,202;101,202;101,2)"); } +TEST(56_RegionsFromShapes) +{ + db::Shapes shapes; + + shapes.insert (db::Box (0, 0, 100, 200)); + shapes.insert (db::Box (50, 50, 150, 250)); + + EXPECT_EQ (db::Region (shapes).area (), 32500); + EXPECT_EQ (db::Region (shapes, false).area (), 40000); + EXPECT_EQ (db::Region (shapes, db::ICplxTrans (0.5)).area (), 8125); + EXPECT_EQ (db::Region (shapes, db::ICplxTrans (0.5), false).area (), 10000); + + // for cross-checking: same for RecursiveShapeIterator + + db::Layout layout; + unsigned int l1 = layout.insert_layer (); + db::Cell &top = layout.cell (layout.add_cell ("TOP")); + + top.shapes (l1).insert (db::Box (0, 0, 100, 200)); + top.shapes (l1).insert (db::Box (50, 50, 150, 250)); + + db::RecursiveShapeIterator si (layout, top, l1); + + EXPECT_EQ (db::Region (si).area (), 32500); + EXPECT_EQ (db::Region (si, false).area (), 40000); + EXPECT_EQ (db::Region (si, db::ICplxTrans (0.5)).area (), 8125); + EXPECT_EQ (db::Region (si, db::ICplxTrans (0.5), false).area (), 10000); +} + TEST(100_Processors) { db::Region r; diff --git a/testdata/ruby/dbRegionTest.rb b/testdata/ruby/dbRegionTest.rb index 0d81b5efd..8c59d587c 100644 --- a/testdata/ruby/dbRegionTest.rb +++ b/testdata/ruby/dbRegionTest.rb @@ -1066,6 +1066,47 @@ class DBRegion_TestClass < TestBase end + # regions from Shapes + def test_regions_from_shapes + + shapes = RBA::Shapes::new; + + shapes.insert(RBA::Box::new(0, 0, 100, 200)) + shapes.insert(RBA::Box::new(50, 50, 150, 250)) + + assert_equal(RBA::Region::new(shapes).area, 32500) + region = RBA::Region::new(shapes) + region.merged_semantics = false + assert_equal(region.area, 40000) + + assert_equal(RBA::Region::new(shapes, RBA::ICplxTrans::new(0.5)).area, 8125) + region = RBA::Region::new(shapes, RBA::ICplxTrans::new(0.5)) + region.merged_semantics = false + assert_equal(region.area, 10000) + + # for cross-checking: same for RecursiveShapeIterator + + layout = RBA::Layout::new + l1 = layout.insert_layer(RBA::LayerInfo::new(1, 0)) + top = layout.create_cell("TOP") + + top.shapes(l1).insert (RBA::Box::new(0, 0, 100, 200)) + top.shapes(l1).insert (RBA::Box::new(50, 50, 150, 250)) + + si = RBA::RecursiveShapeIterator::new(layout, top, l1) + + assert_equal(RBA::Region::new(si).area, 32500) + region = RBA::Region::new(si) + region.merged_semantics = false + assert_equal(region.area, 40000) + + assert_equal(RBA::Region::new(si, RBA::ICplxTrans::new(0.5)).area, 8125) + region = RBA::Region::new(si, RBA::ICplxTrans::new(0.5)) + region.merged_semantics = false + assert_equal(region.area, 10000) + + end + # deep region tests def test_deep1 From 3cf8b29699a1e883fbec8ca102e3a635517e28fc Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 Mar 2024 21:57:39 +0100 Subject: [PATCH 05/14] RecursiveShapeIterator debugging --- src/db/db/dbRecursiveShapeIterator.cc | 97 ++++++++++++------- .../dbRecursiveShapeIteratorTests.cc | 72 ++++++++++++++ 2 files changed, 132 insertions(+), 37 deletions(-) diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index 0534ebd3f..33f6c0448 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -790,44 +790,11 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const box_type new_region = box_type::world (); // compute the region inside the new cell - if (new_region != m_region) { - new_region = m_trans.inverted () * m_region; + if (new_region != m_local_region_stack.back ()) { + new_region = m_inst->complex_trans (*m_inst_array).inverted () * m_local_region_stack.back (); new_region &= cell_bbox (cell_index ()); } - // try some optimization - only consider optimizing by dropping the shape-covered area under certain circumstances: - // - single layer - // - less than 32 shapes to consider - // - total shape bbox in current region covers at least a third of it - // - total area of shapes in current region is at least a third of it - // TODO: the current implementation does not touch the complex search region - - if (m_for_merged_input && (! m_has_layers || m_layers.size () == 1) && ! new_region.empty ()) { - - unsigned int l = m_has_layers ? m_layers.front () : m_layer; - const shapes_type &shapes = m_cells.back ()->shapes (l); - box_type region_in_parent = m_inst->complex_trans (*m_inst_array) * new_region; - - // NOTE: new_region is already in the coordinate system of the child cell - - if (shapes.size () < 32 && - 3 * (shapes.bbox () & region_in_parent).area () > region_in_parent.area ()) { - - region_type shapes_region (shapes); - if (3 * shapes_region.area (region_in_parent) > region_in_parent.area ()) { - - shapes_region.transform (m_inst->complex_trans (*m_inst_array).inverted ()); - - // reduce the search region for less instances to look up - region_type new_complex_region = region_type (new_region) - shapes_region; - new_region = new_complex_region.bbox (); - - } - - } - - } - m_local_region_stack.push_back (new_region); if (! m_local_complex_region_stack.empty ()) { @@ -966,7 +933,59 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const new_layer (); - m_inst = cell ()->begin_touching (m_local_region_stack.back ()); + // try some optimization - only consider optimizing by dropping the shape-covered area under certain circumstances: + // - single layer + // - less than 32 shapes to consider + // - total shape bbox in current region covers at least a third of it + // - total area of shapes in current region is at least a third of it + // + // NOTE that this implementation can modify the search box on the box stack + // because we did "new_layer()" already and this function is not going to + // be called, because we do so only for single layers. + + const box_type ®ion = m_local_region_stack.back (); + + if (m_for_merged_input && (! m_has_layers || m_layers.size () == 1) && ! region.empty ()) { + + unsigned int l = m_has_layers ? m_layers.front () : m_layer; + const shapes_type &shapes = cell ()->shapes (l); + + if (! shapes.empty () && shapes.size () < 32 && + 3 * (shapes.bbox () & region).area () > region.area ()) { + + region_type shapes_region (shapes); + if (3 * shapes_region.area (region) > region.area ()) { + + // Need to enlarge the empty area somewhat so we really exclude instances + // entirely enclosed by the shape - also the ones at the border. + box_type::vector_type bias; + if (! m_overlapping) { + bias = box_type::vector_type (1, 1); + } + + // reduce the search region for less instances to look up + // NOTE: because we use "touching" for the instances below, we + region_type new_complex_region; + if (region == box_type::world ()) { + new_complex_region = region_type (cell ()->bbox ()) - shapes_region; + } else { + new_complex_region = region_type (cell ()->bbox () & region.enlarged (bias)) - shapes_region; + } + + // TODO: the current implementation does not touch the complex search region + m_local_region_stack.back () = new_complex_region.bbox ().enlarged (-bias); + + } + + } + + } + + if (m_overlapping) { + m_inst = cell ()->begin_touching (m_local_region_stack.back ().enlarged (box_type::vector_type (-1, -1))); + } else { + m_inst = cell ()->begin_touching (m_local_region_stack.back ()); + } m_inst_quad_id = 0; @@ -1015,7 +1034,11 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const // a singular iterator m_inst_array = db::CellInstArray::iterator (m_inst->cell_inst ().front (), false); } else if (with_region) { - m_inst_array = m_inst->cell_inst ().begin_touching (m_local_region_stack.back (), m_box_convert); + if (m_overlapping) { + m_inst_array = m_inst->cell_inst ().begin_touching (m_local_region_stack.back ().enlarged (box_type::vector_type (-1, -1)), m_box_convert); + } else { + m_inst_array = m_inst->cell_inst ().begin_touching (m_local_region_stack.back (), m_box_convert); + } } else { m_inst_array = m_inst->cell_inst ().begin (); } diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index c89af7605..c4fec7476 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -1554,3 +1554,75 @@ TEST(11_LayoutIsWeakPointer) x = collect(i1, *g); EXPECT_EQ (x, ""); } + +TEST(12_ForMerged) +{ + std::unique_ptr g (new db::Layout ()); + g->insert_layer (0); + g->insert_layer (1); + db::Cell &c0 (g->cell (g->add_cell ())); + db::Cell &c1 (g->cell (g->add_cell ())); + db::Cell &c2 (g->cell (g->add_cell ())); + db::Cell &c3 (g->cell (g->add_cell ())); + + db::Box b (0, 100, 1000, 1200); + c0.shapes (0).insert (db::Box (0, 0, 3000, 2000)); + c1.shapes (0).insert (b); + c2.shapes (0).insert (b); + c3.shapes (0).insert (b); + + db::Trans tt; + c0.insert (db::CellInstArray (db::CellInst (c1.cell_index ()), tt)); + c0.insert (db::CellInstArray (db::CellInst (c2.cell_index ()), db::Trans (db::Vector (100, -100)))); + c0.insert (db::CellInstArray (db::CellInst (c3.cell_index ()), db::Trans (1))); + c2.insert (db::CellInstArray (db::CellInst (c3.cell_index ()), db::Trans (db::Vector (1100, 0)))); + + std::string x; + + db::RecursiveShapeIterator i1 (*g, c0, 0); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + + i1.set_for_merged_input (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + + std::vector lv; + lv.push_back (0); + i1 = db::RecursiveShapeIterator (*g, c0, lv); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + + i1.set_for_merged_input (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + + lv.push_back (1); // empty, but kills "for merged" optimization + i1 = db::RecursiveShapeIterator (*g, c0, lv); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + + i1.set_for_merged_input (true); + x = collect(i1, *g); + // no longer optimized + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$2](0,100;1000,1200)/[$3](100,0;1100,1100)/[$4](1200,0;2200,1100)/[$4](-1200,0;-100,1000)"); + + i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-100, 0, 100, 50)); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$3](100,0;1100,1100)/[$4](-1200,0;-100,1000)"); + + i1.set_for_merged_input (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + + i1 = db::RecursiveShapeIterator (*g, c0, 0, db::Box (-101, 0, 100, 50)); + i1.set_overlapping (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + + i1.set_for_merged_input (true); + x = collect(i1, *g); + EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); + + // ... +} From ab93dde25c09b2f53500e1e9f28165183540430c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 Mar 2024 22:11:07 +0100 Subject: [PATCH 06/14] Tests for GSI binding --- testdata/ruby/dbRecursiveShapeIterator.rb | 32 +++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/testdata/ruby/dbRecursiveShapeIterator.rb b/testdata/ruby/dbRecursiveShapeIterator.rb index 29724541c..f4d2d9b9b 100644 --- a/testdata/ruby/dbRecursiveShapeIterator.rb +++ b/testdata/ruby/dbRecursiveShapeIterator.rb @@ -322,6 +322,38 @@ END end + def test_3 + + l = RBA::Layout.new + l.insert_layer_at(0, RBA::LayerInfo.new(1, 0)) + l.insert_layer_at(1, RBA::LayerInfo.new(2, 0)) + c0 = l.cell(l.add_cell("c0")) + c1 = l.cell(l.add_cell("c1")) + c2 = l.cell(l.add_cell("c2")) + c3 = l.cell(l.add_cell("c3")) + + b = RBA::Box.new(0, 100, 1000, 1200) + c0.shapes(0).insert(RBA::Box.new(0, 0, 3000, 2000)) + c1.shapes(0).insert(b) + c2.shapes(0).insert(b) + c3.shapes(0).insert(b) + + tt = RBA::Trans.new + c0.insert(RBA::CellInstArray.new(c1.cell_index, tt)) + c0.insert(RBA::CellInstArray.new(c2.cell_index, RBA::Trans.new(RBA::Vector.new(100, -100)))) + c0.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(1))) + c2.insert(RBA::CellInstArray.new(c3.cell_index, RBA::Trans.new(RBA::Vector.new(1100, 0)))) + + ii = RBA::RecursiveShapeIterator.new(l, c0, 0) + assert_equal(ii.for_merged_input, false) + assert_equal(collect(ii, l), "[c0](0,0;3000,2000)/[c1](0,100;1000,1200)/[c2](100,0;1100,1100)/[c3](1200,0;2200,1100)/[c3](-1200,0;-100,1000)") + + ii.for_merged_input = true + assert_equal(ii.for_merged_input, true) + assert_equal(collect(ii, l), "[c0](0,0;3000,2000)/[c3](-1200,0;-100,1000)") + + end + end load("test_epilogue.rb") From b4c7176c52117b04bac158048fc18bac7f525919 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 Mar 2024 22:45:58 +0100 Subject: [PATCH 07/14] Bug fixing --- src/db/db/dbRecursiveShapeIterator.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index 33f6c0448..7fe8b99ed 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -967,9 +967,9 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const // NOTE: because we use "touching" for the instances below, we region_type new_complex_region; if (region == box_type::world ()) { - new_complex_region = region_type (cell ()->bbox ()) - shapes_region; + new_complex_region = region_type (cell ()->bbox (l)) - shapes_region; } else { - new_complex_region = region_type (cell ()->bbox () & region.enlarged (bias)) - shapes_region; + new_complex_region = region_type (cell ()->bbox (l) & region.enlarged (bias)) - shapes_region; } // TODO: the current implementation does not touch the complex search region From 254f598a087fa5329bfd77c83a2d8ee2db36067d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 Mar 2024 23:03:08 +0100 Subject: [PATCH 08/14] Deploying solution for XOR tool. Needs testing. --- .../tools/xor/lay_plugin/layXORToolDialog.cc | 184 +++++++++--------- 1 file changed, 91 insertions(+), 93 deletions(-) diff --git a/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc b/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc index 08558adc4..83d2c66e1 100644 --- a/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc +++ b/src/plugins/tools/xor/lay_plugin/layXORToolDialog.cc @@ -772,6 +772,9 @@ XORWorker::do_perform_deep (const XORTask *xor_task) db::RecursiveShapeIterator s_a (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la, xor_task->region_a ()); db::RecursiveShapeIterator s_b (mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb, xor_task->region_b ()); + s_a.set_for_merged_input (true); + s_b.set_for_merged_input (true); + db::Region ra (s_a, dss, db::ICplxTrans (mp_job->cva ()->layout ().dbu () / mp_job->dbu ())); db::Region rb (s_b, dss, db::ICplxTrans (mp_job->cvb ()->layout ().dbu () / mp_job->dbu ())); @@ -800,6 +803,8 @@ XORWorker::do_perform_deep (const XORTask *xor_task) dbu_scale = db::ICplxTrans (mp_job->cvb ()->layout ().dbu () / mp_job->dbu ()); } + s.set_for_merged_input (true); + rr = db::Region (s, dss, dbu_scale); } @@ -867,119 +872,110 @@ XORWorker::do_perform_tiled (const XORTask *xor_task) if ((!la.empty () && !lb.empty ()) || mp_job->el_handling () == XORJob::EL_process) { - if (! mp_job->has_tiles ()) { + tl::SelfTimer timer (tl::verbosity () >= 31, "Boolean part"); + size_t n; - tl::SelfTimer timer (tl::verbosity () >= 21, "Boolean part"); + if (! merge_before_bool ()) { - if (! merge_before_bool ()) { -# - // Straightforward implementation - sp.boolean (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la, - mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb, - xor_results_cell.shapes (0), mp_job->op (), true, false, true); + // Straightforward implementation + sp.clear (); + db::CplxTrans dbu_scale_a (mp_job->cva ()->layout ().dbu () / xor_results.dbu ()); + db::CplxTrans dbu_scale_b (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); + + n = 0; + db::RecursiveShapeIterator s_a; + if (mp_job->has_tiles ()) { + s_a = db::RecursiveShapeIterator (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la, xor_task->region_a ()); } else { - - // This implementation is faster when a lot of overlapping shapes are involved - db::Layout merge_helper; - db::Cell &merge_helper_cell = merge_helper.cell (merge_helper.add_cell ()); - merge_helper.insert_layer (0); - merge_helper.insert_layer (1); - - if (!la.empty ()) { - sp.merge (mp_job->cva ()->layout (), mp_job->cva ()->layout ().cell (mp_job->cva ().cell_index ()), la, - merge_helper_cell.shapes (0), true, 0, false, true); - } - if (!lb.empty ()) { - sp.merge (mp_job->cvb ()->layout (), mp_job->cvb ()->layout ().cell (mp_job->cvb ().cell_index ()), lb, - merge_helper_cell.shapes (1), true, 0, false, true); - } - sp.boolean (merge_helper, merge_helper_cell, 0, - merge_helper, merge_helper_cell, 1, - xor_results_cell.shapes (0), mp_job->op (), true, false, true); - + s_a = db::RecursiveShapeIterator (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la); } + s_a.set_for_merged_input (true); + for ( ; ! s_a.at_end (); ++s_a, ++n) { + sp.insert (s_a.shape (), dbu_scale_a * s_a.trans (), n * 2); + } + + n = 0; + db::RecursiveShapeIterator s_b; + if (mp_job->has_tiles ()) { + s_b = db::RecursiveShapeIterator (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb, xor_task->region_b ()); + } else { + s_b = db::RecursiveShapeIterator (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb); + } + s_b.set_for_merged_input (true); + for (; ! s_b.at_end (); ++s_b, ++n) { + sp.insert (s_b.shape (), dbu_scale_b * s_b.trans (), n * 2 + 1); + } + + db::BooleanOp bool_op (mp_job->op ()); + db::ShapeGenerator sg (xor_results_cell.shapes (0), true /*clear shapes*/); + db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); + sp.process (out, bool_op); } else { - tl::SelfTimer timer (tl::verbosity () >= 31, "Boolean part"); - size_t n; + // This implementation is faster when a lot of overlapping shapes are involved + db::Layout merge_helper; + merge_helper.dbu (mp_job->dbu ()); + db::Cell &merge_helper_cell = merge_helper.cell (merge_helper.add_cell ()); + merge_helper.insert_layer (0); + merge_helper.insert_layer (1); - if (! merge_before_bool ()) { + // This implementation is faster when a lot of overlapping shapes are involved + if (!la.empty ()) { - // Straightforward implementation sp.clear (); - db::CplxTrans dbu_scale_a (mp_job->cva ()->layout ().dbu () / xor_results.dbu ()); - db::CplxTrans dbu_scale_b (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); + db::CplxTrans dbu_scale (mp_job->cva ()->layout ().dbu () / xor_results.dbu ()); n = 0; - for (db::RecursiveShapeIterator s (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la, xor_task->region_a ()); ! s.at_end (); ++s, ++n) { - sp.insert (s.shape (), dbu_scale_a * s.trans (), n * 2); + db::RecursiveShapeIterator s; + if (mp_job->has_tiles ()) { + s = db::RecursiveShapeIterator (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la, xor_task->region_a ()); + } else { + s = db::RecursiveShapeIterator (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la); + } + s.set_for_merged_input (true); + for ( ; ! s.at_end (); ++s, ++n) { + sp.insert (s.shape (), dbu_scale * s.trans (), n); } - n = 0; - for (db::RecursiveShapeIterator s (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb, xor_task->region_b ()); ! s.at_end (); ++s, ++n) { - sp.insert (s.shape (), dbu_scale_b * s.trans (), n * 2 + 1); - } - - db::BooleanOp bool_op (mp_job->op ()); - db::ShapeGenerator sg (xor_results_cell.shapes (0), true /*clear shapes*/); + db::MergeOp op (0); + db::ShapeGenerator sg (merge_helper_cell.shapes (0), true /*clear shapes*/); db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); - sp.process (out, bool_op); - - } else { - - // This implementation is faster when a lot of overlapping shapes are involved - db::Layout merge_helper; - merge_helper.dbu (mp_job->dbu ()); - db::Cell &merge_helper_cell = merge_helper.cell (merge_helper.add_cell ()); - merge_helper.insert_layer (0); - merge_helper.insert_layer (1); - - // This implementation is faster when a lot of overlapping shapes are involved - if (!la.empty ()) { - - sp.clear (); - - db::CplxTrans dbu_scale (mp_job->cva ()->layout ().dbu () / xor_results.dbu ()); - - n = 0; - for (db::RecursiveShapeIterator s (mp_job->cva ()->layout (), *mp_job->cva ().cell (), la, xor_task->region_a ()); ! s.at_end (); ++s, ++n) { - sp.insert (s.shape (), dbu_scale * s.trans (), n); - } - - db::MergeOp op (0); - db::ShapeGenerator sg (merge_helper_cell.shapes (0), true /*clear shapes*/); - db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); - sp.process (out, op); - - } - - if (!lb.empty ()) { - - sp.clear (); - - db::CplxTrans dbu_scale (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); - - n = 0; - for (db::RecursiveShapeIterator s (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb, xor_task->region_b ()); ! s.at_end (); ++s, ++n) { - sp.insert (s.shape (), dbu_scale * s.trans (), n); - } - - db::MergeOp op (0); - db::ShapeGenerator sg (merge_helper_cell.shapes (1), true /*clear shapes*/); - db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); - sp.process (out, op); - - } - - sp.boolean (merge_helper, merge_helper_cell, 0, - merge_helper, merge_helper_cell, 1, - xor_results_cell.shapes (0), mp_job->op (), true, false, true); + sp.process (out, op); } + if (!lb.empty ()) { + + sp.clear (); + + db::CplxTrans dbu_scale (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); + + n = 0; + db::RecursiveShapeIterator s; + if (mp_job->has_tiles ()) { + s = db::RecursiveShapeIterator (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb, xor_task->region_b ()); + } else { + s = db::RecursiveShapeIterator (mp_job->cvb ()->layout (), *mp_job->cvb ().cell (), lb); + } + s.set_for_merged_input (true); + for ( ; ! s.at_end (); ++s, ++n) { + sp.insert (s.shape (), dbu_scale * s.trans (), n); + } + + db::MergeOp op (0); + db::ShapeGenerator sg (merge_helper_cell.shapes (1), true /*clear shapes*/); + db::PolygonGenerator out (sg, false /*don't resolve holes*/, false /*no min. coherence*/); + sp.process (out, op); + + } + + sp.boolean (merge_helper, merge_helper_cell, 0, + merge_helper, merge_helper_cell, 1, + xor_results_cell.shapes (0), mp_job->op (), true, false, true); + } } else if (mp_job->op () == db::BooleanOp::Xor || @@ -1005,6 +1001,8 @@ XORWorker::do_perform_tiled (const XORTask *xor_task) dbu_scale = db::CplxTrans (mp_job->cvb ()->layout ().dbu () / xor_results.dbu ()); } + s.set_for_merged_input (true); + for (; ! s.at_end (); ++s) { if (s->is_polygon () || s->is_box () || s->is_path ()) { db::Polygon p; From 40a8f21f9c3510b58d62a10c4f1e2388b4073a7b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 26 Mar 2024 01:05:35 +0100 Subject: [PATCH 09/14] Simplified optimization as performance was bad. --- src/db/db/dbRecursiveShapeIterator.cc | 105 ++++++++++++++++++-------- 1 file changed, 72 insertions(+), 33 deletions(-) diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index 7fe8b99ed..60c9601cc 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -918,6 +918,64 @@ RecursiveShapeIterator::new_layer () const } } +static +RecursiveShapeIterator::box_type +shape_box (const RecursiveShapeIterator::shape_type &shape) +{ + if (shape.is_box ()) { + return shape.box (); + } + + switch (shape.type ()) { + case db::Shape::Polygon: + return shape.polygon ().is_box () ? shape.polygon ().box () : RecursiveShapeIterator::box_type (); + case db::Shape::PolygonRef: + case db::Shape::PolygonPtrArrayMember: + return shape.polygon_ref ().is_box () ? shape.polygon_ref ().box () : RecursiveShapeIterator::box_type (); + case db::Shape::SimplePolygon: + return shape.simple_polygon ().is_box () ? shape.simple_polygon ().box () : RecursiveShapeIterator::box_type (); + case db::Shape::SimplePolygonRef: + case db::Shape::SimplePolygonPtrArrayMember: + return shape.simple_polygon_ref ().is_box () ? shape.simple_polygon_ref ().box () : RecursiveShapeIterator::box_type (); + default: + return RecursiveShapeIterator::box_type (); + } +} + +static +RecursiveShapeIterator::box_type +subtract_box (const RecursiveShapeIterator::box_type &from, const RecursiveShapeIterator::box_type &box) +{ + RecursiveShapeIterator::box_type res (from); + if (box.empty ()) { + return res; + } + + if (! res.empty ()) { + if (box.bottom () <= res.bottom () && box.top () >= res.top ()) { + if (box.left () <= res.left ()) { + res.set_left (std::max (box.right (), res.left ())); + } + if (box.right () >= res.right ()) { + res.set_right (std::min (box.left (), res.right ())); + } + } + } + + if (! res.empty ()) { + if (box.left () <= res.left () && box.right () >= res.right ()) { + if (box.bottom () <= res.bottom ()) { + res.set_bottom (std::max (box.top (), res.bottom ())); + } + if (box.top () >= res.top ()) { + res.set_top (std::min (box.bottom (), res.top ())); + } + } + } + + return res; +} + void RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const { @@ -935,48 +993,29 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const // try some optimization - only consider optimizing by dropping the shape-covered area under certain circumstances: // - single layer - // - less than 32 shapes to consider - // - total shape bbox in current region covers at least a third of it - // - total area of shapes in current region is at least a third of it + // - at least one shape to consider and it is a box + // - that box clips the region entirely on one side // // NOTE that this implementation can modify the search box on the box stack // because we did "new_layer()" already and this function is not going to // be called, because we do so only for single layers. - const box_type ®ion = m_local_region_stack.back (); + if (m_for_merged_input && (! m_has_layers || m_layers.size () == 1) && ! m_shape.at_end ()) { - if (m_for_merged_input && (! m_has_layers || m_layers.size () == 1) && ! region.empty ()) { - - unsigned int l = m_has_layers ? m_layers.front () : m_layer; - const shapes_type &shapes = cell ()->shapes (l); - - if (! shapes.empty () && shapes.size () < 32 && - 3 * (shapes.bbox () & region).area () > region.area ()) { - - region_type shapes_region (shapes); - if (3 * shapes_region.area (region) > region.area ()) { - - // Need to enlarge the empty area somewhat so we really exclude instances - // entirely enclosed by the shape - also the ones at the border. - box_type::vector_type bias; - if (! m_overlapping) { - bias = box_type::vector_type (1, 1); - } - - // reduce the search region for less instances to look up - // NOTE: because we use "touching" for the instances below, we - region_type new_complex_region; - if (region == box_type::world ()) { - new_complex_region = region_type (cell ()->bbox (l)) - shapes_region; - } else { - new_complex_region = region_type (cell ()->bbox (l) & region.enlarged (bias)) - shapes_region; - } - - // TODO: the current implementation does not touch the complex search region - m_local_region_stack.back () = new_complex_region.bbox ().enlarged (-bias); + box_type box = shape_box (*m_shape); + if (! box.empty ()) { + // Need to enlarge the empty area somewhat so we really exclude instances + // entirely enclosed by the shape - also the ones at the border. + if (! m_overlapping) { + box.enlarge (box_type::vector_type (1, 1)); } + const box_type ®ion = m_local_region_stack.back (); + unsigned int l = m_has_layers ? m_layers.front () : m_layer; + box = subtract_box (cell ()->bbox (l) & region, box); + m_local_region_stack.back () = box; + } } From 3fc32e77c3f9b64da6a863938459874fd9a252b6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 26 Mar 2024 19:15:50 +0100 Subject: [PATCH 10/14] Added full-circuit test for recursive shape iterator --- .../dbRecursiveShapeIteratorTests.cc | 166 +++++++++++++++++- 1 file changed, 164 insertions(+), 2 deletions(-) diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index c4fec7476..2719312bf 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -26,6 +26,10 @@ #include "dbLayoutDiff.h" #include "tlString.h" #include "tlUnitTest.h" +#include "tlFileUtils.h" +#include "tlStream.h" +#include "dbReader.h" +#include "dbWriter.h" #include @@ -1623,6 +1627,164 @@ TEST(12_ForMerged) i1.set_for_merged_input (true); x = collect(i1, *g); EXPECT_EQ (x, "[$1](0,0;3000,2000)/[$4](-1200,0;-100,1000)"); - - // ... } + + +static void write (const db::Region ®ion, const std::string &fn) +{ + db::Layout layout; + const db::Cell &top = layout.cell (layout.add_cell ("TOP")); + unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); + region.insert_into (&layout, top.cell_index (), li); + + tl::OutputStream os (fn); + db::SaveLayoutOptions opt; + opt.set_format_from_filename (fn); + db::Writer writer (opt); + writer.write (layout, os); +} + +TEST(13_ForMergedPerformance) +{ + test_is_long_runner (); + + std::string fn (tl::combine_path (tl::testdata_private (), "oasis/caravel.oas.gz")); + + db::Layout ly; + + { + tl::InputStream is (fn); + db::Reader reader (is); + reader.read (ly); + } + + unsigned l1 = ly.get_layer (db::LayerProperties (66, 20)); + unsigned l2 = ly.get_layer (db::LayerProperties (235, 4)); + + db::RecursiveShapeIterator si1 (ly, ly.cell (*ly.begin_top_down ()), l1); + db::RecursiveShapeIterator si2 (ly, ly.cell (*ly.begin_top_down ()), l2); + + { + tl::SelfTimer timer ("Standard loop on 66/20"); + size_t n = 0; + while (! si1.at_end ()) { + ++si1; + ++n; + } + tl::info << "Counted " << n << " shapes on 66/20"; + EXPECT_EQ (n, size_t (1218378)); + } + + { + tl::SelfTimer timer ("Standard loop on 235/4"); + size_t n = 0; + while (! si2.at_end ()) { + ++si2; + ++n; + } + tl::info << "Counted " << n << " shapes on 235/4"; + EXPECT_EQ (n, size_t (57462)); + } + + si1.set_for_merged_input (true); + si2.set_for_merged_input (true); + + { + tl::SelfTimer timer ("'for_merged' loop on 66/20"); + size_t n = 0; + while (! si1.at_end ()) { + ++si1; + ++n; + } + tl::info << "Counted " << n << " shapes on 66/20"; + EXPECT_EQ (n, size_t (1217072)); + } + + { + tl::SelfTimer timer ("'for_merged' loop on 235/4"); + size_t n = 0; + while (! si2.at_end ()) { + ++si2; + ++n; + } + tl::info << "Counted " << n << " shapes on 235/4"; + EXPECT_EQ (n, size_t (919)); + } + + si1.set_for_merged_input (false); + si1.set_region (db::Box (0, 0, 1000000, 1000000)); + si2.set_for_merged_input (false); + si2.set_region (db::Box (0, 0, 1000000, 1000000)); + + { + tl::SelfTimer timer ("Standard loop on 66/20"); + size_t n = 0; + while (! si1.at_end ()) { + ++si1; + ++n; + } + tl::info << "Counted " << n << " shapes on 66/20"; + EXPECT_EQ (n, size_t (218823)); + } + + { + tl::SelfTimer timer ("Standard loop on 235/4"); + size_t n = 0; + while (! si2.at_end ()) { + ++si2; + ++n; + } + tl::info << "Counted " << n << " shapes on 235/4"; + EXPECT_EQ (n, size_t (2578)); + } + + si1.set_for_merged_input (true); + si2.set_for_merged_input (true); + + { + tl::SelfTimer timer ("'for_merged' loop on 66/20"); + size_t n = 0; + while (! si1.at_end ()) { + ++si1; + ++n; + } + tl::info << "Counted " << n << " shapes on 66/20"; + EXPECT_EQ (n, size_t (218736)); + } + + { + tl::SelfTimer timer ("'for_merged' loop on 235/4"); + size_t n = 0; + while (! si2.at_end ()) { + ++si2; + ++n; + } + tl::info << "Counted " << n << " shapes on 235/4"; + EXPECT_EQ (n, size_t (1)); + } + + { + tl::SelfTimer timer ("XOR on tile of 66/20"); + si1.set_for_merged_input (false); + db::Region r1 (si1); + si1.set_for_merged_input (true); + db::Region r2 (si1); + + EXPECT_EQ (r1.count (), size_t (218823)); + EXPECT_EQ (r2.count (), size_t (218736)); + EXPECT_EQ ((r1 ^ r2).count (), size_t (0)); + } + + { + tl::SelfTimer timer ("XOR on tile of 235/4"); + si2.set_for_merged_input (false); + db::Region r1 (si2); + si2.set_for_merged_input (true); + db::Region r2 (si2); + + EXPECT_EQ (r1.count (), size_t (2578)); + EXPECT_EQ (r2.count (), size_t (1)); + EXPECT_EQ ((r1 ^ r2).count (), size_t (0)); + } +} + From 5699c91d3f1ad7244d374fc190320e51074ce1d0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 26 Mar 2024 21:48:38 +0100 Subject: [PATCH 11/14] Some utility features derived from the latest code changes - Subtraction of boxes (pya/RBA: Box minus operator) - Shape#rectangle, Shape#drectangle - EdgePairs#write, Edges#write, Texts#write, Region#write for debugging --- src/db/db/dbBox.h | 82 ++++++++++++++++++ src/db/db/dbEdgePairs.cc | 20 +++++ src/db/db/dbEdgePairs.h | 8 ++ src/db/db/dbEdges.cc | 20 +++++ src/db/db/dbEdges.h | 8 ++ src/db/db/dbRecursiveShapeIterator.cc | 62 +------------ src/db/db/dbRegion.cc | 20 +++++ src/db/db/dbRegion.h | 8 ++ src/db/db/dbShape.cc | 55 ++++++++++++ src/db/db/dbShape.h | 10 +++ src/db/db/dbTexts.cc | 21 ++++- src/db/db/dbTexts.h | 8 ++ src/db/db/gsiDeclDbBox.cc | 15 ++++ src/db/db/gsiDeclDbEdgePairs.cc | 6 ++ src/db/db/gsiDeclDbEdges.cc | 6 ++ src/db/db/gsiDeclDbRegion.cc | 6 ++ src/db/db/gsiDeclDbShape.cc | 36 ++++++++ src/db/db/gsiDeclDbTexts.cc | 6 ++ src/db/unit_tests/dbBoxTests.cc | 11 +++ .../dbRecursiveShapeIteratorTests.cc | 14 --- src/db/unit_tests/dbShapeTests.cc | 86 +++++++++++++++++++ testdata/ruby/dbBoxTest.rb | 3 + testdata/ruby/dbShapesTest.rb | 4 + 23 files changed, 440 insertions(+), 75 deletions(-) diff --git a/src/db/db/dbBox.h b/src/db/db/dbBox.h index 94e2ac58c..cf7d4b233 100644 --- a/src/db/db/dbBox.h +++ b/src/db/db/dbBox.h @@ -254,6 +254,27 @@ struct DB_PUBLIC_TEMPLATE box */ box &operator+= (const point &p); + /** + * @brief Subtraction of boxes. + * + * The -= operator subtracts the argument box from *this. + * Subtraction leaves the bounding box of the region resulting + * from the geometrical NOT of *this and the argument box. + * Subtracting a box from itself gives an empty box. + * Subtracting a box that does not cover a full side of + * *this will not modify the box. + * + * @param b The box to subtract from *this. + * + * @return The result box. + */ + box &operator-= (const box &b); + + /** + * @brief A method version for operator- (mainly for automation purposes) + */ + box subtracted (const box &b) const; + /** * @brief Intersection of boxes. * @@ -784,6 +805,50 @@ box::operator+= (const point &p) return *this; } +template +inline box +box::subtracted (const box &b) const +{ + box r (*this); + r -= b; + return r; +} + +template +inline box & +box::operator-= (const box &bx) +{ + if (bx.empty () || empty ()) { + return *this; + } + + coord_type l = m_p1.x (), r = m_p2.x (); + coord_type b = m_p1.y (), t = m_p2.y (); + + if (bx.bottom () <= bottom () && bx.top () >= top ()) { + if (bx.left () <= left ()) { + l = std::max (bx.right (), left ()); + } + if (bx.right () >= right ()) { + r = std::min (bx.left (), right ()); + } + } + + if (bx.left () <= left () && bx.right () >= right ()) { + if (bx.bottom () <= bottom ()) { + b = std::max (bx.top (), bottom ()); + } + if (bx.top () >= top ()) { + t = std::min (bx.bottom (), top ()); + } + } + + m_p1 = point_type (l, b); + m_p2 = point_type (r, t); + + return *this; +} + template inline box & box::operator&= (const box &b) @@ -1363,6 +1428,23 @@ operator+ (const box &b1, const box &b2) return bb; } +/** + * @brief Box subtraction mapped on the - operator + * + * @param b1 The first box + * @param b2 The second box to subtract from the first + * + * @return The bounding box of the region formed but subtracting b2 from b1 + */ +template +inline box +operator- (const box &b1, const box &b2) +{ + box bb (b1); + bb -= b2; + return bb; +} + /** * @brief "Folding" of two boxes * diff --git a/src/db/db/dbEdgePairs.cc b/src/db/db/dbEdgePairs.cc index fcfd405f9..6e0d9059e 100644 --- a/src/db/db/dbEdgePairs.cc +++ b/src/db/db/dbEdgePairs.cc @@ -30,6 +30,9 @@ #include "dbOriginalLayerEdgePairs.h" #include "dbEdges.h" #include "dbRegion.h" +#include "dbLayout.h" +#include "dbWriter.h" +#include "tlStream.h" #include "tlVariant.h" @@ -93,6 +96,23 @@ EdgePairs::EdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss, con mp_delegate = new DeepEdgePairs (si, dss, trans); } +void +EdgePairs::write (const std::string &fn) const +{ + // method provided for debugging purposes + + db::Layout layout; + const db::Cell &top = layout.cell (layout.add_cell ("EDGE_PAIRS")); + unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); + insert_into (&layout, top.cell_index (), li); + + tl::OutputStream os (fn); + db::SaveLayoutOptions opt; + opt.set_format_from_filename (fn); + db::Writer writer (opt); + writer.write (layout, os); +} + template void EdgePairs::insert (const Sh &shape) { diff --git a/src/db/db/dbEdgePairs.h b/src/db/db/dbEdgePairs.h index 9d1b1aee2..53a499689 100644 --- a/src/db/db/dbEdgePairs.h +++ b/src/db/db/dbEdgePairs.h @@ -185,6 +185,14 @@ public: */ explicit EdgePairs (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans); + /** + * @brief Writes the edge pair collection to a file + * + * This method is provided for debugging purposes. A flat image of the + * region is written to a layout file with a single top cell on layer 0/0. + */ + void write (const std::string &fn) const; + /** * @brief Implementation of the ShapeCollection interface */ diff --git a/src/db/db/dbEdges.cc b/src/db/db/dbEdges.cc index e5a6cf007..5cdefe414 100644 --- a/src/db/db/dbEdges.cc +++ b/src/db/db/dbEdges.cc @@ -28,6 +28,9 @@ #include "dbFlatEdges.h" #include "dbEdgesUtils.h" #include "dbRegion.h" +#include "dbLayout.h" +#include "dbWriter.h" +#include "tlStream.h" namespace db { @@ -141,6 +144,23 @@ Edges::set_delegate (EdgesDelegate *delegate, bool keep_attributes) } } +void +Edges::write (const std::string &fn) const +{ + // method provided for debugging purposes + + db::Layout layout; + const db::Cell &top = layout.cell (layout.add_cell ("EDGES")); + unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); + insert_into (&layout, top.cell_index (), li); + + tl::OutputStream os (fn); + db::SaveLayoutOptions opt; + opt.set_format_from_filename (fn); + db::Writer writer (opt); + writer.write (layout, os); +} + void Edges::clear () { diff --git a/src/db/db/dbEdges.h b/src/db/db/dbEdges.h index 28decbbb6..ffb8ab6ff 100644 --- a/src/db/db/dbEdges.h +++ b/src/db/db/dbEdges.h @@ -833,6 +833,14 @@ public: return *this; } + /** + * @brief Writes the edge collection to a file + * + * This method is provided for debugging purposes. A flat image of the + * region is written to a layout file with a single top cell on layer 0/0. + */ + void write (const std::string &fn) const; + /** * @brief Intersections with other edges * Intersections are similar to "AND", but will also report diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index 60c9601cc..c918068ff 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -918,64 +918,6 @@ RecursiveShapeIterator::new_layer () const } } -static -RecursiveShapeIterator::box_type -shape_box (const RecursiveShapeIterator::shape_type &shape) -{ - if (shape.is_box ()) { - return shape.box (); - } - - switch (shape.type ()) { - case db::Shape::Polygon: - return shape.polygon ().is_box () ? shape.polygon ().box () : RecursiveShapeIterator::box_type (); - case db::Shape::PolygonRef: - case db::Shape::PolygonPtrArrayMember: - return shape.polygon_ref ().is_box () ? shape.polygon_ref ().box () : RecursiveShapeIterator::box_type (); - case db::Shape::SimplePolygon: - return shape.simple_polygon ().is_box () ? shape.simple_polygon ().box () : RecursiveShapeIterator::box_type (); - case db::Shape::SimplePolygonRef: - case db::Shape::SimplePolygonPtrArrayMember: - return shape.simple_polygon_ref ().is_box () ? shape.simple_polygon_ref ().box () : RecursiveShapeIterator::box_type (); - default: - return RecursiveShapeIterator::box_type (); - } -} - -static -RecursiveShapeIterator::box_type -subtract_box (const RecursiveShapeIterator::box_type &from, const RecursiveShapeIterator::box_type &box) -{ - RecursiveShapeIterator::box_type res (from); - if (box.empty ()) { - return res; - } - - if (! res.empty ()) { - if (box.bottom () <= res.bottom () && box.top () >= res.top ()) { - if (box.left () <= res.left ()) { - res.set_left (std::max (box.right (), res.left ())); - } - if (box.right () >= res.right ()) { - res.set_right (std::min (box.left (), res.right ())); - } - } - } - - if (! res.empty ()) { - if (box.left () <= res.left () && box.right () >= res.right ()) { - if (box.bottom () <= res.bottom ()) { - res.set_bottom (std::max (box.top (), res.bottom ())); - } - if (box.top () >= res.top ()) { - res.set_top (std::min (box.bottom (), res.top ())); - } - } - } - - return res; -} - void RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const { @@ -1002,7 +944,7 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const if (m_for_merged_input && (! m_has_layers || m_layers.size () == 1) && ! m_shape.at_end ()) { - box_type box = shape_box (*m_shape); + box_type box = m_shape->rectangle (); if (! box.empty ()) { // Need to enlarge the empty area somewhat so we really exclude instances @@ -1013,7 +955,7 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const const box_type ®ion = m_local_region_stack.back (); unsigned int l = m_has_layers ? m_layers.front () : m_layer; - box = subtract_box (cell ()->bbox (l) & region, box); + box = (cell ()->bbox (l) & region) - box; m_local_region_stack.back () = box; } diff --git a/src/db/db/dbRegion.cc b/src/db/db/dbRegion.cc index c05f9d338..2d04a2758 100644 --- a/src/db/db/dbRegion.cc +++ b/src/db/db/dbRegion.cc @@ -31,6 +31,9 @@ #include "dbFlatEdges.h" #include "dbPolygonTools.h" #include "dbCompoundOperation.h" +#include "dbLayout.h" +#include "dbWriter.h" +#include "tlStream.h" #include "tlGlobPattern.h" // NOTE: include this to provide the symbols for "make_variant" @@ -129,6 +132,23 @@ Region::Region (DeepShapeStore &dss) mp_delegate = new db::DeepRegion (db::DeepLayer (&dss, layout_index, dss.layout (layout_index).insert_layer ())); } +void +Region::write (const std::string &fn) const +{ + // method provided for debugging purposes + + db::Layout layout; + const db::Cell &top = layout.cell (layout.add_cell ("REGION")); + unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); + insert_into (&layout, top.cell_index (), li); + + tl::OutputStream os (fn); + db::SaveLayoutOptions opt; + opt.set_format_from_filename (fn); + db::Writer writer (opt); + writer.write (layout, os); +} + const db::RecursiveShapeIterator & Region::iter () const { diff --git a/src/db/db/dbRegion.h b/src/db/db/dbRegion.h index 1329adcc8..145018ef0 100644 --- a/src/db/db/dbRegion.h +++ b/src/db/db/dbRegion.h @@ -248,6 +248,14 @@ public: */ explicit Region (DeepShapeStore &dss); + /** + * @brief Writes the region to a file + * + * This method is provided for debugging purposes. A flat image of the + * region is written to a layout file with a single top cell on layer 0/0. + */ + void write (const std::string &fn) const; + /** * @brief Implementation of the ShapeCollection interface */ diff --git a/src/db/db/dbShape.cc b/src/db/db/dbShape.cc index 47fc28776..e5c1d7820 100644 --- a/src/db/db/dbShape.cc +++ b/src/db/db/dbShape.cc @@ -807,6 +807,61 @@ Shape::box_type Shape::bbox () const } } +Shape::box_type Shape::rectangle () const +{ + if (is_box ()) { + return box (); + } + + switch (m_type) { + case db::Shape::Polygon: + return polygon ().is_box () ? polygon ().box () : box_type (); + case db::Shape::PolygonRef: + case db::Shape::PolygonPtrArrayMember: + return polygon_ref ().is_box () ? polygon_ref ().box () : box_type (); + case db::Shape::SimplePolygon: + return simple_polygon ().is_box () ? simple_polygon ().box () : box_type (); + case db::Shape::SimplePolygonRef: + case db::Shape::SimplePolygonPtrArrayMember: + return simple_polygon_ref ().is_box () ? simple_polygon_ref ().box () : box_type (); + case db::Shape::Path: + { + const path_type &p = path (); + if (! p.round () && p.points () <= 2 && p.points () > 0) { + point_type p1 = *p.begin (); + point_type p2 = p1; + if (p.points () == 2) { + p2 = *++p.begin (); + } + if (p1.x () == p2.x () || p1.y () == p2.y ()) { + return p.box (); + } + } + } + break; + case db::Shape::PathRef: + case db::Shape::PathPtrArrayMember: + { + const path_ref_type &p = path_ref (); + if (! p.ptr ()->round () && p.ptr ()->points () <= 2 && p.ptr ()->points () > 0) { + point_type p1 = *p.begin (); + point_type p2 = p1; + if (p.ptr ()->points () == 2) { + p2 = *++p.begin (); + } + if (p1.x () == p2.x () || p1.y () == p2.y ()) { + return p.box (); + } + } + } + break; + default: + break; + } + + return box_type (); +} + std::string Shape::to_string () const { diff --git a/src/db/db/dbShape.h b/src/db/db/dbShape.h index 5ab336667..dd003b47e 100644 --- a/src/db/db/dbShape.h +++ b/src/db/db/dbShape.h @@ -2651,6 +2651,16 @@ public: */ box_type bbox () const; + /** + * @brief Returns the box if the object represents a rectangle or an empty box if not + * + * This method returns the rectangle (aka box) the shape represents a polygon + * that is a rectangle, a path with two points and no rounded ends or an actual box. + * + * If not, an empty box is returned. + */ + box_type rectangle () const; + /** * @brief Compute the area of the shape */ diff --git a/src/db/db/dbTexts.cc b/src/db/db/dbTexts.cc index 0b90c8897..388c89696 100644 --- a/src/db/db/dbTexts.cc +++ b/src/db/db/dbTexts.cc @@ -30,7 +30,9 @@ #include "dbOriginalLayerTexts.h" #include "dbEdges.h" #include "dbRegion.h" - +#include "dbLayout.h" +#include "dbWriter.h" +#include "tlStream.h" #include "tlVariant.h" #include @@ -90,6 +92,23 @@ Texts::Texts (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::I mp_delegate = new DeepTexts (si, dss, trans); } +void +Texts::write (const std::string &fn) const +{ + // method provided for debugging purposes + + db::Layout layout; + const db::Cell &top = layout.cell (layout.add_cell ("TEXTS")); + unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); + insert_into (&layout, top.cell_index (), li); + + tl::OutputStream os (fn); + db::SaveLayoutOptions opt; + opt.set_format_from_filename (fn); + db::Writer writer (opt); + writer.write (layout, os); +} + template void Texts::insert (const Sh &shape) { diff --git a/src/db/db/dbTexts.h b/src/db/db/dbTexts.h index 82d017187..cacda8589 100644 --- a/src/db/db/dbTexts.h +++ b/src/db/db/dbTexts.h @@ -181,6 +181,14 @@ public: */ explicit Texts (const RecursiveShapeIterator &si, DeepShapeStore &dss, const db::ICplxTrans &trans); + /** + * @brief Writes the text collection to a file + * + * This method is provided for debugging purposes. A flat image of the + * region is written to a layout file with a single top cell on layer 0/0. + */ + void write (const std::string &fn) const; + /** * @brief Implementation of the ShapeCollection interface */ diff --git a/src/db/db/gsiDeclDbBox.cc b/src/db/db/gsiDeclDbBox.cc index 50dc0d883..d2c5fcc8f 100644 --- a/src/db/db/gsiDeclDbBox.cc +++ b/src/db/db/gsiDeclDbBox.cc @@ -324,6 +324,21 @@ struct box_defs "\n" "@return The joined box\n" ) + + method ("-", &C::subtracted, gsi::arg ("box"), + "@brief Subtraction of boxes\n" + "\n" + "\n" + "The - operator subtracts the argument box from self.\n" + "This will return the bounding box of the are covered by self, but not by argument box. " + "Subtracting a box from itself will render an empty box. Subtracting another box from " + "self will modify the first box only if the argument box covers one side entirely.\n" + "\n" + "@param box The box to subtract from this box.\n" + "\n" + "@return The result box\n" + "\n" + "This feature has been introduced in version 0.29." + ) + method ("&", &C::intersection, gsi::arg ("box"), "@brief Returns the intersection of this box with another box\n" "\n" diff --git a/src/db/db/gsiDeclDbEdgePairs.cc b/src/db/db/gsiDeclDbEdgePairs.cc index 4a22f7045..b4f6b5447 100644 --- a/src/db/db/gsiDeclDbEdgePairs.cc +++ b/src/db/db/gsiDeclDbEdgePairs.cc @@ -602,6 +602,12 @@ Class decl_EdgePairs (decl_dbShapeCollection, "db", "EdgePairs", "\n" "This constructor has been introduced in version 0.26." ) + + method ("write", &db::EdgePairs::write, gsi::arg ("filename"), + "@brief Writes the region to a file\n" + "This method is provided for debugging purposes. It writes the object to a flat layer 0/0 in a single top cell.\n" + "\n" + "This method has been introduced in version 0.29." + ) + method ("insert_into", &db::EdgePairs::insert_into, gsi::arg ("layout"), gsi::arg ("cell_index"), gsi::arg ("layer"), "@brief Inserts this edge pairs into the given layout, below the given cell and into the given layer.\n" "If the edge pair collection is a hierarchical one, a suitable hierarchy will be built below the top cell or " diff --git a/src/db/db/gsiDeclDbEdges.cc b/src/db/db/gsiDeclDbEdges.cc index d325dfa26..5fc83ec49 100644 --- a/src/db/db/gsiDeclDbEdges.cc +++ b/src/db/db/gsiDeclDbEdges.cc @@ -1567,6 +1567,12 @@ Class decl_Edges (decl_dbShapeCollection, "db", "Edges", "\n" "This method has been added in version 0.28.\n" ) + + method ("write", &db::Edges::write, gsi::arg ("filename"), + "@brief Writes the region to a file\n" + "This method is provided for debugging purposes. It writes the object to a flat layer 0/0 in a single top cell.\n" + "\n" + "This method has been introduced in version 0.29." + ) + method ("clear", &db::Edges::clear, "@brief Clears the edge collection\n" ) + diff --git a/src/db/db/gsiDeclDbRegion.cc b/src/db/db/gsiDeclDbRegion.cc index ad941cc27..c5d6c6570 100644 --- a/src/db/db/gsiDeclDbRegion.cc +++ b/src/db/db/gsiDeclDbRegion.cc @@ -1221,6 +1221,12 @@ Class decl_Region (decl_dbShapeCollection, "db", "Region", "\n" "This method has been introduced in version 0.26." ) + + method ("write", &db::Region::write, gsi::arg ("filename"), + "@brief Writes the region to a file\n" + "This method is provided for debugging purposes. It writes the object to a flat layer 0/0 in a single top cell.\n" + "\n" + "This method has been introduced in version 0.29." + ) + factory_ext ("texts", &texts_as_boxes1, gsi::arg ("expr", std::string ("*")), gsi::arg ("as_pattern", true), gsi::arg ("enl", 1), "@hide\n" "This method is provided for DRC implementation only." diff --git a/src/db/db/gsiDeclDbShape.cc b/src/db/db/gsiDeclDbShape.cc index c6671d9b7..68f6ff4fb 100644 --- a/src/db/db/gsiDeclDbShape.cc +++ b/src/db/db/gsiDeclDbShape.cc @@ -669,6 +669,26 @@ static tl::Variant get_dbox (const db::Shape *s) } } +static tl::Variant get_rectangle (const db::Shape *s) +{ + db::Shape::box_type b = s->rectangle (); + if (! b.empty ()) { + return tl::Variant (b); + } else { + return tl::Variant (); + } +} + +static tl::Variant get_drectangle (const db::Shape *s) +{ + db::Shape::box_type b = s->rectangle (); + if (! b.empty ()) { + return tl::Variant (db::CplxTrans (shape_dbu (s)) * b); + } else { + return tl::Variant (); + } +} + static tl::Variant get_edge (const db::Shape *s) { db::Shape::edge_type p; @@ -1982,6 +2002,22 @@ Class decl_Shape ("db", "Shape", "\n" "This method has been added in version 0.25.\n" ) + + gsi::method_ext ("rectangle", &get_rectangle, + "@brief Gets the rectangle if the object represents one or nil if not\n" + "\n" + "If the shape represents a rectangle - i.e. a box or box polygon, a path with two points and no round ends - " + "this method returns the box. If not, nil is returned.\n" + "\n" + "This method has been introduced in version 0.29." + ) + + gsi::method_ext ("drectangle", &get_drectangle, + "@brief Gets the rectangle in micron units if the object represents one or nil if not\n" + "\n" + "If the shape represents a rectangle - i.e. a box or box polygon, a path with two points and no round ends - " + "this method returns the box. If not, nil is returned.\n" + "\n" + "This method has been introduced in version 0.29." + ) + gsi::method ("is_user_object?", &db::Shape::is_user_object, "@brief Returns true if the shape is a user defined object\n" ) + diff --git a/src/db/db/gsiDeclDbTexts.cc b/src/db/db/gsiDeclDbTexts.cc index 8be9238b8..359249795 100644 --- a/src/db/db/gsiDeclDbTexts.cc +++ b/src/db/db/gsiDeclDbTexts.cc @@ -436,6 +436,12 @@ Class decl_Texts (decl_dbShapeCollection, "db", "Texts", "r = RBA::Texts::new(layout.begin_shapes(cell, layer), RBA::ICplxTrans::new(layout.dbu / dbu))\n" "@/code\n" ) + + method ("write", &db::Texts::write, gsi::arg ("filename"), + "@brief Writes the region to a file\n" + "This method is provided for debugging purposes. It writes the object to a flat layer 0/0 in a single top cell.\n" + "\n" + "This method has been introduced in version 0.29." + ) + method ("insert_into", &db::Texts::insert_into, gsi::arg ("layout"), gsi::arg ("cell_index"), gsi::arg ("layer"), "@brief Inserts this texts into the given layout, below the given cell and into the given layer.\n" "If the text collection is a hierarchical one, a suitable hierarchy will be built below the top cell or " diff --git a/src/db/unit_tests/dbBoxTests.cc b/src/db/unit_tests/dbBoxTests.cc index d70e942fc..1ac60f799 100644 --- a/src/db/unit_tests/dbBoxTests.cc +++ b/src/db/unit_tests/dbBoxTests.cc @@ -49,6 +49,17 @@ TEST(2) EXPECT_EQ (b & db::Box (110, 220, 120, 250), empty); EXPECT_EQ (b & db::Box (50, 100, 120, 250), db::Box (50, 100, 100, 200)); EXPECT_EQ (b & db::Box (50, 100, 60, 120), db::Box (50, 100, 60, 120)); + EXPECT_EQ (b - b, db::Box ()); + EXPECT_EQ (b - db::Box (), b); + EXPECT_EQ (db::Box () - b, db::Box ()); + EXPECT_EQ (db::Box () - db::Box (), db::Box ()); + EXPECT_EQ (b - db::Box (0, 0, 50, 50), b); + EXPECT_EQ (b - db::Box (0, 0, 50, 200), db::Box (50, 0, 100, 200)); + EXPECT_EQ (b - db::Box (50, 0, 100, 200), db::Box (0, 0, 50, 200)); + EXPECT_EQ (b - db::Box (0, 0, 100, 100), db::Box (0, 100, 100, 200)); + EXPECT_EQ (b - db::Box (0, 100, 100, 200), db::Box (0, 0, 100, 100)); + EXPECT_EQ (db::Box::world () - b, db::Box::world ()); + EXPECT_EQ (b - db::Box::world (), db::Box ()); empty.move (db::Vector (10, 20)); EXPECT_EQ (empty == db::Box (), true); diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index 2719312bf..4dda034db 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -1630,20 +1630,6 @@ TEST(12_ForMerged) } -static void write (const db::Region ®ion, const std::string &fn) -{ - db::Layout layout; - const db::Cell &top = layout.cell (layout.add_cell ("TOP")); - unsigned int li = layout.insert_layer (db::LayerProperties (0, 0)); - region.insert_into (&layout, top.cell_index (), li); - - tl::OutputStream os (fn); - db::SaveLayoutOptions opt; - opt.set_format_from_filename (fn); - db::Writer writer (opt); - writer.write (layout, os); -} - TEST(13_ForMergedPerformance) { test_is_long_runner (); diff --git a/src/db/unit_tests/dbShapeTests.cc b/src/db/unit_tests/dbShapeTests.cc index b6f8b514c..1d4a19003 100644 --- a/src/db/unit_tests/dbShapeTests.cc +++ b/src/db/unit_tests/dbShapeTests.cc @@ -948,3 +948,89 @@ TEST(9) EXPECT_EQ (si.at_end (), true); } +// Rectangle +TEST(10) +{ + db::Manager m (true); + db::Shapes s (&m, 0, db::default_editable_mode ()); + db::ShapeIterator si; + + s.insert (db::Point (100, 200)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::Edge (db::Point (100, 200), db::Point (200, 400))); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::EdgePair (db::Edge (db::Point (100, 200), db::Point (200, 400)), db::Edge (db::Point (0, 300), db::Point (100, 500)))); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::Box (0, 0, 1000, 2000)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (0, 0, 1000, 2000)); + + s.clear (); + s.insert (db::ShortBox (0, 0, 1000, 2000)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (0, 0, 1000, 2000)); + + s.clear (); + s.insert (db::Polygon (db::Box (0, 0, 1000, 2000))); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (0, 0, 1000, 2000)); + + s.clear (); + s.insert (db::Polygon ()); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::SimplePolygon (db::Box (0, 0, 1000, 2000))); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (0, 0, 1000, 2000)); + + s.clear (); + s.insert (db::SimplePolygon ()); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::Path ()); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + db::Point pts1 [1] = { db::Point (0, 0) }; + db::Point pts2 [2] = { db::Point (0, 0), db::Point (1000, 0) }; + db::Point pts2b [2] = { db::Point (0, 0), db::Point (1000, 1000) }; + db::Point pts3 [3] = { db::Point (0, 0), db::Point (1000, 0), db::Point (1000, 1000) }; + + s.clear (); + s.insert (db::Path (pts1 + 0, pts1 + 1, 1000, 500, 500)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (-500, -500, 500, 500)); + + s.clear (); + s.insert (db::Path (pts2 + 0, pts2 + 2, 1000, 500, 500)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle (), db::Box (-500, -500, 1500, 500)); + + s.clear (); + s.insert (db::Path (pts2 + 0, pts2 + 2, 1000, 500, 500, true)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::Path (pts2b + 0, pts2b + 2, 1000, 500, 500)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); + + s.clear (); + s.insert (db::Path (pts3 + 0, pts3 + 3, 1000, 500, 500)); + si = s.begin (db::ShapeIterator::All); + EXPECT_EQ (si->rectangle ().empty (), true); +} diff --git a/testdata/ruby/dbBoxTest.rb b/testdata/ruby/dbBoxTest.rb index 8e9a916e9..1a207ef81 100644 --- a/testdata/ruby/dbBoxTest.rb +++ b/testdata/ruby/dbBoxTest.rb @@ -146,6 +146,9 @@ class DBBox_TestClass < TestBase assert_equal( a + b, b ) assert_equal( (b + c).to_s, "(1,-10;22,22)" ) + assert_equal( b - a, b ) + assert_equal( (b - c).to_s, "(1,-1;17,22)" ) + assert_equal( a + RBA::DPoint::new( 1, -5 ), RBA::DBox::new( 1, -5, 1, -5 ) ) assert_equal( (b + RBA::DPoint::new( 1, -5 )).to_s, "(1,-5;17,22)" ) diff --git a/testdata/ruby/dbShapesTest.rb b/testdata/ruby/dbShapesTest.rb index 3417c8688..bca7672cf 100644 --- a/testdata/ruby/dbShapesTest.rb +++ b/testdata/ruby/dbShapesTest.rb @@ -178,6 +178,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].is_polygon?, false ) assert_equal( arr[0].is_box?, true ) assert_equal( arr[0].box.to_s, "(10,-10;50,40)" ) + assert_equal( arr[0].rectangle.to_s, "(10,-10;50,40)" ) assert_equal( arr[0].bbox.to_s, "(10,-10;50,40)" ) # edges @@ -198,6 +199,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].edge.to_s, "(-1,2;5,2)" ) assert_equal( arr[0].edge_pair.inspect, "nil" ) assert_equal( arr[0].box.inspect, "nil" ) + assert_equal( arr[0].rectangle.inspect, "nil" ) assert_equal( arr[0].path.inspect, "nil" ) assert_equal( arr[0].text.inspect, "nil" ) assert_equal( arr[0].edge == a, true ) @@ -533,6 +535,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].dedge.inspect, "nil" ) assert_equal( arr[0].dedge_pair.inspect, "nil" ) assert_equal( arr[0].dbox.to_s, "(0.01,-0.01;0.05,0.04)" ) + assert_equal( arr[0].drectangle.to_s, "(0.01,-0.01;0.05,0.04)" ) assert_equal( arr[0].dpath.inspect, "nil" ) assert_equal( arr[0].dtext.inspect, "nil" ) assert_equal( arr[0].is_polygon?, false ) @@ -557,6 +560,7 @@ class DBShapes_TestClass < TestBase assert_equal( arr[0].dedge.to_s, "(-0.001,0.002;0.005,0.002)" ) assert_equal( arr[0].dedge_pair.inspect, "nil" ) assert_equal( arr[0].dbox.inspect, "nil" ) + assert_equal( arr[0].drectangle.inspect, "nil" ) assert_equal( arr[0].dpath.inspect, "nil" ) assert_equal( arr[0].dtext.inspect, "nil" ) assert_equal( arr[0].dbbox.to_s, "(-0.001,0.002;0.005,0.002)" ) From cb5a1f7d3edbd413aeb3abc3858ad9fde264d95f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 27 Mar 2024 23:46:33 +0100 Subject: [PATCH 12/14] Refining shape iterator optimization, so it checks instances for overlap with shapes rather the other way round. This suits better to real test cases. --- src/buddies/src/bd/strmxor.cc | 10 +++- src/db/db/dbRecursiveShapeIterator.cc | 56 +++++++++---------- .../dbRecursiveShapeIteratorTests.cc | 12 ++-- 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/src/buddies/src/bd/strmxor.cc b/src/buddies/src/bd/strmxor.cc index 9bd72e860..de1f265e4 100644 --- a/src/buddies/src/bd/strmxor.cc +++ b/src/buddies/src/bd/strmxor.cc @@ -680,13 +680,17 @@ bool run_tiled_xor (const XORData &xor_data) if (ll->second.first < 0) { proc.input (in_a, db::RecursiveShapeIterator ()); } else { - proc.input (in_a, db::RecursiveShapeIterator (*xor_data.layout_a, xor_data.layout_a->cell (xor_data.cell_a), ll->second.first)); + db::RecursiveShapeIterator si (*xor_data.layout_a, xor_data.layout_a->cell (xor_data.cell_a), ll->second.first); + si.set_for_merged_input (true); + proc.input (in_a, si); } if (ll->second.second < 0) { proc.input (in_b, db::RecursiveShapeIterator ()); } else { - proc.input (in_b, db::RecursiveShapeIterator (*xor_data.layout_b, xor_data.layout_b->cell (xor_data.cell_b), ll->second.second)); + db::RecursiveShapeIterator si (*xor_data.layout_b, xor_data.layout_b->cell (xor_data.cell_b), ll->second.second); + si.set_for_merged_input (true); + proc.input (in_b, si); } std::string expr = "var x=" + in_a + "^" + in_b + "; "; @@ -805,10 +809,12 @@ bool run_deep_xor (const XORData &xor_data) if (ll->second.first >= 0) { ri_a = db::RecursiveShapeIterator (*xor_data.layout_a, xor_data.layout_a->cell (xor_data.cell_a), ll->second.first); + ri_a.set_for_merged_input (true); } if (ll->second.second >= 0) { ri_b = db::RecursiveShapeIterator (*xor_data.layout_b, xor_data.layout_b->cell (xor_data.cell_b), ll->second.second); + ri_b.set_for_merged_input (true); } db::Region in_a (ri_a, dss, db::ICplxTrans (xor_data.layout_a->dbu () / dbu)); diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index c918068ff..b86f5d010 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -933,35 +933,6 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const new_layer (); - // try some optimization - only consider optimizing by dropping the shape-covered area under certain circumstances: - // - single layer - // - at least one shape to consider and it is a box - // - that box clips the region entirely on one side - // - // NOTE that this implementation can modify the search box on the box stack - // because we did "new_layer()" already and this function is not going to - // be called, because we do so only for single layers. - - if (m_for_merged_input && (! m_has_layers || m_layers.size () == 1) && ! m_shape.at_end ()) { - - box_type box = m_shape->rectangle (); - if (! box.empty ()) { - - // Need to enlarge the empty area somewhat so we really exclude instances - // entirely enclosed by the shape - also the ones at the border. - if (! m_overlapping) { - box.enlarge (box_type::vector_type (1, 1)); - } - - const box_type ®ion = m_local_region_stack.back (); - unsigned int l = m_has_layers ? m_layers.front () : m_layer; - box = (cell ()->bbox (l) & region) - box; - m_local_region_stack.back () = box; - - } - - } - if (m_overlapping) { m_inst = cell ()->begin_touching (m_local_region_stack.back ().enlarged (box_type::vector_type (-1, -1))); } else { @@ -994,6 +965,33 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const } } + if (m_for_merged_input && (! m_has_layers || m_layers.size () == 1)) { + + // Try some optimization: if the instance we're looking at is entirely covered + // by a rectangle (other objects are too expensive to check), then wil skip it + // + // We check 10 shapes max. + + unsigned int l = m_has_layers ? m_layers.front () : m_layer; + box_type inst_bx = m_inst->bbox (m_box_convert); + auto si = cell ()->shapes (l).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel); + bool skip = false; + size_t nmax = 10; + while (! skip && ! si.at_end () && nmax-- > 0) { + if (inst_bx.inside (si->rectangle ())) { + skip = true; + break; + } + ++si; + } + + if (skip) { + ++m_inst; + continue; + } + + } + bool all_of_instance = false; bool with_region = false; diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index 4dda034db..0e4a7430a 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -1683,7 +1683,7 @@ TEST(13_ForMergedPerformance) ++n; } tl::info << "Counted " << n << " shapes on 66/20"; - EXPECT_EQ (n, size_t (1217072)); + EXPECT_EQ (n, size_t (1212844)); } { @@ -1694,7 +1694,7 @@ TEST(13_ForMergedPerformance) ++n; } tl::info << "Counted " << n << " shapes on 235/4"; - EXPECT_EQ (n, size_t (919)); + EXPECT_EQ (n, size_t (10)); } si1.set_for_merged_input (false); @@ -1735,7 +1735,7 @@ TEST(13_ForMergedPerformance) ++n; } tl::info << "Counted " << n << " shapes on 66/20"; - EXPECT_EQ (n, size_t (218736)); + EXPECT_EQ (n, size_t (218552)); } { @@ -1746,7 +1746,7 @@ TEST(13_ForMergedPerformance) ++n; } tl::info << "Counted " << n << " shapes on 235/4"; - EXPECT_EQ (n, size_t (1)); + EXPECT_EQ (n, size_t (2)); } { @@ -1757,7 +1757,7 @@ TEST(13_ForMergedPerformance) db::Region r2 (si1); EXPECT_EQ (r1.count (), size_t (218823)); - EXPECT_EQ (r2.count (), size_t (218736)); + EXPECT_EQ (r2.count (), size_t (218552)); EXPECT_EQ ((r1 ^ r2).count (), size_t (0)); } @@ -1769,7 +1769,7 @@ TEST(13_ForMergedPerformance) db::Region r2 (si2); EXPECT_EQ (r1.count (), size_t (2578)); - EXPECT_EQ (r2.count (), size_t (1)); + EXPECT_EQ (r2.count (), size_t (2)); EXPECT_EQ ((r1 ^ r2).count (), size_t (0)); } } From 7080ed9a0c4e5b20bed5adb3b1fd6925fe8d41d7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 28 Mar 2024 20:57:10 +0100 Subject: [PATCH 13/14] Refined XOR optimization solution such that it is compatible with deep mode and 'wants_all_cells', added more tests --- src/db/db/dbRecursiveShapeIterator.cc | 87 +++++++++++------- src/db/db/dbRecursiveShapeIterator.h | 1 + src/db/unit_tests/dbHierProcessorTests.cc | 88 +++++++++++++++++++ .../dbRecursiveShapeIteratorTests.cc | 6 +- 4 files changed, 148 insertions(+), 34 deletions(-) diff --git a/src/db/db/dbRecursiveShapeIterator.cc b/src/db/db/dbRecursiveShapeIterator.cc index b86f5d010..cc02f75a9 100644 --- a/src/db/db/dbRecursiveShapeIterator.cc +++ b/src/db/db/dbRecursiveShapeIterator.cc @@ -82,6 +82,7 @@ RecursiveShapeIterator &RecursiveShapeIterator::operator= (const RecursiveShapeI m_cells = d.m_cells; m_local_complex_region_stack = d.m_local_complex_region_stack; m_local_region_stack = d.m_local_region_stack; + m_skip_shapes_stack = d.m_skip_shapes_stack; m_needs_reinit = d.m_needs_reinit; m_inst_quad_id = d.m_inst_quad_id; m_inst_quad_id_stack = d.m_inst_quad_id_stack; @@ -462,6 +463,8 @@ RecursiveShapeIterator::validate (RecursiveShapeReceiver *receiver) const m_local_region_stack.clear (); m_local_region_stack.push_back (m_global_trans.inverted () * m_region); + m_skip_shapes_stack.clear (); + m_skip_shapes_stack.push_back (false); m_local_complex_region_stack.clear (); if (mp_complex_region.get ()) { @@ -736,9 +739,23 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const } - if (is_empty || !down (receiver)) { + if (is_empty) { + + // skip entire cell ++m_inst; new_inst (receiver); + + } else if (!down (receiver)) { + + // skip this instance array member + ++m_inst_array; + new_inst_member (receiver); + + if (m_inst_array.at_end ()) { + ++m_inst; + new_inst (receiver); + } + } } else { @@ -769,6 +786,39 @@ RecursiveShapeIterator::next_shape (RecursiveShapeReceiver *receiver) const bool RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const { + bool skip_shapes = false; + + if (m_for_merged_input && ! m_skip_shapes_stack.back () && (! m_has_layers || m_layers.size () == 1)) { + + // Try some optimization: if the instance we're looking at is entirely covered + // by a rectangle (other objects are too expensive to check), then we skip it + // + // We check 10 shapes max. + + box_type inst_bx; + if (m_inst->size () == 1) { + inst_bx = m_inst->bbox (m_box_convert); + } else { + inst_bx = m_inst->complex_trans (*m_inst_array) * m_box_convert (m_inst->cell_inst ().object ()); + } + + unsigned int l = m_has_layers ? m_layers.front () : m_layer; + auto si = cell ()->shapes (l).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel); + size_t nmax = 10; + while (! si.at_end () && nmax-- > 0) { + if (inst_bx.inside (si->rectangle ())) { + skip_shapes = true; + break; + } + ++si; + } + + } + + if (skip_shapes && (! receiver || ! receiver->wants_all_cells ())) { + return false; + } + tl_assert (mp_layout); m_trans_stack.push_back (m_trans); @@ -796,6 +846,7 @@ RecursiveShapeIterator::down (RecursiveShapeReceiver *receiver) const } m_local_region_stack.push_back (new_region); + m_skip_shapes_stack.push_back (m_skip_shapes_stack.back () || skip_shapes); if (! m_local_complex_region_stack.empty ()) { @@ -878,6 +929,7 @@ RecursiveShapeIterator::pop () const mp_cell = m_cells.back (); m_cells.pop_back (); m_local_region_stack.pop_back (); + m_skip_shapes_stack.pop_back (); if (! m_local_complex_region_stack.empty ()) { m_local_complex_region_stack.pop_back (); } @@ -902,7 +954,7 @@ RecursiveShapeIterator::start_shapes () const void RecursiveShapeIterator::new_layer () const { - if (int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) { + if (m_skip_shapes_stack.back () || int (m_trans_stack.size ()) < m_min_depth || int (m_trans_stack.size ()) > m_max_depth) { m_shape = shape_iterator (); } else if (! m_overlapping) { m_shape = cell ()->shapes (m_layer).begin_touching (m_local_region_stack.back (), m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel); @@ -942,7 +994,7 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const m_inst_quad_id = 0; // skip instance quad if possible - if (! m_local_complex_region_stack.empty ()) { + if (! m_local_complex_region_stack.empty () && (! receiver || ! receiver->wants_all_cells ())) { skip_inst_iter_for_complex_region (); } @@ -958,40 +1010,13 @@ RecursiveShapeIterator::new_inst (RecursiveShapeReceiver *receiver) const while (! m_inst.at_end ()) { // skip instance quad if possible - if (! m_local_complex_region_stack.empty ()) { + if (! m_local_complex_region_stack.empty () && (! receiver || ! receiver->wants_all_cells ())) { skip_inst_iter_for_complex_region (); if (m_inst.at_end ()) { break; } } - if (m_for_merged_input && (! m_has_layers || m_layers.size () == 1)) { - - // Try some optimization: if the instance we're looking at is entirely covered - // by a rectangle (other objects are too expensive to check), then wil skip it - // - // We check 10 shapes max. - - unsigned int l = m_has_layers ? m_layers.front () : m_layer; - box_type inst_bx = m_inst->bbox (m_box_convert); - auto si = cell ()->shapes (l).begin_overlapping (inst_bx, m_shape_flags, mp_shape_prop_sel, m_shape_inv_prop_sel); - bool skip = false; - size_t nmax = 10; - while (! skip && ! si.at_end () && nmax-- > 0) { - if (inst_bx.inside (si->rectangle ())) { - skip = true; - break; - } - ++si; - } - - if (skip) { - ++m_inst; - continue; - } - - } - bool all_of_instance = false; bool with_region = false; diff --git a/src/db/db/dbRecursiveShapeIterator.h b/src/db/db/dbRecursiveShapeIterator.h index b8e6a640a..532fdd247 100644 --- a/src/db/db/dbRecursiveShapeIterator.h +++ b/src/db/db/dbRecursiveShapeIterator.h @@ -867,6 +867,7 @@ private: mutable std::vector m_cells; mutable std::vector m_local_complex_region_stack; mutable std::vector m_local_region_stack; + mutable std::vector m_skip_shapes_stack; mutable bool m_needs_reinit; mutable size_t m_inst_quad_id; mutable std::vector m_inst_quad_id_stack; diff --git a/src/db/unit_tests/dbHierProcessorTests.cc b/src/db/unit_tests/dbHierProcessorTests.cc index 23833d2ff..4679c5951 100644 --- a/src/db/unit_tests/dbHierProcessorTests.cc +++ b/src/db/unit_tests/dbHierProcessorTests.cc @@ -23,6 +23,7 @@ #include "tlUnitTest.h" #include "tlStream.h" +#include "tlFileUtils.h" #include "dbHierProcessor.h" #include "dbTestSupport.h" #include "dbReader.h" @@ -32,6 +33,9 @@ #include "dbLocalOperationUtils.h" #include "dbRegionLocalOperations.h" #include "dbPolygon.h" +#include "dbRecursiveInstanceIterator.h" +#include "dbDeepShapeStore.h" +#include "dbRegion.h" static std::string testdata (const std::string &fn) { @@ -1284,3 +1288,87 @@ TEST(Arrays) run_test_bool2 (_this, "hlp18.oas", TMNot, 100); } +TEST(XORTool) +{ + test_is_long_runner (); + + std::string fna (tl::combine_path (tl::testdata_private (), "xor/a.gds.gz")); + std::string fnb (tl::combine_path (tl::testdata_private (), "xor/b.gds.gz")); + std::string fn_au (tl::combine_path (tl::testdata_private (), "xor/xor_au.oas.gz")); + + db::Layout lya, lyb; + + unsigned int l1, l2; + + db::LayerMap lmap; + + lmap.map (db::LDPair (1, 0), l1 = lya.insert_layer ()); + lyb.insert_layer (); + + lmap.map (db::LDPair (2, 0), l2 = lya.insert_layer ()); + lyb.insert_layer (); + + { + tl::InputStream stream (fna); + db::Reader reader (stream); + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + reader.read (lya, options); + } + + { + tl::InputStream stream (fnb); + db::Reader reader (stream); + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + reader.read (lyb, options); + } + + db::Layout ly_out; + db::cell_index_type top_out = ly_out.add_cell ("TOP"); + unsigned int l1_out = ly_out.insert_layer (db::LayerProperties (1, 0)); + unsigned int l2_out = ly_out.insert_layer (db::LayerProperties (2, 0)); + + db::DeepShapeStore dss; + dss.set_wants_all_cells (true); // saves time for less cell mapping operations + + { + db::RecursiveShapeIterator ri_a, ri_b; + + ri_a = db::RecursiveShapeIterator (lya, lya.cell (*lya.begin_top_down ()), l1); + ri_a.set_for_merged_input (true); + + ri_b = db::RecursiveShapeIterator (lyb, lyb.cell (*lyb.begin_top_down ()), l1); + ri_b.set_for_merged_input (true); + + db::Region in_a (ri_a, dss, db::ICplxTrans (1.0)); + db::Region in_b (ri_b, dss, db::ICplxTrans (1.0)); + + db::Region xor_res = in_a ^ in_b; + EXPECT_EQ (xor_res.count (), size_t (12)); + + xor_res.insert_into (&ly_out, top_out, l1_out); + } + + { + db::RecursiveShapeIterator ri_a, ri_b; + + ri_a = db::RecursiveShapeIterator (lya, lya.cell (*lya.begin_top_down ()), l2); + ri_a.set_for_merged_input (true); + + ri_b = db::RecursiveShapeIterator (lyb, lyb.cell (*lyb.begin_top_down ()), l2); + ri_b.set_for_merged_input (true); + + db::Region in_a (ri_a, dss, db::ICplxTrans (1.0)); + db::Region in_b (ri_b, dss, db::ICplxTrans (1.0)); + + db::Region xor_res = in_a ^ in_b; + EXPECT_EQ (xor_res.count (), size_t (15984)); + + xor_res.insert_into (&ly_out, top_out, l2_out); + } + + db::compare_layouts (_this, ly_out, fn_au, db::WriteOAS); +} diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index 0e4a7430a..4c109fef0 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -1683,7 +1683,7 @@ TEST(13_ForMergedPerformance) ++n; } tl::info << "Counted " << n << " shapes on 66/20"; - EXPECT_EQ (n, size_t (1212844)); + EXPECT_EQ (n, size_t (1203078)); } { @@ -1735,7 +1735,7 @@ TEST(13_ForMergedPerformance) ++n; } tl::info << "Counted " << n << " shapes on 66/20"; - EXPECT_EQ (n, size_t (218552)); + EXPECT_EQ (n, size_t (218069)); } { @@ -1757,7 +1757,7 @@ TEST(13_ForMergedPerformance) db::Region r2 (si1); EXPECT_EQ (r1.count (), size_t (218823)); - EXPECT_EQ (r2.count (), size_t (218552)); + EXPECT_EQ (r2.count (), size_t (218069)); EXPECT_EQ ((r1 ^ r2).count (), size_t (0)); } From e0e6017a80686bf478a7dfba1ef4829b136710dc Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 28 Mar 2024 21:06:00 +0100 Subject: [PATCH 14/14] Need to differentiate test results between editable and non-editable mode --- .../dbRecursiveShapeIteratorTests.cc | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc index 4c109fef0..6184176db 100644 --- a/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc +++ b/src/db/unit_tests/dbRecursiveShapeIteratorTests.cc @@ -1650,6 +1650,9 @@ TEST(13_ForMergedPerformance) db::RecursiveShapeIterator si1 (ly, ly.cell (*ly.begin_top_down ()), l1); db::RecursiveShapeIterator si2 (ly, ly.cell (*ly.begin_top_down ()), l2); + size_t n1_expected_full = db::default_editable_mode () ? 1203072 : 1203078; + size_t n2_expected_full = 10; + { tl::SelfTimer timer ("Standard loop on 66/20"); size_t n = 0; @@ -1683,7 +1686,7 @@ TEST(13_ForMergedPerformance) ++n; } tl::info << "Counted " << n << " shapes on 66/20"; - EXPECT_EQ (n, size_t (1203078)); + EXPECT_EQ (n, size_t (n1_expected_full)); } { @@ -1694,7 +1697,7 @@ TEST(13_ForMergedPerformance) ++n; } tl::info << "Counted " << n << " shapes on 235/4"; - EXPECT_EQ (n, size_t (10)); + EXPECT_EQ (n, size_t (n2_expected_full)); } si1.set_for_merged_input (false); @@ -1727,6 +1730,9 @@ TEST(13_ForMergedPerformance) si1.set_for_merged_input (true); si2.set_for_merged_input (true); + size_t n1_expected = db::default_editable_mode () ? 218068 : 218069; + size_t n2_expected = 2; + { tl::SelfTimer timer ("'for_merged' loop on 66/20"); size_t n = 0; @@ -1735,7 +1741,7 @@ TEST(13_ForMergedPerformance) ++n; } tl::info << "Counted " << n << " shapes on 66/20"; - EXPECT_EQ (n, size_t (218069)); + EXPECT_EQ (n, size_t (n1_expected)); } { @@ -1746,7 +1752,7 @@ TEST(13_ForMergedPerformance) ++n; } tl::info << "Counted " << n << " shapes on 235/4"; - EXPECT_EQ (n, size_t (2)); + EXPECT_EQ (n, size_t (n2_expected)); } { @@ -1757,7 +1763,7 @@ TEST(13_ForMergedPerformance) db::Region r2 (si1); EXPECT_EQ (r1.count (), size_t (218823)); - EXPECT_EQ (r2.count (), size_t (218069)); + EXPECT_EQ (r2.count (), size_t (n1_expected)); EXPECT_EQ ((r1 ^ r2).count (), size_t (0)); } @@ -1769,7 +1775,7 @@ TEST(13_ForMergedPerformance) db::Region r2 (si2); EXPECT_EQ (r1.count (), size_t (2578)); - EXPECT_EQ (r2.count (), size_t (2)); + EXPECT_EQ (r2.count (), size_t (n2_expected)); EXPECT_EQ ((r1 ^ r2).count (), size_t (0)); } }