]> git.mxchange.org Git - simgear.git/commitdiff
optimize creation and sharing of effects
authorTim Moore <timoore@redhat.com>
Fri, 13 Nov 2009 13:59:29 +0000 (14:59 +0100)
committerTim Moore <timoore@redhat.com>
Fri, 13 Nov 2009 21:41:11 +0000 (22:41 +0100)
Implement equality test and hash for for property trees.  In an
Effect, make a hash table of Effects that inherit from it keyed on
their unmerged property tree. Using that, makeEffect() should return a
single Effect for given property tree description. Animations may
change that in the future...

simgear/props/props.cxx
simgear/props/props.hxx
simgear/scene/material/Effect.cxx
simgear/scene/material/Effect.hxx
simgear/scene/material/makeEffect.cxx

index 6730ef132876cf120b86d57d02e569d3724a9069..c662eb7f95f808ab54af4f33b4453c08c015063e 100644 (file)
 
 #include <sstream>
 #include <iomanip>
+#include <iterator>
 #include <stdio.h>
 #include <string.h>
 
 #include <boost/algorithm/string/find_iterator.hpp>
 #include <boost/algorithm/string/predicate.hpp>
 #include <boost/algorithm/string/classification.hpp>
+#include <boost/bind.hpp>
+#include <boost/functional/hash.hpp>
 #include <boost/range.hpp>
 
 #include <simgear/math/SGMath.hxx>
@@ -2379,6 +2382,146 @@ std::istream& readFrom<SGVec4d>(std::istream& stream, SGVec4d& result)
     }
     return stream;
 }
+
+namespace
+{
+bool compareNodeValue(const SGPropertyNode& lhs, const SGPropertyNode& rhs)
+{
+    props::Type ltype = lhs.getType();
+    props::Type rtype = rhs.getType();
+    if (ltype != rtype)
+        return false;
+    switch (ltype) {
+    case props::NONE:
+        return true;
+    case props::ALIAS:
+        return false;           // XXX Should we look in aliases?
+    case props::BOOL:
+        return lhs.getValue<bool>() == rhs.getValue<bool>();
+    case props::INT:
+        return lhs.getValue<int>() == rhs.getValue<int>();
+    case props::LONG:
+        return lhs.getValue<long>() == rhs.getValue<long>();
+    case props::FLOAT:
+        return lhs.getValue<float>() == rhs.getValue<float>();
+    case props::DOUBLE:
+        return lhs.getValue<double>() == rhs.getValue<double>();
+    case props::STRING:
+    case props::UNSPECIFIED:
+        return !strcmp(lhs.getStringValue(), rhs.getStringValue());
+    case props::VEC3D:
+        return lhs.getValue<SGVec3d>() == rhs.getValue<SGVec3d>();
+    case props::VEC4D:
+        return lhs.getValue<SGVec4d>() == rhs.getValue<SGVec4d>();
+    default:
+        return false;
+    }
+}
+}
+}
+
+bool SGPropertyNode::compare(const SGPropertyNode& lhs,
+                             const SGPropertyNode& rhs)
+{
+    if (&lhs == &rhs)
+        return true;
+    int lhsChildren = lhs.nChildren();
+    int rhsChildren = rhs.nChildren();
+    if (lhsChildren != rhsChildren)
+        return false;
+    if (lhsChildren == 0)
+        return compareNodeValue(lhs, rhs);
+    for (size_t i = 0; i < lhs._children.size(); ++i) {
+        const SGPropertyNode* lchild = lhs._children[i];
+        const SGPropertyNode* rchild = rhs._children[i];
+        // I'm guessing that the nodes will usually be in the same
+        // order.
+        if (lchild->getIndex() != rchild->getIndex()
+            || lchild->getNameString() != rchild->getNameString()) {
+            rchild = 0;
+            for (PropertyList::const_iterator itr = rhs._children.begin(),
+                     end = rhs._children.end();
+                 itr != end;
+                ++itr)
+                if (lchild->getIndex() == (*itr)->getIndex()
+                    && lchild->getNameString() == (*itr)->getNameString()) {
+                    rchild = *itr;
+                    break;
+                }
+            if (!rchild)
+                return false;
+        }
+        if (!compare(*lchild, *rchild))
+            return false;
+    }
+    return true;
+}
+
+struct PropertyPlaceLess {
+    typedef bool result_type;
+    bool operator()(SGPropertyNode_ptr lhs, SGPropertyNode_ptr rhs) const
+    {
+        int comp = lhs->getNameString().compare(rhs->getNameString());
+        if (comp == 0)
+            return lhs->getIndex() < rhs->getIndex();
+        else
+            return comp < 0;
+    }
+};
+
+size_t hash_value(const SGPropertyNode& node)
+{
+    using namespace boost;
+
+    if (node.nChildren() == 0) {
+        switch (node.getType()) {
+        case props::NONE:
+            return 0;
+
+        case props::BOOL:
+            return hash_value(node.getValue<bool>());
+        case props::INT:
+            return hash_value(node.getValue<int>());
+        case props::LONG:
+            return hash_value(node.getValue<long>());
+        case props::FLOAT:
+            return hash_value(node.getValue<float>());
+        case props::DOUBLE:
+            return hash_value(node.getValue<double>());
+        case props::STRING:
+        case props::UNSPECIFIED:
+        {
+            const char *val = node.getStringValue();
+            return hash_range(val, val + strlen(val));
+        }
+        case props::VEC3D:
+        {
+            const SGVec3d val = node.getValue<SGVec3d>();
+            return hash_range(&val[0], &val[3]);
+        }
+        case props::VEC4D:
+        {
+            const SGVec4d val = node.getValue<SGVec4d>();
+            return hash_range(&val[0], &val[4]);
+        }
+        case props::ALIAS:      // XXX Should we look in aliases?
+        default:
+            return 0;
+        }
+    } else {
+        size_t seed = 0;
+        PropertyList children(node._children.begin(), node._children.end());
+        sort(children.begin(), children.end(), PropertyPlaceLess());
+        for (PropertyList::const_iterator itr  = children.begin(),
+                 end = children.end();
+             itr != end;
+             ++itr) {
+            hash_combine(seed, (*itr)->_name);
+            hash_combine(seed, (*itr)->_index);
+            hash_combine(seed, hash_value(**itr));
+        }
+        return seed;
+    }
 }
 
 // end of props.cxx
index e32d9a64d0811fcb5837220cb073b6c5f1203737..984bb1b5717b37657b03ff9db88714910a39ca4c 100644 (file)
@@ -1599,6 +1599,16 @@ public:
    */
   void clearValue ();
 
+  /**
+   * Compare two property trees. The property trees are equal if: 1)
+   * They have no children, and have the same type and the values are
+   * equal, or 2) have the same number of children, and the
+   * corresponding children in each tree are equal. "corresponding"
+   * means have the same name and index.
+   *
+   * Attributes, removed children, and aliases aren't considered.
+   */
+  static bool compare (const SGPropertyNode& lhs, const SGPropertyNode& rhs);
 
 protected:
 
@@ -1615,7 +1625,7 @@ protected:
 
 private:
 
-                               // Get the raw value
+  // Get the raw value
   bool get_bool () const;
   int get_int () const;
   long get_long () const;
@@ -1623,7 +1633,7 @@ private:
   double get_double () const;
   const char * get_string () const;
 
-                               // Set the raw value
+  // Set the raw value
   bool set_bool (bool value);
   bool set_int (int value);
   bool set_long (long value);
@@ -1672,7 +1682,7 @@ private:
   bool _tied;
   int _attr;
 
-                               // The right kind of pointer...
+  // The right kind of pointer...
   union {
     SGPropertyNode * alias;
     SGRaw* val;
@@ -1691,8 +1701,8 @@ private:
 
 
   /**
-    * Register/unregister node that links to this node in its path cache.
-    */
+   * Register/unregister node that links to this node in its path cache.
+   */
   void add_linked_node (hash_table * node) { _linkedNodes.push_back(node); }
   bool remove_linked_node (hash_table * node);
 
@@ -1759,6 +1769,8 @@ private:
   template<typename SplitItr>
   friend SGPropertyNode* find_node_aux(SGPropertyNode * current, SplitItr& itr,
                                        bool create, int last_index);
+  // For boost
+  friend size_t hash_value(const SGPropertyNode& node);
 };
 
 // Convenience functions for use in templates
@@ -1947,6 +1959,49 @@ inline SGPropertyNode* makeNode(SGPropertyNode* parent, const StringType& name)
     return parent->getNode(name, true);
 }
 }
+
+// For boost::hash
+size_t hash_value(const SGPropertyNode& node);
+
+// Helper comparison and hash functions for common cases
+
+namespace simgear
+{
+namespace props
+{
+struct Compare
+{
+    bool operator()(const SGPropertyNode* lhs, const SGPropertyNode* rhs) const
+    {
+        return SGPropertyNode::compare(*lhs, *rhs);
+    }
+    bool operator()(SGPropertyNode_ptr lhs, const SGPropertyNode* rhs) const
+    {
+        return SGPropertyNode::compare(*lhs, *rhs);
+    }
+    bool operator()(const SGPropertyNode* lhs, SGPropertyNode_ptr rhs) const
+    {
+        return SGPropertyNode::compare(*lhs, *rhs);
+    }
+    bool operator()(SGPropertyNode_ptr lhs, SGPropertyNode_ptr rhs) const
+    {
+        return SGPropertyNode::compare(*lhs, *rhs);
+    }
+};
+
+struct Hash
+{
+    size_t operator()(const SGPropertyNode* node) const
+    {
+        return hash_value(*node);
+    }
+    size_t operator()(SGPropertyNode_ptr node) const
+    {
+        return hash_value(*node);
+    }
+};
+}
+}
 #endif // __PROPS_HXX
 
 // end of props.hxx
index d75a1ac4bb14365e7755ee496e3101a8c910b14a..1cff2323f42ff86f034544bf69a05c62ee6970c8 100644 (file)
@@ -78,11 +78,13 @@ using namespace osgUtil;
 using namespace effect;
 
 Effect::Effect()
+    : _cache(0), _isRealized(false)
 {
 }
 
 Effect::Effect(const Effect& rhs, const CopyOp& copyop)
-    : root(rhs.root), parametersProp(rhs.parametersProp)
+    : root(rhs.root), parametersProp(rhs.parametersProp), _cache(0),
+      _isRealized(rhs._isRealized)
 {
     typedef vector<ref_ptr<Technique> > TechniqueList;
     for (TechniqueList::const_iterator itr = rhs.techniques.begin(),
@@ -132,6 +134,7 @@ void Effect::releaseGLObjects(osg::State* state) const
 
 Effect::~Effect()
 {
+    delete _cache;
 }
 
 void buildPass(Effect* effect, Technique* tniq, const SGPropertyNode* prop,
@@ -867,11 +870,14 @@ bool makeParametersFromStateSet(SGPropertyNode* effectRoot, const StateSet* ss)
 // passes.
 bool Effect::realizeTechniques(const osgDB::ReaderWriter::Options* options)
 {
+    if (_isRealized)
+        return true;
     PropertyList tniqList = root->getChildren("technique");
     for (PropertyList::iterator itr = tniqList.begin(), e = tniqList.end();
          itr != e;
          ++itr)
         buildTechnique(this, *itr, options);
+    _isRealized = true;
     return true;
 }
 
@@ -895,6 +901,23 @@ void Effect::InitializeCallback::doUpdate(osg::Node* node, osg::NodeVisitor* nv)
     }
 }
 
+bool Effect::Key::EqualTo::operator()(const Effect::Key& lhs,
+                                      const Effect::Key& rhs) const
+{
+    if (lhs.paths.size() != rhs.paths.size()
+        || !equal(lhs.paths.begin(), lhs.paths.end(), rhs.paths.begin()))
+        return false;
+    return props::Compare()(lhs.unmerged, rhs.unmerged);
+}
+
+size_t hash_value(const Effect::Key& key)
+{
+    size_t seed = 0;
+    boost::hash_combine(seed, *key.unmerged);
+    boost::hash_range(seed, key.paths.begin(), key.paths.end());
+    return seed;
+}
+
 bool Effect_writeLocalData(const Object& obj, osgDB::Output& fw)
 {
     const Effect& effect = static_cast<const Effect&>(obj);
index c3436c432f6eb2453b377206f17556ed4cf143e8..1523dc25f012d6a30d3ad3f5d9a5a4a5c2267729 100644 (file)
@@ -20,6 +20,8 @@
 #include <vector>
 #include <string>
 
+#include <boost/unordered_map.hpp>
+
 #include <osg/Object>
 #include <osgDB/ReaderWriter>
 
@@ -104,11 +106,43 @@ public:
     {
         void doUpdate(osg::Node* node, osg::NodeVisitor* nv);
     };
-    
 protected:
     std::vector<SGSharedPtr<Updater> > _extraData;
     ~Effect();
+    // Support for a cache of effects that inherit from this one, so
+    // Effect objects with the same parameters and techniques can be
+    // shared.
+    struct Key
+    {
+        Key(SGPropertyNode* unmerged_, const osgDB::FilePathList& paths_)
+            : unmerged(unmerged_), paths(paths_)
+        {
+        }
+        const SGPropertyNode_ptr unmerged;
+        const osgDB::FilePathList paths;
+        struct EqualTo
+        {
+            bool operator()(const Key& lhs, const Key& rhs) const;
+        };
+    };
+    typedef boost::unordered_map<Key, osg::ref_ptr<Effect>, boost::hash<Key>,
+                                 Key::EqualTo> Cache;
+    Cache* getCache()
+    {
+        if (!_cache)
+            _cache = new Cache;
+        return _cache;
+    }
+    Cache* _cache;
+    friend size_t hash_value(const Key& key);
+    friend Effect* makeEffect(SGPropertyNode* prop, bool realizeTechniques,
+                              const osgDB::ReaderWriter::Options* options);
+    bool _isRealized;
 };
+// Automatic support for boost hash function
+size_t hash_value(const Effect::Key&);
+
+
 Effect* makeEffect(const std::string& name,
                    bool realizeTechniques,
                    const osgDB::ReaderWriter::Options* options = 0);
index 59d3b2d9b4c5e3e2f5c2943cc5953c821383287c..d04173b5f5ec8e1d04ec927b20de8de1268ea657 100644 (file)
@@ -165,23 +165,32 @@ Effect* makeEffect(SGPropertyNode* prop,
             }
         }
     }
-    Effect* effect = new Effect;
+    Effect* effect = 0;
     // Merge with the parent effect, if any
-    const SGPropertyNode* inheritProp = prop->getChild("inherits-from");
+    SGPropertyNode_ptr inheritProp = prop->getChild("inherits-from");
     Effect* parent = 0;
     if (inheritProp) {
-        parent = makeEffect(inheritProp->getStringValue(), false,
-                            options);
-        if(parent)
-        {
-            effect->root = new SGPropertyNode;
-            mergePropertyTrees(effect->root, prop, parent->root);
-            effect->root->removeChild("inherits-from");
+        //prop->removeChild("inherits-from");
+        parent = makeEffect(inheritProp->getStringValue(), false, options);
+        if (parent) {
+            Effect::Cache* cache = parent->getCache();
+            Effect::Key key(prop, options->getDatabasePathList());
+            Effect::Cache::iterator itr = cache->find(key);
+            if (itr != cache->end()) {
+                effect = itr->second.get();
+            } else {
+                effect = new Effect;
+                effect->root = new SGPropertyNode;
+                mergePropertyTrees(effect->root, prop, parent->root);
+                cache->insert(make_pair(key, effect));
+            }
         } else {
-            effect->root = prop;
-            effect->root->removeChild("inherits-from");
+            SG_LOG(SG_INPUT, SG_WARN, "can't find base effect " <<
+                   inheritProp->getStringValue());
         }
-    } else {
+    }
+    if (!effect) {
+        effect = new Effect;
         effect->root = prop;
     }
     effect->parametersProp = effect->root->getChild("parameters");