mirror of https://github.com/KLayout/klayout.git
Faster hierarchical edges.
This commit is contained in:
parent
a863bbd0ba
commit
9b7879b2a9
|
|
@ -1070,56 +1070,163 @@ DeepRegion::snapped (db::Coord gx, db::Coord gy)
|
|||
return res.release ();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class PolygonToEdgeLocalOperation
|
||||
: public local_operation<db::PolygonRef, db::PolygonRef, db::Edge>
|
||||
{
|
||||
public:
|
||||
PolygonToEdgeLocalOperation ()
|
||||
: local_operation<db::PolygonRef, db::PolygonRef, db::Edge> ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
virtual db::Coord dist () const { return 1; }
|
||||
virtual bool requests_single_subjects () const { return true; }
|
||||
virtual std::string description () const { return std::string ("polygon to edges"); }
|
||||
|
||||
virtual void do_compute_local (db::Layout * /*layout*/, const shape_interactions<db::PolygonRef, db::PolygonRef> &interactions, std::vector<std::unordered_set<db::Edge> > &results, size_t /*max_vertex_count*/, double /*area_ratio*/) const
|
||||
{
|
||||
db::EdgeProcessor ep;
|
||||
|
||||
for (shape_interactions<db::PolygonRef, db::PolygonRef>::subject_iterator s = interactions.begin_subjects (); s != interactions.end_subjects (); ++s) {
|
||||
ep.insert (s->second);
|
||||
}
|
||||
|
||||
if (interactions.num_intruders () == 0) {
|
||||
|
||||
db::EdgeToEdgeSetGenerator eg (results.front ());
|
||||
db::MergeOp op (0);
|
||||
ep.process (eg, op);
|
||||
|
||||
} else {
|
||||
|
||||
// With intruders: to compute our local contribution we take the edges without and with intruders
|
||||
// and deliver what is in both sets
|
||||
|
||||
db::MergeOp op (0);
|
||||
|
||||
std::vector<Edge> edges1;
|
||||
db::EdgeContainer ec1 (edges1);
|
||||
ep.process (ec1, op);
|
||||
|
||||
ep.clear ();
|
||||
|
||||
for (shape_interactions<db::PolygonRef, db::PolygonRef>::subject_iterator s = interactions.begin_subjects (); s != interactions.end_subjects (); ++s) {
|
||||
ep.insert (s->second);
|
||||
}
|
||||
for (shape_interactions<db::PolygonRef, db::PolygonRef>::intruder_iterator i = interactions.begin_intruders (); i != interactions.end_intruders (); ++i) {
|
||||
ep.insert (i->second.second);
|
||||
}
|
||||
|
||||
std::vector<Edge> edges2;
|
||||
db::EdgeContainer ec2 (edges2);
|
||||
ep.process (ec2, op);
|
||||
|
||||
// Runs the boolean AND between the result with and without intruders
|
||||
|
||||
db::box_scanner<db::Edge, size_t> scanner;
|
||||
scanner.reserve (edges1.size () + edges2.size ());
|
||||
|
||||
for (std::vector<Edge>::const_iterator i = edges1.begin (); i != edges1.end (); ++i) {
|
||||
scanner.insert (i.operator-> (), 0);
|
||||
}
|
||||
for (std::vector<Edge>::const_iterator i = edges2.begin (); i != edges2.end (); ++i) {
|
||||
scanner.insert (i.operator-> (), 1);
|
||||
}
|
||||
|
||||
EdgeBooleanClusterCollector<std::unordered_set<db::Edge> > cluster_collector (&results.front (), EdgeAnd);
|
||||
scanner.process (cluster_collector, 1, db::box_convert<db::Edge> ());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
EdgesDelegate *
|
||||
DeepRegion::edges (const EdgeFilterBase *filter) const
|
||||
{
|
||||
const db::DeepLayer &polygons = merged_deep_layer ();
|
||||
if (! filter && merged_semantics ()) {
|
||||
|
||||
std::unique_ptr<VariantsCollectorBase> vars;
|
||||
// Hierarchical edge detector - no pre-merge required
|
||||
|
||||
if (filter && filter->vars ()) {
|
||||
const db::DeepLayer &polygons = deep_layer ();
|
||||
|
||||
vars.reset (new db::VariantsCollectorBase (filter->vars ()));
|
||||
db::PolygonToEdgeLocalOperation op;
|
||||
|
||||
vars->collect (polygons.layout (), polygons.initial_cell ());
|
||||
db::local_processor<db::PolygonRef, db::PolygonRef, db::Edge> proc (const_cast<db::Layout *> (&polygons.layout ()),
|
||||
const_cast<db::Cell *> (&polygons.initial_cell ()),
|
||||
polygons.breakout_cells ());
|
||||
|
||||
// NOTE: m_merged_polygons is mutable, so why is the const_cast needed?
|
||||
const_cast<db::DeepLayer &> (polygons).separate_variants (*vars);
|
||||
proc.set_description (progress_desc ());
|
||||
proc.set_report_progress (report_progress ());
|
||||
proc.set_base_verbosity (base_verbosity ());
|
||||
proc.set_threads (polygons.store ()->threads ());
|
||||
|
||||
}
|
||||
// a boolean core makes somewhat better hierarchy
|
||||
proc.set_boolean_core (true);
|
||||
|
||||
db::Layout &layout = const_cast<db::Layout &> (polygons.layout ());
|
||||
std::unique_ptr<db::DeepEdges> res (new db::DeepEdges (polygons.derived ()));
|
||||
|
||||
std::unique_ptr<db::DeepEdges> res (new db::DeepEdges (polygons.derived ()));
|
||||
for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) {
|
||||
proc.run (&op, polygons.layer (), foreign_idlayer (), res->deep_layer ().layer ());
|
||||
|
||||
return res.release ();
|
||||
|
||||
} else {
|
||||
|
||||
const db::DeepLayer &polygons = merged_deep_layer ();
|
||||
|
||||
std::unique_ptr<VariantsCollectorBase> vars;
|
||||
|
||||
if (filter && filter->vars ()) {
|
||||
|
||||
vars.reset (new db::VariantsCollectorBase (filter->vars ()));
|
||||
|
||||
vars->collect (polygons.layout (), polygons.initial_cell ());
|
||||
|
||||
// NOTE: m_merged_polygons is mutable, so why is the const_cast needed?
|
||||
const_cast<db::DeepLayer &> (polygons).separate_variants (*vars);
|
||||
|
||||
db::ICplxTrans tr;
|
||||
if (vars.get ()) {
|
||||
const std::map<db::ICplxTrans, size_t> &v = vars->variants (c->cell_index ());
|
||||
tl_assert (v.size () == size_t (1));
|
||||
tr = v.begin ()->first;
|
||||
}
|
||||
|
||||
const db::Shapes &s = c->shapes (polygons.layer ());
|
||||
db::Shapes &st = c->shapes (res->deep_layer ().layer ());
|
||||
db::Layout &layout = const_cast<db::Layout &> (polygons.layout ());
|
||||
|
||||
for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) {
|
||||
std::unique_ptr<db::DeepEdges> res (new db::DeepEdges (polygons.derived ()));
|
||||
for (db::Layout::iterator c = layout.begin (); c != layout.end (); ++c) {
|
||||
|
||||
db::Polygon poly;
|
||||
si->polygon (poly);
|
||||
db::ICplxTrans tr;
|
||||
if (vars.get ()) {
|
||||
const std::map<db::ICplxTrans, size_t> &v = vars->variants (c->cell_index ());
|
||||
tl_assert (v.size () == size_t (1));
|
||||
tr = v.begin ()->first;
|
||||
}
|
||||
|
||||
for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) {
|
||||
if (! filter || filter->selected ((*e).transformed (tr))) {
|
||||
st.insert (*e);
|
||||
const db::Shapes &s = c->shapes (polygons.layer ());
|
||||
db::Shapes &st = c->shapes (res->deep_layer ().layer ());
|
||||
|
||||
for (db::Shapes::shape_iterator si = s.begin (db::ShapeIterator::All); ! si.at_end (); ++si) {
|
||||
|
||||
db::Polygon poly;
|
||||
si->polygon (poly);
|
||||
|
||||
for (db::Polygon::polygon_edge_iterator e = poly.begin_edge (); ! e.at_end (); ++e) {
|
||||
if (! filter || filter->selected ((*e).transformed (tr))) {
|
||||
st.insert (*e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
res->set_is_merged (merged_semantics () || is_merged ());
|
||||
return res.release ();
|
||||
|
||||
res->set_is_merged (merged_semantics () || is_merged ());
|
||||
return res.release ();
|
||||
}
|
||||
}
|
||||
|
||||
RegionDelegate *
|
||||
|
|
|
|||
|
|
@ -409,6 +409,16 @@ local_processor_cell_contexts<TS, TI, TR>::create (const context_key_type &intru
|
|||
return &m_contexts[intruders];
|
||||
}
|
||||
|
||||
template <class TR>
|
||||
static void
|
||||
subtract_set (std::unordered_set<TR> &res, const std::unordered_set<TR> &other)
|
||||
{
|
||||
// for everything else, we don't use a boolean core but just set intersection
|
||||
for (typename std::unordered_set<TR>::const_iterator o = other.begin (); o != other.end (); ++o) {
|
||||
res.erase (*o);
|
||||
}
|
||||
}
|
||||
|
||||
template <class TS, class TI>
|
||||
static void
|
||||
subtract (std::unordered_set<db::PolygonRef> &res, const std::unordered_set<db::PolygonRef> &other, db::Layout *layout, const db::local_processor<TS, TI, db::PolygonRef> *proc)
|
||||
|
|
@ -417,6 +427,11 @@ subtract (std::unordered_set<db::PolygonRef> &res, const std::unordered_set<db::
|
|||
return;
|
||||
}
|
||||
|
||||
if (! proc->boolean_core ()) {
|
||||
subtract_set (res, other);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t max_vertex_count = proc->max_vertex_count ();
|
||||
double area_ratio = proc->area_ratio ();
|
||||
|
||||
|
|
@ -449,14 +464,60 @@ subtract (std::unordered_set<db::PolygonRef> &res, const std::unordered_set<db::
|
|||
ep.process (pg, op);
|
||||
}
|
||||
|
||||
template <class TS, class TI>
|
||||
static void
|
||||
subtract (std::unordered_set<db::Edge> &res, const std::unordered_set<db::Edge> &other, db::Layout *layout, const db::local_processor<TS, TI, db::Edge> *proc)
|
||||
{
|
||||
if (other.empty ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! proc->boolean_core ()) {
|
||||
subtract_set (res, other);
|
||||
return;
|
||||
}
|
||||
|
||||
db::box_scanner<db::Edge, size_t> scanner;
|
||||
scanner.reserve (res.size () + other.size ());
|
||||
|
||||
for (std::unordered_set<Edge>::const_iterator i = res.begin (); i != res.end (); ++i) {
|
||||
scanner.insert (i.operator-> (), 0);
|
||||
}
|
||||
for (std::unordered_set<Edge>::const_iterator i = other.begin (); i != other.end (); ++i) {
|
||||
scanner.insert (i.operator-> (), 1);
|
||||
}
|
||||
|
||||
std::unordered_set<db::Edge> result;
|
||||
EdgeBooleanClusterCollector<std::unordered_set<db::Edge> > cluster_collector (&result, EdgeNot);
|
||||
scanner.process (cluster_collector, 1, db::box_convert<db::Edge> ());
|
||||
|
||||
res.swap (result);
|
||||
}
|
||||
|
||||
template <class TS, class TI, class TR>
|
||||
static void
|
||||
subtract (std::unordered_set<TR> &res, const std::unordered_set<TR> &other, db::Layout * /*layout*/, const db::local_processor<TS, TI, TR> * /*proc*/)
|
||||
{
|
||||
// for edges, we don't use a boolean core but just set intersection
|
||||
for (typename std::unordered_set<TR>::const_iterator o = other.begin (); o != other.end (); ++o) {
|
||||
res.erase (*o);
|
||||
}
|
||||
subtract_set (res, other);
|
||||
}
|
||||
|
||||
// determines the default boolean core flag per result type
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template <class TR>
|
||||
struct default_boolean_core
|
||||
{
|
||||
bool operator() () const { return false; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct default_boolean_core<db::PolygonRef>
|
||||
{
|
||||
bool operator() () const { return true; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
|
@ -1221,7 +1282,7 @@ local_processor<TS, TI, TR>::local_processor (db::Layout *layout, db::Cell *top,
|
|||
: mp_subject_layout (layout), mp_intruder_layout (layout),
|
||||
mp_subject_top (top), mp_intruder_top (top),
|
||||
mp_subject_breakout_cells (breakout_cells), mp_intruder_breakout_cells (breakout_cells),
|
||||
m_report_progress (true), m_nthreads (0), m_max_vertex_count (0), m_area_ratio (0.0), m_base_verbosity (30), m_progress (0), mp_progress (0)
|
||||
m_report_progress (true), m_nthreads (0), m_max_vertex_count (0), m_area_ratio (0.0), m_boolean_core (default_boolean_core<TR> () ()), m_base_verbosity (30), m_progress (0), mp_progress (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
|
@ -1231,7 +1292,7 @@ local_processor<TS, TI, TR>::local_processor (db::Layout *subject_layout, db::Ce
|
|||
: mp_subject_layout (subject_layout), mp_intruder_layout (intruder_layout),
|
||||
mp_subject_top (subject_top), mp_intruder_top (intruder_top),
|
||||
mp_subject_breakout_cells (subject_breakout_cells), mp_intruder_breakout_cells (intruder_breakout_cells),
|
||||
m_report_progress (true), m_nthreads (0), m_max_vertex_count (0), m_area_ratio (0.0), m_base_verbosity (30), m_progress (0), mp_progress (0)
|
||||
m_report_progress (true), m_nthreads (0), m_max_vertex_count (0), m_area_ratio (0.0), m_boolean_core (default_boolean_core<TR> () ()), m_base_verbosity (30), m_progress (0), mp_progress (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
|
@ -1535,6 +1596,11 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
|
|||
scanner.process (rec, dist, inst_bcs, db::box_convert<TI> ());
|
||||
}
|
||||
|
||||
// this cache should reduce the effort of checking array vs. array
|
||||
typedef std::pair<unsigned int, std::pair<db::cell_index_type, db::ICplxTrans> > effective_instance_cache_key_type;
|
||||
typedef std::map<effective_instance_cache_key_type, std::pair<bool, db::CellInstArray> > effective_instance_cache_type;
|
||||
effective_instance_cache_type effective_instance_cache;
|
||||
|
||||
for (typename std::unordered_map<const db::CellInstArray *, interaction_value_type>::const_iterator i = interactions.begin (); i != interactions.end (); ++i) {
|
||||
|
||||
db::Cell &subject_child_cell = mp_subject_layout->cell (i->first->object ().cell_index ());
|
||||
|
|
@ -1568,17 +1634,28 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
|
|||
db::box_convert <db::CellInst, true> inst_bcii (*mp_intruder_layout, ail);
|
||||
|
||||
for (std::unordered_set<const db::CellInstArray *>::const_iterator j = i->second.first.begin (); j != i->second.first.end (); ++j) {
|
||||
|
||||
for (db::CellInstArray::iterator k = (*j)->begin_touching (safe_box_enlarged (nbox, -1, -1), inst_bcii); ! k.at_end (); ++k) {
|
||||
|
||||
db::ICplxTrans tk = (*j)->complex_trans (*k);
|
||||
// NOTE: no self-interactions
|
||||
if (i->first != *j || tn != tk) {
|
||||
|
||||
// optimize the intruder instance so it will be as low as possible
|
||||
std::pair<bool, db::CellInstArray> ei = effective_instance (contexts.subject_layer (), i->first->object ().cell_index (), ail, (*j)->object ().cell_index (), tni * tk, dist);
|
||||
if (ei.first) {
|
||||
intruders_below.first.insert (ei.second);
|
||||
effective_instance_cache_key_type key (ail, std::make_pair ((*j)->object ().cell_index (), tni * tk));
|
||||
effective_instance_cache_type::iterator cached = effective_instance_cache.find (key);
|
||||
if (cached == effective_instance_cache.end ()) {
|
||||
std::pair<bool, db::CellInstArray> ei = effective_instance (contexts.subject_layer (), i->first->object ().cell_index (), ail, (*j)->object ().cell_index (), tni * tk, dist);
|
||||
cached = effective_instance_cache.insert (std::make_pair (key, ei)).first;
|
||||
}
|
||||
if (cached->second.first) {
|
||||
intruders_below.first.insert (cached->second.second);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -479,6 +479,16 @@ public:
|
|||
return m_area_ratio;
|
||||
}
|
||||
|
||||
void set_boolean_core (double boolean_core)
|
||||
{
|
||||
m_boolean_core = boolean_core;
|
||||
}
|
||||
|
||||
double boolean_core () const
|
||||
{
|
||||
return m_boolean_core;
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename, typename, typename> friend class local_processor_cell_contexts;
|
||||
template<typename, typename, typename> friend class local_processor_context_computation_task;
|
||||
|
|
@ -494,6 +504,7 @@ private:
|
|||
unsigned int m_nthreads;
|
||||
size_t m_max_vertex_count;
|
||||
double m_area_ratio;
|
||||
bool m_boolean_core;
|
||||
int m_base_verbosity;
|
||||
mutable std::unique_ptr<tl::Job<local_processor_context_computation_worker<TS, TI, TR> > > mp_cc_job;
|
||||
mutable size_t m_progress;
|
||||
|
|
|
|||
|
|
@ -800,7 +800,7 @@ TEST(13_Edges)
|
|||
|
||||
db::Region r3 (db::RecursiveShapeIterator (ly, top_cell, l3), dss);
|
||||
db::Edges r3edges = r3.edges ();
|
||||
EXPECT_EQ (r3edges.is_merged (), true);
|
||||
EXPECT_EQ (r3edges.is_merged (), false);
|
||||
|
||||
db::EdgeLengthFilter f (0, 500, true);
|
||||
db::Edges r3edges_filtered = r3.edges (f);
|
||||
|
|
@ -816,6 +816,46 @@ TEST(13_Edges)
|
|||
db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au13.gds");
|
||||
}
|
||||
|
||||
TEST(13b_Edges)
|
||||
{
|
||||
db::Layout ly;
|
||||
{
|
||||
std::string fn (tl::testsrc ());
|
||||
fn += "/testdata/algo/deep_region_edges.gds";
|
||||
tl::InputStream stream (fn);
|
||||
db::Reader reader (stream);
|
||||
reader.read (ly);
|
||||
}
|
||||
|
||||
db::cell_index_type top_cell_index = *ly.begin_top_down ();
|
||||
db::Cell &top_cell = ly.cell (top_cell_index);
|
||||
|
||||
db::DeepShapeStore dss;
|
||||
|
||||
unsigned int l1 = ly.get_layer (db::LayerProperties (1, 0));
|
||||
unsigned int l2 = ly.get_layer (db::LayerProperties (2, 0));
|
||||
|
||||
db::Region r1 (db::RecursiveShapeIterator (ly, top_cell, l1), dss);
|
||||
db::Edges r1edges = r1.edges ();
|
||||
EXPECT_EQ (r1edges.is_merged (), false);
|
||||
|
||||
db::Region r2 (db::RecursiveShapeIterator (ly, top_cell, l2), dss);
|
||||
db::Edges r2edges = r2.edges ();
|
||||
EXPECT_EQ (r2edges.is_merged (), false);
|
||||
|
||||
|
||||
db::Layout target;
|
||||
unsigned int target_top_cell_index = target.add_cell (ly.cell_name (top_cell_index));
|
||||
|
||||
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (1, 0)), r1);
|
||||
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (2, 0)), r2);
|
||||
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (11, 0)), r1edges);
|
||||
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (12, 0)), r2edges);
|
||||
|
||||
CHECKPOINT();
|
||||
db::compare_layouts (_this, target, tl::testsrc () + "/testdata/algo/deep_region_au13b.gds");
|
||||
}
|
||||
|
||||
TEST(14_Interacting)
|
||||
{
|
||||
db::Layout ly;
|
||||
|
|
@ -936,7 +976,7 @@ TEST(14_Interacting)
|
|||
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (32, 0)), r6r.selected_interacting (r1er));
|
||||
target.insert (target_top_cell_index, target.get_layer (db::LayerProperties (33, 0)), r6r.selected_not_interacting (r1er));
|
||||
|
||||
EXPECT_EQ (r6.selected_interacting (r1e).is_merged (), true);
|
||||
EXPECT_EQ (r6.selected_interacting (r1e).is_merged (), false);
|
||||
EXPECT_EQ (r6.selected_interacting (r1er).is_merged (), false);
|
||||
EXPECT_EQ (r6r.selected_interacting (r1e).is_merged (), false);
|
||||
EXPECT_EQ (r6r.selected_interacting (r1er).is_merged (), false);
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue