Fixed caching issue: Sometimes we didn't use existing objects in the CGAL cache, but recalculated the objects, causing reduced performance when reusing CGAL-calculated objects in preview mode

customizer
Marius Kintel 2014-01-26 22:32:20 -05:00
parent 3eb4489245
commit c3562e26ab
4 changed files with 74 additions and 51 deletions

View File

@ -7,6 +7,15 @@
GeometryCache *GeometryCache::inst = NULL; GeometryCache *GeometryCache::inst = NULL;
shared_ptr<const Geometry> GeometryCache::get(const std::string &id) const
{
const shared_ptr<const Geometry> &geom = this->cache[id]->geom;
#ifdef DEBUG
PRINTB("Geometry Cache hit: %s (%d bytes)", id.substr(0, 40) % (geom ? geom->memsize() : 0));
#endif
return geom;
}
bool GeometryCache::insert(const std::string &id, const shared_ptr<const Geometry> &geom) bool GeometryCache::insert(const std::string &id, const shared_ptr<const Geometry> &geom)
{ {
bool inserted = this->cache.insert(id, new cache_entry(geom), geom ? geom->memsize() : 0); bool inserted = this->cache.insert(id, new cache_entry(geom), geom ? geom->memsize() : 0);

View File

@ -13,7 +13,7 @@ public:
static GeometryCache *instance() { if (!inst) inst = new GeometryCache; return inst; } static GeometryCache *instance() { if (!inst) inst = new GeometryCache; return inst; }
bool contains(const std::string &id) const { return this->cache.contains(id); } bool contains(const std::string &id) const { return this->cache.contains(id); }
shared_ptr<const class Geometry> get(const std::string &id) const { return this->cache[id]->geom; } shared_ptr<const class Geometry> get(const std::string &id) const;
bool insert(const std::string &id, const shared_ptr<const Geometry> &geom); bool insert(const std::string &id, const shared_ptr<const Geometry> &geom);
size_t maxSize() const; size_t maxSize() const;
void setMaxSize(size_t limit); void setMaxSize(size_t limit);

View File

@ -34,18 +34,13 @@ GeometryEvaluator::GeometryEvaluator(const class Tree &tree):
{ {
} }
bool GeometryEvaluator::isCached(const AbstractNode &node) const
{
return GeometryCache::instance()->contains(this->tree.getIdString(node));
}
/*! /*!
Set allownef to false to force the result to _not_ be a Nef polyhedron Set allownef to false to force the result to _not_ be a Nef polyhedron
*/ */
shared_ptr<const Geometry> GeometryEvaluator::evaluateGeometry(const AbstractNode &node, shared_ptr<const Geometry> GeometryEvaluator::evaluateGeometry(const AbstractNode &node,
bool allownef) bool allownef)
{ {
if (!isCached(node)) { if (!GeometryCache::instance()->contains(this->tree.getIdString(node))) {
shared_ptr<const CGAL_Nef_polyhedron> N; shared_ptr<const CGAL_Nef_polyhedron> N;
if (CGALCache::instance()->contains(this->tree.getIdString(node))) { if (CGALCache::instance()->contains(this->tree.getIdString(node))) {
N = CGALCache::instance()->get(this->tree.getIdString(node)); N = CGALCache::instance()->get(this->tree.getIdString(node));
@ -63,7 +58,7 @@ shared_ptr<const Geometry> GeometryEvaluator::evaluateGeometry(const AbstractNod
if (!allownef) { if (!allownef) {
if (shared_ptr<const CGAL_Nef_polyhedron> N = dynamic_pointer_cast<const CGAL_Nef_polyhedron>(this->root)) { if (shared_ptr<const CGAL_Nef_polyhedron> N = dynamic_pointer_cast<const CGAL_Nef_polyhedron>(this->root)) {
this->root.reset(N->convertToPolyset()); this->root.reset(N->convertToPolyset());
smartCache(node, this->root); smartCacheInsert(node, this->root);
} }
} }
return this->root; return this->root;
@ -245,7 +240,7 @@ std::vector<const class Polygon2d *> GeometryEvaluator::collectChildren2D(const
// a node is a valid object. If we inserted as we created them, the // a node is a valid object. If we inserted as we created them, the
// cache could have been modified before we reach this point due to a large // cache could have been modified before we reach this point due to a large
// sibling object. // sibling object.
smartCache(*chnode, chgeom); smartCacheInsert(*chnode, chgeom);
if (chgeom) { if (chgeom) {
if (chgeom->getDimension() == 2) { if (chgeom->getDimension() == 2) {
@ -266,24 +261,42 @@ std::vector<const class Polygon2d *> GeometryEvaluator::collectChildren2D(const
the appropriate cache. the appropriate cache.
This method inserts the geometry into the appropriate cache if it's not already cached. This method inserts the geometry into the appropriate cache if it's not already cached.
*/ */
void GeometryEvaluator::smartCache(const AbstractNode &node, void GeometryEvaluator::smartCacheInsert(const AbstractNode &node,
const shared_ptr<const Geometry> &geom) const shared_ptr<const Geometry> &geom)
{ {
const std::string &key = this->tree.getIdString(node);
shared_ptr<const CGAL_Nef_polyhedron> N = dynamic_pointer_cast<const CGAL_Nef_polyhedron>(geom); shared_ptr<const CGAL_Nef_polyhedron> N = dynamic_pointer_cast<const CGAL_Nef_polyhedron>(geom);
if (N) { if (N) {
if (!CGALCache::instance()->contains(this->tree.getIdString(node))) { if (!CGALCache::instance()->contains(key)) CGALCache::instance()->insert(key, N);
CGALCache::instance()->insert(this->tree.getIdString(node), N);
}
} }
else { else {
if (!isCached(node)) { if (!GeometryCache::instance()->contains(key)) {
if (!GeometryCache::instance()->insert(this->tree.getIdString(node), geom)) { if (!GeometryCache::instance()->insert(key, geom)) {
PRINT("WARNING: GeometryEvaluator: Root node didn't fit into cache"); PRINT("WARNING: GeometryEvaluator: Node didn't fit into cache");
} }
} }
} }
} }
bool GeometryEvaluator::isSmartCached(const AbstractNode &node)
{
const std::string &key = this->tree.getIdString(node);
return (GeometryCache::instance()->contains(key) ||
CGALCache::instance()->contains(key));
}
shared_ptr<const Geometry> GeometryEvaluator::smartCacheGet(const AbstractNode &node, bool preferNef)
{
const std::string &key = this->tree.getIdString(node);
shared_ptr<const Geometry> geom;
bool hasgeom = GeometryCache::instance()->contains(key);
bool hascgal = CGALCache::instance()->contains(key);
if (hascgal && (preferNef || !hasgeom)) geom = CGALCache::instance()->get(key);
else if (hasgeom) geom = GeometryCache::instance()->get(key);
return geom;
}
/*! /*!
Returns a list of 3D Geometry children of the given node. Returns a list of 3D Geometry children of the given node.
May return empty geometries, but not NULL objects May return empty geometries, but not NULL objects
@ -301,7 +314,7 @@ Geometry::ChildList GeometryEvaluator::collectChildren3D(const AbstractNode &nod
// a node is a valid object. If we inserted as we created them, the // a node is a valid object. If we inserted as we created them, the
// cache could have been modified before we reach this point due to a large // cache could have been modified before we reach this point due to a large
// sibling object. // sibling object.
smartCache(*chnode, chgeom); smartCacheInsert(*chnode, chgeom);
if (chgeom) { if (chgeom) {
if (chgeom->isEmpty() || chgeom->getDimension() == 3) { if (chgeom->isEmpty() || chgeom->getDimension() == 3) {
@ -368,24 +381,24 @@ void GeometryEvaluator::addToParent(const State &state,
} }
else { else {
// Root node, insert into cache // Root node, insert into cache
smartCache(node, geom); smartCacheInsert(node, geom);
this->root = geom; this->root = geom;
} }
} }
/*! /*!
Custom nodes, as well as RenderNode are handled here => implicit union Custom nodes are handled here => implicit union
*/ */
Response GeometryEvaluator::visit(State &state, const AbstractNode &node) Response GeometryEvaluator::visit(State &state, const AbstractNode &node)
{ {
if (state.isPrefix() && isCached(node)) return PruneTraversal; if (state.isPrefix() && isSmartCached(node)) return PruneTraversal;
if (state.isPostfix()) { if (state.isPostfix()) {
shared_ptr<const class Geometry> geom; shared_ptr<const class Geometry> geom;
if (!isCached(node)) { if (!isSmartCached(node)) {
geom = applyToChildren(node, OPENSCAD_UNION).constptr(); geom = applyToChildren(node, OPENSCAD_UNION).constptr();
} }
else { else {
geom = GeometryCache::instance()->get(this->tree.getIdString(node)); geom = smartCacheGet(node);
} }
addToParent(state, node, geom); addToParent(state, node, geom);
} }
@ -397,10 +410,10 @@ Response GeometryEvaluator::visit(State &state, const AbstractNode &node)
*/ */
Response GeometryEvaluator::visit(State &state, const RenderNode &node) Response GeometryEvaluator::visit(State &state, const RenderNode &node)
{ {
if (state.isPrefix() && isCached(node)) return PruneTraversal; if (state.isPrefix() && isSmartCached(node)) return PruneTraversal;
if (state.isPostfix()) { if (state.isPostfix()) {
shared_ptr<const class Geometry> geom; shared_ptr<const class Geometry> geom;
if (!isCached(node)) { if (!isSmartCached(node)) {
ResultObject res = applyToChildren(node, OPENSCAD_UNION); ResultObject res = applyToChildren(node, OPENSCAD_UNION);
geom = res.constptr(); geom = res.constptr();
@ -422,7 +435,7 @@ Response GeometryEvaluator::visit(State &state, const RenderNode &node)
} }
} }
else { else {
geom = GeometryCache::instance()->get(this->tree.getIdString(node)); geom = smartCacheGet(node);
} }
addToParent(state, node, geom); addToParent(state, node, geom);
} }
@ -439,7 +452,7 @@ Response GeometryEvaluator::visit(State &state, const LeafNode &node)
{ {
if (state.isPrefix()) { if (state.isPrefix()) {
shared_ptr<const Geometry> geom; shared_ptr<const Geometry> geom;
if (!isCached(node)) { if (!isSmartCached(node)) {
const Geometry *geometry = node.createGeometry(); const Geometry *geometry = node.createGeometry();
if (const Polygon2d *polygon = dynamic_cast<const Polygon2d*>(geometry)) { if (const Polygon2d *polygon = dynamic_cast<const Polygon2d*>(geometry)) {
if (!polygon->isSanitized()) { if (!polygon->isSanitized()) {
@ -450,7 +463,7 @@ Response GeometryEvaluator::visit(State &state, const LeafNode &node)
} }
geom.reset(geometry); geom.reset(geometry);
} }
else geom = GeometryCache::instance()->get(this->tree.getIdString(node)); else geom = smartCacheGet(node);
addToParent(state, node, geom); addToParent(state, node, geom);
} }
return PruneTraversal; return PruneTraversal;
@ -464,14 +477,14 @@ Response GeometryEvaluator::visit(State &state, const LeafNode &node)
*/ */
Response GeometryEvaluator::visit(State &state, const CsgNode &node) Response GeometryEvaluator::visit(State &state, const CsgNode &node)
{ {
if (state.isPrefix() && isCached(node)) return PruneTraversal; if (state.isPrefix() && isSmartCached(node)) return PruneTraversal;
if (state.isPostfix()) { if (state.isPostfix()) {
shared_ptr<const Geometry> geom; shared_ptr<const Geometry> geom;
if (!isCached(node)) { if (!isSmartCached(node)) {
geom = applyToChildren(node, node.type).constptr(); geom = applyToChildren(node, node.type).constptr();
} }
else { else {
geom = GeometryCache::instance()->get(this->tree.getIdString(node)); geom = smartCacheGet(node);
} }
addToParent(state, node, geom); addToParent(state, node, geom);
} }
@ -487,10 +500,10 @@ Response GeometryEvaluator::visit(State &state, const CsgNode &node)
*/ */
Response GeometryEvaluator::visit(State &state, const TransformNode &node) Response GeometryEvaluator::visit(State &state, const TransformNode &node)
{ {
if (state.isPrefix() && isCached(node)) return PruneTraversal; if (state.isPrefix() && isSmartCached(node)) return PruneTraversal;
if (state.isPostfix()) { if (state.isPostfix()) {
shared_ptr<const class Geometry> geom; shared_ptr<const class Geometry> geom;
if (!isCached(node)) { if (!isSmartCached(node)) {
if (matrix_contains_infinity(node.matrix) || matrix_contains_nan(node.matrix)) { if (matrix_contains_infinity(node.matrix) || matrix_contains_nan(node.matrix)) {
// due to the way parse/eval works we can't currently distinguish between NaN and Inf // due to the way parse/eval works we can't currently distinguish between NaN and Inf
PRINT("Warning: Transformation matrix contains Not-a-Number and/or Infinity - removing object."); PRINT("Warning: Transformation matrix contains Not-a-Number and/or Infinity - removing object.");
@ -541,7 +554,7 @@ Response GeometryEvaluator::visit(State &state, const TransformNode &node)
} }
} }
else { else {
geom = GeometryCache::instance()->get(this->tree.getIdString(node)); geom = smartCacheGet(node);
} }
addToParent(state, node, geom); addToParent(state, node, geom);
} }
@ -671,10 +684,10 @@ static Geometry *extrudePolygon(const LinearExtrudeNode &node, const Polygon2d &
*/ */
Response GeometryEvaluator::visit(State &state, const LinearExtrudeNode &node) Response GeometryEvaluator::visit(State &state, const LinearExtrudeNode &node)
{ {
if (state.isPrefix() && isCached(node)) return PruneTraversal; if (state.isPrefix() && isSmartCached(node)) return PruneTraversal;
if (state.isPostfix()) { if (state.isPostfix()) {
shared_ptr<const Geometry> geom; shared_ptr<const Geometry> geom;
if (!isCached(node)) { if (!isSmartCached(node)) {
const Geometry *geometry = NULL; const Geometry *geometry = NULL;
if (!node.filename.empty()) { if (!node.filename.empty()) {
DxfData dxf(node.fn, node.fs, node.fa, node.filename, node.layername, node.origin_x, node.origin_y, node.scale_x); DxfData dxf(node.fn, node.fs, node.fa, node.filename, node.layername, node.origin_x, node.origin_y, node.scale_x);
@ -694,7 +707,7 @@ Response GeometryEvaluator::visit(State &state, const LinearExtrudeNode &node)
} }
} }
else { else {
geom = GeometryCache::instance()->get(this->tree.getIdString(node)); geom = smartCacheGet(node);
} }
addToParent(state, node, geom); addToParent(state, node, geom);
} }
@ -781,10 +794,10 @@ static Geometry *rotatePolygon(const RotateExtrudeNode &node, const Polygon2d &p
*/ */
Response GeometryEvaluator::visit(State &state, const RotateExtrudeNode &node) Response GeometryEvaluator::visit(State &state, const RotateExtrudeNode &node)
{ {
if (state.isPrefix() && isCached(node)) return PruneTraversal; if (state.isPrefix() && isSmartCached(node)) return PruneTraversal;
if (state.isPostfix()) { if (state.isPostfix()) {
shared_ptr<const Geometry> geom; shared_ptr<const Geometry> geom;
if (!isCached(node)) { if (!isSmartCached(node)) {
const Geometry *geometry = NULL; const Geometry *geometry = NULL;
if (!node.filename.empty()) { if (!node.filename.empty()) {
DxfData dxf(node.fn, node.fs, node.fa, node.filename, node.layername, node.origin_x, node.origin_y, node.scale); DxfData dxf(node.fn, node.fs, node.fa, node.filename, node.layername, node.origin_x, node.origin_y, node.scale);
@ -802,7 +815,7 @@ Response GeometryEvaluator::visit(State &state, const RotateExtrudeNode &node)
} }
} }
else { else {
geom = GeometryCache::instance()->get(this->tree.getIdString(node)); geom = smartCacheGet(node);
} }
addToParent(state, node, geom); addToParent(state, node, geom);
} }
@ -827,10 +840,10 @@ Response GeometryEvaluator::visit(State &state, const AbstractPolyNode &node)
*/ */
Response GeometryEvaluator::visit(State &state, const ProjectionNode &node) Response GeometryEvaluator::visit(State &state, const ProjectionNode &node)
{ {
if (state.isPrefix() && isCached(node)) return PruneTraversal; if (state.isPrefix() && isSmartCached(node)) return PruneTraversal;
if (state.isPostfix()) { if (state.isPostfix()) {
shared_ptr<const class Geometry> geom; shared_ptr<const class Geometry> geom;
if (!isCached(node)) { if (!isSmartCached(node)) {
if (!node.cut_mode) { if (!node.cut_mode) {
ClipperLib::Clipper sumclipper; ClipperLib::Clipper sumclipper;
@ -912,7 +925,7 @@ Response GeometryEvaluator::visit(State &state, const ProjectionNode &node)
} }
} }
else { else {
geom = GeometryCache::instance()->get(this->tree.getIdString(node)); geom = smartCacheGet(node);
} }
addToParent(state, node, geom); addToParent(state, node, geom);
} }
@ -927,10 +940,10 @@ Response GeometryEvaluator::visit(State &state, const ProjectionNode &node)
*/ */
Response GeometryEvaluator::visit(State &state, const CgaladvNode &node) Response GeometryEvaluator::visit(State &state, const CgaladvNode &node)
{ {
if (state.isPrefix() && isCached(node)) return PruneTraversal; if (state.isPrefix() && isSmartCached(node)) return PruneTraversal;
if (state.isPostfix()) { if (state.isPostfix()) {
shared_ptr<const Geometry> geom; shared_ptr<const Geometry> geom;
if (!isCached(node)) { if (!isSmartCached(node)) {
switch (node.type) { switch (node.type) {
case MINKOWSKI: { case MINKOWSKI: {
geom = applyToChildren(node, OPENSCAD_MINKOWSKI).constptr(); geom = applyToChildren(node, OPENSCAD_MINKOWSKI).constptr();
@ -987,7 +1000,7 @@ Response GeometryEvaluator::visit(State &state, const CgaladvNode &node)
} }
} }
else { else {
geom = GeometryCache::instance()->get(this->tree.getIdString(node)); geom = smartCacheGet(node);
} }
addToParent(state, node, geom); addToParent(state, node, geom);
} }
@ -996,14 +1009,14 @@ Response GeometryEvaluator::visit(State &state, const CgaladvNode &node)
Response GeometryEvaluator::visit(State &state, const AbstractIntersectionNode &node) Response GeometryEvaluator::visit(State &state, const AbstractIntersectionNode &node)
{ {
if (state.isPrefix() && isCached(node)) return PruneTraversal; if (state.isPrefix() && isSmartCached(node)) return PruneTraversal;
if (state.isPostfix()) { if (state.isPostfix()) {
shared_ptr<const class Geometry> geom; shared_ptr<const class Geometry> geom;
if (!isCached(node)) { if (!isSmartCached(node)) {
geom = applyToChildren(node, OPENSCAD_INTERSECTION).constptr(); geom = applyToChildren(node, OPENSCAD_INTERSECTION).constptr();
} }
else { else {
geom = GeometryCache::instance()->get(this->tree.getIdString(node)); geom = smartCacheGet(node);
} }
addToParent(state, node, geom); addToParent(state, node, geom);
} }

View File

@ -52,8 +52,9 @@ private:
shared_ptr<const Geometry> const_pointer; shared_ptr<const Geometry> const_pointer;
}; };
bool isCached(const AbstractNode &node) const; void smartCacheInsert(const AbstractNode &node, const shared_ptr<const Geometry> &geom);
void smartCache(const AbstractNode &node, const shared_ptr<const Geometry> &geom); shared_ptr<const Geometry> smartCacheGet(const AbstractNode &node, bool preferNef = false);
bool isSmartCached(const AbstractNode &node);
std::vector<const class Polygon2d *> collectChildren2D(const AbstractNode &node); std::vector<const class Polygon2d *> collectChildren2D(const AbstractNode &node);
Geometry::ChildList collectChildren3D(const AbstractNode &node); Geometry::ChildList collectChildren3D(const AbstractNode &node);
Polygon2d *applyMinkowski2D(const AbstractNode &node); Polygon2d *applyMinkowski2D(const AbstractNode &node);