WIP: trying performance improvement of two-layer DRC checks (do merge internally instead of before)

This commit is contained in:
Matthias Koefferlein 2026-07-19 19:55:05 +02:00
parent b51187bf7c
commit a75f1d77e3
5 changed files with 140 additions and 32 deletions

View File

@ -1184,13 +1184,8 @@ AsIfFlatRegion::width_check (db::Coord d, const RegionCheckOptions &options) con
EdgePairsDelegate *
AsIfFlatRegion::space_or_isolated_check (db::Coord d, const RegionCheckOptions &options, bool isolated) const
{
if (options.opposite_filter != NoOppositeFilter || options.rect_filter != NoRectFilter || options.shielded) {
// NOTE: we have to use the "foreign" scheme with a filter because only this scheme
// guarantees that all subject shapes are visited.
return run_check (db::SpaceRelation, isolated, foreign_regionptr (), d, options);
} else {
return run_check (db::SpaceRelation, isolated, subject_regionptr (), d, options);
}
// NOTE: we have to use "foreign" to make sure every subject sees neighboring subjects
return run_check (db::SpaceRelation, isolated, foreign_regionptr (), d, options);
}
EdgePairsDelegate *
@ -1238,12 +1233,14 @@ AsIfFlatRegion::inside_check (const Region &other, db::Coord d, const RegionChec
EdgePairsDelegate *
AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, const RegionCheckOptions &options) const
{
bool has_other = other && other != subject_regionptr () && other != foreign_regionptr ();
// force different polygons in the different properties case to skip intra-polygon checks
if (pc_always_different (options.prop_constraint)) {
if (! has_other && pc_always_different (options.prop_constraint)) {
different_polygons = true;
}
bool needs_merged_primary = different_polygons || options.needs_merged ();
bool needs_merged_primary = (! has_other && different_polygons) || options.needs_merged ();
db::RegionIterator polygons (needs_merged_primary ? begin_merged () : begin ());
bool primary_is_merged = ! merged_semantics () || needs_merged_primary || is_merged ();
@ -1252,15 +1249,18 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons,
std::vector<db::RegionIterator> others;
std::vector<bool> foreign;
bool has_other = false;
bool other_is_merged = true;
if (other == subject_regionptr () || other == foreign_regionptr ()) {
if (! has_other) {
foreign.push_back (other == foreign_regionptr ());
others.push_back (polygons);
other_is_merged = primary_is_merged;
} else {
foreign.push_back (false);
if (! other->merged_semantics ()) {
others.push_back (other->begin ());
other_is_merged = true;
@ -1272,7 +1272,13 @@ AsIfFlatRegion::run_check (db::edge_relation_type rel, bool different_polygons,
others.push_back (other->begin ());
other_is_merged = other->is_merged ();
}
has_other = true;
// adds another intruder section to implement subject merging ("primary_intruders")
if (! primary_is_merged) {
foreign.push_back (true);
others.push_back (polygons);
}
}
std::unique_ptr<FlatEdgePairs> output (new FlatEdgePairs ());

View File

@ -2368,6 +2368,7 @@ DeepRegion::cop_to_edges (db::CompoundRegionOperationNode &node, db::PropertyCon
EdgePairsDelegate *
DeepRegion::run_check (db::edge_relation_type rel, bool different_polygons, const Region *other, db::Coord d, const RegionCheckOptions &options) const
{
// @@@
if (empty ()) {
return new db::DeepEdgePairs (deep_layer ().derived ());
} else if (other && ! is_subject_regionptr (other) && other->empty () && ! options.negative) {

View File

@ -376,10 +376,10 @@ private:
*/
template <class TS, class TI>
DB_PUBLIC_TEMPLATE
std::map<db::properties_id_type, std::pair<std::vector<const TS *>, std::set<const TI *> > >
std::map<db::properties_id_type, std::pair<std::vector<const TS *>, std::set<std::pair<unsigned int, const TI *> > > >
separate_interactions_by_properties (const shape_interactions<db::object_with_properties<TS>, db::object_with_properties<TI> > &interactions, db::PropertyConstraint property_constraint)
{
std::map<db::properties_id_type, std::pair<std::vector<const TS *>, std::set<const TI *> > > by_prop_id;
std::map<db::properties_id_type, std::pair<std::vector<const TS *>, std::set<std::pair<unsigned int, const TI *> > > > by_prop_id;
for (auto i = interactions.begin (); i != interactions.end (); ++i) {
@ -387,7 +387,7 @@ separate_interactions_by_properties (const shape_interactions<db::object_with_pr
db::properties_id_type prop_id = subject.properties_id ();
std::pair<std::vector<const TS *>, std::set<const TI *> > &s2p = by_prop_id [prop_id];
auto &s2p = by_prop_id [prop_id];
s2p.first.push_back (&subject);
for (auto ii = i->second.begin (); ii != i->second.end (); ++ii) {
@ -395,7 +395,7 @@ separate_interactions_by_properties (const shape_interactions<db::object_with_pr
const std::pair<unsigned int, db::object_with_properties<TI> > &intruder = interactions.intruder_shape (*ii);
if (pc_match (property_constraint, prop_id, intruder.second.properties_id ())) {
s2p.second.insert (&intruder.second);
s2p.second.insert (std::make_pair (intruder.first, &intruder.second));
}
}

View File

@ -211,7 +211,7 @@ check_local_operation_base<TS, TI>::check_local_operation_base (const EdgeRelati
template <class TS, class TI>
void
check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cell *subject_cell, const std::vector<const TS *> &subjects, const std::set<const TI *> &intruders, std::unordered_set<db::EdgePair> &result, std::unordered_set<db::EdgePair> &intra_polygon_result, const db::LocalProcessorBase *proc) const
check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cell *subject_cell, const std::vector<const TS *> &subjects, const std::set<const TI *> &primary_intruders, const std::set<const TI *> &intruders, std::unordered_set<db::EdgePair> &result, std::unordered_set<db::EdgePair> &intra_polygon_result, const db::LocalProcessorBase *proc) const
{
// NOTE: the rectangle and opposite filters are unsymmetric
bool symmetric_edge_pairs = ! m_has_other && m_options.opposite_filter == db::NoOppositeFilter && m_options.rect_filter == RectFilter::NoRectFilter;
@ -229,7 +229,7 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
db::EdgeProcessor ep;
ep.set_base_verbosity (50);
bool take_all = edge_check.has_negative_edge_output () || intruders.empty ();
bool take_all = edge_check.has_negative_edge_output () || (intruders.empty () && primary_intruders.empty ());
db::Box common_box;
bool subjects_are_fully_covered = false;
@ -259,6 +259,14 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
all_intruders_box += intruder_box.enlarged (e);
}
for (auto i = primary_intruders.begin (); i != primary_intruders.end (); ++i) {
db::Box intruder_box = db::box_convert<TI> () (**i);
if (! subjects_are_fully_covered && (*i)->is_box () && common_box.inside (intruder_box)) {
subjects_are_fully_covered = true;
}
all_intruders_box += intruder_box.enlarged (e);
}
common_box &= all_intruders_box;
}
@ -269,7 +277,7 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
size_t n = 0;
if (m_is_merged || (subjects.size () == 1 && subjects.front ()->is_box ())) {
if (m_is_merged || (subjects.size () == 1 && subjects.front ()->is_box () && primary_intruders.empty ())) {
for (auto i = subjects.begin (); i != subjects.end (); ++i) {
if (! take_all) {
@ -280,9 +288,10 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
n += 2;
}
} else {
} else if (primary_intruders.empty ()) {
// merge needed for the subject shapes
// Here we don't have other subjects to consider. This is the simple case.
ep.clear ();
size_t nn = 0;
@ -310,6 +319,79 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
n += 2;
}
} else {
// merge needed for the subject and primary intruder shapes ("other subjects") - we merge both and then
// reclaim original subject edges that are "outer" edges and not covered by other subjects shapes.
ep.clear ();
size_t nn = 0;
for (auto i = subjects.begin (); i != subjects.end (); ++i) {
for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) {
ep.insert (*e, nn);
}
++nn;
}
for (auto i = primary_intruders.begin (); i != primary_intruders.end (); ++i) {
for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) {
ep.insert (*e, nn);
}
++nn;
}
std::vector<typename TS::edge_type> edges;
db::EdgeContainer ee (edges);
db::SimpleMerge op (1 /*wc>0*/);
ep.process (ee, op);
ep.clear ();
std::vector<typename TS::edge_type> subject_edges;
size_t sz = 0;
for (auto i = subjects.begin (); i != subjects.end (); ++i) {
sz += (*i)->vertices ();
}
subject_edges.reserve (sz);
for (auto i = subjects.begin (); i != subjects.end (); ++i) {
for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) {
subject_edges.push_back (*e);
}
}
std::set<typename TI::edge_type> partial_edges;
EdgeBooleanClusterCollector<std::set<typename TI::edge_type> > cluster_collector (&partial_edges, db::EdgeAnd);
db::box_scanner<typename TI::edge_type, size_t> scanner;
scanner.reserve (edges.size () + subject_edges.size ());
for (auto i = edges.begin (); i != edges.end (); ++i) {
if (! i->is_degenerate ()) {
scanner.insert (i.operator-> (), 0);
}
}
for (auto i = subject_edges.begin (); i != subject_edges.end (); ++i) {
if (! i->is_degenerate ()) {
scanner.insert (i.operator-> (), 1);
}
}
scanner.process (cluster_collector, 1, db::box_convert<typename TI::edge_type> ());
for (auto e = partial_edges.begin (); e != partial_edges.end (); ++e) {
if (! take_all) {
poly_check.enter (*e, 0, common_box);
} else {
poly_check.enter (*e, 0);
}
}
}
// merge the intruders to remove inner edges
@ -384,11 +466,11 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
} else {
if (m_is_merged || (subjects.size () == 1 && intruders.empty () && subjects.front ()->is_box ())) {
if (m_is_merged || (subjects.size () == 1 && primary_intruders.empty () && subjects.front ()->is_box ())) {
// no merge required
// NOTE: we need to eliminate identical shapes from intruders and subjects because those will shield
// NOTE: we need to eliminate identical shapes from primary intruders and subjects because those will shield
size_t n = 0;
std::unordered_set<TI> subjects_hash;
@ -406,7 +488,7 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
n = 1;
for (auto i = intruders.begin (); i != intruders.end (); ++i) {
for (auto i = primary_intruders.begin (); i != primary_intruders.end (); ++i) {
if (subjects_hash.find (**i) == subjects_hash.end ()) {
if (! take_all) {
poly_check.enter (**i, n, common_box);
@ -416,9 +498,9 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
}
}
} else if (intruders.empty ()) {
} else if (primary_intruders.empty ()) {
// merge needed for the subject shapes - no intruders present so this is the simple case
// merge needed for the subject shapes - no primary intruders ("other subjects") present so this is the simple case
size_t n = 0;
@ -451,7 +533,7 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
} else {
// merge needed for the subject and intruder shapes - we merge both and then
// merge needed for the subject and primary intruder shapes ("other subjects") - we merge both and then
// separate edges into those from the subject and those from intruder shapes.
ep.clear ();
@ -464,7 +546,7 @@ check_local_operation_base<TS, TI>::compute_results (db::Layout *layout, db::Cel
++nn;
}
for (auto i = intruders.begin (); i != intruders.end (); ++i) {
for (auto i = primary_intruders.begin (); i != primary_intruders.end (); ++i) {
for (auto e = (*i)->begin_edge (); ! e.at_end (); ++e) {
ep.insert (*e, nn);
}
@ -727,11 +809,19 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, db::Cell *s
subjects.reserve (interactions.size ());
std::set<const TI *> intruders;
std::set<const TI *> primary_intruders;
unsigned int primary_intruder_layer = check_local_operation_base<TS, TI>::m_has_other ? 1 : 0;
for (auto i = interactions.begin (); i != interactions.end (); ++i) {
subjects.push_back (&interactions.subject_shape (i->first));
for (auto ii = i->second.begin (); ii != i->second.end (); ++ii) {
intruders.insert (&interactions.intruder_shape (*ii).second);
const auto &is = interactions.intruder_shape (*ii);
if (is.first == primary_intruder_layer) {
primary_intruders.insert (&is.second);
} else {
intruders.insert (&is.second);
}
}
}
@ -740,7 +830,7 @@ check_local_operation<TS, TI>::do_compute_local (db::Layout *layout, db::Cell *s
std::unordered_set<db::EdgePair> result, intra_polygon_result;
// perform the basic check
check_local_operation_base<TS, TI>::compute_results (layout, subject_cell, subjects, intruders, result, intra_polygon_result, proc);
check_local_operation_base<TS, TI>::compute_results (layout, subject_cell, subjects, primary_intruders, intruders, result, intra_polygon_result, proc);
// detect and remove parts of the result which have or do not have results "opposite"
// ("opposite" is defined by the projection of edges "through" the subject shape)
@ -816,6 +906,8 @@ check_local_operation_with_properties<TS, TI>::do_compute_local (db::Layout *lay
{
tl_assert (results.size () == 1);
unsigned int primary_intruder_layer = check_local_operation_base<TS, TI>::m_has_other ? 1 : 0;
auto by_prop_id = separate_interactions_by_properties (interactions, check_local_operation_base<TS, TI>::m_options.prop_constraint);
for (auto s2p = by_prop_id.begin (); s2p != by_prop_id.end (); ++s2p) {
@ -823,10 +915,18 @@ check_local_operation_with_properties<TS, TI>::do_compute_local (db::Layout *lay
std::unordered_set<db::EdgePair> result, intra_polygon_result;
const std::vector<const TS *> &subjects = s2p->second.first;
const std::set<const TI *> &intruders = s2p->second.second;
std::set<const TI *> intruders, primary_intruders;
for (auto i = s2p->second.second.begin (); i != s2p->second.second.end (); ++i) {
if (i->first == primary_intruder_layer) {
primary_intruders.insert (i->second);
} else {
intruders.insert (i->second);
}
}
// perform the basic check
check_local_operation_base<TS, TI>::compute_results (layout, subject_cell, subjects, intruders, result, intra_polygon_result, proc);
check_local_operation_base<TS, TI>::compute_results (layout, subject_cell, subjects, primary_intruders, intruders, result, intra_polygon_result, proc);
// detect and remove parts of the result which have or do not have results "opposite"
// ("opposite" is defined by the projection of edges "through" the subject shape)
@ -2295,6 +2395,7 @@ PolygonToEdgeLocalOperation::do_compute_local (db::Layout * /*layout*/, db::Cell
} else {
// @@@ that's not correct, isn't it?
// With intruders: to compute our local contribution we take the edges without and with intruders
// and deliver what is in both sets

View File

@ -194,7 +194,7 @@ protected:
bool m_other_is_merged;
db::RegionCheckOptions m_options;
void compute_results (db::Layout *layout, db::Cell *subject_cell, const std::vector<const TS *> &subjects, const std::set<const TI *> &intruders, std::unordered_set<db::EdgePair> &result, std::unordered_set<db::EdgePair> &intra_polygon_result, const LocalProcessorBase *proc) const;
void compute_results (db::Layout *layout, db::Cell *subject_cell, const std::vector<const TS *> &subjects, const std::set<const TI *> &primary_intruders, const std::set<const TI *> &intruders, std::unordered_set<db::EdgePair> &result, std::unordered_set<db::EdgePair> &intra_polygon_result, const LocalProcessorBase *proc) const;
void apply_opposite_filter (const std::vector<const TS *> &subjects, std::unordered_set<db::EdgePair> &result, std::unordered_set<db::EdgePair> &intra_polygon_result) const;
void apply_rectangle_filter (const std::vector<const TS *> &subjects, std::unordered_set<db::EdgePair> &result) const;
};