David Rose 25 лет назад
Родитель
Сommit
79377c587e

+ 1 - 1
panda/src/cull/cullLevelState.h

@@ -21,7 +21,7 @@
 class EXPCL_PANDA CullLevelState {
 public:
   CullStateLookup *_lookup;
-  UpdateSeq _now;
+  UpdateSeq _as_of;
 };
 
 #endif

+ 7 - 4
panda/src/cull/cullState.cxx

@@ -20,21 +20,24 @@
 ////////////////////////////////////////////////////////////////////
 bool CullState::
 check_currency(Node *node, const AllTransitionsWrapper &,
-	       UpdateSeq now) {
+	       UpdateSeq as_of) {
   // First, check the verified time stamp.
   Verified::iterator vi;
   vi = _verified.find(node);
-  nassertr(vi != _verified.end(), false);
+  if (vi == _verified.end()) {
+    // We have never seen this node before.
+    return false;
+  }
 
   UpdateSeq verified_stamp = (*vi).second;
 
   if (cull_cat.is_spam()) {
     cull_cat.spam()
       << "Checking currency for " << *node << ", verified_stamp = "
-      << verified_stamp << " now = " << now << "\n";
+      << verified_stamp << " as_of = " << as_of << "\n";
   }
 
-  if (verified_stamp == now && !verified_stamp.is_fresh()) {
+  if (as_of <= verified_stamp && !verified_stamp.is_fresh()) {
     return true;
   }
 

+ 1 - 1
panda/src/cull/cullState.h

@@ -47,7 +47,7 @@ public:
 
   bool check_currency(Node *node, 
 		      const AllTransitionsWrapper &trans,
-		      UpdateSeq now);
+		      UpdateSeq as_of);
 
   INLINE void mark_verified(Node *node, UpdateSeq now);
 

+ 1 - 1
panda/src/cull/cullStateSubtree.I

@@ -35,7 +35,7 @@ update(const AllTransitionsWrapper &trans, Node *top_subtree, UpdateSeq now) {
   // blow out the cache for all of our children.  We do it for now
   // just to cut down on things that can go wrong, but this should
   // come out of here before long.
-  clear();
+  //  clear();
 
   _trans = trans;
   _parent->compose_trans(_trans, _trans_from_root);

+ 3 - 3
panda/src/cull/cullStateSubtree.cxx

@@ -25,11 +25,11 @@ CullStateSubtree::
 ////////////////////////////////////////////////////////////////////
 bool CullStateSubtree::
 check_currency(const AllTransitionsWrapper &, Node *top_subtree, 
-	       UpdateSeq now) {
+	       UpdateSeq as_of) {
   if (cull_cat.is_spam()) {
     cull_cat.spam()
       << "Checking currency for subtree " << (void *)this
-      << ", _verified = " << _verified << " now = " << now << "\n";
+      << ", _verified = " << _verified << " as_of = " << as_of << "\n";
   }
 
   // Make sure we've still got the same top_subtree node.
@@ -38,7 +38,7 @@ check_currency(const AllTransitionsWrapper &, Node *top_subtree,
   }
 
   // First, check the verified time stamp.
-  if (_verified == now && !_verified.is_fresh()) {
+  if (as_of <= _verified && !_verified.is_fresh()) {
     return true;
   }
 

+ 1 - 1
panda/src/cull/cullStateSubtree.h

@@ -32,7 +32,7 @@ public:
 
   bool check_currency(const AllTransitionsWrapper &trans, 
 		      Node *top_subtree,
-		      UpdateSeq now);
+		      UpdateSeq as_of);
   INLINE void update(const AllTransitionsWrapper &trans,
 		     Node *top_subtree,
 		     UpdateSeq now);

+ 1 - 1
panda/src/cull/cullTraverser.I

@@ -107,7 +107,7 @@ INLINE CullStateLookup *CullTraverser::
 add_instance(NodeRelation *arc, const AllTransitionsWrapper &trans,
 	     Node *top_subtree, const CullLevelState &level_state) {
   return level_state._lookup->get_subtree
-    (arc, trans, top_subtree, level_state._now);
+    (arc, trans, top_subtree, level_state._as_of);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 15 - 12
panda/src/cull/cullTraverser.cxx

@@ -91,11 +91,12 @@ traverse(Node *root,
   bool is_initial = (_nested_count == 0);
   if (is_initial) {
     if (cull_force_update) {
-      _now = UpdateSeq::fresh();
+      _as_of = UpdateSeq::fresh();
     } else {
-      _now = UpdateSeq::initial();
+      _as_of = UpdateSeq::initial();
     }
   }
+  _now = last_graph_update[_graph_type];
   _nested_count++;
 
   if (is_initial) {
@@ -109,7 +110,7 @@ traverse(Node *root,
 
   CullLevelState level_state;
   level_state._lookup = &_lookup;
-  level_state._now = _now;
+  level_state._as_of = _as_of;
 
   // Determine the relative transform matrix from the camera to our
   // starting node.  This is important for proper view-frustum
@@ -304,7 +305,7 @@ add_geom_node(GeomNode *node, const AllTransitionsWrapper &trans,
   complete_trans.clear_transition(DirectRenderTransition::get_class_type());
 
   CullState *cs = level_state._lookup->find_node
-    (node, complete_trans, level_state._now);
+    (node, complete_trans, level_state._as_of);
   if (cs == (CullState *)NULL) {
     if (cull_cat.is_spam()) {
       cull_cat.spam()
@@ -316,7 +317,7 @@ add_geom_node(GeomNode *node, const AllTransitionsWrapper &trans,
     cs = find_bin_state(complete_trans);
     nassertv(cs != (CullState *)NULL);
 
-    level_state._lookup->record_node(node, cs, level_state._now);
+    level_state._lookup->record_node(node, cs, level_state._as_of);
   }
 
   cs->record_current_geom_node(arc_chain);
@@ -347,14 +348,14 @@ add_direct_node(Node *node, const AllTransitionsWrapper &trans,
   complete_trans.clear_transition(DirectRenderTransition::get_class_type());
 
   CullState *cs = level_state._lookup->find_node
-    (node, complete_trans, level_state._now);
+    (node, complete_trans, level_state._as_of);
   if (cs == (CullState *)NULL) {
     // The node didn't have a previously-associated CullState that we
     // could use, so determine a new one for it.
     cs = find_bin_state(complete_trans);
     nassertv(cs != (CullState *)NULL);
 
-    level_state._lookup->record_node(node, cs, level_state._now);
+    level_state._lookup->record_node(node, cs, level_state._as_of);
   }
 
   cs->record_current_direct_node(arc_chain);
@@ -385,8 +386,8 @@ forward_arc(NodeRelation *arc, NullTransitionWrapper &,
   AllTransitionsWrapper trans;
 
   UpdateSeq last_update = arc->get_last_update();
-  if (level_state._now < last_update) {
-    level_state._now = last_update;
+  if (level_state._as_of < last_update) {
+    level_state._as_of = last_update;
   }
 
   bool is_instanced = (node->get_num_parents(_graph_type) > 1);
@@ -435,16 +436,17 @@ forward_arc(NodeRelation *arc, NullTransitionWrapper &,
 #endif
 
   if (arc_has_sub_render) {
-    level_state._now = UpdateSeq::fresh();
+    level_state._as_of = UpdateSeq::fresh();
   }
-  _now = level_state._now;
+  _as_of = level_state._as_of;
 
   mark_forward_arc(arc);
 
   if (cull_cat.is_spam()) {
     cull_cat.spam() 
       << "Reached " << *node << ":\n"
-      << " now = " << level_state._now
+      << " as_of = " << level_state._as_of
+      << " now = " << _now
       << " is_instanced = " << is_instanced
       << " is_geom = " << is_geom
       << " node_has_sub_render = " << node_has_sub_render
@@ -458,6 +460,7 @@ forward_arc(NodeRelation *arc, NullTransitionWrapper &,
     // In any of these cases, we'll need to determine the net
     // transition to this node.
     wrt_subtree(arc, level_state._lookup->get_top_subtree(), 
+		level_state._as_of, _now,
 		trans, _graph_type);
   }
 

+ 1 - 0
panda/src/cull/cullTraverser.h

@@ -103,6 +103,7 @@ private:
 
   CullStateLookup _lookup;
   int _nested_count;
+  UpdateSeq _as_of;
   UpdateSeq _now;
 
 public:

+ 74 - 7
panda/src/graph/allTransitionsWrapper.I

@@ -21,7 +21,8 @@ AllTransitionsWrapper() {
 ////////////////////////////////////////////////////////////////////
 INLINE AllTransitionsWrapper::
 AllTransitionsWrapper(const AllTransitionsWrapper &copy) :
-  _cache(copy._cache)
+  _cache(copy._cache),
+  _all_verified(copy._all_verified)
 {
 }
 
@@ -33,6 +34,7 @@ AllTransitionsWrapper(const AllTransitionsWrapper &copy) :
 INLINE void AllTransitionsWrapper::
 operator = (const AllTransitionsWrapper &copy) {
   _cache = copy._cache;
+  _all_verified = copy._all_verified;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -102,11 +104,12 @@ set_transition(TypeHandle handle, NodeTransition *trans) {
   if (_cache == (NodeTransitionCache *)NULL) {
     _cache = new NodeTransitionCache;
   }
+  _all_verified.clear();
   return _cache->set_transition(handle, trans);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: AllTransitionsWrapperset_transition
+//     Function: AllTransitionsWrapper::set_transition
 //       Access: Public
 //  Description: This flavor of set_transition() accepts a pointer to
 //               a NodeTransition only.  It infers the type of the
@@ -137,6 +140,7 @@ clear_transition(TypeHandle handle) {
   if (_cache == (NodeTransitionCache *)NULL) {
     return NULL;
   }
+  _all_verified.clear();
   return _cache->clear_transition(handle);
 }
 
@@ -245,6 +249,7 @@ extract_from(const NodeRelation *arc) {
   } else {
     _cache = new NodeTransitionCache(arc->_transitions);
   }
+  _all_verified.clear();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -267,6 +272,7 @@ store_to(NodeRelation *arc) const {
 INLINE void AllTransitionsWrapper::
 compose_in_place(const AllTransitionsWrapper &other) {
   _cache = NodeTransitionCache::compose(_cache, other._cache);
+  _all_verified.clear();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -277,6 +283,7 @@ compose_in_place(const AllTransitionsWrapper &other) {
 INLINE void AllTransitionsWrapper::
 invert_in_place() {
   _cache = NodeTransitionCache::invert(_cache);
+  _all_verified.clear();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -288,6 +295,7 @@ invert_in_place() {
 INLINE void AllTransitionsWrapper::
 invert_compose_in_place(const AllTransitionsWrapper &other) {
   _cache = NodeTransitionCache::invert_compose(_cache, other._cache);
+  _all_verified.clear();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -300,19 +308,63 @@ invert_compose_in_place(const AllTransitionsWrapper &other) {
 INLINE Node *AllTransitionsWrapper::
 extract_from_cache(const NodeRelation *arc) {
   _cache = arc->_net_transitions;
+  _all_verified = arc->_all_verified;
+#ifndef NDEBUG
+  if (wrt_cat.is_spam()) {
+    wrt_cat.spam()
+      << "AllTransitionsWrapper::extract_from_cache(" << *arc
+      << "), _all_verified = " << _all_verified << "\n";
+  }
+#endif
   return arc->_top_subtree;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: AllTransitionsWrapper::store_to_cache
 //       Access: Public
-//  Description:
+//  Description: Completely replaces the cached state on the arc with
+//               what is represented here.  This makes sense if we
+//               have computed the complete set of net transitions to
+//               the arc.
 ////////////////////////////////////////////////////////////////////
 INLINE void AllTransitionsWrapper::
 store_to_cache(NodeRelation *arc, Node *top_subtree) {
+  arc->_top_subtree = top_subtree;
+  arc->_net_transitions = _cache;
+  arc->_all_verified = _all_verified;
+
+#ifndef NDEBUG
+  if (wrt_cat.is_spam()) {
+    wrt_cat.spam()
+      << "AllTransitionsWrapper::store_to_cache(" << *arc
+      << "), _all_verified = " << _all_verified << "\n";
+  }
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AllTransitionsWrapper::store_to_cache_partial
+//       Access: Public
+//  Description: Updates the cached state on the arc with what is
+//               represented here, but does not remove transitions on
+//               the arc's cache that are not mentioned here.  This
+//               makes sense if this cache represents only some of the
+//               net transitions to the arc, but not necessarily all
+//               of them.
+////////////////////////////////////////////////////////////////////
+INLINE void AllTransitionsWrapper::
+store_to_cache_partial(NodeRelation *arc, Node *top_subtree) {
   arc->_top_subtree = top_subtree;
   arc->_net_transitions = 
     NodeTransitionCache::c_union(arc->_net_transitions, _cache);
+
+#ifndef NDEBUG
+  if (wrt_cat.is_spam()) {
+    wrt_cat.spam()
+      << "AllTransitionsWrapper::store_to_cache_partial(" << *arc
+      << "), _all_verified = " << _all_verified << "\n";
+  }
+#endif
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -321,10 +373,16 @@ store_to_cache(NodeRelation *arc, Node *top_subtree) {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE bool AllTransitionsWrapper::
-is_cache_verified(UpdateSeq) const {
-  // We can't ever know if all elements in the set are verified,
-  // because the set might not be complete.
-  return false;
+is_cache_verified(UpdateSeq as_of) const {
+#ifndef NDEBUG
+  if (wrt_cat.is_spam()) {
+    wrt_cat.spam()
+      << "AllTransitionsWrapper::is_cache_verified(" << as_of 
+      << "), _all_verified = " << _all_verified << ", result = "
+      << (as_of <= _all_verified) << "\n";
+  }
+#endif
+  return as_of <= _all_verified;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -335,6 +393,14 @@ is_cache_verified(UpdateSeq) const {
 INLINE void AllTransitionsWrapper::
 set_computed_verified(UpdateSeq now) {
   _cache = NodeTransitionCache::set_computed_verified(_cache, now);
+  _all_verified = now;
+
+#ifndef NDEBUG
+  if (wrt_cat.is_spam()) {
+    wrt_cat.spam()
+      << "AllTransitionsWrapper::set_computed_verified(" << now << ")\n";
+  }
+#endif
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -350,6 +416,7 @@ cached_compose(const AllTransitionsWrapper &cache,
 	       UpdateSeq now) {
   _cache = NodeTransitionCache::cached_compose(_cache, cache._cache,
 					       value._cache, now);
+  _all_verified = now;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 4 - 1
panda/src/graph/allTransitionsWrapper.h

@@ -23,6 +23,7 @@
 
 #include "nodeTransition.h"
 #include "nodeTransitionCache.h"
+#include "config_graph.h"
 
 #include <pointerTo.h>
 
@@ -76,7 +77,8 @@ public:
 
   INLINE Node *extract_from_cache(const NodeRelation *arc);
   INLINE void store_to_cache(NodeRelation *arc, Node *top_subtree);
-  INLINE bool is_cache_verified(UpdateSeq now) const;
+  INLINE void store_to_cache_partial(NodeRelation *arc, Node *top_subtree);
+  INLINE bool is_cache_verified(UpdateSeq as_of) const;
   INLINE void set_computed_verified(UpdateSeq now);
 
   INLINE void cached_compose(const AllTransitionsWrapper &cache, 
@@ -104,6 +106,7 @@ public:
 
 private:
   PT(NodeTransitionCache) _cache;
+  UpdateSeq _all_verified;
 
   // This is a special cache object which is always around and always
   // empty.  It's used just so we can have a sensible return value

+ 38 - 4
panda/src/graph/nodeRelation.cxx

@@ -442,15 +442,33 @@ attach() {
   nassertv(!_attached);
 
   _attached = true;
+
+  DownRelationPointers &parent_list = _parent->_children[_type];
+  UpRelationPointers &child_list = _child->_parents[_type];
   
-  bool inserted_one = internal_insert_arc(_parent->_children[_type], this);
-  bool inserted_two = internal_insert_arc(_child->_parents[_type], this);
+  bool inserted_one = internal_insert_arc(parent_list, this);
+  bool inserted_two = internal_insert_arc(child_list, this);
   nassertv(inserted_one && inserted_two);
 
   // Blow out the cache and increment the current update sequence.
   _net_transitions.clear();
   _last_update = ++last_graph_update[_type];
 
+  /*
+  // If we have just added a new parent arc to a node that previously
+  // had exactly one parent, we also need to increment the update
+  // counter for all the children of that node.
+  if (child_list.size() == 2) {
+    cerr << "Attaching " << *this << " at " << _last_update << "\n";
+    DownRelationPointers &child_children = _child->_children[_type];
+    DownRelationPointers::iterator drpi;
+    for (drpi = child_children.begin(); drpi != child_children.end(); ++drpi) {
+      cerr << "Forcing update for " << *(*drpi) << "\n";
+      (*drpi)->_last_update = _last_update;
+    }
+  }
+  */
+
   _parent->force_bound_stale();
   mark_bound_stale();
 }
@@ -481,8 +499,11 @@ detach() {
 
   force_bound_stale();
 
-  bool removed_one = internal_remove_arc(_parent->_children[_type], this);
-  bool removed_two = internal_remove_arc(_child->_parents[_type], this);
+  DownRelationPointers &parent_list = _parent->_children[_type];
+  UpRelationPointers &child_list = _child->_parents[_type];
+
+  bool removed_one = internal_remove_arc(parent_list, this);
+  bool removed_two = internal_remove_arc(child_list, this);
 
   nassertr(removed_one, result);
   nassertr(removed_two, result);
@@ -493,6 +514,19 @@ detach() {
   _net_transitions.clear();
   _last_update = ++last_graph_update[_type];
 
+  /*
+  // If we have just removed a parent arc from a node, leaving exactly
+  // one parent, we also need to increment the update counter for all
+  // the children of that node.
+  if (child_list.size() == 1) {
+    DownRelationPointers &child_children = _child->_children[_type];
+    DownRelationPointers::iterator drpi;
+    for (drpi = child_children.begin(); drpi != child_children.end(); ++drpi) {
+      (*drpi)->_last_update = _last_update;
+    }
+  }
+  */
+
   return result;
 }
 

+ 5 - 0
panda/src/graph/nodeRelation.h

@@ -156,6 +156,11 @@ private:
   PT(NodeTransitionCache) _net_transitions;
   Node *_top_subtree;
 
+  // This is updated with the current update sequence whenever we
+  // verify that *all* the transitions to this arc are accurately
+  // reflected in the above set.
+  UpdateSeq _all_verified;
+
   // This is updated with the current update sequence each time the
   // arc is changed (for instance, to change its state or to reparent
   // it or something).  It exists to support caching in the cull

+ 7 - 2
panda/src/graph/nodeTransitionCache.cxx

@@ -390,7 +390,11 @@ invert_compose(const NodeTransitionCache *a, const NodeTransitionCache *b) {
 ////////////////////////////////////////////////////////////////////
 //     Function: NodeTransitionCache::cached_compose
 //       Access: Public, Static
-//  Description: 
+//  Description: Returns a cache pointer (which might be a pointer to
+//               the same cache object, or to a newly allocated
+//               object) that represents the result of compose(a, b),
+//               as computed using the cache value as a hint.  Mark
+//               the result as computed at time 'now'.
 ////////////////////////////////////////////////////////////////////
 NodeTransitionCache *NodeTransitionCache::
 cached_compose(const NodeTransitionCache *a, 
@@ -440,7 +444,8 @@ cached_compose(const NodeTransitionCache *a,
 //  Description: Returns a new pointer that represents the adjustment
 //               of the given cache to set each computed and verified
 //               value to the current now value.  This may modify the
-//               source cache only if there is only one pointer to it.
+//               source cache only if there are no other pointers to
+//               it.
 ////////////////////////////////////////////////////////////////////
 NodeTransitionCache *NodeTransitionCache::
 set_computed_verified(const NodeTransitionCache *a, UpdateSeq now) {

+ 12 - 3
panda/src/graph/nodeTransitionCacheEntry.I

@@ -142,8 +142,16 @@ get_trans() const {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE bool NodeTransitionCacheEntry::
-is_cache_verified(UpdateSeq now) const {
-  return _verified == now;
+is_cache_verified(UpdateSeq as_of) const {
+#ifndef NDEBUG
+  if (wrt_cat.is_spam()) {
+    wrt_cat.spam()
+      << "NodeTransitionCacheEntry::is_cache_verified(" << as_of 
+      << "), _verified = " << _verified << ", result = "
+      << (as_of <= _verified) << "\n";
+  }
+#endif
+  return as_of <= _verified;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -275,7 +283,8 @@ invert_compose(const NodeTransitionCacheEntry &a,
 //     Function: NodeTransitionCacheEntry::cached_compose
 //       Access: Public, Static
 //  Description: Sets this cache entry to the result of compose(a, b),
-//               as computed using the cache value as a hint.
+//               as computed using the cache value as a hint.  Mark
+//               the result as computed at time 'now'.
 ////////////////////////////////////////////////////////////////////
 INLINE NodeTransitionCacheEntry NodeTransitionCacheEntry::
 cached_compose(const NodeTransitionCacheEntry &a, 

+ 2 - 1
panda/src/graph/nodeTransitionCacheEntry.h

@@ -9,6 +9,7 @@
 #include <pandabase.h>
 
 #include "nodeTransition.h"
+#include "config_graph.h"
 
 #include <updateSeq.h>
 #include <pointerTo.h>
@@ -35,7 +36,7 @@ public:
   INLINE bool has_trans() const;
   INLINE NodeTransition *get_trans() const;
 
-  INLINE bool is_cache_verified(UpdateSeq now) const;
+  INLINE bool is_cache_verified(UpdateSeq as_of) const;
   INLINE bool is_freshly_computed(UpdateSeq changed) const;
   INLINE void set_computed_verified(UpdateSeq now);
 

+ 2 - 2
panda/src/graph/nodeTransitionWrapper.I

@@ -214,8 +214,8 @@ store_to_cache(NodeRelation *arc, Node *top_subtree) {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE bool NodeTransitionWrapper::
-is_cache_verified(UpdateSeq now) const {
-  return _entry.is_cache_verified(now);
+is_cache_verified(UpdateSeq as_of) const {
+  return _entry.is_cache_verified(as_of);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/graph/nodeTransitionWrapper.h

@@ -66,7 +66,7 @@ public:
 
   INLINE Node *extract_from_cache(const NodeRelation *arc);
   INLINE void store_to_cache(NodeRelation *arc, Node *top_subtree);
-  INLINE bool is_cache_verified(UpdateSeq now) const;
+  INLINE bool is_cache_verified(UpdateSeq as_of) const;
   INLINE void set_computed_verified(UpdateSeq now);
 
   INLINE void cached_compose(const NodeTransitionWrapper &cache, 

+ 50 - 33
panda/src/graph/wrt.I

@@ -21,15 +21,20 @@
 //               transitions returned by this function, the result is
 //               always unambiguous; any wrt() ambiguity is resolved
 //               later.
+//
+//               as_of is the most recent update so far on this branch
+//               of the scene graph; now is the stamp to use to mark
+//               any computed cache values.
 ////////////////////////////////////////////////////////////////////
 template<class TransitionWrapper>
 void
-get_cached_net_transition(NodeRelation *arc, Node *&root, UpdateSeq now,
+get_cached_net_transition(NodeRelation *arc, Node *&root, 
+			  UpdateSeq as_of, UpdateSeq now,
 			  TransitionWrapper &net, TypeHandle graph_type) {
   TransitionWrapper cur_cache = TransitionWrapper::init_from(net);
   Node *top_subtree = cur_cache.extract_from_cache(arc);
 
-  if (cur_cache.is_cache_verified(now)) {
+  if (cur_cache.is_cache_verified(as_of)) {
     // This arc's cached value has recently been verified, so we don't
     // even need to go anywhere--we trust it's still good.
     root = top_subtree;
@@ -45,7 +50,11 @@ get_cached_net_transition(NodeRelation *arc, Node *&root, UpdateSeq now,
 	wrt_cat.debug(false) << *root;
       }
       wrt_cat.debug(false)
-	<< ") is current as of " << now << "\n";
+	<< ") is current as of " << as_of << " (now is " 
+	<< now << ")\n";
+      if (wrt_cat.is_spam()) {
+	wrt_cat.spam() << "result of " << *arc << " is " << net << "\n";
+      }	
     }
 #endif
 
@@ -83,7 +92,7 @@ get_cached_net_transition(NodeRelation *arc, Node *&root, UpdateSeq now,
       // The node has exactly one parent.  Carry on.
 
       NodeRelation *parent = *(*uri).second.begin();
-      get_cached_net_transition(parent, root, now, net, graph_type);
+      get_cached_net_transition(parent, root, as_of, now, net, graph_type);
 
       // Now recompute our own cache.
       TransitionWrapper cur_value = TransitionWrapper::init_from(net);
@@ -103,7 +112,11 @@ get_cached_net_transition(NodeRelation *arc, Node *&root, UpdateSeq now,
 	wrt_cat.debug(false) << *root;
       }
       wrt_cat.debug(false)
-	<< ") is recomputed as of " << now << "\n";
+	<< ") is recomputed as of " << as_of << " (now is " 
+	<< now << ")\n";
+      if (wrt_cat.is_spam()) {
+	wrt_cat.spam() << "result of " << *arc << " is " << net << "\n";
+      }	
     }
 #endif
   }
@@ -120,13 +133,17 @@ get_cached_net_transition(NodeRelation *arc, Node *&root, UpdateSeq now,
 //               contains a list of NodeRelation pointers to resolve
 //               ambiguities when a node with multiple parents is
 //               encountered.
+//
+//               as_of is the most recent update so far on this branch
+//               of the scene graph; now is the stamp to use to mark
+//               any computed cache values.
 ////////////////////////////////////////////////////////////////////
 template<class InputIterator, class TransitionWrapper>
 void
 get_cached_net_transition(const Node *node, 
 			  InputIterator arc_list_begin, 
 			  InputIterator arc_list_end,
-			  UpdateSeq now,
+			  UpdateSeq as_of, UpdateSeq now,
 			  TransitionWrapper &result,
 			  TypeHandle graph_type) {
   if (node == NULL) {
@@ -219,15 +236,15 @@ get_cached_net_transition(const Node *node,
 
   // Get the net transition leading into this node.
   Node *root;
-  get_cached_net_transition((NodeRelation *)parent_arc, root, now, result, 
-			    graph_type);
+  get_cached_net_transition((NodeRelation *)parent_arc, root, 
+			    as_of, now, result, graph_type);
 
   if (root != NULL) {
     // There's more to get.  The above function call was forced to
     // stop at a node with multiple parents.
     TransitionWrapper more = TransitionWrapper::init_from(result);
     get_cached_net_transition(root, arc_list_begin, arc_list_end, 
-			      now, more, graph_type);
+			      as_of, now, more, graph_type);
     more.compose_in_place(result);
     result = more;
   }
@@ -396,14 +413,15 @@ cached_wrt_base(const Node *from,
       wrt_cat.debug(false) << *to;
     }
     wrt_cat.debug(false)
-      << "), as of " << now << "\n";
+      << "), as of " << now << " (now is " 
+      << now << ")\n";
   }
 #endif
 
-  get_cached_net_transition(from, from_arcs_begin, from_arcs_end, now,
-			    net_from_trans, graph_type);
-  get_cached_net_transition(to, to_arcs_begin, to_arcs_end, now,
-			    result, graph_type);
+  get_cached_net_transition(from, from_arcs_begin, from_arcs_end, 
+			    now, now, net_from_trans, graph_type);
+  get_cached_net_transition(to, to_arcs_begin, to_arcs_end, 
+			    now, now, result, graph_type);
   
 #ifndef NDEBUG
   if (paranoid_wrt) {
@@ -686,8 +704,8 @@ get_uncached_wrt_subtree(Node *node, Node *to, TransitionWrapper &result,
 
 template<class TransitionWrapper>
 INLINE Node *
-uncached_wrt_subtree(NodeRelation *arc, Node *to, TransitionWrapper &result, 
-		     TypeHandle graph_type) {
+uncached_wrt_subtree(NodeRelation *arc, Node *to,
+		     TransitionWrapper &result, TypeHandle graph_type) {
   Node *stop = 
     get_uncached_wrt_subtree(arc->get_parent(), to,
 			     result, graph_type);
@@ -717,14 +735,12 @@ uncached_wrt_subtree(NodeRelation *arc, Node *to, TransitionWrapper &result,
 
 template<class TransitionWrapper>
 Node *
-cached_wrt_subtree(NodeRelation *arc, Node *to, TransitionWrapper &result, 
-		   TypeHandle graph_type) {
-  UpdateSeq now = last_graph_update[graph_type];
-
+cached_wrt_subtree(NodeRelation *arc, Node *to, UpdateSeq as_of, UpdateSeq now,
+		   TransitionWrapper &result, TypeHandle graph_type) {
   // First, determine the net transition up to the top of the current
   // subtree.
   Node *top_subtree;
-  get_cached_net_transition(arc, top_subtree, now, result, graph_type);
+  get_cached_net_transition(arc, top_subtree, as_of, now, result, graph_type);
 
   if (top_subtree == to || to == (Node *)NULL) {
     // If the top of the subtree is the node we asked to wrt to,
@@ -749,12 +765,6 @@ cached_wrt_subtree(NodeRelation *arc, Node *to, TransitionWrapper &result,
     // Otherwise, it must be the case that the node we want to wrt to is
     // some descendent of the top_subtree node.  It also therefore
     // follows that this node has exactly one parent.
-    
-    nassertr(to->get_num_parents(graph_type) == 1, NULL);
-    NodeRelation *to_arc = to->get_parent(graph_type, 0);
-    
-    // Save the result from the first pass.
-    TransitionWrapper net_from_trans = result;
 
 #ifndef NDEBUG
     if (wrt_cat.is_spam()) {
@@ -765,15 +775,22 @@ cached_wrt_subtree(NodeRelation *arc, Node *to, TransitionWrapper &result,
       } else {
 	wrt_cat.spam(false) << *top_subtree;
       }
-      wrt_cat.spam() << ", first result is:\n";
+      wrt_cat.spam(false) << ", first result is:\n";
       result.write(wrt_cat.spam(false), 2);
     }
 #endif
     
+    nassertr(to->get_num_parents(graph_type) == 1, NULL);
+    NodeRelation *to_arc = to->get_parent(graph_type, 0);
+    
+    // Save the result from the first pass.
+    TransitionWrapper net_from_trans = result;
+    
     // Now determine the net transition to the top of the subtree from
     // this arc.  It had better be the same subtree!
     Node *top_subtree_2;
-    get_cached_net_transition(to_arc, top_subtree_2, now, result, graph_type);
+    get_cached_net_transition(to_arc, top_subtree_2, as_of, now,
+			      result, graph_type);
     nassertr(top_subtree == top_subtree_2, NULL);
     
     // And now compute the actual wrt.
@@ -855,15 +872,15 @@ cached_wrt_subtree(NodeRelation *arc, Node *to, TransitionWrapper &result,
 
 template<class TransitionWrapper>
 INLINE Node *
-wrt_subtree(NodeRelation *arc, Node *to, TransitionWrapper &result, 
-	    TypeHandle graph_type) {
+wrt_subtree(NodeRelation *arc, Node *to, UpdateSeq as_of, UpdateSeq now,
+	    TransitionWrapper &result, TypeHandle graph_type) {
 #ifndef NDEBUG
   if (!cache_wrt) {
-    // If we aren't caching wrt, do this the hard way.
+    // If we aren't caching wrt, compute it explicitly.
     return uncached_wrt_subtree(arc, to, result, graph_type);
   }
 #endif
-  return cached_wrt_subtree(arc, to, result, graph_type);
+  return cached_wrt_subtree(arc, to, as_of, now, result, graph_type);
 }
 
 

+ 6 - 2
panda/src/graph/wrt.h

@@ -118,10 +118,14 @@ uncached_wrt(const Node *from,
 // general utility; it is of primary interest to code (like the
 // CullTraverser) that needs to cache a wrt-type value for many nodes
 // across the entire tree.
+
+// as_of is the most recent update between 'arc' and 'to'; now is the
+// current most recent update sequence anywhere, used to use to mark
+// any computed cache values.
 template<class TransitionWrapper>
 INLINE Node *
-wrt_subtree(NodeRelation *arc, Node *to, TransitionWrapper &result, 
-	    TypeHandle graph_type);
+wrt_subtree(NodeRelation *arc, Node *to, UpdateSeq as_of, UpdateSeq now,
+	    TransitionWrapper &result, TypeHandle graph_type);
 
 
 #include "wrt.I"