From e2fa88fcfb709f41013420efe9ef49efbe7b4de6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 10 Sep 2023 16:49:06 +0200 Subject: [PATCH 01/13] Progress for CIF reader, avoid duplicate sorting --- src/plugins/streamers/cif/db_plugin/dbCIFReader.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/streamers/cif/db_plugin/dbCIFReader.cc b/src/plugins/streamers/cif/db_plugin/dbCIFReader.cc index 2a9af45890..f24e04cdcb 100644 --- a/src/plugins/streamers/cif/db_plugin/dbCIFReader.cc +++ b/src/plugins/streamers/cif/db_plugin/dbCIFReader.cc @@ -164,6 +164,7 @@ CIFReader::get_char () error ("Unexpected end of file"); return 0; } else { + m_progress.set (m_stream.line_number ()); return m_stream.get_char (); } } @@ -823,6 +824,8 @@ void CIFReader::do_read (db::Layout &layout) { try { + + db::LayoutLocker locker (&layout); double sf = 0.01 / m_dbu; layout.dbu (m_dbu); From b7ee16b63f41030f4a000f2decde6a4b21922ef8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 10 Sep 2023 19:47:43 +0200 Subject: [PATCH 02/13] Performance enhancement for instance sorting - done only on demand --- src/db/db/dbCell.cc | 21 +++++++++++++++++---- src/db/db/dbCell.h | 8 +++++--- src/db/db/dbLayout.cc | 5 +++-- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/db/db/dbCell.cc b/src/db/db/dbCell.cc index 83acc11826..bda9d71f88 100644 --- a/src/db/db/dbCell.cc +++ b/src/db/db/dbCell.cc @@ -92,7 +92,8 @@ Cell::box_type Cell::ms_empty_box = Cell::box_type (); Cell::Cell (cell_index_type ci, db::Layout &l) : db::Object (l.manager ()), - m_cell_index (ci), mp_layout (&l), m_instances (this), m_prop_id (0), m_hier_levels (0), m_bbox_needs_update (false), m_ghost_cell (false), + m_cell_index (ci), mp_layout (&l), m_instances (this), m_prop_id (0), m_hier_levels (0), + m_instances_need_sort (false), m_instances_need_sort_box_tree (false), m_bbox_needs_update (false), m_ghost_cell (false), mp_last (0), mp_next (0) { // .. nothing yet @@ -131,6 +132,10 @@ Cell::operator= (const Cell &d) m_prop_id = d.m_prop_id; m_bbox_needs_update = d.m_bbox_needs_update; + // need to update the instances inst_by_cell_index pointers + m_instances_need_sort = true; + m_instances_need_sort_box_tree = true; + } return *this; } @@ -613,6 +618,8 @@ Cell::invalidate_insts () { mp_layout->invalidate_hier (); // HINT: must come before the change is done! mp_layout->invalidate_bboxes (std::numeric_limits::max ()); + m_instances_need_sort = true; + m_instances_need_sort_box_tree = true; m_bbox_needs_update = true; } @@ -700,7 +707,10 @@ Cell::clear_parent_insts (size_t sz) void Cell::sort_child_insts () { - m_instances.sort_child_insts (); + if (m_instances_need_sort) { + m_instances.sort_child_insts (); + } + m_instances_need_sort = false; } std::pair @@ -744,9 +754,12 @@ Cell::change_pcell_parameters (const instance_type &ref, const std::vector layers) { layers = cp.layers (); From 22bab6d6a652d548350ddafd9eaea6b01c16f6a8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 10 Sep 2023 22:05:08 +0200 Subject: [PATCH 03/13] Some refactoring --- src/db/db/dbCell.cc | 18 +----- src/db/db/dbCell.h | 4 +- src/db/db/dbInstances.cc | 125 ++++++++++++++++++++++++--------------- src/db/db/dbInstances.h | 59 ++++++++++++++---- src/db/db/dbLayout.cc | 21 +++---- 5 files changed, 136 insertions(+), 91 deletions(-) diff --git a/src/db/db/dbCell.cc b/src/db/db/dbCell.cc index bda9d71f88..e8d2a1c9f7 100644 --- a/src/db/db/dbCell.cc +++ b/src/db/db/dbCell.cc @@ -93,7 +93,7 @@ Cell::box_type Cell::ms_empty_box = Cell::box_type (); Cell::Cell (cell_index_type ci, db::Layout &l) : db::Object (l.manager ()), m_cell_index (ci), mp_layout (&l), m_instances (this), m_prop_id (0), m_hier_levels (0), - m_instances_need_sort (false), m_instances_need_sort_box_tree (false), m_bbox_needs_update (false), m_ghost_cell (false), + m_bbox_needs_update (false), m_ghost_cell (false), mp_last (0), mp_next (0) { // .. nothing yet @@ -132,10 +132,6 @@ Cell::operator= (const Cell &d) m_prop_id = d.m_prop_id; m_bbox_needs_update = d.m_bbox_needs_update; - // need to update the instances inst_by_cell_index pointers - m_instances_need_sort = true; - m_instances_need_sort_box_tree = true; - } return *this; } @@ -618,8 +614,6 @@ Cell::invalidate_insts () { mp_layout->invalidate_hier (); // HINT: must come before the change is done! mp_layout->invalidate_bboxes (std::numeric_limits::max ()); - m_instances_need_sort = true; - m_instances_need_sort_box_tree = true; m_bbox_needs_update = true; } @@ -707,10 +701,7 @@ Cell::clear_parent_insts (size_t sz) void Cell::sort_child_insts () { - if (m_instances_need_sort) { - m_instances.sort_child_insts (); - } - m_instances_need_sort = false; + m_instances.sort_child_insts (); } std::pair @@ -756,10 +747,7 @@ Cell::change_pcell_parameters (const instance_type &ref, const std::vectorlayout () ? mp_cell->layout ()->is_editable () : true; + return cell () && cell ()->layout () ? cell ()->layout ()->is_editable () : true; } db::Layout * Instances::layout () const { - return mp_cell ? mp_cell->layout () : 0; + return cell () ? cell ()->layout () : 0; +} + +void +Instances::invalidate_insts () +{ + if (cell ()) { + cell ()->invalidate_insts (); + } + + set_instance_by_cell_index_needs_made (true); + set_instance_tree_needs_sort (true); } template @@ -962,14 +976,15 @@ Instances::erase_positions (Tag tag, ET editable_tag, I first, I last) { typedef instances_editable_traits editable_traits; - if (mp_cell) { - mp_cell->invalidate_insts (); // HINT: must come before the change is done! - if (mp_cell->manager () && mp_cell->manager ()->transacting ()) { + invalidate_insts (); // HINT: must come before the change is done! + + if (cell ()) { + if (cell ()->manager () && cell ()->manager ()->transacting ()) { check_is_editable_for_undo_redo (this); if (! is_editable ()) { throw tl::Exception (tl::to_string (tr ("No undo/redo support for non-editable instance lists in 'erase_positions'"))); } - mp_cell->manager ()->queue (mp_cell, new db::InstOp (false /*not insert*/, first, last, true /*dummy*/)); + cell ()->manager ()->queue (cell (), new db::InstOp (false /*not insert*/, first, last, true /*dummy*/)); } } @@ -983,18 +998,19 @@ Instances::insert (const InstArray &inst) { bool editable = is_editable (); - if (mp_cell) { - if (mp_cell->manager () && mp_cell->manager ()->transacting ()) { + if (cell ()) { + if (cell ()->manager () && cell ()->manager ()->transacting ()) { check_is_editable_for_undo_redo (this); if (editable) { - mp_cell->manager ()->queue (mp_cell, new db::InstOp (true /*insert*/, inst)); + cell ()->manager ()->queue (cell (), new db::InstOp (true /*insert*/, inst)); } else { - mp_cell->manager ()->queue (mp_cell, new db::InstOp (true /*insert*/, inst)); + cell ()->manager ()->queue (cell (), new db::InstOp (true /*insert*/, inst)); } } - mp_cell->invalidate_insts (); } + invalidate_insts (); + // TODO: simplify this, i.e. through instance_from_pointer if (editable) { return instance_type (this, inst_tree (typename InstArray::tag (), InstancesEditableTag ()).insert (inst)); @@ -1010,14 +1026,14 @@ Instances::insert (I from, I to) typedef std::iterator_traits it_traits; typedef typename it_traits::value_type value_type; - if (mp_cell) { - if (mp_cell->manager () && mp_cell->manager ()->transacting ()) { + if (cell ()) { + if (cell ()->manager () && cell ()->manager ()->transacting ()) { check_is_editable_for_undo_redo (this); - mp_cell->manager ()->queue (mp_cell, new db::InstOp (true /*insert*/, from, to)); + cell ()->manager ()->queue (cell (), new db::InstOp (true /*insert*/, from, to)); } - mp_cell->invalidate_insts (); } + invalidate_insts (); inst_tree (typename value_type::tag (), ET ()).insert (from, to); } @@ -1061,20 +1077,21 @@ template void Instances::replace (const InstArray *replace, const InstArray &with) { - if (mp_cell) { - if (mp_cell->manager () && mp_cell->manager ()->transacting ()) { + if (cell ()) { + if (cell ()->manager () && cell ()->manager ()->transacting ()) { check_is_editable_for_undo_redo (this); if (is_editable ()) { - mp_cell->manager ()->queue (mp_cell, new db::InstOp (false /*not insert*/, *replace)); - mp_cell->manager ()->queue (mp_cell, new db::InstOp (true /*insert*/, with)); + cell ()->manager ()->queue (cell (), new db::InstOp (false /*not insert*/, *replace)); + cell ()->manager ()->queue (cell (), new db::InstOp (true /*insert*/, with)); } else { - mp_cell->manager ()->queue (mp_cell, new db::InstOp (false /*not insert*/, *replace)); - mp_cell->manager ()->queue (mp_cell, new db::InstOp (true /*insert*/, with)); + cell ()->manager ()->queue (cell (), new db::InstOp (false /*not insert*/, *replace)); + cell ()->manager ()->queue (cell (), new db::InstOp (true /*insert*/, with)); } } - mp_cell->invalidate_insts (); } + invalidate_insts (); + // HINT: this only works because we know our box trees well: *((InstArray *)replace) = with; } @@ -1150,11 +1167,12 @@ Instances::erase_inst_by_iter (Tag tag, ET editable_tag, I iter) throw tl::Exception (tl::to_string (tr ("Trying to erase an object from a list that it does not belong to"))); } - if (mp_cell) { - mp_cell->invalidate_insts (); - if (mp_cell->manager () && mp_cell->manager ()->transacting ()) { + invalidate_insts (); + + if (cell ()) { + if (cell ()->manager () && cell ()->manager ()->transacting ()) { check_is_editable_for_undo_redo (this); - mp_cell->manager ()->queue (mp_cell, new db::InstOp (false /*not insert*/, *iter)); + cell ()->manager ()->queue (cell (), new db::InstOp (false /*not insert*/, *iter)); } } @@ -1165,11 +1183,12 @@ template void Instances::erase_inst_by_tag (Tag tag, ET editable_tag, const typename Tag::object_type &obj) { - if (mp_cell) { - mp_cell->invalidate_insts (); - if (mp_cell->manager () && mp_cell->manager ()->transacting ()) { + invalidate_insts (); + + if (cell ()) { + if (cell ()->manager () && cell ()->manager ()->transacting ()) { check_is_editable_for_undo_redo (this); - mp_cell->manager ()->queue (mp_cell, new db::InstOp (false /*not insert*/, obj)); + cell ()->manager ()->queue (cell (), new db::InstOp (false /*not insert*/, obj)); } } @@ -1197,16 +1216,17 @@ template void Instances::clear_insts (ET editable_tag) { - if (mp_cell) { - mp_cell->invalidate_insts (); - if (mp_cell->manager () && mp_cell->manager ()->transacting ()) { + invalidate_insts (); + + if (cell ()) { + if (cell ()->manager () && cell ()->manager ()->transacting ()) { check_is_editable_for_undo_redo (this); const Instances *const_this = this; if (! const_this->inst_tree (cell_inst_array_type::tag (), editable_tag).empty ()) { - mp_cell->manager ()->queue (mp_cell, new db::InstOp (false /*not insert*/, const_this->inst_tree (cell_inst_array_type::tag (), editable_tag).begin (), const_this->inst_tree (cell_inst_array_type::tag (), editable_tag).end ())); + cell ()->manager ()->queue (cell (), new db::InstOp (false /*not insert*/, const_this->inst_tree (cell_inst_array_type::tag (), editable_tag).begin (), const_this->inst_tree (cell_inst_array_type::tag (), editable_tag).end ())); } if (! const_this->inst_tree (cell_inst_wp_array_type::tag (), editable_tag).empty ()) { - mp_cell->manager ()->queue (mp_cell, new db::InstOp (false /*not insert*/, const_this->inst_tree (cell_inst_wp_array_type::tag (), editable_tag).begin (), const_this->inst_tree (cell_inst_wp_array_type::tag (), editable_tag).end ())); + cell ()->manager ()->queue (cell (), new db::InstOp (false /*not insert*/, const_this->inst_tree (cell_inst_wp_array_type::tag (), editable_tag).begin (), const_this->inst_tree (cell_inst_wp_array_type::tag (), editable_tag).end ())); } } } @@ -1227,9 +1247,7 @@ Instances::clear_insts () void Instances::clear (Instances::cell_inst_array_type::tag) { - if (mp_cell) { - mp_cell->invalidate_insts (); - } + invalidate_insts (); if (m_generic.any) { if (is_editable ()) { @@ -1244,9 +1262,7 @@ Instances::clear (Instances::cell_inst_array_type::tag) void Instances::clear (Instances::cell_inst_wp_array_type::tag) { - if (mp_cell) { - mp_cell->invalidate_insts (); - } + invalidate_insts (); if (m_generic_wp.any) { if (is_editable ()) { @@ -1373,6 +1389,11 @@ struct cell_inst_compare_f void Instances::sort_child_insts () { + if (! instance_by_cell_index_needs_made ()) { + return; + } + set_instance_by_cell_index_needs_made (false); + m_insts_by_cell_index = sorted_inst_vector (); m_insts_by_cell_index.reserve (cell_instances ()); @@ -1406,8 +1427,13 @@ Instances::sort_child_insts () } void -Instances::sort_inst_tree (const Layout *g) +Instances::sort_inst_tree (const Layout *g, bool force) { + if (! force && ! instance_tree_needs_sort ()) { + return; + } + set_instance_tree_needs_sort (false); + if (m_generic.any) { if (is_editable ()) { m_generic.stable_tree->sort (cell_inst_array_box_converter (*g)); @@ -1603,16 +1629,17 @@ void Instances::apply_op (const Op &op, ET editable_tag) bool has_insts = ! const_this->inst_tree (cell_inst_array_type::tag (), editable_tag).empty (); bool has_wp_insts = ! const_this->inst_tree (cell_inst_wp_array_type::tag (), editable_tag).empty (); - if (mp_cell) { - mp_cell->invalidate_insts (); - if (mp_cell->manager () && mp_cell->manager ()->transacting ()) { + invalidate_insts (); + + if (cell ()) { + if (cell ()->manager () && cell ()->manager ()->transacting ()) { check_is_editable_for_undo_redo (this); transacting = true; if (has_insts) { - mp_cell->manager ()->queue (mp_cell, new db::InstOp (false /*not insert*/, const_this->inst_tree (cell_inst_array_type::tag (), editable_tag).begin (), const_this->inst_tree (cell_inst_array_type::tag (), editable_tag).end ())); + cell ()->manager ()->queue (cell (), new db::InstOp (false /*not insert*/, const_this->inst_tree (cell_inst_array_type::tag (), editable_tag).begin (), const_this->inst_tree (cell_inst_array_type::tag (), editable_tag).end ())); } if (has_wp_insts) { - mp_cell->manager ()->queue (mp_cell, new db::InstOp (false /*not insert*/, const_this->inst_tree (cell_inst_wp_array_type::tag (), editable_tag).begin (), const_this->inst_tree (cell_inst_wp_array_type::tag (), editable_tag).end ())); + cell ()->manager ()->queue (cell (), new db::InstOp (false /*not insert*/, const_this->inst_tree (cell_inst_wp_array_type::tag (), editable_tag).begin (), const_this->inst_tree (cell_inst_wp_array_type::tag (), editable_tag).end ())); } } } @@ -1637,10 +1664,10 @@ void Instances::apply_op (const Op &op, ET editable_tag) if (transacting) { if (has_insts) { - mp_cell->manager ()->queue (mp_cell, new db::InstOp (true /*insert*/, const_this->inst_tree (cell_inst_array_type::tag (), editable_tag).begin (), const_this->inst_tree (cell_inst_array_type::tag (), editable_tag).end ())); + cell ()->manager ()->queue (cell (), new db::InstOp (true /*insert*/, const_this->inst_tree (cell_inst_array_type::tag (), editable_tag).begin (), const_this->inst_tree (cell_inst_array_type::tag (), editable_tag).end ())); } if (has_wp_insts) { - mp_cell->manager ()->queue (mp_cell, new db::InstOp (true /*insert*/, const_this->inst_tree (cell_inst_wp_array_type::tag (), editable_tag).begin (), const_this->inst_tree (cell_inst_wp_array_type::tag (), editable_tag).end ())); + cell ()->manager ()->queue (cell (), new db::InstOp (true /*insert*/, const_this->inst_tree (cell_inst_wp_array_type::tag (), editable_tag).begin (), const_this->inst_tree (cell_inst_wp_array_type::tag (), editable_tag).end ())); } } } diff --git a/src/db/db/dbInstances.h b/src/db/db/dbInstances.h index 9a75759418..e57b64eace 100644 --- a/src/db/db/dbInstances.h +++ b/src/db/db/dbInstances.h @@ -1553,10 +1553,11 @@ class DB_PUBLIC Instances /** * @brief Sort the cell instance list * - * This will sort the cell instance list. As a prerequesite + * This will sort the cell instance list (quad tree sort). As a prerequesite * the cell's bounding boxes must have been computed. + * If force is true, the instance tree is always sorted. */ - void sort_inst_tree (const layout_type *g); + void sort_inst_tree (const layout_type *g, bool force); /** * @brief Update the child-parent relationships @@ -1701,14 +1702,6 @@ class DB_PUBLIC Instances return m_parent_insts.end () == m_parent_insts.begin (); } - /** - * @brief Gets the cell pointer the instance container is in - */ - db::Cell *cell () const - { - return mp_cell; - } - /** * @brief Gets the layout the instances collection lives in */ @@ -1724,6 +1717,14 @@ class DB_PUBLIC Instances */ bool is_editable () const; + /** + * @brief Gets the cell pointer + */ + cell_type *cell () const + { + return reinterpret_cast (size_t (mp_cell) & ~size_t (3)); + } + /** * @brief Delegate for the undo method */ @@ -1735,6 +1736,7 @@ class DB_PUBLIC Instances void redo (db::Op *op); private: + friend class Instance; friend struct NormalInstanceIteratorTraits; friend struct TouchingInstanceIteratorTraits; friend struct OverlappingInstanceIteratorTraits; @@ -1761,6 +1763,43 @@ class DB_PUBLIC Instances static stable_cell_inst_wp_tree_type ms_empty_stable_wp_tree; static stable_cell_inst_tree_type ms_empty_stable_tree; + /** + * @brief Sets a flag indicating that the instance tree needs sorting + */ + void set_instance_tree_needs_sort (bool f) + { + mp_cell = reinterpret_cast ((size_t (mp_cell) & ~size_t (1)) | size_t (f ? 1 : 0)); + } + + /** + * @brief Sets a flag indicating that the instance tree needs sorting + */ + bool instance_tree_needs_sort () const + { + return (size_t (mp_cell) & 1) != 0; + } + + /** + * @brief Sets a flag indicating that the instance by cell index cache needs made + */ + void set_instance_by_cell_index_needs_made (bool f) + { + mp_cell = reinterpret_cast ((size_t (mp_cell) & ~size_t (2)) | size_t (f ? 2 : 0)); + } + + /** + * @brief Sets a flag indicating that the instance tree needs sorting + */ + bool instance_by_cell_index_needs_made () const + { + return (size_t (mp_cell) & 2) != 0; + } + + /** + * @brief Invalidates the instance information - called whenever something changes + */ + void invalidate_insts (); + /** * @brief Get the non-editable instance tree by instance type */ diff --git a/src/db/db/dbLayout.cc b/src/db/db/dbLayout.cc index dc550432c5..a02ffa9498 100644 --- a/src/db/db/dbLayout.cc +++ b/src/db/db/dbLayout.cc @@ -1097,15 +1097,17 @@ Layout::do_prune_cell_or_subcell (cell_index_type id, int levels, bool subcells) // collect the called cells std::set called; cref.collect_called_cells (called, levels); - called.insert (id); + if (! subcells) { + called.insert (id); + } // From these cells erase all cells that have parents outside the subtree of our cell. // Make sure this is done recursively by doing this top-down. for (top_down_iterator c = begin_top_down (); c != end_top_down (); ++c) { - if (called.find (*c) != called.end () && *c != id) { + if (*c != id && called.find (*c) != called.end ()) { db::Cell &ccref = cell (*c); for (db::Cell::parent_cell_iterator pc = ccref.begin_parent_cells (); pc != ccref.end_parent_cells (); ++pc) { - if (called.find (*pc) == called.end ()) { + if (*pc != id && called.find (*pc) == called.end ()) { // we have a parent outside the subset considered currently (either the cell was never in or // it was removed itself already): remove this cell from the set of valid subcells. called.erase (*c); @@ -1115,17 +1117,8 @@ Layout::do_prune_cell_or_subcell (cell_index_type id, int levels, bool subcells) } } - // order the called cells bottom-up - std::vector cells_to_delete; - cells_to_delete.reserve (called.size ()); - for (bottom_up_iterator c = begin_bottom_up (); c != end_bottom_up (); ++c) { - if (called.find (*c) != called.end () && (!subcells || *c != id)) { - cells_to_delete.push_back (*c); - } - } - - // and delete these cells - delete_cells (cells_to_delete.begin (), cells_to_delete.end ()); + // and delete the cells + delete_cells (called); // erase all instances in the subcells case (because, by definition we don't have any more instances) if (subcells) { From 64bcc6bb654f0d35c24ae1c1a15c1a916a75c576 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 10 Sep 2023 22:51:22 +0200 Subject: [PATCH 04/13] Bugfixed last commit --- src/db/db/dbCell.cc | 2 +- src/db/db/dbInstances.cc | 8 ++++---- src/db/db/dbInstances.h | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/db/db/dbCell.cc b/src/db/db/dbCell.cc index e8d2a1c9f7..5c9780a189 100644 --- a/src/db/db/dbCell.cc +++ b/src/db/db/dbCell.cc @@ -701,7 +701,7 @@ Cell::clear_parent_insts (size_t sz) void Cell::sort_child_insts () { - m_instances.sort_child_insts (); + m_instances.sort_child_insts (false); } std::pair diff --git a/src/db/db/dbInstances.cc b/src/db/db/dbInstances.cc index c0a0f4ca31..1ed3aa44ff 100644 --- a/src/db/db/dbInstances.cc +++ b/src/db/db/dbInstances.cc @@ -1387,9 +1387,9 @@ struct cell_inst_compare_f }; void -Instances::sort_child_insts () +Instances::sort_child_insts (bool force) { - if (! instance_by_cell_index_needs_made ()) { + if (! force && ! instance_by_cell_index_needs_made ()) { return; } set_instance_by_cell_index_needs_made (false); @@ -1440,7 +1440,7 @@ Instances::sort_inst_tree (const Layout *g, bool force) } else { m_generic.unstable_tree->sort (cell_inst_array_box_converter (*g)); // since we use unstable instance trees in non-editable mode, we need to resort the child instances in this case - sort_child_insts (); + sort_child_insts (true); } } if (m_generic_wp.any) { @@ -1449,7 +1449,7 @@ Instances::sort_inst_tree (const Layout *g, bool force) } else { m_generic_wp.unstable_tree->sort (cell_inst_wp_array_box_converter (*g)); // since we use unstable instance trees in non-editable mode, we need to resort the child instances in this case - sort_child_insts (); + sort_child_insts (true); } } diff --git a/src/db/db/dbInstances.h b/src/db/db/dbInstances.h index e57b64eace..60c2a2a696 100644 --- a/src/db/db/dbInstances.h +++ b/src/db/db/dbInstances.h @@ -1547,8 +1547,9 @@ class DB_PUBLIC Instances /** * @brief Establish the instance index list giving the instances by cell index + * If force is true, the instance tree is always sorted. */ - void sort_child_insts (); + void sort_child_insts (bool force); /** * @brief Sort the cell instance list From 109696a1e05e399757c2e85e7db9515471540cbf Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 10 Sep 2023 23:36:54 +0200 Subject: [PATCH 05/13] Preventing issues with debugger and PCell parameter dialog - callbacks were issued while a breakpoint was triggered --- src/edt/edt/edtPCellParametersPage.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/edt/edt/edtPCellParametersPage.cc b/src/edt/edt/edtPCellParametersPage.cc index 173b72b93e..6605323bcb 100644 --- a/src/edt/edt/edtPCellParametersPage.cc +++ b/src/edt/edt/edtPCellParametersPage.cc @@ -29,6 +29,7 @@ #include "layQtTools.h" #include "layLayoutViewBase.h" #include "layDispatcher.h" +#include "layBusy.h" #include "tlScriptError.h" #include @@ -604,6 +605,10 @@ PCellParametersPage::parameter_changed () if (! mp_view->cellview (m_cv_index).is_valid ()) { return; } + if (lay::BusySection::is_busy ()) { + // ignore events for example during debugger execution + return; + } const std::vector &pcp = mp_pcell_decl->parameter_declarations (); const db::PCellParameterDeclaration *pd = 0; @@ -623,6 +628,7 @@ PCellParametersPage::parameter_changed () // This is just about providing the inputs for the callback. get_parameters_internal (states, edit_error); + // Note: checking for is_busy prevents callbacks during debugger execution if (! edit_error) { mp_pcell_decl->callback (mp_view->cellview (m_cv_index)->layout (), pd ? pd->get_name () : std::string (), states); m_states = states; From 9bb8b4e5483bf434a22d00d0ce4ed7aa178ba6b6 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 10 Sep 2023 23:41:01 +0200 Subject: [PATCH 06/13] Bugfix: PCellDeclarationHelper was not present after 'import * from pya', more modern version of PCell samples --- .../built-in-pymacros/pcell_declaration_helper.lym | 1 + src/lay/lay/macro_templates/pcell_sample.lym | 12 ++++-------- src/lay/lay/macro_templates/pcell_sample_python.lym | 11 +++-------- testdata/python/dbPCells.py | 5 +++++ 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/db/db/built-in-pymacros/pcell_declaration_helper.lym b/src/db/db/built-in-pymacros/pcell_declaration_helper.lym index 81ea44c449..24c889f31d 100644 --- a/src/db/db/built-in-pymacros/pcell_declaration_helper.lym +++ b/src/db/db/built-in-pymacros/pcell_declaration_helper.lym @@ -563,6 +563,7 @@ for k in dir(pya.PCellParameterDeclaration): # Inject the PCellDeclarationHelper into pya module for consistency: setattr(pya, "PCellDeclarationHelper", _PCellDeclarationHelper) +pya.__all__.append("PCellDeclarationHelper") diff --git a/src/lay/lay/macro_templates/pcell_sample.lym b/src/lay/lay/macro_templates/pcell_sample.lym index 5bd0725661..358945d32e 100644 --- a/src/lay/lay/macro_templates/pcell_sample.lym +++ b/src/lay/lay/macro_templates/pcell_sample.lym @@ -38,7 +38,7 @@ module MyLib super # declare the parameters - param(:l, TypeLayer, "Layer", :default => LayerInfo::new(1, 0)) + param(:l, TypeLayer, "Layer") param(:s, TypeShape, "", :default => DPoint::new(0, 0)) param(:r, TypeDouble, "Radius", :default => 0.1) param(:n, TypeInt, "Number of points", :default => 64) @@ -100,18 +100,14 @@ module MyLib # This is the main part of the implementation: create the layout - # fetch the parameters - ru_dbu = ru / layout.dbu - # compute the circle - pts = [] da = Math::PI * 2 / n - n.times do |i| - pts.push(Point.from_dpoint(DPoint.new(ru_dbu * Math::cos(i * da), ru_dbu * Math::sin(i * da)))) + pts = n.times.collect do |i| + DPoint.new(ru * Math::cos(i * da), ru * Math::sin(i * da)) end # create the shape - cell.shapes(l_layer).insert(Polygon.new(pts)) + cell.shapes(l_layer).insert(DPolygon.new(pts)) end diff --git a/src/lay/lay/macro_templates/pcell_sample_python.lym b/src/lay/lay/macro_templates/pcell_sample_python.lym index 42f9e524de..f2a60bdbca 100644 --- a/src/lay/lay/macro_templates/pcell_sample_python.lym +++ b/src/lay/lay/macro_templates/pcell_sample_python.lym @@ -32,7 +32,7 @@ class Circle(pya.PCellDeclarationHelper): super(Circle, self).__init__() # declare the parameters - self.param("l", self.TypeLayer, "Layer", default = pya.LayerInfo(1, 0)) + self.param("l", self.TypeLayer, "Layer") self.param("s", self.TypeShape, "", default = pya.DPoint(0, 0)) self.param("r", self.TypeDouble, "Radius", default = 0.1) self.param("n", self.TypeInt, "Number of points", default = 64) @@ -88,17 +88,12 @@ class Circle(pya.PCellDeclarationHelper): # This is the main part of the implementation: create the layout - # fetch the parameters - ru_dbu = self.ru / self.layout.dbu - # compute the circle - pts = [] da = math.pi * 2 / self.n - for i in range(0, self.n): - pts.append(pya.Point.from_dpoint(pya.DPoint(ru_dbu * math.cos(i * da), ru_dbu * math.sin(i * da)))) + pts = [ pya.DPoint(self.ru * math.cos(i * da), self.ru * math.sin(i * da)) for i in range(0, self.n) ] # create the shape - self.cell.shapes(self.l_layer).insert(pya.Polygon(pts)) + self.cell.shapes(self.l_layer).insert(pya.DPolygon(pts)) class MyLib(pya.Library): diff --git a/testdata/python/dbPCells.py b/testdata/python/dbPCells.py index 88f3f6f366..14ca10e5e1 100644 --- a/testdata/python/dbPCells.py +++ b/testdata/python/dbPCells.py @@ -163,6 +163,11 @@ def nh(h): class DBPCellTests(unittest.TestCase): + def test_0(self): + + # PCellDeclarationHelper is inside "pya.__all__" + self.assertEqual("PCellDeclarationHelper" in pya.__all__, True) + def test_1(self): # instantiate and register the library From d52087fab4847d2a22ca8faa221a89f58f3ddceb Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 9 Sep 2023 21:33:56 +0200 Subject: [PATCH 07/13] Also fix issue-1471 (lef: make warning about 'FOREIGN differs from MACRO name' more informative) --- src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.cc index 92a8046ff3..277bd9861d 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.cc @@ -968,7 +968,7 @@ LEFImporter::read_macro (Layout &layout) foreign_name = cn; if (foreign_name != mn) { - warn (tl::to_string (tr ("FOREIGN name differs from MACRO name in macro: ")) + mn); + warn (tl::to_string (tl::sprintf (tl::to_string (tr ("FOREIGN name %s differs from MACRO %s")), foreign_name, mn))); } } From 5c0f810006806e0ddb5ae3786934dda792cd0184 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 9 Sep 2023 21:47:25 +0200 Subject: [PATCH 08/13] Also fixed issue #1470 (def pinname VDD.extra1 should be written as VDD TEXT in gds/oasis) --- .../lefdef/db_plugin/dbDEFImporter.cc | 18 +++++++++++++++++- .../lefdef/unit_tests/dbLEFDEFImportTests.cc | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc index c36f6a1148..63515348c6 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc @@ -1150,6 +1150,22 @@ DEFImporter::read_vias (db::Layout &layout, db::Cell & /*design*/, double scale) } } +// issue #1470 +static std::string fix_pin_name (const std::string &pin_name) +{ + auto pos = pin_name.find (".extra"); + if (pos == std::string::npos) { + return pin_name; + } else { + // TODO: do we need to be more specific? + // Formally, the allowed specs are: + // pinname.extraN + // pinname.extraN[n] + // pinname.extraN[n][m]... + return std::string (pin_name.begin (), pin_name.begin () + pos); + } +} + void DEFImporter::read_pins (db::Layout &layout, db::Cell &design, double scale) { @@ -1282,7 +1298,7 @@ DEFImporter::read_pins (db::Layout &layout, db::Cell &design, double scale) if (flush || ! peek ("+")) { // TODO: put a label on every single object? - std::string label = pin_name; + std::string label = fix_pin_name (pin_name); /* don't add the direction currently, a name is sufficient if (! dir.empty ()) { label += ":"; diff --git a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc index a6c99e04df..f0d472393d 100644 --- a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc +++ b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc @@ -333,7 +333,7 @@ TEST(def2) { db::LEFDEFReaderOptions options = default_options (); options.set_cell_outline_layer ("OUTLINE (10/0)"); - run_test (_this, "def2", "lef:0.lef+lef:1.lef+def:in.def.gz", "au_2.oas.gz", options); + run_test (_this, "def2", "lef:0.lef+lef:1.lef+def:in.def.gz", "au_3.oas.gz", options); } TEST(def3) From d31a0847482a145d9afe5c7eeaca8dffa472c0eb Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 11 Sep 2023 01:06:49 +0200 Subject: [PATCH 09/13] Fixed issue-1477 (Macro IDE: changing the colors does not have an effect) --- src/lay/lay/layMacroEditorPage.cc | 12 ++++++------ src/lay/lay/layMacroEditorPage.h | 4 ++-- src/layui/layui/layDialogs.cc | 2 +- src/layui/layui/layGenericSyntaxHighlighter.cc | 18 +++++++++++++++--- src/layui/layui/layGenericSyntaxHighlighter.h | 14 +++++++++++++- 5 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/lay/lay/layMacroEditorPage.cc b/src/lay/lay/layMacroEditorPage.cc index b0980c3154..2cb13c178c 100644 --- a/src/lay/lay/layMacroEditorPage.cc +++ b/src/lay/lay/layMacroEditorPage.cc @@ -148,18 +148,18 @@ MacroEditorHighlighters::MacroEditorHighlighters (QObject *parent) for (std::vector >::iterator a = m_attributes.begin (); a != m_attributes.end (); ++a) { // Note: this loads and initializes the attributes - delete highlighter_for_scheme (parent, a->first, &a->second); + delete highlighter_for_scheme (parent, a->first, &a->second, true); } } QSyntaxHighlighter * -MacroEditorHighlighters::highlighter_for (QObject *parent, lym::Macro::Interpreter lang, const std::string &dsl_name) +MacroEditorHighlighters::highlighter_for (QObject *parent, lym::Macro::Interpreter lang, const std::string &dsl_name, bool initialize) { std::string scheme = scheme_for (lang, dsl_name); for (std::vector >::iterator a = m_attributes.begin (); a != m_attributes.end (); ++a) { if (a->first == scheme) { - return highlighter_for_scheme (parent, a->first, &a->second); + return highlighter_for_scheme (parent, a->first, &a->second, initialize); } } @@ -167,7 +167,7 @@ MacroEditorHighlighters::highlighter_for (QObject *parent, lym::Macro::Interpret } lay::GenericSyntaxHighlighter * -MacroEditorHighlighters::highlighter_for_scheme (QObject *parent, const std::string &scheme, GenericSyntaxHighlighterAttributes *attributes) +MacroEditorHighlighters::highlighter_for_scheme (QObject *parent, const std::string &scheme, GenericSyntaxHighlighterAttributes *attributes, bool initialize) { if (! scheme.empty ()) { @@ -186,7 +186,7 @@ MacroEditorHighlighters::highlighter_for_scheme (QObject *parent, const std::str QBuffer input (&data); input.open (QIODevice::ReadOnly); - lay::GenericSyntaxHighlighter *hl = new GenericSyntaxHighlighter (parent, input, attributes); + lay::GenericSyntaxHighlighter *hl = new GenericSyntaxHighlighter (parent, input, attributes, initialize); input.close (); return hl; @@ -1096,7 +1096,7 @@ void MacroEditorPage::connect_macro (lym::Macro *macro) mp_text->setPlainText (tl::to_qstring (mp_macro->text ())); mp_text->setReadOnly (macro->is_readonly ()); mp_readonly_label->setVisible (macro->is_readonly ()); - mp_highlighter = mp_highlighters->highlighter_for (mp_text, mp_macro->interpreter (), mp_macro->dsl_interpreter ()); + mp_highlighter = mp_highlighters->highlighter_for (mp_text, mp_macro->interpreter (), mp_macro->dsl_interpreter (), false); if (mp_highlighter) { mp_highlighter->setDocument (mp_text->document ()); } diff --git a/src/lay/lay/layMacroEditorPage.h b/src/lay/lay/layMacroEditorPage.h index 3e4e1fbf63..3305d159de 100644 --- a/src/lay/lay/layMacroEditorPage.h +++ b/src/lay/lay/layMacroEditorPage.h @@ -63,7 +63,7 @@ class MacroEditorHighlighters public: MacroEditorHighlighters (QObject *parent); - QSyntaxHighlighter *highlighter_for (QObject *parent, lym::Macro::Interpreter lang, const std::string &dsl_name); + QSyntaxHighlighter *highlighter_for (QObject *parent, lym::Macro::Interpreter lang, const std::string &dsl_name, bool initialize); GenericSyntaxHighlighterAttributes *attributes_for (lym::Macro::Interpreter lang, const std::string &dsl_name); GenericSyntaxHighlighterAttributes *basic_attributes (); @@ -98,7 +98,7 @@ class MacroEditorHighlighters std::vector > m_attributes; GenericSyntaxHighlighterAttributes m_basic_attributes; - lay::GenericSyntaxHighlighter *highlighter_for_scheme (QObject *parent, const std::string &scheme, GenericSyntaxHighlighterAttributes *attributes); + lay::GenericSyntaxHighlighter *highlighter_for_scheme (QObject *parent, const std::string &scheme, GenericSyntaxHighlighterAttributes *attributes, bool initialize); std::string scheme_for (lym::Macro::Interpreter lang, const std::string &dsl_name); }; diff --git a/src/layui/layui/layDialogs.cc b/src/layui/layui/layDialogs.cc index dae96cc00d..b35d5bc11d 100644 --- a/src/layui/layui/layDialogs.cc +++ b/src/layui/layui/layDialogs.cc @@ -1095,7 +1095,7 @@ UserPropertiesForm::UserPropertiesForm (QWidget *parent) input.open (QIODevice::ReadOnly); mp_hl_basic_attributes.reset (new GenericSyntaxHighlighterAttributes ()); mp_hl_attributes.reset (new GenericSyntaxHighlighterAttributes (mp_hl_basic_attributes.get ())); - lay::GenericSyntaxHighlighter *hl = new GenericSyntaxHighlighter (mp_ui->text_edit, input, mp_hl_attributes.get ()); + lay::GenericSyntaxHighlighter *hl = new GenericSyntaxHighlighter (mp_ui->text_edit, input, mp_hl_attributes.get (), true); input.close (); hl->setDocument (mp_ui->text_edit->document ()); diff --git a/src/layui/layui/layGenericSyntaxHighlighter.cc b/src/layui/layui/layGenericSyntaxHighlighter.cc index 8a8d9ff2df..3a3cd7e75a 100644 --- a/src/layui/layui/layGenericSyntaxHighlighter.cc +++ b/src/layui/layui/layGenericSyntaxHighlighter.cc @@ -975,6 +975,12 @@ GenericSyntaxHighlighterAttributes::assign (const GenericSyntaxHighlighterAttrib m_ids = other.m_ids; } +bool +GenericSyntaxHighlighterAttributes::has_attribute (const QString &name) const +{ + return m_ids.find (name) != m_ids.end (); +} + int GenericSyntaxHighlighterAttributes::id (const QString &name) { @@ -1467,9 +1473,15 @@ parse_context (QDomElement e, const std::map &contexts_by_ } static void -parse_item_data (QDomElement e, GenericSyntaxHighlighterAttributes &attributes) +parse_item_data (QDomElement e, GenericSyntaxHighlighterAttributes &attributes, bool initialize) { QString name = e.attributeNode (QString::fromUtf8 ("name")).value (); + + // skip attribute if already present so we don't overwrite specific settings + if (! initialize && attributes.has_attribute (name)) { + return; + } + int attribute_id = attributes.id (name); def_style ds = dsNormal; @@ -1532,7 +1544,7 @@ parse_item_data (QDomElement e, GenericSyntaxHighlighterAttributes &attributes) attributes.set_styles (attribute_id, ds, format); } -GenericSyntaxHighlighter::GenericSyntaxHighlighter (QObject *parent, QIODevice &input, GenericSyntaxHighlighterAttributes *attributes) +GenericSyntaxHighlighter::GenericSyntaxHighlighter (QObject *parent, QIODevice &input, GenericSyntaxHighlighterAttributes *attributes, bool initialize_attributes) : QSyntaxHighlighter (parent), mp_attributes (attributes), m_generation_id (0) { QDomDocument d; @@ -1593,7 +1605,7 @@ GenericSyntaxHighlighter::GenericSyntaxHighlighter (QObject *parent, QIODevice & if (nn.isElement()) { QDomElement ee = nn.toElement (); if (ee.tagName () == QString::fromUtf8 ("itemData")) { - parse_item_data (ee, *mp_attributes); + parse_item_data (ee, *mp_attributes, initialize_attributes); } } } diff --git a/src/layui/layui/layGenericSyntaxHighlighter.h b/src/layui/layui/layGenericSyntaxHighlighter.h index 037536df30..35fef725b9 100644 --- a/src/layui/layui/layGenericSyntaxHighlighter.h +++ b/src/layui/layui/layGenericSyntaxHighlighter.h @@ -607,6 +607,11 @@ class LAYUI_PUBLIC GenericSyntaxHighlighterAttributes return m_ids.end (); } + /** + * @brief Gets a value indicating whether the given name is present already + */ + bool has_attribute (const QString &name) const; + /** * @brief Get the attribute ID for a given name * @@ -716,7 +721,14 @@ class LAYUI_PUBLIC GenericSyntaxHighlighter : public QSyntaxHighlighter { public: - GenericSyntaxHighlighter (QObject *parent, QIODevice &input, GenericSyntaxHighlighterAttributes *attributes); + /** + * @brief Creates a GenericSyntaxHighlighter + * @param parent The owner of the highlighter + * @param input The stream from which to pull + * @param attributes The attributes + * @param initialize_attributes If true, the attributes are initialized from the itemData lines + */ + GenericSyntaxHighlighter (QObject *parent, QIODevice &input, GenericSyntaxHighlighterAttributes *attributes, bool initialize_attributes); /** * @brief Implementation of the highlighter From 50c0ec2738550dff88e23437dd12c422e94fc4aa Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 11 Sep 2023 20:08:39 +0200 Subject: [PATCH 10/13] New test data for LEFDEF parser --- src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc index f0d472393d..deabd9dbcf 100644 --- a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc +++ b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc @@ -429,7 +429,7 @@ TEST(def16) // (complete example) db::LEFDEFReaderOptions opt = default_options (); opt.set_macro_resolution_mode (1); - run_test (_this, "def16", "lef:a.lef+lef:tech.lef+def:a.def", "au_2.oas.gz", opt); + run_test (_this, "def16", "lef:a.lef+lef:tech.lef+def:a.def", "au_3.oas.gz", opt); } TEST(100) From 1e65abb7bf3f96bf986a1fea84987a5b35f69adb Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 11 Sep 2023 21:39:06 +0200 Subject: [PATCH 11/13] Fixed issue-1307 (partial, UNPLACED component placements render placement if they come with point and orientation) --- .../streamers/lefdef/db_plugin/dbDEFImporter.cc | 14 ++++++++++++++ testdata/lefdef/mapfile/in.def | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc index 63515348c6..3baced4f59 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc @@ -1557,6 +1557,20 @@ DEFImporter::read_components (db::Layout &layout, std::listsecond.bbox.transformed (ft).lower_left (); is_placed = true; + } else if (test ("UNPLACED")) { + + // invalid "UNPLACED", but yet it appears to be existing (#1307) + if (test ("(")) { + + db::Point pt = get_point (scale); + test (")"); + + ft = get_orient (false /*mandatory*/); + d = pt - m->second.bbox.transformed (ft).lower_left (); + is_placed = true; + + } + } else if (test ("MASKSHIFT")) { maskshift = get (); diff --git a/testdata/lefdef/mapfile/in.def b/testdata/lefdef/mapfile/in.def index f24be607b0..5c6012aab7 100644 --- a/testdata/lefdef/mapfile/in.def +++ b/testdata/lefdef/mapfile/in.def @@ -8,7 +8,7 @@ UNITS DISTANCE MICRONS 1000 ; DIEAREA ( 0 0 ) ( 1000 2000 ) ; COMPONENTS 3 ; - - macro1 macro1 + PLACED ( 0 0 ) N ; + - macro1 macro1 + UNPLACED ( 0 0 ) N ; END COMPONENTS SPECIALNETS 1 ; From b360df16e3831d9358c3437def0c450e26e964b0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 11 Sep 2023 23:04:56 +0200 Subject: [PATCH 12/13] Fixed problem discussed in the form (#2365) - processEvents needed for LayoutView#get_image etc. --- src/laybasic/laybasic/layLayoutViewBase.cc | 57 ++++++++++------------ src/laybasic/laybasic/layLayoutViewBase.h | 2 + 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/src/laybasic/laybasic/layLayoutViewBase.cc b/src/laybasic/laybasic/layLayoutViewBase.cc index 27ffa530b3..9f4fa5b47d 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.cc +++ b/src/laybasic/laybasic/layLayoutViewBase.cc @@ -2777,8 +2777,7 @@ LayoutViewBase::get_screenshot () { tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Save screenshot"))); - // Execute all deferred methods - ensure there are no pending tasks - tl::DeferredMethodScheduler::execute (); + refresh (); return mp_canvas->screenshot ().to_image_copy (); } @@ -2789,8 +2788,7 @@ LayoutViewBase::get_screenshot_pb () { tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Save screenshot"))); - // Execute all deferred methods - ensure there are no pending tasks - tl::DeferredMethodScheduler::execute (); + refresh (); return mp_canvas->screenshot (); } @@ -2828,9 +2826,8 @@ LayoutViewBase::save_screenshot (const std::string &fn) writer.setText (tl::to_qstring (i->first), tl::to_qstring (i->second)); } - // Execute all deferred methods - ensure there are no pending tasks - tl::DeferredMethodScheduler::execute (); - + refresh (); + if (! writer.write (mp_canvas->screenshot ().to_image ())) { throw tl::Exception (tl::to_string (tr ("Unable to write screenshot to file: %s (%s)")), fn, tl::to_string (writer.errorString ())); } @@ -2843,8 +2840,7 @@ LayoutViewBase::save_screenshot (const std::string &fn) { tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Save screenshot"))); - // Execute all deferred methods - ensure there are no pending tasks - tl::DeferredMethodScheduler::execute (); + refresh (); tl::OutputStream stream (fn); tl::PixelBuffer img = mp_canvas->screenshot (); @@ -2867,9 +2863,8 @@ LayoutViewBase::get_image (unsigned int width, unsigned int height) { tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Get image"))); - // Execute all deferred methods - ensure there are no pending tasks - tl::DeferredMethodScheduler::execute (); - + refresh (); + return mp_canvas->image (width, height).to_image_copy (); } #endif @@ -2879,8 +2874,7 @@ LayoutViewBase::get_pixels (unsigned int width, unsigned int height) { tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Get image"))); - // Execute all deferred methods - ensure there are no pending tasks - tl::DeferredMethodScheduler::execute (); + refresh (); return mp_canvas->image (width, height); } @@ -2892,9 +2886,8 @@ LayoutViewBase::get_image_with_options (unsigned int width, unsigned int height, { tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Get image"))); - // Execute all deferred methods - ensure there are no pending tasks - tl::DeferredMethodScheduler::execute (); - + refresh (); + if (monochrome) { return mp_canvas->image_with_options_mono (width, height, linewidth, background, foreground, active, target_box).to_image_copy (); } else { @@ -2909,8 +2902,7 @@ LayoutViewBase::get_pixels_with_options (unsigned int width, unsigned int height { tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Get image"))); - // Execute all deferred methods - ensure there are no pending tasks - tl::DeferredMethodScheduler::execute (); + refresh (); return mp_canvas->image_with_options (width, height, linewidth, oversampling, resolution, background, foreground, active, target_box); } @@ -2921,8 +2913,7 @@ LayoutViewBase::get_pixels_with_options_mono (unsigned int width, unsigned int h { tl::SelfTimer timer (tl::verbosity () >= 11, tl::to_string (tr ("Get image"))); - // Execute all deferred methods - ensure there are no pending tasks - tl::DeferredMethodScheduler::execute (); + refresh (); return mp_canvas->image_with_options_mono (width, height, linewidth, background, foreground, active, target_box); } @@ -2941,9 +2932,8 @@ LayoutViewBase::save_image (const std::string &fn, unsigned int width, unsigned writer.setText (tl::to_qstring (i->first), tl::to_qstring (i->second)); } - // Execute all deferred methods - ensure there are no pending tasks - tl::DeferredMethodScheduler::execute (); - + refresh (); + if (! writer.write (mp_canvas->image (width, height).to_image ())) { throw tl::Exception (tl::to_string (tr ("Unable to write screenshot to file: %s (%s)")), fn, tl::to_string (writer.errorString ())); } @@ -2958,8 +2948,7 @@ LayoutViewBase::save_image (const std::string &fn, unsigned int width, unsigned lay::Viewport vp (width, height, mp_canvas->viewport ().target_box ()); - // Execute all deferred methods - ensure there are no pending tasks - tl::DeferredMethodScheduler::execute (); + refresh (); tl::OutputStream stream (fn); tl::PixelBuffer img = mp_canvas->image (width, height); @@ -2993,8 +2982,7 @@ LayoutViewBase::save_image_with_options (const std::string &fn, writer.setText (tl::to_qstring (i->first), tl::to_qstring (i->second)); } - // Execute all deferred methods - ensure there are no pending tasks - tl::DeferredMethodScheduler::execute (); + refresh (); if (monochrome) { if (! writer.write (mp_canvas->image_with_options_mono (width, height, linewidth, background, foreground, active, target_box).to_image ())) { @@ -3019,8 +3007,7 @@ LayoutViewBase::save_image_with_options (const std::string &fn, lay::Viewport vp (width, height, mp_canvas->viewport ().target_box ()); std::vector > texts = png_texts (this, vp.box ()); - // Execute all deferred methods - ensure there are no pending tasks - tl::DeferredMethodScheduler::execute (); + refresh (); tl::OutputStream stream (fn); if (monochrome) { @@ -3698,6 +3685,16 @@ LayoutViewBase::timer () } } +void +LayoutViewBase::refresh () +{ + // Execute all deferred methods - ensure there are no pending tasks + tl::DeferredMethodScheduler::execute (); + + // Issue a "tick" to execute all other pending tasks + timer (); +} + void LayoutViewBase::force_update_content () { diff --git a/src/laybasic/laybasic/layLayoutViewBase.h b/src/laybasic/laybasic/layLayoutViewBase.h index 5faf144a89..fe826a2e01 100644 --- a/src/laybasic/laybasic/layLayoutViewBase.h +++ b/src/laybasic/laybasic/layLayoutViewBase.h @@ -2917,6 +2917,8 @@ class LAYBASIC_PUBLIC LayoutViewBase : void init_layer_properties (LayerProperties &props, const LayerPropertiesList &lp_list) const; void merge_dither_pattern (lay::LayerPropertiesList &props); + void refresh (); + protected: /** * @brief Constructor for calling from a LayoutView From 3e3e8d4ba366b94e6ed90a9957d303797d05ee23 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 11 Sep 2023 23:58:38 +0200 Subject: [PATCH 13/13] Fixed pymod test --- testdata/python/dbPCells.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testdata/python/dbPCells.py b/testdata/python/dbPCells.py index 14ca10e5e1..e51c648fd3 100644 --- a/testdata/python/dbPCells.py +++ b/testdata/python/dbPCells.py @@ -166,7 +166,8 @@ class DBPCellTests(unittest.TestCase): def test_0(self): # PCellDeclarationHelper is inside "pya.__all__" - self.assertEqual("PCellDeclarationHelper" in pya.__all__, True) + if hasattr(pya, "__all__"): + self.assertEqual("PCellDeclarationHelper" in pya.__all__, True) def test_1(self):