From cdf4d08fd377b9a6b32e354b52022a51730c3215 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 1 Jun 2020 23:28:04 +0200 Subject: [PATCH 1/6] WIP * Maybe fixed a performance issue on box-trees: the iterator wasn't going down to the very bottom of the tree on initialization * Added array quad skipping in display of shape arrays --- src/db/db/dbArray.h | 77 ++++++++++++++- src/db/db/dbBoxTree.h | 56 ++++++++--- src/db/db/dbInstances.cc | 5 +- src/db/db/dbInstances.h | 1 - src/db/db/dbShapeIterator.cc | 97 ++++++++++++++++++- src/db/db/dbShapes.h | 33 +++++++ src/db/unit_tests/dbBoxTreeTests.cc | 88 +++++++++++++++++ .../laybasic/layRedrawThreadWorker.cc | 49 ++++++++-- 8 files changed, 374 insertions(+), 32 deletions(-) diff --git a/src/db/db/dbArray.h b/src/db/db/dbArray.h index 8b55c6c35..7def9fe86 100644 --- a/src/db/db/dbArray.h +++ b/src/db/db/dbArray.h @@ -70,7 +70,11 @@ struct basic_array_iterator virtual long index_a () const { return -1; } virtual long index_b () const { return -1; } - + + virtual size_t quad_id () const { return 0; } + virtual box_type quad_box () const { return box_type::world (); } + virtual void skip_quad () { } + virtual disp_type get () const = 0; virtual basic_array_iterator *clone () const = 0; @@ -806,6 +810,21 @@ struct iterated_array_iterator return new iterated_array_iterator (*this); } + virtual size_t quad_id () const + { + return m_t.quad_id (); + } + + virtual box_type quad_box () const + { + return m_t.quad_box (); + } + + virtual void skip_quad () + { + m_t.skip_quad (); + } + private: box_tree_const_iterator m_b, m_e; box_tree_touching_iterator m_t; @@ -1420,6 +1439,36 @@ struct array_iterator return mp_base ? mp_base->index_b () : -1; } + /** + * @brief For iterators supporting quads (iterated arrays), this method will return the quad ID + */ + size_t quad_id () const + { + return mp_base ? mp_base->quad_id () : 0; + } + + /** + * @brief For iterators supporting quads (iterated arrays), this method will return the quad bounding box + * + * Note that this method will only return a valid quad box is the quad_id is non-null. + * + * This method will return the bounding box of all array offsets in the quad. + */ + db::box quad_box () const + { + return mp_base ? mp_base->quad_box () : db::box::world (); + } + + /** + * @brief For iterators supporting quads (iterated arrays), this method will skip the current quad + */ + void skip_quad () + { + if (mp_base) { + mp_base->skip_quad (); + } + } + private: trans_type m_trans; basic_array_iterator *mp_base; @@ -1835,6 +1884,32 @@ struct array } } + /** + * @brief Gets the bounding box from the iterator's current quad + * + * The bounding box is that of all objects in the current quad and + * is confined to the array's total bounding box. + */ + template + box_type quad_box (const Iter &iter, const BoxConv &bc) const + { + box_type bb; + if (mp_base) { + bb = mp_base->bbox (box_type (0, 0, 0, 0)); + } + bb &= iter.quad_box (); + + if (mp_base) { + if (mp_base->is_complex ()) { + return bb * box_type (mp_base->complex_trans (simple_trans_type (m_trans)) * bc (m_obj)); + } else { + return bb * (m_trans * bc (m_obj)); + } + } else { + return bb * (m_trans * bc (m_obj)); + } + } + /** * @brief The number of single instances in the array */ diff --git a/src/db/db/dbBoxTree.h b/src/db/db/dbBoxTree.h index 0e3a8ae67..7217d2159 100644 --- a/src/db/db/dbBoxTree.h +++ b/src/db/db/dbBoxTree.h @@ -577,12 +577,16 @@ private: return m_quad < 4; } - // down one level + // down as many levels as required for the next non-empty quad // returns true if this is possible bool down () { - box_tree_node *c = mp_node->child (m_quad); - if (c) { + while (true) { + + box_tree_node *c = mp_node->child (m_quad); + if (! c) { + return false; + } mp_node = c; m_quad = -1; @@ -595,12 +599,11 @@ private: // nothing to visit: up again up (); return false; - } else { + } else if (m_quad < 0) { + // stay in main chunk return true; } - } else { - return false; } } @@ -670,7 +673,7 @@ private: * whose box overlaps or touches a specified test box. */ -template +template class box_tree { public: @@ -1175,7 +1178,16 @@ private: // the bins are: overall, ur, ul, ll, lr, empty element_iterator qloc [6] = { from, from, from, from, from, from }; - point_type center (bbox.center ()); + point_type center; + if (bbox.width () < thin_aspect * bbox.height ()) { + // separate by height only + center = point_type (bbox.left (), bbox.bottom () + bbox.height () / 2); + } else if (bbox.height () < thin_aspect * bbox.width ()) { + // separate by width only + center = point_type (bbox.left () + bbox.width () / 2, bbox.bottom ()); + } else { + center = bbox.center (); + } for (element_iterator e = from; e != to; ++e) { @@ -1578,12 +1590,16 @@ private: return m_quad < 4; } - // down one level + // down as many levels as required for the next non-empty quad // returns true if this is possible bool down () { - box_tree_node *c = mp_node->child (m_quad); - if (c) { + while (true) { + + box_tree_node *c = mp_node->child (m_quad); + if (! c) { + return false; + } mp_node = c; m_quad = -1; @@ -1596,12 +1612,11 @@ private: // nothing to visit: up again up (); return false; - } else { + } else if (m_quad < 0) { + // stay in main chunk return true; } - } else { - return false; } } @@ -1638,7 +1653,7 @@ private: * is sorted. */ -template +template class unstable_box_tree { public: @@ -2103,7 +2118,16 @@ private: } obj_iterator qloc [5] = { from, from, from, from, from }; - point_type center (bbox.center ()); + point_type center; + if (bbox.width () < thin_aspect * bbox.height ()) { + // separate by height only + center = point_type (bbox.left (), bbox.bottom () + bbox.height () / 2); + } else if (bbox.height () < thin_aspect * bbox.width ()) { + // separate by width only + center = point_type (bbox.left () + bbox.width () / 2, bbox.bottom ()); + } else { + center = bbox.center (); + } for (obj_iterator e = from; e != to; ++e) { diff --git a/src/db/db/dbInstances.cc b/src/db/db/dbInstances.cc index 096d515de..488b86a92 100644 --- a/src/db/db/dbInstances.cc +++ b/src/db/db/dbInstances.cc @@ -640,13 +640,12 @@ OverlappingInstanceIteratorTraits::init (instance_iteratorbegin_sorted_insts ()), - m_end (insts->end_sorted_insts ()), - mp_insts (insts) + m_end (insts->end_sorted_insts ()) { } cell_index_type diff --git a/src/db/db/dbInstances.h b/src/db/db/dbInstances.h index e754392db..fd2e13a08 100644 --- a/src/db/db/dbInstances.h +++ b/src/db/db/dbInstances.h @@ -945,7 +945,6 @@ public: private: inst_iterator_type m_iter, m_end; - const instances_type *mp_insts; }; /** diff --git a/src/db/db/dbShapeIterator.cc b/src/db/db/dbShapeIterator.cc index a0e9db1e1..f4433255b 100644 --- a/src/db/db/dbShapeIterator.cc +++ b/src/db/db/dbShapeIterator.cc @@ -546,9 +546,13 @@ ShapeIterator::advance_aref (int &mode) if (mode && m_array_iterator_valid) { - if (mode > 0) { + if (mode == 1) { array_iterator *arr_iter = (array_iterator *) m_ad.iter; ++*arr_iter; + } else if (mode == 2) { + // skip array quad -> skip rest of array quad and move to shape in the next quad or to end + do_skip_array_quad (); + mode = 1; } else { // skip quad -> skip rest of array and move to next shape array skip_array (); // sets m_array_iterator_valid = false @@ -810,9 +814,100 @@ ShapeIterator::quad_box () const return quad_box_generic (); } } + return db::Box (); } +template +void +ShapeIterator::do_skip_array_quad_iter () +{ + Iter *arr_iter = (Iter *) m_ad.iter; + arr_iter->skip_quad (); +} + +void +ShapeIterator::do_skip_array_quad () +{ + if (m_array_iterator_valid) { + if (m_type == PolygonPtrArray) { + do_skip_array_quad_iter (); + } else if (m_type == SimplePolygonPtrArray) { + do_skip_array_quad_iter (); + } else if (m_type == PathPtrArray) { + do_skip_array_quad_iter (); + } else if (m_type == TextPtrArray) { + do_skip_array_quad_iter (); + } else if (m_type == BoxArray) { + do_skip_array_quad_iter (); + } else if (m_type == ShortBoxArray) { + do_skip_array_quad_iter (); + } + } +} + +template +size_t +ShapeIterator::get_array_quad_id () const +{ + Iter *arr_iter = (Iter *) m_ad.iter; + return arr_iter->quad_id (); +} + +size_t +ShapeIterator::array_quad_id () const +{ + if (m_array_iterator_valid) { + if (m_type == PolygonPtrArray) { + return get_array_quad_id (); + } else if (m_type == SimplePolygonPtrArray) { + return get_array_quad_id (); + } else if (m_type == PathPtrArray) { + return get_array_quad_id (); + } else if (m_type == TextPtrArray) { + return get_array_quad_id (); + } else if (m_type == BoxArray) { + return get_array_quad_id (); + } else if (m_type == ShortBoxArray) { + return get_array_quad_id (); + } + } + + return 0; +} + +template +db::Box +ShapeIterator::get_array_quad_box () const +{ + const Array *arr = m_array.basic_ptr (typename Array::tag ()); + Iter *arr_iter = (Iter *) m_ad.iter; + db::box_convert bc; + return arr->quad_box (*arr_iter, bc); +} + +db::Box +ShapeIterator::array_quad_box () const +{ + if (m_array_iterator_valid) { + if (m_type == PolygonPtrArray) { + return get_array_quad_box (); + } else if (m_type == SimplePolygonPtrArray) { + return get_array_quad_box (); + } else if (m_type == PathPtrArray) { + return get_array_quad_box (); + } else if (m_type == TextPtrArray) { + return get_array_quad_box (); + } else if (m_type == BoxArray) { + return get_array_quad_box (); + } else if (m_type == ShortBoxArray) { + return get_array_quad_box (); + } + } + + return db::Box::world (); +} + void ShapeIterator::cleanup () { diff --git a/src/db/db/dbShapes.h b/src/db/db/dbShapes.h index ce8368518..1942906a0 100644 --- a/src/db/db/dbShapes.h +++ b/src/db/db/dbShapes.h @@ -319,6 +319,33 @@ public: advance (-1); } + /** + * @brief Gets the arrays quad ID + * + * The arrays quad ID is a unique identifier for the current quad for iterated arrays. This can be used to + * detect whether the iterator entered a new quad and optimize the search in that case. + */ + size_t array_quad_id () const; + + /** + * @brief Gets the quad box + * + * Gets the box the current quad uses. This box may be larger than the actual shape containers + * bounding box. Specifically if there is no quad tree at all, this box is the world box. + */ + db::Box array_quad_box () const; + + /** + * @brief Skips the current quad + * + * Moves to the next quad. This method can be used to shortcut searching if we are inside + * a quad that is not relevant for the search. + */ + void skip_array_quad () + { + advance (2); + } + private: // a helper union for the iter_size union // (basically computing the size required for all iterators for a certain shape/array type) @@ -418,6 +445,12 @@ private: template db::Box quad_box_by_shape (OverlappingRegionTag) const; template db::Box quad_box_generic () const; + template db::Box get_array_quad_box () const; + template size_t get_array_quad_id () const; + + template void do_skip_array_quad_iter (); + void do_skip_array_quad (); + template bool advance_shape (int &mode); template void init_array_iter (NoRegionTag); template void init_array_iter (TouchingRegionTag); diff --git a/src/db/unit_tests/dbBoxTreeTests.cc b/src/db/unit_tests/dbBoxTreeTests.cc index c2cbe01ed..9b7e3feb5 100644 --- a/src/db/unit_tests/dbBoxTreeTests.cc +++ b/src/db/unit_tests/dbBoxTreeTests.cc @@ -1031,4 +1031,92 @@ TEST(6U) } +TEST(7) +{ + Box2Box conv; + TestTree t; + int n = 200000; + + for (int i = n - 1; i >= 0; --i) { + t.insert (db::Box (i * 10, 0, i * 10 + 5, 5)); + } + t.sort (conv); + + { + tl::SelfTimer timer ("test 7 lookup"); + size_t n = 0; + for (unsigned int i = 0; i < 2000; ++i) { + db::Coord sx = 0, sy = 0; + TestTree::touching_iterator it = t.begin_touching (db::Box (db::Point (2000, 0), db::Point (3000, 0)), conv); + while (!it.at_end ()) { + sx += abs (it->left ()); + sy += abs (it->bottom ()); + ++it; + ++n; + } + EXPECT_EQ (sx, 252500); + EXPECT_EQ (sy, 0); + } + EXPECT_EQ (n, size_t (101 * 2000)); + } + + { + tl::SelfTimer timer ("test 7 traverse"); + db::Coord m = std::numeric_limits::max (); + size_t n = 0; + for (unsigned int i = 0; i < 10; ++i) { + TestTree::touching_iterator it = t.begin_touching (db::Box (db::Point (-m,-m), db::Point (m, m)), conv); + while (!it.at_end ()) { + ++it; + ++n; + } + } + EXPECT_EQ (n, t.size () * 10); + } +} + +TEST(7U) +{ + Box2Box conv; + UnstableTestTree t; + + int n = 200000; + + for (int i = n - 1; i >= 0; --i) { + t.insert (db::Box (i * 10, 0, i * 10 + 5, 5)); + } + t.sort (conv); + + { + tl::SelfTimer timer ("test 7U lookup"); + size_t n = 0; + for (unsigned int i = 0; i < 2000; ++i) { + db::Coord sx = 0, sy = 0; + UnstableTestTree::touching_iterator it = t.begin_touching (db::Box (db::Point (2000, 0), db::Point (3000, 0)), conv); + while (!it.at_end ()) { + sx += abs (it->left ()); + sy += abs (it->bottom ()); + ++it; + ++n; + } + EXPECT_EQ (sx, 252500); + EXPECT_EQ (sy, 0); + } + EXPECT_EQ (n, size_t (101 * 2000)); + } + + { + tl::SelfTimer timer ("test 7U traverse"); + db::Coord m = std::numeric_limits::max (); + size_t n = 0; + for (unsigned int i = 0; i < 10; ++i) { + UnstableTestTree::touching_iterator it = t.begin_touching (db::Box (db::Point (-m,-m), db::Point (m, m)), conv); + while (!it.at_end ()) { + ++it; + ++n; + } + } + EXPECT_EQ (n, t.size () * 10); + } +} diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.cc b/src/laybasic/laybasic/layRedrawThreadWorker.cc index d48752965..87dedc4ce 100644 --- a/src/laybasic/laybasic/layRedrawThreadWorker.cc +++ b/src/laybasic/laybasic/layRedrawThreadWorker.cc @@ -1582,6 +1582,8 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_ db::Shape last_array; size_t current_quad_id = 0; + size_t current_array_quad_id = 0; + db::ShapeIterator shape (shapes.begin_touching (*v, db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::Paths, mp_prop_sel, m_inv_prop_sel)); while (! shape.at_end ()) { @@ -1596,16 +1598,18 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_ } if (skip) { - shape.skip_quad (); + continue; + } - } else { + if (shape.in_array ()) { - bool simplified = false; - - if (shape.in_array () && last_array != shape.array ()) { + if (last_array != shape.array ()) { last_array = shape.array (); + current_array_quad_id = 0; + + bool simplified = false; if (last_array.type () == db::Shape::PolygonPtrArray) { simplified = draw_array_simplified (mp_renderer.get (), last_array, frame, vertex, trans); @@ -1619,17 +1623,42 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_ simplified = draw_array_simplified (mp_renderer.get (), last_array, frame, vertex, trans); } + if (simplified) { + shape.finish_array (); + // continue with the next shape, array or quad + continue; + } + } - if (simplified) { - shape.finish_array (); - } else { - mp_renderer->draw (*shape, trans, fill, frame, vertex, text); - ++shape; + } else { + current_array_quad_id = 0; + } + + // try whether the array quad can be simplified + + size_t aqid = shape.array_quad_id (); + if (aqid != 0 && aqid != current_array_quad_id) { + + current_array_quad_id = aqid; + + db::DBox qbbox = trans * shape.array_quad_box (); + if (qbbox.width () < 1.5 && qbbox.height () < 1.5) { + + // draw a single box instead of the quad + mp_renderer->draw (qbbox, fill, frame, vertex, text); + shape.skip_array_quad (); + + // continue with the next shape, array or quad + continue; + } } + mp_renderer->draw (*shape, trans, fill, frame, vertex, text); + ++shape; + } } From 9c4648a5b59019ad55230e48c8981f5a1f24b32e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 2 Jun 2020 23:59:09 +0200 Subject: [PATCH 2/6] WIP: some bug fixing and enhancements. --- src/db/db/dbBoxTree.h | 119 +++++++++++++++++++++--------------------- 1 file changed, 60 insertions(+), 59 deletions(-) diff --git a/src/db/db/dbBoxTree.h b/src/db/db/dbBoxTree.h index 7217d2159..f34d49f01 100644 --- a/src/db/db/dbBoxTree.h +++ b/src/db/db/dbBoxTree.h @@ -168,19 +168,20 @@ public: typedef typename Tree::coord_type coord_type; typedef typename Tree::box_type box_type; - box_tree_node (box_tree_node *parent, const point_type ¢er, unsigned int quad) - : m_center (center) + box_tree_node (box_tree_node *parent, const point_type ¢er, const box_type &qbox, unsigned int quad) { - for (int i = 0; i < 5; ++i) { - m_lenq[i] = 0; - } - for (int i = 0; i < 4; ++i) { - mp_children[i] = 0; - } - mp_parent = (box_tree_node *)((char *) parent + quad); - if (parent) { - parent->mp_children [quad] = this; + point_type corner; + if (quad == 0) { + corner = qbox.upper_right (); + } else if (quad == 1) { + corner = qbox.upper_left (); + } else if (quad == 2) { + corner = qbox.lower_left (); + } else if (quad == 3) { + corner = qbox.lower_right (); } + + init (parent, center, corner, quad); } ~box_tree_node () @@ -195,7 +196,7 @@ public: box_tree_node *clone (box_tree_node *parent = 0, unsigned int quad = 0) const { - box_tree_node *n = new box_tree_node (parent, m_center, quad); + box_tree_node *n = new box_tree_node (parent, m_center, m_corner, quad); for (unsigned int i = 0; i < 5; ++i) { n->m_lenq[i] = m_lenq[i]; } @@ -249,14 +250,52 @@ public: return m_center; } + box_type quad_box (int quad) const + { + box_type qb = box_type::world (); + if (parent ()) { + qb = box_type (m_corner, parent ()->center ()); + } + + switch (quad) { + case 0: return box_type (m_center, qb.upper_right ()); + case 1: return box_type (m_center, qb.upper_left ()); + case 2: return box_type (m_center, qb.lower_left ()); + case 3: return box_type (m_center, qb.lower_right ()); + default: return qb; + } + } + private: box_tree_node *mp_parent; size_t m_lenq [5]; box_tree_node *mp_children [4]; - point_type m_center; + point_type m_center, m_corner; box_tree_node (const box_tree_node &d); box_tree_node &operator= (const box_tree_node &d); + + box_tree_node (box_tree_node *parent, const point_type ¢er, const point_type &corner, unsigned int quad) + { + init (parent, center, corner, quad); + } + + void init (box_tree_node *parent, const point_type ¢er, const point_type &corner, unsigned int quad) + { + m_center = center; + m_corner = corner; + + for (int i = 0; i < 5; ++i) { + m_lenq[i] = 0; + } + for (int i = 0; i < 4; ++i) { + mp_children[i] = 0; + } + mp_parent = (box_tree_node *)((char *) parent + quad); + if (parent) { + parent->mp_children [quad] = this; + } + } }; /** @@ -459,28 +498,9 @@ public: box_type quad_box () const { if (! mp_node) { - return box_type::world (); - } else { - - point_type c = mp_node->center (); - box_type qb; - if (! mp_node->parent ()) { - qb = box_type::world (); - } else { - point_type pc = mp_node->parent ()->center (); - qb = box_type (c - (pc - c), pc); - } - - switch (m_quad) { - case 0: return box_type (c, qb.upper_right ()); - case 1: return box_type (c, qb.upper_left ()); - case 2: return box_type (c, qb.lower_left ()); - case 3: return box_type (c, qb.lower_right ()); - default: return qb; - } - + return mp_node->quad_box (m_quad); } } @@ -1179,10 +1199,10 @@ private: // the bins are: overall, ur, ul, ll, lr, empty element_iterator qloc [6] = { from, from, from, from, from, from }; point_type center; - if (bbox.width () < thin_aspect * bbox.height ()) { + if (bbox.width () < bbox.height () / thin_aspect) { // separate by height only center = point_type (bbox.left (), bbox.bottom () + bbox.height () / 2); - } else if (bbox.height () < thin_aspect * bbox.width ()) { + } else if (bbox.height () < bbox.width () / thin_aspect) { // separate by width only center = point_type (bbox.left () + bbox.width () / 2, bbox.bottom ()); } else { @@ -1236,7 +1256,7 @@ private: if (nn >= min_quads) { // create a new node representing this tree - box_tree_node *node = new box_tree_node (parent, center, quad); + box_tree_node *node = new box_tree_node (parent, center, bbox, quad); if (parent == 0) { mp_root = node; } @@ -1472,28 +1492,9 @@ public: box_type quad_box () const { if (! mp_node) { - return box_type::world (); - } else { - - point_type c = mp_node->center (); - box_type qb; - if (! mp_node->parent ()) { - qb = box_type::world (); - } else { - point_type pc = mp_node->parent ()->center (); - qb = box_type (c - (pc - c), pc); - } - - switch (m_quad) { - case 0: return box_type (c, qb.upper_right ()); - case 1: return box_type (c, qb.upper_left ()); - case 2: return box_type (c, qb.lower_left ()); - case 3: return box_type (c, qb.lower_right ()); - default: return qb; - } - + return mp_node->quad_box (m_quad); } } @@ -2119,10 +2120,10 @@ private: obj_iterator qloc [5] = { from, from, from, from, from }; point_type center; - if (bbox.width () < thin_aspect * bbox.height ()) { + if (bbox.width () < bbox.height () / thin_aspect) { // separate by height only center = point_type (bbox.left (), bbox.bottom () + bbox.height () / 2); - } else if (bbox.height () < thin_aspect * bbox.width ()) { + } else if (bbox.height () < bbox.width () / thin_aspect) { // separate by width only center = point_type (bbox.left () + bbox.width () / 2, bbox.bottom ()); } else { @@ -2182,7 +2183,7 @@ private: if (nn >= min_quads) { // create a new node representing this tree - box_tree_node *node = new box_tree_node (parent, center, quad); + box_tree_node *node = new box_tree_node (parent, center, bbox, quad); if (parent == 0) { mp_root = node; } From 999c0652620b839cd1655eb9176a14df099ce899 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 4 Jun 2020 12:17:34 +0200 Subject: [PATCH 3/6] Introducing iterated arrays for instances Iterated instances are created for OASIS files using irregular repetitions in viewer mode. Reason: this way, the same drawing optimization than for iterated shape arrays can be applied. As this is a new API feature, some adjustments had to be made to incorporate them into the code. --- src/db/db/dbArray.h | 26 +++ src/db/db/dbHash.h | 6 + src/db/db/dbInstances.cc | 24 ++- src/db/db/dbLayoutDiff.cc | 4 + src/db/db/dbTextWriter.cc | 65 +++--- src/db/db/gsiDeclDbCell.cc | 103 +++------- src/db/unit_tests/dbLayoutDiffTests.cc | 4 + .../laybasic/layBrowseInstancesForm.cc | 10 +- src/laybasic/laybasic/layBrowseShapesForm.cc | 10 +- .../laybasic/layRedrawThreadWorker.cc | 42 +++- .../gds2/db_plugin/dbGDS2WriterBase.cc | 188 +++++++++--------- .../oasis/db_plugin/dbOASISReader.cc | 38 ++++ .../oasis/db_plugin/dbOASISWriter.cc | 28 ++- .../diff/lay_plugin/layDiffToolDialog.cc | 4 + src/tl/tl/tlUnitTest.cc | 1 + 15 files changed, 342 insertions(+), 211 deletions(-) diff --git a/src/db/db/dbArray.h b/src/db/db/dbArray.h index 7def9fe86..78e7fd402 100644 --- a/src/db/db/dbArray.h +++ b/src/db/db/dbArray.h @@ -1641,6 +1641,32 @@ struct array // .. nothing yet .. } + /** + * @brief The iterated array constructor + * + * This is basically a convenience function that creates + * an appropriate basic_array object. + */ + template + array (const Obj &obj, const trans_type &trans, Iter from, Iter to) + : m_obj (obj), m_trans (trans), mp_base (new iterated_array (from, to)) + { + // .. nothing yet .. + } + + /** + * @brief The complex iterated array constructor + * + * This is basically a convenience function that creates + * an appropriate basic_array object. + */ + template + array (const Obj &obj, const complex_trans_type &ct, Iter from, Iter to) + : m_obj (obj), m_trans (ct), mp_base (new iterated_complex_array (ct.rcos (), ct.mag (), from, to)) + { + // .. nothing yet .. + } + /** * @brief The singular complex instance constructor * diff --git a/src/db/db/dbHash.h b/src/db/db/dbHash.h index 91c950468..d816a667e 100644 --- a/src/db/db/dbHash.h +++ b/src/db/db/dbHash.h @@ -283,6 +283,12 @@ namespace std h = hfunc (b, h); h = hfunc (na, h); h = hfunc (nb, h); + } else if (o.size () > 1) { + // iterated array + typename db::array >::iterator i = o.begin (); + while (! (++i).at_end ()) { + h = hfunc (*i, h); + } } if (o.is_complex ()) { diff --git a/src/db/db/dbInstances.cc b/src/db/db/dbInstances.cc index 488b86a92..4d9a0e6ab 100644 --- a/src/db/db/dbInstances.cc +++ b/src/db/db/dbInstances.cc @@ -241,16 +241,28 @@ Instance::to_string (bool resolve_cell_name) const r = "cell_index=" + tl::to_string (ci.object ().cell_index ()); } - if (ci.is_complex ()) { - r += " " + ci.complex_trans ().to_string (); - } else { - r += " " + (*ci.begin ()).to_string (); - } - db::vector a, b; unsigned long amax, bmax; if (ci.is_regular_array (a, b, amax, bmax)) { + + if (ci.is_complex ()) { + r += " " + ci.complex_trans ().to_string (); + } else { + r += " " + (*ci.begin ()).to_string (); + } + r += " array=(" + a.to_string () + "," + b.to_string () + " " + tl::to_string (amax) + "x" + tl::to_string (bmax) + ")"; + + } else { + + for (db::CellInstArray::iterator i = ci.begin (); ! i.at_end (); ++i) { + if (ci.is_complex ()) { + r += " " + ci.complex_trans (*i).to_string (); + } else { + r += " " + (*i).to_string (); + } + } + } if (has_prop_id ()) { diff --git a/src/db/db/dbLayoutDiff.cc b/src/db/db/dbLayoutDiff.cc index c3b778abc..f9c29bbce 100644 --- a/src/db/db/dbLayoutDiff.cc +++ b/src/db/db/dbLayoutDiff.cc @@ -1189,6 +1189,8 @@ PrintingDifferenceReceiver::print_cell_inst (const db::CellInstArrayWithProperti unsigned long amax, bmax; if (ci.is_regular_array (a, b, amax, bmax)) { enough (tl::info) << "[a=" << a.to_string () << ", b=" << b.to_string () << ", na=" << amax << ", nb=" << bmax << "]" << tl::noendl; + } else if (ci.size () > 1) { + enough (tl::info) << " (+" << (ci.size () - 1) << " irregular locations)" << tl::noendl; } else { enough (tl::info) << "" << tl::noendl; } @@ -1208,6 +1210,8 @@ PrintingDifferenceReceiver::print_cell_inst (const db::CellInstArrayWithProperti unsigned long amax, bmax; if (ci.is_regular_array (a, b, amax, bmax)) { enough (tl::info) << "[a=" << a.to_string () << ", b=" << b.to_string () << ", na=" << amax << ", nb=" << bmax << "]" << tl::noendl; + } else if (ci.size () > 1) { + enough (tl::info) << " (+" << (ci.size () - 1) << " irregular locations)" << tl::noendl; } else { enough (tl::info) << "" << tl::noendl; } diff --git a/src/db/db/dbTextWriter.cc b/src/db/db/dbTextWriter.cc index 2dc06d0d3..d484a7a11 100644 --- a/src/db/db/dbTextWriter.cc +++ b/src/db/db/dbTextWriter.cc @@ -198,43 +198,52 @@ TextWriter::write (const db::Layout &layout) for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) { - std::string pfx = ""; - - if (inst->has_prop_id () && inst->prop_id () != 0) { - pfx = "p $props"; - write_props (layout, inst->prop_id ()); - } - db::Vector a, b; unsigned long amax, bmax; bool is_reg = inst->is_regular_array (a, b, amax, bmax); - *this << (is_reg ? "aref" : "sref") << pfx << " {" << layout.cell_name (inst->cell_index ()) << "}"; + for (db::CellInstArray::iterator i = inst->begin (); ! i.at_end (); ++i) { - db::Trans t = inst->front (); + std::string pfx = ""; + + if (inst->has_prop_id () && inst->prop_id () != 0) { + pfx = "p $props"; + write_props (layout, inst->prop_id ()); + } + + *this << (is_reg ? "aref" : "sref") << pfx << " {" << layout.cell_name (inst->cell_index ()) << "}"; + + db::Trans t = *i; + + if (inst->is_complex ()) { + db::CellInstArray::complex_trans_type ct = inst->complex_trans (t); + *this << " " << ct.angle (); + *this << " " << (ct.is_mirror () ? 1 : 0); + *this << " " << ct.mag (); + } else { + *this << " " << (t.rot () % 4) * 90.0; + *this << " " << (t.is_mirror () ? 1 : 0); + *this << " " << 1.0; + } + + if (is_reg) { + *this << " " << int (std::max ((unsigned long) 1, amax)); + *this << " " << int (std::max ((unsigned long) 1, bmax)); + } + *this << " " << t.disp (); + if (is_reg) { + *this << " " << (t.disp () + a * (long) amax); + *this << " " << (t.disp () + b * (long) bmax); + } + *this << endl (); + + if (is_reg) { + break; + } - if (inst->is_complex ()) { - *this << " " << inst->complex_trans ().angle (); - *this << " " << (inst->complex_trans ().is_mirror () ? 1 : 0); - *this << " " << inst->complex_trans ().mag (); - } else { - *this << " " << (t.rot () % 4) * 90.0; - *this << " " << (t.is_mirror () ? 1 : 0); - *this << " " << 1.0; } - if (is_reg) { - *this << " " << int (std::max ((unsigned long) 1, amax)); - *this << " " << int (std::max ((unsigned long) 1, bmax)); - } - *this << " " << t.disp (); - if (is_reg) { - *this << " " << (t.disp () + a * (long) amax); - *this << " " << (t.disp () + b * (long) bmax); - } - *this << endl (); - } end_sorted_section (); diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index bf4000a24..d6f2bef9d 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -251,6 +251,8 @@ struct cell_inst_array_defs unsigned long na = 1, nb = 1; if (arr->is_regular_array (a, b, na, nb)) { *arr = C (arr->object (), t, a, b, na, nb); + } else if (arr->is_iterated_array ()) { + throw tl::Exception (tl::to_string (tr ("Can't set the transformation on an iterated array (layout not editable?)"))); } else { *arr = C (arr->object (), t); } @@ -262,6 +264,8 @@ struct cell_inst_array_defs unsigned long na = 1, nb = 1; if (arr->is_regular_array (a, b, na, nb)) { *arr = C (arr->object (), t, a, b, na, nb); + } else if (arr->is_iterated_array ()) { + throw tl::Exception (tl::to_string (tr ("Can't set the transformation on an iterated array (layout not editable?)"))); } else { *arr = C (arr->object (), t); } @@ -292,6 +296,8 @@ struct cell_inst_array_defs s += "*"; s += tl::to_string (nb); s += "]"; + } else if (arr->size () > 1) { + s += std::string (" (+") + tl::to_string (arr->size () - 1) + " irregular locations)"; } return s; @@ -303,14 +309,27 @@ struct cell_inst_array_defs { typedef db::array > target_array; + std::vector iterated; + std::vector iterated_transformed; typename C::vector_type a, b; unsigned long amax = 0, bmax = 0; + if (arr.is_regular_array (a, b, amax, bmax)) { if (arr.is_complex ()) { return target_array (arr.object (), t * arr.complex_trans () * t.inverted (), t * a, t * b, amax, bmax); } else { return target_array (arr.object (), typename target_array::trans_type (t * typename C::complex_trans_type (arr.front ()) * t.inverted ()), t * a, t * b, amax, bmax); } + } else if (arr.is_iterated_array (&iterated)) { + iterated_transformed.reserve (iterated.size ()); + for (typename std::vector::const_iterator i = iterated.begin (); i != iterated.end (); ++i) { + iterated_transformed.push_back (t * *i); + } + if (arr.is_complex ()) { + return target_array (arr.object (), t * arr.complex_trans () * t.inverted (), iterated_transformed.begin (), iterated_transformed.end ()); + } else { + return target_array (arr.object (), typename target_array::trans_type (t * typename C::complex_trans_type (arr.front ()) * t.inverted ()), iterated_transformed.begin (), iterated_transformed.end ()); + } } else if (arr.is_complex ()) { return target_array (arr.object (), t * arr.complex_trans () * t.inverted ()); } else { @@ -352,88 +371,12 @@ struct cell_inst_array_defs static bool less (const C *i, const C &other) { - if (i->object ().cell_index () != other.object ().cell_index ()) { - return i->object ().cell_index () < other.object ().cell_index (); - } - - db::vector a, b; - unsigned long na = 1, nb = 1; - bool r = i->is_regular_array (a, b, na, nb); - - db::vector aother, bother; - unsigned long naother = 1, nbother = 1; - bool rother = other.is_regular_array (aother, bother, naother, nbother); - - if (r != rother) { - return r < rother; - } else if (r) { - if (a.not_equal (aother)) { - return a.less (aother); - } - if (b.not_equal (bother)) { - return b.less (bother); - } - if (na != naother) { - return na < naother; - } - if (nb != nbother) { - return nb < nbother; - } - } - - bool c = i->is_complex (); - bool cother = other.is_complex (); - - if (c != cother) { - return c < cother; - } else if (c) { - return i->complex_trans ().less (other.complex_trans ()); - } else { - return i->front ().less (other.front ()); - } + return *i < other; } static bool equal (const C *i, const C &other) { - if (i->object ().cell_index () != other.object ().cell_index ()) { - return false; - } - - db::vector a, b; - unsigned long na = 1, nb = 1; - bool r = i->is_regular_array (a, b, na, nb); - - db::vector aother, bother; - unsigned long naother = 1, nbother = 1; - bool rother = other.is_regular_array (aother, bother, naother, nbother); - - if (r != rother) { - return false; - } else if (r) { - if (a.not_equal (aother)) { - return false; - } - if (b.not_equal (bother)) { - return false; - } - if (na != naother) { - return false; - } - if (nb != nbother) { - return false; - } - } - - bool c = i->is_complex (); - bool cother = other.is_complex (); - - if (c != cother) { - return false; - } else if (c) { - return i->complex_trans ().equal (other.complex_trans ()); - } else { - return i->front ().equal (other.front ()); - } + return *i == other; } static bool not_equal (const C *i, const C &other) @@ -508,7 +451,9 @@ struct cell_inst_array_defs ) + gsi::method ("size", &C::size, "@brief Gets the number of single instances in the array\n" - "If the instance represents a single instance, the count is 1. Otherwise it is na*nb." + "If the instance represents a single instance, the count is 1. Otherwise it is na*nb. " + "Starting with version 0.27, there may be iterated instances for which the size is larger than 1, but \\is_regular_array? will return false. " + "In this case, use \\each_trans or \\each_cplx_trans to retrieve the individual placements of the iterated instance." ) + gsi::method_ext ("cell_index", &cell_index, "@brief Gets the cell index of the cell instantiated \n" diff --git a/src/db/unit_tests/dbLayoutDiffTests.cc b/src/db/unit_tests/dbLayoutDiffTests.cc index e4b533a2b..5dadccfd2 100644 --- a/src/db/unit_tests/dbLayoutDiffTests.cc +++ b/src/db/unit_tests/dbLayoutDiffTests.cc @@ -97,6 +97,8 @@ TestDifferenceReceiver::print_cell_inst (const db::CellInstArrayWithProperties & unsigned long amax, bmax; if (ci.is_regular_array (a, b, amax, bmax)) { m_os << "[a=" << a.to_string () << ", b=" << b.to_string () << ", na=" << amax << ", nb=" << bmax << "]"; + } else if (ci.size () > 1) { + m_os << " (+" << (ci.size () - 1) << " irregular placements)"; } if (ci.properties_id () != 0) { m_os << " [" << ci.properties_id () << "]" << std::endl; @@ -112,6 +114,8 @@ TestDifferenceReceiver::print_cell_inst (const db::CellInstArrayWithProperties & unsigned long amax, bmax; if (ci.is_regular_array (a, b, amax, bmax)) { m_os << "[a=" << a.to_string () << ", b=" << b.to_string () << ", na=" << amax << ", nb=" << bmax << "]"; + } else if (ci.size () > 1) { + m_os << " (+" << (ci.size () - 1) << " irregular placements)"; } if (ci.properties_id () != 0) { m_os << " [" << ci.properties_id () << "]" << std::endl; diff --git a/src/laybasic/laybasic/layBrowseInstancesForm.cc b/src/laybasic/laybasic/layBrowseInstancesForm.cc index a4149cf95..82c08848c 100644 --- a/src/laybasic/laybasic/layBrowseInstancesForm.cc +++ b/src/laybasic/laybasic/layBrowseInstancesForm.cc @@ -702,7 +702,15 @@ BrowseInstancesForm::fill_cell_instances (const db::ICplxTrans &t, const db::Lay std::string aref; if (r > 1 || c > 1) { - aref = tl::sprintf ("[%ld,%ld]", c, r); + aref = "["; + aref += tl::to_string (c); + aref += ","; + aref += tl::to_string (r); + aref += "]"; + } else if (parent_inst.size () > 1) { + aref = "(+"; + aref += tl::to_string (parent_inst.size () - 1); + aref += "x)"; } std::string new_path; diff --git a/src/laybasic/laybasic/layBrowseShapesForm.cc b/src/laybasic/laybasic/layBrowseShapesForm.cc index 24c26737c..fc29b8761 100644 --- a/src/laybasic/laybasic/layBrowseShapesForm.cc +++ b/src/laybasic/laybasic/layBrowseShapesForm.cc @@ -868,7 +868,15 @@ BrowseShapesForm::fill_cell_instances (const db::ICplxTrans &t, const db::Layout std::string aref; if (r > 1 || c > 1) { - aref = tl::sprintf ("[%ld,%ld]", c, r); + aref = "["; + aref += tl::to_string (c); + aref += ","; + aref += tl::to_string (r); + aref += "]"; + } else if (parent_inst.size () > 1) { + aref = "(+"; + aref += tl::to_string (parent_inst.size () - 1); + aref += "x)"; } std::string new_path; diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.cc b/src/laybasic/laybasic/layRedrawThreadWorker.cc index 87dedc4ce..0aae08e5a 100644 --- a/src/laybasic/laybasic/layRedrawThreadWorker.cc +++ b/src/laybasic/laybasic/layRedrawThreadWorker.cc @@ -809,15 +809,31 @@ RedrawThreadWorker::draw_boxes (bool drawing_context, db::cell_index_type ci, co } else { - // The array (or single instance) must be iterated instance - // by instance - for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ++p) { + size_t qid = 0; + + // The array (or single instance) must be iterated instance by instance + for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ) { test_snapshot (0); db::ICplxTrans t (cell_inst.complex_trans (*p)); db::Box new_vp = db::Box (t.inverted () * *v); draw_boxes (drawing_context, new_ci, trans * t, new_vp, level + 1); + if (p.quad_id () > 0 && p.quad_id () != qid) { + + qid = p.quad_id (); + + // if the quad is very small we don't gain anything from looking further into the quad - skip this one + db::DBox qb = trans * cell_inst.quad_box (p, bc); + if (qb.width () < 1.0 && qb.height () < 1.0) { + p.skip_quad (); + continue; + } + + } + + ++p; + } } @@ -986,7 +1002,6 @@ RedrawThreadWorker::draw_box_properties (bool drawing_context, db::cell_index_ty ++inst; } - } } @@ -1756,7 +1771,9 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_ } else if (anything) { - for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ++p) { + size_t qid = 0; + + for (db::CellInstArray::iterator p = cell_inst.begin_touching (*v, bc); ! p.at_end (); ) { if (! m_draw_array_border_instances || p.index_a () <= 0 || (unsigned long)p.index_a () == amax - 1 || p.index_b () <= 0 || (unsigned long)p.index_b () == bmax - 1) { @@ -1765,6 +1782,21 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_ db::Box new_vp = db::Box (t.inverted () * *v); draw_layer (from_level, to_level, new_ci, trans * t, new_vp, level + 1, fill, frame, vertex, text, update_snapshot); + if (p.quad_id () > 0 && p.quad_id () != qid) { + + qid = p.quad_id (); + + // if the quad is very small we don't gain anything from looking further into the quad - skip this one + db::DBox qb = trans * cell_inst.quad_box (p, bc); + if (qb.width () < 1.0 && qb.height () < 1.0) { + p.skip_quad (); + continue; + } + + } + + ++p; + } } diff --git a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc index 0ba72cd21..55b82c1aa 100644 --- a/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc +++ b/src/plugins/streamers/gds2/db_plugin/dbGDS2WriterBase.cc @@ -354,120 +354,130 @@ GDS2WriterBase::write_inst (double sf, const db::Instance &instance, bool normal bool is_reg = instance.is_regular_array (a, b, amax, bmax); - db::Trans t = instance.front (); + for (db::CellInstArray::iterator ii = instance.begin (); ! ii.at_end (); ++ii) { - if (normalize) { + db::Trans t = *ii; - // try to normalize orthogonal arrays into "Cadence notation", that is - // column and row vectors are positive in the coordinate system of the - // rotated array. - - if (is_reg) { + if (normalize) { - if (amax < 2) { - a = db::Vector (); - } - if (bmax < 2) { - b = db::Vector (); - } + // try to normalize orthogonal arrays into "Cadence notation", that is + // column and row vectors are positive in the coordinate system of the + // rotated array. - // normalisation only works for orthogonal vectors, parallel to x or y axis, which are not parallel - if ((a.x () == 0 || a.y () == 0) && (b.x () == 0 || b.y () == 0) && !((a.x () != 0 && b.x () != 0) || (a.y () != 0 && b.y () != 0))) { - - db::FTrans fp = db::FTrans(t.rot ()).inverted (); - - a.transform (fp); - b.transform (fp); + if (is_reg) { - db::Vector p; - for (int i = 0; i < 2; ++i) { + if (amax < 2) { + a = db::Vector (); + } + if (bmax < 2) { + b = db::Vector (); + } - db::Vector *q = (i == 0) ? &a : &b; - unsigned long n = (i == 0) ? amax : bmax; + // normalisation only works for orthogonal vectors, parallel to x or y axis, which are not parallel + if ((a.x () == 0 || a.y () == 0) && (b.x () == 0 || b.y () == 0) && !((a.x () != 0 && b.x () != 0) || (a.y () != 0 && b.y () != 0))) { + + db::FTrans fp = db::FTrans(t.rot ()).inverted (); + + a.transform (fp); + b.transform (fp); + + db::Vector p; + for (int i = 0; i < 2; ++i) { + + db::Vector *q = (i == 0) ? &a : &b; + unsigned long n = (i == 0) ? amax : bmax; + + if (n == 0) { + *q = db::Vector (); + } else { + if (q->x () < 0) { + p += db::Vector ((n - 1) * q->x (), 0); + q->set_x (-q->x ()); + } + if (q->y () < 0) { + p += db::Vector (0, (n - 1) * q->y ()); + q->set_y (-q->y ()); + } + } - if (n == 0) { - *q = db::Vector (); - } else { - if (q->x () < 0) { - p += db::Vector ((n - 1) * q->x (), 0); - q->set_x (-q->x ()); - } - if (q->y () < 0) { - p += db::Vector (0, (n - 1) * q->y ()); - q->set_y (-q->y ()); - } } + if (a.x () != 0 || b.y () != 0) { + std::swap (a, b); + std::swap (amax, bmax); + } + + fp = db::FTrans (t.rot ()); + a.transform (fp); + b.transform (fp); + + t = t * db::Trans (p); + } - if (a.x () != 0 || b.y () != 0) { - std::swap (a, b); - std::swap (amax, bmax); - } - - fp = db::FTrans (t.rot ()); - a.transform (fp); - b.transform (fp); - - t = t * db::Trans (p); - } } - } + write_record_size (4); + write_record (is_reg ? sAREF : sSREF); - write_record_size (4); - write_record (is_reg ? sAREF : sSREF); + write_string_record (sSNAME, m_cell_name_map.cell_name (instance.cell_index ())); - write_string_record (sSNAME, m_cell_name_map.cell_name (instance.cell_index ())); + if (t.rot () != 0 || instance.is_complex ()) { - if (t.rot () != 0 || instance.is_complex ()) { + write_record_size (6); + write_record (sSTRANS); + write_short (t.is_mirror () ? 0x8000 : 0); - write_record_size (6); - write_record (sSTRANS); - write_short (t.is_mirror () ? 0x8000 : 0); - - if (instance.is_complex ()) { - write_record_size (4 + 8); - write_record (sMAG); - write_double (instance.complex_trans ().mag ()); - write_record_size (4 + 8); - write_record (sANGLE); - write_double (instance.complex_trans ().angle ()); - } else { - if ((t.rot () % 4) != 0) { + if (instance.is_complex ()) { + db::CellInstArray::complex_trans_type ct = instance.complex_trans (t); + write_record_size (4 + 8); + write_record (sMAG); + write_double (ct.mag ()); write_record_size (4 + 8); write_record (sANGLE); - write_double ((t.rot () % 4) * 90.0); + write_double (ct.angle ()); + } else { + if ((t.rot () % 4) != 0) { + write_record_size (4 + 8); + write_record (sANGLE); + write_double ((t.rot () % 4) * 90.0); + } } + + } + + if (is_reg) { + write_record_size (4 + 2 * 2); + write_record (sCOLROW); + if (amax > 32767 || bmax > 32767) { + throw tl::Exception (tl::to_string (tr ("Cannot write array references with more than 32767 columns or rows to GDS2 streams"))); + } + write_short (std::max ((unsigned long) 1, bmax)); + write_short (std::max ((unsigned long) 1, amax)); + } + + write_record_size (4 + (is_reg ? 3 : 1) * 2 * 4); + write_record (sXY); + write_int (scale (sf, t.disp ().x ())); + write_int (scale (sf, t.disp ().y ())); + + if (is_reg) { + write_int (scale (sf, t.disp ().x () + b.x () * bmax)); + write_int (scale (sf, t.disp ().y () + b.y () * bmax)); + write_int (scale (sf, t.disp ().x () + a.x () * amax)); + write_int (scale (sf, t.disp ().y () + a.y () * amax)); + } + + finish (layout, prop_id); + + if (is_reg) { + // we have already written all instances + break; } } - - if (is_reg) { - write_record_size (4 + 2 * 2); - write_record (sCOLROW); - if (amax > 32767 || bmax > 32767) { - throw tl::Exception (tl::to_string (tr ("Cannot write array references with more than 32767 columns or rows to GDS2 streams"))); - } - write_short (std::max ((unsigned long) 1, bmax)); - write_short (std::max ((unsigned long) 1, amax)); - } - - write_record_size (4 + (is_reg ? 3 : 1) * 2 * 4); - write_record (sXY); - write_int (scale (sf, t.disp ().x ())); - write_int (scale (sf, t.disp ().y ())); - - if (is_reg) { - write_int (scale (sf, t.disp ().x () + b.x () * bmax)); - write_int (scale (sf, t.disp ().y () + b.y () * bmax)); - write_int (scale (sf, t.disp ().x () + a.x () * amax)); - write_int (scale (sf, t.disp ().y () + a.y () * amax)); - } - - finish (layout, prop_id); } void diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc index 029dc7f5a..30c87b8c2 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISReader.cc @@ -2018,6 +2018,8 @@ OASISReader::do_read_placement (unsigned char r, db::Vector pos (mm_placement_x.get (), mm_placement_y.get ()); + const std::vector *points = 0; + if ((m & 0x8) && read_repetition ()) { std::pair pp = read_element_properties (layout.properties_repository (), false); @@ -2042,6 +2044,42 @@ OASISReader::do_read_placement (unsigned char r, instances.push_back (inst); } + } else if (! layout.is_editable () && (points = mm_repetition.get ().is_iterated ()) != 0) { + + db::CellInstArray inst; + + if (mag_set || angle < 0) { + + db::ICplxTrans ct (mag, angle_deg, mirror, pos); + + db::CellInstArray::iterated_complex_array_type array (ct.rcos (), ct.mag ()); + array.reserve (points->size () + 1); + array.insert (db::Vector ()); + array.insert (points->begin (), points->end ()); + array.sort (); + + inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), + db::Trans (ct), layout.array_repository ().insert (array)); + + } else { + + db::CellInstArray::iterated_array_type array; + array.reserve (points->size () + 1); + array.insert (db::Vector ()); + array.insert (points->begin (), points->end ()); + array.sort (); + + inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), + db::Trans (angle, mirror, pos), layout.array_repository ().insert (array)); + + } + + if (pp.first) { + instances_with_props.push_back (db::CellInstArrayWithProperties (inst, pp.second)); + } else { + instances.push_back (inst); + } + } else { RepetitionIterator p = mm_repetition.get ().begin (); diff --git a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc index d7d54a313..7791b9e98 100644 --- a/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc +++ b/src/plugins/streamers/oasis/db_plugin/dbOASISWriter.cc @@ -1965,11 +1965,35 @@ OASISWriter::write (const db::CellInstArray &inst, db::properties_id_type prop_i { m_progress.set (mp_stream->pos ()); + std::vector pts; db::Vector a, b; unsigned long amax, bmax; - bool is_reg = inst.is_regular_array (a, b, amax, bmax); - if (is_reg && (amax > 1 || bmax > 1)) { + if (inst.is_iterated_array (&pts) && pts.size () > 1) { + + // Remove the first point which is implicitly contained in the repetition + // Note: we can do so because below we instantiate the shape at the front of the array which includes + // the first transformation already. + db::Vector po = pts.front (); + std::vector::iterator pw = pts.begin(); + for (std::vector::iterator p = pw + 1; p != pts.end (); ++p) { + *pw++ = *p - po; + } + pts.erase (pw, pts.end ()); + + db::IrregularRepetition *rep_base = new db::IrregularRepetition (); + rep_base->points ().swap (pts); + db::Repetition array_rep (rep_base); + + if (rep != db::Repetition ()) { + for (db::RepetitionIterator r = rep.begin (); ! r.at_end (); ++r) { + write_inst_with_rep (inst, prop_id, *r, array_rep); + } + } else { + write_inst_with_rep (inst, prop_id, db::Vector (), array_rep); + } + + } else if (inst.is_regular_array (a, b, amax, bmax) && (amax > 1 || bmax > 1)) { // we cannot use the repetition - instead we write every single instance and use the repetition // for the array information diff --git a/src/plugins/tools/diff/lay_plugin/layDiffToolDialog.cc b/src/plugins/tools/diff/lay_plugin/layDiffToolDialog.cc index 208856ef2..173383945 100644 --- a/src/plugins/tools/diff/lay_plugin/layDiffToolDialog.cc +++ b/src/plugins/tools/diff/lay_plugin/layDiffToolDialog.cc @@ -224,6 +224,10 @@ RdbDifferenceReceiver::produce_cell_inst (const db::CellInstArrayWithProperties unsigned long amax, bmax; if (ci.is_regular_array (a, b, amax, bmax)) { r += tl::sprintf (" [a=%s, b=%s, na=%ld, nb=%ld]", a.to_string (), b.to_string (), amax, bmax); + } else if (ci.size () > 1) { + r += " (+"; + r += tl::to_string (ci.size () - 1); + r += " irregular placements)"; } item->add_value (r); diff --git a/src/tl/tl/tlUnitTest.cc b/src/tl/tl/tlUnitTest.cc index ecebd781f..649d12d04 100644 --- a/src/tl/tl/tlUnitTest.cc +++ b/src/tl/tl/tlUnitTest.cc @@ -232,6 +232,7 @@ bool TestBase::do_test (bool editable, bool slow) { m_editable = editable; m_slow = slow; + m_any_failed = false; // Ensures the test temp directory is present std::string tmpdir = tl::combine_path (tl::absolute_file_path (testtmp ()), m_testdir); From adfd6532133feb15b0ecf286788f9d762b366f7b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 4 Jun 2020 13:01:11 +0200 Subject: [PATCH 4/6] WIP: refactoring - include fuzzy compare in array, don't put into GSI method impl. --- src/db/db/dbArray.h | 146 ++++++++++++++++++++++++++- src/db/db/gsiDeclDbCell.cc | 6 +- testdata/ruby/dbCellInstArrayTest.rb | 16 +-- 3 files changed, 156 insertions(+), 12 deletions(-) diff --git a/src/db/db/dbArray.h b/src/db/db/dbArray.h index 78e7fd402..a5e1222ca 100644 --- a/src/db/db/dbArray.h +++ b/src/db/db/dbArray.h @@ -115,8 +115,12 @@ struct ArrayBase virtual bool equal (const ArrayBase *) const = 0; + virtual bool fuzzy_equal (const ArrayBase *) const = 0; + virtual bool less (const ArrayBase *) const = 0; + virtual bool fuzzy_less (const ArrayBase *) const = 0; + virtual void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self = false, void *parent = 0) const = 0; bool in_repository; @@ -558,6 +562,12 @@ struct regular_array return (m_a == d->m_a && m_b == d->m_b && m_amax == d->m_amax && m_bmax == d->m_bmax); } + virtual bool fuzzy_equal (const ArrayBase *b) const + { + const regular_array *d = static_cast *> (b); + return (m_a.equal (d->m_a) && m_b.equal (d->m_b) && m_amax == d->m_amax && m_bmax == d->m_bmax); + } + virtual bool less (const ArrayBase *b) const { const regular_array *d = static_cast *> (b); @@ -566,6 +576,14 @@ struct regular_array m_amax < d->m_amax || (m_amax == d->m_amax && m_bmax < d->m_bmax))))); } + virtual bool fuzzy_less (const ArrayBase *b) const + { + const regular_array *d = static_cast *> (b); + return m_a.less (d->m_a) || (m_a.equal (d->m_a) && ( + m_b.less (d->m_b) || (m_b.equal (d->m_b) && ( + m_amax < d->m_amax || (m_amax == d->m_amax && m_bmax < d->m_bmax))))); + } + virtual bool is_regular_array (vector_type &a, vector_type &b, unsigned long &amax, unsigned long &bmax) const { a = m_a; @@ -706,6 +724,18 @@ struct regular_complex_array return regular_array::equal (b); } + virtual bool fuzzy_equal (const ArrayBase *b) const + { + const regular_complex_array *d = static_cast *> (b); + if (fabs (m_acos - d->m_acos) > epsilon) { + return false; + } + if (fabs (m_mag - d->m_mag) > epsilon) { + return false; + } + return regular_array::fuzzy_equal (b); + } + virtual bool less (const ArrayBase *b) const { const regular_complex_array *d = static_cast *> (b); @@ -718,6 +748,18 @@ struct regular_complex_array return regular_array::less (b); } + virtual bool fuzzy_less (const ArrayBase *b) const + { + const regular_complex_array *d = static_cast *> (b); + if (fabs (m_acos - d->m_acos) > epsilon) { + return m_acos < d->m_acos; + } + if (fabs (m_mag - d->m_mag) > epsilon) { + return m_mag < d->m_mag; + } + return regular_array::fuzzy_less (b); + } + virtual void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self, void *parent) const { if (!no_self) { @@ -983,6 +1025,20 @@ struct iterated_array return true; } + virtual bool fuzzy_equal (const ArrayBase *b) const + { + const iterated_array *d = static_cast *> (b); + if (m_v.size () != d->m_v.size ()) { + return false; + } + for (const_iterator p1 = m_v.begin (), p2 = d->m_v.begin (); p1 != m_v.end (); ++p1, ++p2) { + if (! p1->equal (*p2)) { + return false; + } + } + return true; + } + virtual bool less (const ArrayBase *b) const { const iterated_array *d = static_cast *> (b); @@ -997,6 +1053,20 @@ struct iterated_array return false; } + virtual bool fuzzy_less (const ArrayBase *b) const + { + const iterated_array *d = static_cast *> (b); + if (m_v.size () != d->m_v.size ()) { + return (m_v.size () < d->m_v.size ()); + } + for (const_iterator p1 = m_v.begin (), p2 = d->m_v.begin (); p1 != m_v.end (); ++p1, ++p2) { + if (! p1->equal (*p2)) { + return (p1->less (*p2)); + } + } + return false; + } + virtual void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self, void *parent) const { if (!no_self) { @@ -1104,6 +1174,18 @@ struct iterated_complex_array return iterated_array::equal (b); } + virtual bool fuzzy_equal (const ArrayBase *b) const + { + const iterated_complex_array *d = static_cast *> (b); + if (fabs (m_acos - d->m_acos) > epsilon) { + return false; + } + if (fabs (m_mag - d->m_mag) > epsilon) { + return false; + } + return iterated_array::fuzzy_equal (b); + } + virtual bool less (const ArrayBase *b) const { const iterated_complex_array *d = static_cast *> (b); @@ -1116,6 +1198,18 @@ struct iterated_complex_array return iterated_array::less (b); } + virtual bool fuzzy_less (const ArrayBase *b) const + { + const iterated_complex_array *d = static_cast *> (b); + if (fabs (m_acos - d->m_acos) > epsilon) { + return m_acos < d->m_acos; + } + if (fabs (m_mag - d->m_mag) > epsilon) { + return m_mag < d->m_mag; + } + return iterated_array::fuzzy_less (b); + } + virtual complex_trans_type complex_trans (const simple_trans_type &s) const { return complex_trans_type (s, m_acos, m_mag); @@ -1206,7 +1300,12 @@ struct single_complex_inst return true; } - virtual bool less (const ArrayBase *b) const + virtual bool fuzzy_equal (const ArrayBase *b) const + { + return equal (b); + } + + virtual bool less (const ArrayBase *b) const { const double epsilon = 1e-10; const single_complex_inst *d = static_cast *> (b); @@ -1219,6 +1318,11 @@ struct single_complex_inst return false; } + virtual bool fuzzy_less (const ArrayBase *b) const + { + return less (b); + } + virtual void mem_stat (MemStatistics *stat, MemStatistics::purpose_t purpose, int cat, bool no_self, void *parent) const { if (!no_self) { @@ -2019,6 +2123,21 @@ struct array } } + /** + * @brief Compare operator for equality (fuzzy version) + */ + bool equal (const array &d) const + { + if (! mp_base) { + return (m_trans.equal (d.m_trans) && m_obj == d.m_obj && ! d.mp_base); + } else { + if (! m_trans.equal (d.m_trans) || ! (m_obj == d.m_obj) || type () != d.type ()) { + return false; + } + return mp_base && mp_base->fuzzy_equal (d.mp_base); + } + } + /** * @brief A sorting order criterion */ @@ -2044,6 +2163,31 @@ struct array } } + /** + * @brief A fuzzy sorting order criterion + */ + bool less (const array &d) const + { + if (! (m_obj == d.m_obj)) { + return (m_obj < d.m_obj); + } + if (! m_trans.equal (d.m_trans)) { + return m_trans.less (d.m_trans); + } + if (type () != d.type ()) { + return (type () < d.type ()); + } + if (mp_base == d.mp_base) { + return false; + } else if (! mp_base) { + return true; + } else if (! d.mp_base) { + return false; + } else { + return mp_base->fuzzy_less (d.mp_base); + } + } + /** * @brief Compare operator * diff --git a/src/db/db/gsiDeclDbCell.cc b/src/db/db/gsiDeclDbCell.cc index d6f2bef9d..a0ff3b0f6 100644 --- a/src/db/db/gsiDeclDbCell.cc +++ b/src/db/db/gsiDeclDbCell.cc @@ -371,17 +371,17 @@ struct cell_inst_array_defs static bool less (const C *i, const C &other) { - return *i < other; + return i->less (other); } static bool equal (const C *i, const C &other) { - return *i == other; + return i->equal (other); } static bool not_equal (const C *i, const C &other) { - return ! equal (i, other); + return ! i->equal (other); } static gsi::Methods methods (bool new_doc) diff --git a/testdata/ruby/dbCellInstArrayTest.rb b/testdata/ruby/dbCellInstArrayTest.rb index 7f2e03f4d..fb61fedc7 100644 --- a/testdata/ruby/dbCellInstArrayTest.rb +++ b/testdata/ruby/dbCellInstArrayTest.rb @@ -81,9 +81,9 @@ class DBCellInst_TestClass < TestBase assert_equal(at.trans.to_s, "r90 0,0") assert_equal(at.cplx_trans.to_s, "r135 *1 0,0") - assert_equal(at < a, true) + assert_equal(at < a, false) assert_equal(at < atdup, false) - assert_equal(a < at, false) + assert_equal(a < at, true) assert_equal(atdup < at, false) assert_equal(a != at, true) assert_equal(a == at, false) @@ -143,9 +143,9 @@ class DBCellInst_TestClass < TestBase assert_equal(at.cplx_trans.to_s, "r135 *1 0,0") assert_equal(at.to_s, "#0 r135 *1 0,0 [-20,10*3;-40,30*5]") - assert_equal(at < a, true) + assert_equal(at < a, false) assert_equal(at < atdup, false) - assert_equal(a < at, false) + assert_equal(a < at, true) assert_equal(atdup < at, false) assert_equal(a != at, true) assert_equal(a == at, false) @@ -210,9 +210,9 @@ class DBCellInst_TestClass < TestBase assert_equal(at.trans.to_s, "r90 0,0") assert_equal(at.cplx_trans.to_s, "r135 *1 0,0") - assert_equal(at < a, true) + assert_equal(at < a, false) assert_equal(at < atdup, false) - assert_equal(a < at, false) + assert_equal(a < at, true) assert_equal(atdup < at, false) assert_equal(a != at, true) assert_equal(a == at, false) @@ -272,9 +272,9 @@ class DBCellInst_TestClass < TestBase assert_equal(at.cplx_trans.to_s, "r135 *1 0,0") assert_equal(at.to_s, "#0 r135 *1 0,0 [-20,10*3;-40,30*5]") - assert_equal(at < a, true) + assert_equal(at < a, false) assert_equal(at < atdup, false) - assert_equal(a < at, false) + assert_equal(a < at, true) assert_equal(atdup < at, false) assert_equal(a != at, true) assert_equal(a == at, false) From 8b0ab115edd494383d787be52613cd6a55210d62 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 4 Jun 2020 15:38:53 +0200 Subject: [PATCH 5/6] Memory footprint optimization for quad tree --- src/db/db/dbBoxTree.h | 64 +++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/src/db/db/dbBoxTree.h b/src/db/db/dbBoxTree.h index f34d49f01..982993496 100644 --- a/src/db/db/dbBoxTree.h +++ b/src/db/db/dbBoxTree.h @@ -187,9 +187,9 @@ public: ~box_tree_node () { for (int i = 0; i < 4; ++i) { - if (mp_children [i]) { - delete mp_children [i]; - mp_children [i] = 0; + box_tree_node *c = child (i); + if (c) { + delete c; } } } @@ -197,12 +197,14 @@ public: box_tree_node *clone (box_tree_node *parent = 0, unsigned int quad = 0) const { box_tree_node *n = new box_tree_node (parent, m_center, m_corner, quad); - for (unsigned int i = 0; i < 5; ++i) { - n->m_lenq[i] = m_lenq[i]; - } + n->m_lenq = m_lenq; + n->m_len = m_len; for (unsigned int i = 0; i < 4; ++i) { - if (mp_children[i]) { - mp_children[i]->clone (n, i); + box_tree_node *c = child (i); + if (c) { + c->clone (n, i); + } else { + n->m_childrefs [i] = m_childrefs [i]; } } return n; @@ -210,17 +212,39 @@ public: box_tree_node *child (int i) const { - return mp_children [i]; + if ((m_childrefs [i] & 1) == 0) { + return reinterpret_cast (m_childrefs [i]); + } else { + return 0; + } } void lenq (int i, size_t l) { - m_lenq[i + 1] = l; + if (i < 0) { + m_lenq = l; + } else { + box_tree_node *c = child (i); + if (c) { + c->m_len = l; + } else { + m_childrefs [i] = l * 2 + 1; + } + } } size_t lenq (int i) const { - return m_lenq[i + 1]; + if (i < 0) { + return m_lenq; + } else { + box_tree_node *c = child (i); + if (c) { + return c->m_len; + } else { + return m_childrefs [i] >> 1; + } + } } box_tree_node *parent () const @@ -239,8 +263,8 @@ public: stat->add (typeid (*this), (void *) this, sizeof (*this), sizeof (*this), parent, purpose, cat); } for (int i = 0; i < 4; ++i) { - if (mp_children [i]) { - mp_children [i]->mem_stat (stat, purpose, cat, no_self, parent); + if (child (i)) { + child (i)->mem_stat (stat, purpose, cat, no_self, parent); } } } @@ -268,8 +292,8 @@ public: private: box_tree_node *mp_parent; - size_t m_lenq [5]; - box_tree_node *mp_children [4]; + size_t m_lenq, m_len; + size_t m_childrefs [4]; point_type m_center, m_corner; box_tree_node (const box_tree_node &d); @@ -285,15 +309,15 @@ private: m_center = center; m_corner = corner; - for (int i = 0; i < 5; ++i) { - m_lenq[i] = 0; - } + m_lenq = m_len = 0; for (int i = 0; i < 4; ++i) { - mp_children[i] = 0; + m_childrefs [i] = 0; } + mp_parent = (box_tree_node *)((char *) parent + quad); if (parent) { - parent->mp_children [quad] = this; + m_len = (parent->m_childrefs [quad] >> 1); + parent->m_childrefs [quad] = size_t (this); } } }; From 0a10635363ce835a9e83b3722d2b703e7e57c8a1 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 13 Jun 2020 19:55:33 +0200 Subject: [PATCH 6/6] Fixed memory statistics for array (wasn't taking the right statistics for irregular arrays) --- src/db/db/dbArray.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/db/dbArray.h b/src/db/db/dbArray.h index a5e1222ca..1a2de4246 100644 --- a/src/db/db/dbArray.h +++ b/src/db/db/dbArray.h @@ -2508,7 +2508,7 @@ struct array } db::mem_stat (stat, purpose, cat, m_obj, true, (void *) this); if (mp_base) { - db::mem_stat (stat, purpose, cat, *mp_base, false, (void *) this); + mp_base->mem_stat (stat, purpose, cat, false, (void *) this); } }