Browse Source

mo' better pipelining

David Rose 20 years ago
parent
commit
79f1462eb1
96 changed files with 3490 additions and 2470 deletions
  1. 2 2
      panda/src/collide/collisionInvSphere.cxx
  2. 1 1
      panda/src/collide/collisionInvSphere.h
  3. 6 6
      panda/src/collide/collisionLevelState.cxx
  4. 3 3
      panda/src/collide/collisionNode.cxx
  5. 1 1
      panda/src/collide/collisionNode.h
  6. 2 2
      panda/src/collide/collisionPlane.cxx
  7. 1 1
      panda/src/collide/collisionPlane.h
  8. 2 2
      panda/src/collide/collisionPolygon.cxx
  9. 1 1
      panda/src/collide/collisionPolygon.h
  10. 2 2
      panda/src/collide/collisionRay.cxx
  11. 1 1
      panda/src/collide/collisionRay.h
  12. 2 2
      panda/src/collide/collisionSegment.cxx
  13. 1 1
      panda/src/collide/collisionSegment.h
  14. 2 2
      panda/src/collide/collisionSphere.cxx
  15. 1 1
      panda/src/collide/collisionSphere.h
  16. 12 12
      panda/src/collide/collisionTraverser.cxx
  17. 2 2
      panda/src/collide/collisionTube.cxx
  18. 1 1
      panda/src/collide/collisionTube.h
  19. 2 1
      panda/src/dgraph/dataNode.cxx
  20. 1 1
      panda/src/dgraph/dataNode.h
  21. 18 0
      panda/src/express/thread.I
  22. 1 0
      panda/src/express/thread.h
  23. 80 18
      panda/src/gobj/boundedObject.I
  24. 77 23
      panda/src/gobj/boundedObject.cxx
  25. 22 6
      panda/src/gobj/boundedObject.h
  26. 1 1
      panda/src/gobj/drawable.cxx
  27. 1 5
      panda/src/gobj/drawable.h
  28. 17 62
      panda/src/gobj/geom.I
  29. 211 289
      panda/src/gobj/geom.cxx
  30. 19 35
      panda/src/gobj/geom.h
  31. 4 7
      panda/src/gobj/geomMunger.cxx
  32. 25 88
      panda/src/gobj/geomPrimitive.I
  33. 213 239
      panda/src/gobj/geomPrimitive.cxx
  34. 20 39
      panda/src/gobj/geomPrimitive.h
  35. 4 38
      panda/src/gobj/geomVertexArrayData.I
  36. 35 74
      panda/src/gobj/geomVertexArrayData.cxx
  37. 5 26
      panda/src/gobj/geomVertexArrayData.h
  38. 8 83
      panda/src/gobj/geomVertexData.I
  39. 265 325
      panda/src/gobj/geomVertexData.cxx
  40. 14 36
      panda/src/gobj/geomVertexData.h
  41. 11 6
      panda/src/parametrics/ropeNode.cxx
  42. 3 2
      panda/src/parametrics/ropeNode.h
  43. 11 6
      panda/src/parametrics/sheetNode.cxx
  44. 3 2
      panda/src/parametrics/sheetNode.h
  45. 1 2
      panda/src/pgraph/accumulatedAttribs.cxx
  46. 4 4
      panda/src/pgraph/cullBinBackToFront.cxx
  47. 4 4
      panda/src/pgraph/cullBinFrontToBack.cxx
  48. 5 5
      panda/src/pgraph/cullTraverser.cxx
  49. 1 1
      panda/src/pgraph/cullTraverser.h
  50. 6 6
      panda/src/pgraph/cullTraverserData.cxx
  51. 18 2
      panda/src/pgraph/geomNode.I
  52. 152 135
      panda/src/pgraph/geomNode.cxx
  53. 5 3
      panda/src/pgraph/geomNode.h
  54. 12 9
      panda/src/pgraph/geomTransformer.cxx
  55. 1 1
      panda/src/pgraph/nodePath.cxx
  56. 113 329
      panda/src/pgraph/pandaNode.I
  57. 573 256
      panda/src/pgraph/pandaNode.cxx
  58. 66 45
      panda/src/pgraph/pandaNode.h
  59. 1 1
      panda/src/pgraph/portalClipper.I
  60. 4 4
      panda/src/pgraph/portalNode.cxx
  61. 2 2
      panda/src/pgraph/portalNode.h
  62. 1 1
      panda/src/pgraph/sceneGraphReducer.cxx
  63. 7 7
      panda/src/pgui/pgItem.cxx
  64. 3 3
      panda/src/pgui/pgItem.h
  65. 4 2
      panda/src/physics/actorNode.cxx
  66. 1 1
      panda/src/physics/actorNode.h
  67. 9 0
      panda/src/putil/Sources.pp
  68. 2 50
      panda/src/putil/cycleDataReader.I
  69. 0 1
      panda/src/putil/cycleDataReader.h
  70. 217 0
      panda/src/putil/cycleDataStageReader.I
  71. 19 0
      panda/src/putil/cycleDataStageReader.cxx
  72. 61 0
      panda/src/putil/cycleDataStageReader.h
  73. 222 0
      panda/src/putil/cycleDataStageWriter.I
  74. 19 0
      panda/src/putil/cycleDataStageWriter.cxx
  75. 68 0
      panda/src/putil/cycleDataStageWriter.h
  76. 37 0
      panda/src/putil/cycleDataWriter.I
  77. 2 0
      panda/src/putil/cycleDataWriter.h
  78. 63 0
      panda/src/putil/cyclerHolder.I
  79. 19 0
      panda/src/putil/cyclerHolder.cxx
  80. 48 0
      panda/src/putil/cyclerHolder.h
  81. 11 0
      panda/src/putil/pipeline.I
  82. 0 11
      panda/src/putil/pipeline.cxx
  83. 1 1
      panda/src/putil/pipeline.h
  84. 44 0
      panda/src/putil/pipelineCycler.I
  85. 72 1
      panda/src/putil/pipelineCycler.h
  86. 132 28
      panda/src/putil/pipelineCyclerDummyImpl.I
  87. 8 1
      panda/src/putil/pipelineCyclerDummyImpl.h
  88. 119 23
      panda/src/putil/pipelineCyclerTrivialImpl.I
  89. 7 1
      panda/src/putil/pipelineCyclerTrivialImpl.h
  90. 83 19
      panda/src/putil/pipelineCyclerTrueImpl.I
  91. 114 50
      panda/src/putil/pipelineCyclerTrueImpl.cxx
  92. 8 2
      panda/src/putil/pipelineCyclerTrueImpl.h
  93. 3 1
      panda/src/putil/putil_composite1.cxx
  94. 1 0
      panda/src/putil/putil_composite2.cxx
  95. 2 2
      panda/src/text/textNode.cxx
  96. 1 1
      panda/src/text/textNode.h

+ 2 - 2
panda/src/collide/collisionInvSphere.cxx

@@ -73,8 +73,8 @@ output(ostream &out) const {
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 BoundingVolume *CollisionInvSphere::
 BoundingVolume *CollisionInvSphere::
-recompute_bound() {
-  BoundedObject::recompute_bound();
+recompute_bound(int pipeline_stage) {
+  BoundedObject::recompute_bound(pipeline_stage);
   // An inverse sphere always has an infinite bounding volume, since
   // An inverse sphere always has an infinite bounding volume, since
   // everything outside the sphere is solid matter.
   // everything outside the sphere is solid matter.
   return set_bound_ptr(new OmniBoundingVolume());
   return set_bound_ptr(new OmniBoundingVolume());

+ 1 - 1
panda/src/collide/collisionInvSphere.h

@@ -50,7 +50,7 @@ public:
   virtual void output(ostream &out) const;
   virtual void output(ostream &out) const;
 
 
 protected:
 protected:
-  virtual BoundingVolume *recompute_bound();
+  virtual BoundingVolume *recompute_bound(int pipeline_stage);
 
 
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
   test_intersection_from_sphere(const CollisionEntry &entry) const;
   test_intersection_from_sphere(const CollisionEntry &entry) const;

+ 6 - 6
panda/src/collide/collisionLevelState.cxx

@@ -59,12 +59,12 @@ prepare_collider(const ColliderDef &def) {
   _colliders.push_back(def);
   _colliders.push_back(def);
 
 
   CollisionSolid *collider = def._collider;
   CollisionSolid *collider = def._collider;
-  const BoundingVolume &bv = collider->get_bound();
-  if (!bv.is_of_type(GeometricBoundingVolume::get_class_type())) {
+  const BoundingVolume *bv = collider->get_bound();
+  if (!bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
     _local_bounds.push_back((GeometricBoundingVolume *)NULL);
     _local_bounds.push_back((GeometricBoundingVolume *)NULL);
   } else {
   } else {
     GeometricBoundingVolume *gbv;
     GeometricBoundingVolume *gbv;
-    DCAST_INTO_V(gbv, bv.make_copy());
+    DCAST_INTO_V(gbv, bv->make_copy());
 
 
     // TODO: we need to make this logic work in the new relative
     // TODO: we need to make this logic work in the new relative
     // world.  The bounding volume should be extended by the object's
     // world.  The bounding volume should be extended by the object's
@@ -118,10 +118,10 @@ any_in_bounds() {
   }
   }
 #endif  // NDEBUG
 #endif  // NDEBUG
 
 
-  const BoundingVolume &node_bv = node()->get_bound();
-  if (node_bv.is_of_type(GeometricBoundingVolume::get_class_type())) {
+  const BoundingVolume *node_bv = node()->get_bound();
+  if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
     const GeometricBoundingVolume *node_gbv;
     const GeometricBoundingVolume *node_gbv;
-    DCAST_INTO_R(node_gbv, &node_bv, false);
+    DCAST_INTO_R(node_gbv, node_bv, false);
 
 
     int num_colliders = get_num_colliders();
     int num_colliders = get_num_colliders();
     for (int c = 0; c < num_colliders; c++) {
     for (int c = 0; c < num_colliders; c++) {

+ 3 - 3
panda/src/collide/collisionNode.cxx

@@ -356,9 +356,9 @@ get_collide_geom() const {
 //               thing.
 //               thing.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 BoundingVolume *CollisionNode::
 BoundingVolume *CollisionNode::
-recompute_internal_bound() {
+recompute_internal_bound(int pipeline_stage) {
   // First, get ourselves a fresh, empty bounding volume.
   // First, get ourselves a fresh, empty bounding volume.
-  BoundingVolume *bound = PandaNode::recompute_internal_bound();
+  BoundingVolume *bound = PandaNode::recompute_internal_bound(pipeline_stage);
   nassertr(bound != (BoundingVolume *)NULL, bound);
   nassertr(bound != (BoundingVolume *)NULL, bound);
 
 
   // Now actually compute the bounding volume by putting it around all
   // Now actually compute the bounding volume by putting it around all
@@ -367,7 +367,7 @@ recompute_internal_bound() {
 
 
   Solids::const_iterator gi;
   Solids::const_iterator gi;
   for (gi = _solids.begin(); gi != _solids.end(); ++gi) {
   for (gi = _solids.begin(); gi != _solids.end(); ++gi) {
-    child_volumes.push_back(&(*gi)->get_bound());
+    child_volumes.push_back((*gi)->get_bound());
   }
   }
 
 
   const BoundingVolume **child_begin = &child_volumes[0];
   const BoundingVolume **child_begin = &child_volumes[0];

+ 1 - 1
panda/src/collide/collisionNode.h

@@ -74,7 +74,7 @@ PUBLISHED:
   INLINE static CollideMask get_default_collide_mask();
   INLINE static CollideMask get_default_collide_mask();
 
 
 protected:
 protected:
-  virtual BoundingVolume *recompute_internal_bound();
+  virtual BoundingVolume *recompute_internal_bound(int pipeline_stage);
 
 
 private:
 private:
   CPT(RenderState) get_last_pos_state();
   CPT(RenderState) get_last_pos_state();

+ 2 - 2
panda/src/collide/collisionPlane.cxx

@@ -93,8 +93,8 @@ output(ostream &out) const {
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 BoundingVolume *CollisionPlane::
 BoundingVolume *CollisionPlane::
-recompute_bound() {
-  BoundedObject::recompute_bound();
+recompute_bound(int pipeline_stage) {
+  BoundedObject::recompute_bound(pipeline_stage);
   // Less than ideal: we throw away whatever we just allocated in
   // Less than ideal: we throw away whatever we just allocated in
   // BoundedObject.
   // BoundedObject.
   return set_bound_ptr(new BoundingPlane(_plane));
   return set_bound_ptr(new BoundingPlane(_plane));

+ 1 - 1
panda/src/collide/collisionPlane.h

@@ -54,7 +54,7 @@ PUBLISHED:
   INLINE const Planef &get_plane() const;
   INLINE const Planef &get_plane() const;
 
 
 protected:
 protected:
-  virtual BoundingVolume *recompute_bound();
+  virtual BoundingVolume *recompute_bound(int pipeline_stage);
 
 
 protected:
 protected:
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)

+ 2 - 2
panda/src/collide/collisionPolygon.cxx

@@ -361,9 +361,9 @@ write(ostream &out, int indent_level) const {
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 BoundingVolume *CollisionPolygon::
 BoundingVolume *CollisionPolygon::
-recompute_bound() {
+recompute_bound(int pipeline_stage) {
   // First, get ourselves a fresh, empty bounding volume.
   // First, get ourselves a fresh, empty bounding volume.
-  BoundingVolume *bound = BoundedObject::recompute_bound();
+  BoundingVolume *bound = BoundedObject::recompute_bound(pipeline_stage);
   nassertr(bound != (BoundingVolume*)0L, bound);
   nassertr(bound != (BoundingVolume*)0L, bound);
 
 
   GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);
   GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);

+ 1 - 1
panda/src/collide/collisionPolygon.h

@@ -69,7 +69,7 @@ public:
   virtual void write(ostream &out, int indent_level = 0) const;
   virtual void write(ostream &out, int indent_level = 0) const;
 
 
 protected:
 protected:
-  virtual BoundingVolume *recompute_bound();
+  virtual BoundingVolume *recompute_bound(int pipeline_stage);
 
 
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
   test_intersection_from_sphere(const CollisionEntry &entry) const;
   test_intersection_from_sphere(const CollisionEntry &entry) const;

+ 2 - 2
panda/src/collide/collisionRay.cxx

@@ -131,8 +131,8 @@ set_from_lens(LensNode *camera, const LPoint2f &point) {
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 BoundingVolume *CollisionRay::
 BoundingVolume *CollisionRay::
-recompute_bound() {
-  BoundedObject::recompute_bound();
+recompute_bound(int pipeline_stage) {
+  BoundedObject::recompute_bound(pipeline_stage);
   // Less than ideal: we throw away whatever we just allocated in
   // Less than ideal: we throw away whatever we just allocated in
   // BoundedObject.
   // BoundedObject.
   return set_bound_ptr(new BoundingLine(_origin, _origin + _direction));
   return set_bound_ptr(new BoundingLine(_origin, _origin + _direction));

+ 1 - 1
panda/src/collide/collisionRay.h

@@ -65,7 +65,7 @@ PUBLISHED:
   INLINE bool set_from_lens(LensNode *camera, float px, float py);
   INLINE bool set_from_lens(LensNode *camera, float px, float py);
 
 
 protected:
 protected:
-  virtual BoundingVolume *recompute_bound();
+  virtual BoundingVolume *recompute_bound(int pipeline_stage);
 
 
 protected:
 protected:
   virtual void fill_viz_geom();
   virtual void fill_viz_geom();

+ 2 - 2
panda/src/collide/collisionSegment.cxx

@@ -128,8 +128,8 @@ set_from_lens(LensNode *camera, const LPoint2f &point) {
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 BoundingVolume *CollisionSegment::
 BoundingVolume *CollisionSegment::
-recompute_bound() {
-  BoundingVolume *bound = BoundedObject::recompute_bound();
+recompute_bound(int pipeline_stage) {
+  BoundingVolume *bound = BoundedObject::recompute_bound(pipeline_stage);
 
 
   if (bound->is_of_type(GeometricBoundingVolume::get_class_type())) {
   if (bound->is_of_type(GeometricBoundingVolume::get_class_type())) {
     GeometricBoundingVolume *gbound;
     GeometricBoundingVolume *gbound;

+ 1 - 1
panda/src/collide/collisionSegment.h

@@ -68,7 +68,7 @@ PUBLISHED:
   INLINE bool set_from_lens(LensNode *camera, float px, float py);
   INLINE bool set_from_lens(LensNode *camera, float px, float py);
 
 
 protected:
 protected:
-  virtual BoundingVolume *recompute_bound();
+  virtual BoundingVolume *recompute_bound(int pipeline_stage);
 
 
 protected:
 protected:
   virtual void fill_viz_geom();
   virtual void fill_viz_geom();

+ 2 - 2
panda/src/collide/collisionSphere.cxx

@@ -104,8 +104,8 @@ output(ostream &out) const {
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 BoundingVolume *CollisionSphere::
 BoundingVolume *CollisionSphere::
-recompute_bound() {
-  BoundingVolume *bound = BoundedObject::recompute_bound();
+recompute_bound(int pipeline_stage) {
+  BoundingVolume *bound = BoundedObject::recompute_bound(pipeline_stage);
   nassertr(bound != (BoundingVolume*)0L, bound);
   nassertr(bound != (BoundingVolume*)0L, bound);
   nassertr(!_center.is_nan() && !cnan(_radius), bound);
   nassertr(!_center.is_nan() && !cnan(_radius), bound);
   BoundingSphere sphere(_center, _radius);
   BoundingSphere sphere(_center, _radius);

+ 1 - 1
panda/src/collide/collisionSphere.h

@@ -56,7 +56,7 @@ PUBLISHED:
   INLINE float get_radius() const;
   INLINE float get_radius() const;
 
 
 protected:
 protected:
-  virtual BoundingVolume *recompute_bound();
+  virtual BoundingVolume *recompute_bound(int pipeline_stage);
 
 
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
   test_intersection_from_sphere(const CollisionEntry &entry) const;
   test_intersection_from_sphere(const CollisionEntry &entry) const;

+ 12 - 12
panda/src/collide/collisionTraverser.cxx

@@ -519,10 +519,10 @@ r_traverse(CollisionLevelState &level_state) {
   if (node->is_exact_type(CollisionNode::get_class_type())) {
   if (node->is_exact_type(CollisionNode::get_class_type())) {
     CollisionNode *cnode;
     CollisionNode *cnode;
     DCAST_INTO_V(cnode, node);
     DCAST_INTO_V(cnode, node);
-    const BoundingVolume &node_bv = cnode->get_bound();
+    const BoundingVolume *node_bv = cnode->get_bound();
     const GeometricBoundingVolume *node_gbv = NULL;
     const GeometricBoundingVolume *node_gbv = NULL;
-    if (node_bv.is_of_type(GeometricBoundingVolume::get_class_type())) {
-      DCAST_INTO_V(node_gbv, &node_bv);
+    if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
+      DCAST_INTO_V(node_gbv, node_bv);
     }
     }
 
 
     CollisionEntry entry;
     CollisionEntry entry;
@@ -560,10 +560,10 @@ r_traverse(CollisionLevelState &level_state) {
     
     
     GeomNode *gnode;
     GeomNode *gnode;
     DCAST_INTO_V(gnode, node);
     DCAST_INTO_V(gnode, node);
-    const BoundingVolume &node_bv = gnode->get_bound();
+    const BoundingVolume *node_bv = gnode->get_bound();
     const GeometricBoundingVolume *node_gbv = NULL;
     const GeometricBoundingVolume *node_gbv = NULL;
-    if (node_bv.is_of_type(GeometricBoundingVolume::get_class_type())) {
-      DCAST_INTO_V(node_gbv, &node_bv);
+    if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
+      DCAST_INTO_V(node_gbv, node_bv);
     }
     }
 
 
     CollisionEntry entry;
     CollisionEntry entry;
@@ -642,16 +642,16 @@ compare_collider_to_node(CollisionEntry &entry,
     for (int s = 0; s < num_solids; ++s) {
     for (int s = 0; s < num_solids; ++s) {
       entry._into = cnode->get_solid(s);
       entry._into = cnode->get_solid(s);
       if (entry._from != entry._into) {
       if (entry._from != entry._into) {
-        const BoundingVolume &solid_bv = entry._into->get_bound();
+        const BoundingVolume *solid_bv = entry._into->get_bound();
         const GeometricBoundingVolume *solid_gbv = NULL;
         const GeometricBoundingVolume *solid_gbv = NULL;
         if (num_solids > 1 &&
         if (num_solids > 1 &&
-            solid_bv.is_of_type(GeometricBoundingVolume::get_class_type())) {
+            solid_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
           // Only bother to test against each solid's bounding
           // Only bother to test against each solid's bounding
           // volume if we have more than one solid in the node, as a
           // volume if we have more than one solid in the node, as a
           // slight optimization.  (If the node contains just one
           // slight optimization.  (If the node contains just one
           // solid, then the node's bounding volume, which we just
           // solid, then the node's bounding volume, which we just
           // tested, is the same as the solid's bounding volume.)
           // tested, is the same as the solid's bounding volume.)
-          DCAST_INTO_V(solid_gbv, &solid_bv);
+          DCAST_INTO_V(solid_gbv, solid_bv);
         }
         }
 
 
         compare_collider_to_solid(entry, from_node_gbv, solid_gbv);
         compare_collider_to_solid(entry, from_node_gbv, solid_gbv);
@@ -684,16 +684,16 @@ compare_collider_to_geom_node(CollisionEntry &entry,
       entry._into = (CollisionSolid *)NULL;
       entry._into = (CollisionSolid *)NULL;
       const Geom *geom = DCAST(Geom, gnode->get_geom(s));
       const Geom *geom = DCAST(Geom, gnode->get_geom(s));
       if (geom != (Geom *)NULL) {
       if (geom != (Geom *)NULL) {
-        const BoundingVolume &geom_bv = geom->get_bound();
+        const BoundingVolume *geom_bv = geom->get_bound();
         const GeometricBoundingVolume *geom_gbv = NULL;
         const GeometricBoundingVolume *geom_gbv = NULL;
         if (num_geoms > 1 &&
         if (num_geoms > 1 &&
-            geom_bv.is_of_type(GeometricBoundingVolume::get_class_type())) {
+            geom_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
           // Only bother to test against each geom's bounding
           // Only bother to test against each geom's bounding
           // volume if we have more than one geom in the node, as a
           // volume if we have more than one geom in the node, as a
           // slight optimization.  (If the node contains just one
           // slight optimization.  (If the node contains just one
           // geom, then the node's bounding volume, which we just
           // geom, then the node's bounding volume, which we just
           // tested, is the same as the geom's bounding volume.)
           // tested, is the same as the geom's bounding volume.)
-          DCAST_INTO_V(geom_gbv, &geom_bv);
+          DCAST_INTO_V(geom_gbv, geom_bv);
         }
         }
 
 
         compare_collider_to_geom(entry, geom, from_node_gbv, geom_gbv);
         compare_collider_to_geom(entry, geom, from_node_gbv, geom_gbv);

+ 2 - 2
panda/src/collide/collisionTube.cxx

@@ -99,8 +99,8 @@ output(ostream &out) const {
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 BoundingVolume *CollisionTube::
 BoundingVolume *CollisionTube::
-recompute_bound() {
-  BoundingVolume *bound = BoundedObject::recompute_bound();
+recompute_bound(int pipeline_stage) {
+  BoundingVolume *bound = BoundedObject::recompute_bound(pipeline_stage);
 
 
   if (bound->is_of_type(GeometricBoundingVolume::get_class_type())) {
   if (bound->is_of_type(GeometricBoundingVolume::get_class_type())) {
     GeometricBoundingVolume *gbound;
     GeometricBoundingVolume *gbound;

+ 1 - 1
panda/src/collide/collisionTube.h

@@ -64,7 +64,7 @@ PUBLISHED:
   INLINE float get_radius() const;
   INLINE float get_radius() const;
 
 
 protected:
 protected:
-  virtual BoundingVolume *recompute_bound();
+  virtual BoundingVolume *recompute_bound(int pipeline_stage);
 
 
 protected:
 protected:
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)

+ 2 - 1
panda/src/dgraph/dataNode.cxx

@@ -254,7 +254,8 @@ define_output(const string &name, TypeHandle data_type) {
 //               node has.
 //               node has.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void DataNode::
 void DataNode::
-parents_changed() {
+parents_changed(int pipeline_stage) {
+  PandaNode::parents_changed(pipeline_stage);
   reconnect();
   reconnect();
 }
 }
 
 

+ 1 - 1
panda/src/dgraph/dataNode.h

@@ -90,7 +90,7 @@ protected:
 
 
 protected:
 protected:
   // Inherited from PandaNode
   // Inherited from PandaNode
-  virtual void parents_changed();
+  virtual void parents_changed(int pipeline_stage);
 
 
   // Local to DataNode
   // Local to DataNode
   virtual void do_transmit_data(const DataNodeTransmit &input,
   virtual void do_transmit_data(const DataNodeTransmit &input,

+ 18 - 0
panda/src/express/thread.I

@@ -219,6 +219,24 @@ get_current_thread() {
 #endif  // HAVE_THREADS
 #endif  // HAVE_THREADS
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: Thread::get_current_pipeline_stage
+//       Access: Published, Static
+//  Description: Returns the integer pipeline stage associated with
+//               the current thread.  This is the same thing as
+//               get_current_thread()->get_pipeline_stage(), but it
+//               may be faster to retrieve in some contexts.
+////////////////////////////////////////////////////////////////////
+INLINE int Thread::
+get_current_pipeline_stage() {
+#if !defined(HAVE_THREADS) || !defined(DO_PIPELINING)
+  // Without threads or pipelining, the result is always 0.
+  return 0;
+#else
+  return get_current_thread()->get_pipeline_stage();
+#endif  // !HAVE_THREADS || !DO_PIPELINING
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Thread::is_threading_supported
 //     Function: Thread::is_threading_supported
 //       Access: Published, Static
 //       Access: Published, Static

+ 1 - 0
panda/src/express/thread.h

@@ -68,6 +68,7 @@ PUBLISHED:
   INLINE static Thread *get_main_thread();
   INLINE static Thread *get_main_thread();
   INLINE static Thread *get_external_thread();
   INLINE static Thread *get_external_thread();
   INLINE static Thread *get_current_thread();
   INLINE static Thread *get_current_thread();
+  INLINE static int get_current_pipeline_stage();
   INLINE static bool is_threading_supported();
   INLINE static bool is_threading_supported();
   INLINE static void sleep(double seconds);
   INLINE static void sleep(double seconds);
 
 

+ 80 - 18
panda/src/gobj/boundedObject.I

@@ -93,9 +93,21 @@ operator = (const BoundedObject &copy) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void BoundedObject::
 INLINE void BoundedObject::
 set_bound(BoundedObject::BoundingVolumeType type) {
 set_bound(BoundedObject::BoundingVolumeType type) {
+  set_bound(type, Thread::get_current_pipeline_stage());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundedObject::set_bound
+//       Access: Published
+//  Description: Sets the type of the bounding volume that will be
+//               dynamically computed for this particular node.
+//               Presently, this should only be BVT_dynamic_sphere.
+////////////////////////////////////////////////////////////////////
+INLINE void BoundedObject::
+set_bound(BoundedObject::BoundingVolumeType type, int pipeline_stage) {
   nassertv(type != BVT_static);
   nassertv(type != BVT_static);
-  mark_bound_stale();
-  CDWriter cdata(_cycler);
+  mark_bound_stale(pipeline_stage);
+  CDStageWriter cdata(_cycler, pipeline_stage);
   cdata->_bound_type = type;
   cdata->_bound_type = type;
 }
 }
 
 
@@ -108,13 +120,41 @@ set_bound(BoundedObject::BoundingVolumeType type) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void BoundedObject::
 INLINE void BoundedObject::
 set_bound(const BoundingVolume &bound) {
 set_bound(const BoundingVolume &bound) {
-  mark_bound_stale();
-  CDWriter cdata(_cycler);
+  set_bound(bound, Thread::get_current_pipeline_stage());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BoundedObject::set_bound
+//       Access: Published
+//  Description: Explicitly sets a new bounding volume on this node.
+//               This will be a static bounding volume that will no
+//               longer be recomputed automatically.
+////////////////////////////////////////////////////////////////////
+INLINE void BoundedObject::
+set_bound(const BoundingVolume &bound, int pipeline_stage) {
+  mark_bound_stale(pipeline_stage);
+  CDStageWriter cdata(_cycler, pipeline_stage);
   cdata->_bound_type = BVT_static;
   cdata->_bound_type = BVT_static;
   cdata->_flags &= ~F_bound_stale;  
   cdata->_flags &= ~F_bound_stale;  
   cdata->_bound = bound.make_copy();
   cdata->_bound = bound.make_copy();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundedObject::get_bound
+//       Access: Published
+//  Description: Returns the current bounding volume on this node,
+//               possibly forcing a recompute.  A node's bounding
+//               volume encloses only the node itself, irrespective of
+//               the nodes above or below it in the graph.  This is
+//               different from the bounding volumes on the arcs,
+//               which enclose all geometry below them.
+////////////////////////////////////////////////////////////////////
+INLINE const BoundingVolume *BoundedObject::
+get_bound() const {
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  return get_bound(pipeline_stage);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BoundedObject::mark_bound_stale
 //     Function: BoundedObject::mark_bound_stale
 //       Access: Published
 //       Access: Published
@@ -124,18 +164,24 @@ set_bound(const BoundingVolume &bound) {
 //               which the node is a part.  Returns true if the
 //               which the node is a part.  Returns true if the
 //               setting was changed, or false if it was already
 //               setting was changed, or false if it was already
 //               marked stale (or if it is a static bounding volume).
 //               marked stale (or if it is a static bounding volume).
+//
+//               This also marks the bounding volume stale on all
+//               upstream pipeline stages at the same time.  However,
+//               the test for staleness is only made against the
+//               current pipeline stage.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool BoundedObject::
 INLINE bool BoundedObject::
-mark_bound_stale() {
-  if (is_bound_stale()) {
+mark_bound_stale(int pipeline_stage) {
+  CDStageReader cdata(_cycler, pipeline_stage);
+  if ((cdata->_flags & F_bound_stale) != 0) {
+    // Already stale.
     return false;
     return false;
   }
   }
-  {
-    CDWriter cdata(_cycler);
-    cdata->_flags |= F_bound_stale;
-  }
-  propagate_stale_bound();
 
 
+  // Mark it stale now.
+  CDStageWriter cdataw(_cycler, pipeline_stage, cdata);
+  cdataw->_flags |= F_bound_stale;
+  propagate_stale_bound(pipeline_stage);
   return true;
   return true;
 }
 }
 
 
@@ -146,13 +192,12 @@ mark_bound_stale() {
 //               effect at least one level, even if it had already
 //               effect at least one level, even if it had already
 //               been marked stale.
 //               been marked stale.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE void BoundedObject::
-force_bound_stale() {
-  {
-    CDWriter cdata(_cycler);
-    cdata->_flags |= F_bound_stale;
-  }
-  propagate_stale_bound();
+void BoundedObject::
+force_bound_stale(int pipeline_stage) {
+  CDStageWriter cdata(_cycler, pipeline_stage);
+  cdata->_flags |= F_bound_stale;
+
+  propagate_stale_bound(pipeline_stage);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -172,6 +217,23 @@ is_bound_stale() const {
   return (cdata->_flags & F_bound_stale) != 0;
   return (cdata->_flags & F_bound_stale) != 0;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: BoundedObject::is_bound_stale
+//       Access: Published
+//  Description: Returns true if the bound is currently marked stale
+//               and will be recomputed the next time get_bound() is
+//               called.
+//
+//               This function is defined up at the top of this file,
+//               because several of the inline functions below
+//               reference it.
+////////////////////////////////////////////////////////////////////
+INLINE bool BoundedObject::
+is_bound_stale(int pipeline_stage) const {
+  CDStageReader cdata(_cycler, pipeline_stage);
+  return (cdata->_flags & F_bound_stale) != 0;
+}
+
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: BoundedObject::set_final
 //     Function: BoundedObject::set_final

+ 77 - 23
panda/src/gobj/boundedObject.cxx

@@ -42,29 +42,83 @@ BoundedObject::
 //               different from the bounding volumes on the arcs,
 //               different from the bounding volumes on the arcs,
 //               which enclose all geometry below them.
 //               which enclose all geometry below them.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-const BoundingVolume &BoundedObject::
-get_bound() const {
-  {
-    CDReader cdata(_cycler);
-    if (cdata->_bound_type == BVT_static) {
-      CDWriter cdata_w(((BoundedObject *)this)->_cycler, cdata);
-      cdata_w->_flags &= ~F_bound_stale;
-      return *cdata_w->_bound;
-    }
-    
-    if (!is_bound_stale() && cdata->_bound != (BoundingVolume *)NULL) {
-      return *cdata->_bound;
+const BoundingVolume *BoundedObject::
+get_bound(int pipeline_stage) const {
+  CDStageReader cdata(_cycler, pipeline_stage);
+  if (cdata->_bound_type == BVT_static) {
+    CDStageWriter cdataw(((BoundedObject *)this)->_cycler, pipeline_stage, cdata);
+    cdataw->_flags &= ~F_bound_stale;
+    return cdataw->_bound;
+  }
+
+  if ((cdata->_flags & F_bound_stale) == 0 &&
+      cdata->_bound != (BoundingVolume *)NULL) {
+    return cdata->_bound;
+  }
+
+  // We need to recompute the bounding volume.  We do this on the
+  // current pipeline stage as well as on all upstream stages, so we
+  // don't lose the cache value.
+  CDStageWriter cdataw(((BoundedObject *)this)->_cycler, pipeline_stage, cdata);
+
+#ifdef DO_PIPELINING
+  ((BoundedObject *)this)->_cycler.lock();
+  for (int i = 0; i < pipeline_stage; ++i) {
+    CDStageReader stage_cdata(_cycler, i);
+    if ((cdataw->_flags & F_bound_stale) != 0) {
+      CDStageWriter stage_cdataw(((BoundedObject *)this)->_cycler, i, stage_cdata);
+      ((BoundedObject *)this)->recompute_bound(i);
     }
     }
+  }
+  ((BoundedObject *)this)->_cycler.release();
+#endif
+
+  ((BoundedObject *)this)->recompute_bound(pipeline_stage);
+      
+  return cdataw->_bound;
+}
 
 
-    // We need to recompute the bounding volume.  First, we need to
-    // release the old CDReader, so we can make a CDWriter in
-    // recompute_bound.
+////////////////////////////////////////////////////////////////////
+//     Function: BoundedObject::mark_bound_stale
+//       Access: Published
+//  Description: Marks the current bounding volume as stale, so that
+//               it will be recomputed later.  This may have a
+//               cascading effect up to the root of all graphs of
+//               which the node is a part.  Returns true if the
+//               setting was changed, or false if it was already
+//               marked stale (or if it is a static bounding volume).
+////////////////////////////////////////////////////////////////////
+bool BoundedObject::
+mark_bound_stale() {
+  // With no pipeline stage specified, we must mark the bound stale on
+  // all upstream stages.
+  bool any_changed = false;
+
+  OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler) {
+    if (mark_bound_stale(pipeline_stage)) {
+      any_changed = true;
+    }
   }
   }
+  CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
+
+  return any_changed;
+}
 
 
-  // Now it's safe to recompute the bounds.
-  ((BoundedObject *)this)->recompute_bound();
-  CDReader cdata(_cycler);
-  return *cdata->_bound;
+////////////////////////////////////////////////////////////////////
+//     Function: BoundedObject::force_bound_stale
+//       Access: Published
+//  Description: Marks the current volume as stale and propagates the
+//               effect at least one level, even if it had already
+//               been marked stale.
+////////////////////////////////////////////////////////////////////
+void BoundedObject::
+force_bound_stale() {
+  // With no pipeline stage specified, we must force the bound stale on
+  // all upstream stages.
+  OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler) {
+    force_bound_stale(pipeline_stage);
+  }
+  CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -85,7 +139,7 @@ make_copy() const {
 //               depend on this one are marked stale also.
 //               depend on this one are marked stale also.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void BoundedObject::
 void BoundedObject::
-propagate_stale_bound() {
+propagate_stale_bound(int pipeline_stage) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -96,13 +150,13 @@ propagate_stale_bound() {
 //               bounding volume; this may be overridden to extend it
 //               bounding volume; this may be overridden to extend it
 //               to create a nonempty bounding volume.  However, after
 //               to create a nonempty bounding volume.  However, after
 //               calling this function, it is guaranteed that the
 //               calling this function, it is guaranteed that the
-//               _bound pointer will not be shared with any other
+//               _bound pointer will not be shared with any downstream
 //               stage of the pipeline, and this new pointer is
 //               stage of the pipeline, and this new pointer is
 //               returned.
 //               returned.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 BoundingVolume *BoundedObject::
 BoundingVolume *BoundedObject::
-recompute_bound() {
-  CDWriter cdata(_cycler);
+recompute_bound(int pipeline_stage) {
+  CDStageWriter cdata(_cycler, pipeline_stage);
   switch (cdata->_bound_type) {
   switch (cdata->_bound_type) {
   case BVT_static:
   case BVT_static:
     // Don't change it if it's a static volume.
     // Don't change it if it's a static volume.

+ 22 - 6
panda/src/gobj/boundedObject.h

@@ -25,6 +25,8 @@
 #include "cycleData.h"
 #include "cycleData.h"
 #include "cycleDataReader.h"
 #include "cycleDataReader.h"
 #include "cycleDataWriter.h"
 #include "cycleDataWriter.h"
+#include "cycleDataStageReader.h"
+#include "cycleDataStageWriter.h"
 #include "pipelineCycler.h"
 #include "pipelineCycler.h"
 #include "typedObject.h"
 #include "typedObject.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
@@ -36,6 +38,12 @@
 //               The user may set a fixed bounding volume, or s/he may
 //               The user may set a fixed bounding volume, or s/he may
 //               specify that the volume should be recomputed
 //               specify that the volume should be recomputed
 //               dynamically.
 //               dynamically.
+//
+//               In general, when in a multistate pipeline
+//               environment, changes to a bounding volume are
+//               automatically and immediately propagated upwards to
+//               upstream stages, and will propagate to downstream
+//               stages normally with each passing frame.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA BoundedObject {
 class EXPCL_PANDA BoundedObject {
 public:
 public:
@@ -51,19 +59,25 @@ PUBLISHED:
   };
   };
 
 
   INLINE void set_bound(BoundingVolumeType type);
   INLINE void set_bound(BoundingVolumeType type);
+  INLINE void set_bound(BoundingVolumeType type, int pipeline_stage);
   INLINE void set_bound(const BoundingVolume &volume);
   INLINE void set_bound(const BoundingVolume &volume);
-  const BoundingVolume &get_bound() const;
-
-  INLINE bool mark_bound_stale();
-  INLINE void force_bound_stale();
+  INLINE void set_bound(const BoundingVolume &volume, int pipeline_stage);
+  INLINE const BoundingVolume *get_bound() const;
+  const BoundingVolume *get_bound(int pipeline_stage) const;
+
+  bool mark_bound_stale();
+  INLINE bool mark_bound_stale(int pipeline_stage);
+  void force_bound_stale();
+  INLINE void force_bound_stale(int pipeline_stage);
   INLINE bool is_bound_stale() const;
   INLINE bool is_bound_stale() const;
+  INLINE bool is_bound_stale(int pipeline_stagea) const;
 
 
   INLINE void set_final(bool flag);
   INLINE void set_final(bool flag);
   INLINE bool is_final() const;
   INLINE bool is_final() const;
 
 
 protected:
 protected:
-  virtual void propagate_stale_bound();
-  virtual BoundingVolume *recompute_bound();
+  virtual void propagate_stale_bound(int pipeline_stage);
+  virtual BoundingVolume *recompute_bound(int pipeline_stage);
 
 
   INLINE const BoundingVolume *get_bound_ptr() const;
   INLINE const BoundingVolume *get_bound_ptr() const;
   INLINE BoundingVolume *set_bound_ptr(BoundingVolume *bound);
   INLINE BoundingVolume *set_bound_ptr(BoundingVolume *bound);
@@ -94,6 +108,8 @@ private:
   PipelineCycler<CData> _cycler;
   PipelineCycler<CData> _cycler;
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataWriter<CData> CDWriter;
   typedef CycleDataWriter<CData> CDWriter;
+  typedef CycleDataStageReader<CData> CDStageReader;
+  typedef CycleDataStageWriter<CData> CDStageWriter;
 
 
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {

+ 1 - 1
panda/src/gobj/drawable.cxx

@@ -76,7 +76,7 @@ is_dynamic() const {
 //               depend on this one are marked stale also.
 //               depend on this one are marked stale also.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void dDrawable::
 void dDrawable::
-propagate_stale_bound() {
+propagate_stale_bound(int) {
   // Unforunately, we don't have a pointer to the GeomNode that
   // Unforunately, we don't have a pointer to the GeomNode that
   // includes us, so we can't propagate the bounding volume change
   // includes us, so we can't propagate the bounding volume change
   // upwards.  Need to address this.
   // upwards.  Need to address this.

+ 1 - 5
panda/src/gobj/drawable.h

@@ -25,10 +25,6 @@
 #include "writableConfigurable.h"
 #include "writableConfigurable.h"
 #include "referenceCount.h"
 #include "referenceCount.h"
 
 
-////////////////////////////////////////////////////////////////////
-// Defines
-////////////////////////////////////////////////////////////////////
-
 class GraphicsStateGuardianBase;
 class GraphicsStateGuardianBase;
 class Datagram;
 class Datagram;
 class DatagramIterator;
 class DatagramIterator;
@@ -58,7 +54,7 @@ public:
   virtual bool is_dynamic() const;
   virtual bool is_dynamic() const;
 
 
 protected:
 protected:
-  virtual void propagate_stale_bound();
+  virtual void propagate_stale_bound(int pipeline_stage);
 
 
 public:
 public:
   virtual void write_datagram(BamWriter* manager, Datagram &me);
   virtual void write_datagram(BamWriter* manager, Datagram &me);

+ 17 - 62
panda/src/gobj/geom.I

@@ -30,8 +30,7 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE Geom::PrimitiveType Geom::
 INLINE Geom::PrimitiveType Geom::
 get_primitive_type() const {
 get_primitive_type() const {
-  CDReader cdata(_cycler);
-  return cdata->_primitive_type;
+  return _primitive_type;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -43,8 +42,7 @@ get_primitive_type() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE Geom::ShadeModel Geom::
 INLINE Geom::ShadeModel Geom::
 get_shade_model() const {
 get_shade_model() const {
-  CDReader cdata(_cycler);
-  return cdata->_shade_model;
+  return _shade_model;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -56,8 +54,7 @@ get_shade_model() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int Geom::
 INLINE int Geom::
 get_geom_rendering() const {
 get_geom_rendering() const {
-  CDReader cdata(_cycler);
-  return cdata->_geom_rendering;
+  return _geom_rendering;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -69,13 +66,10 @@ get_geom_rendering() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE Geom::UsageHint Geom::
 INLINE Geom::UsageHint Geom::
 get_usage_hint() const {
 get_usage_hint() const {
-  CDReader cdata(_cycler);
-  if (!cdata->_got_usage_hint) {
-    CDWriter cdataw(((Geom *)this)->_cycler, cdata);
-    ((Geom *)this)->reset_usage_hint(cdataw);
-    return cdataw->_usage_hint;
+  if (!_got_usage_hint) {
+    ((Geom *)this)->reset_usage_hint();
   }
   }
-  return cdata->_usage_hint;
+  return _usage_hint;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -87,8 +81,7 @@ get_usage_hint() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE CPT(GeomVertexData) Geom::
 INLINE CPT(GeomVertexData) Geom::
 get_vertex_data() const {
 get_vertex_data() const {
-  CDReader cdata(_cycler);
-  return cdata->_data;
+  return _data;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -100,8 +93,7 @@ get_vertex_data() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int Geom::
 INLINE int Geom::
 get_num_primitives() const {
 get_num_primitives() const {
-  CDReader cdata(_cycler);
-  return cdata->_primitives.size();
+  return _primitives.size();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -114,9 +106,8 @@ get_num_primitives() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const GeomPrimitive *Geom::
 INLINE const GeomPrimitive *Geom::
 get_primitive(int i) const {
 get_primitive(int i) const {
-  CDReader cdata(_cycler);
-  nassertr(i >= 0 && i < (int)cdata->_primitives.size(), NULL);
-  return cdata->_primitives[i];
+  nassertr(i >= 0 && i < (int)_primitives.size(), NULL);
+  return _primitives[i];
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -130,14 +121,13 @@ get_primitive(int i) const {
 INLINE GeomPrimitive *Geom::
 INLINE GeomPrimitive *Geom::
 modify_primitive(int i) {
 modify_primitive(int i) {
   clear_cache();
   clear_cache();
-  CDWriter cdata(_cycler);
-  nassertr(i >= 0 && i < (int)cdata->_primitives.size(), NULL);
-  cdata->_got_usage_hint = false;
-  cdata->_modified = Geom::get_next_modified();
-  if (cdata->_primitives[i]->get_ref_count() > 1) {
-    cdata->_primitives[i] = cdata->_primitives[i]->make_copy();
+  nassertr(i >= 0 && i < (int)_primitives.size(), NULL);
+  _got_usage_hint = false;
+  _modified = Geom::get_next_modified();
+  if (_primitives[i]->get_ref_count() > 1) {
+    _primitives[i] = _primitives[i]->make_copy();
   }
   }
-  return cdata->_primitives[i];
+  return _primitives[i];
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -196,8 +186,7 @@ unify() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE UpdateSeq Geom::
 INLINE UpdateSeq Geom::
 get_modified() const {
 get_modified() const {
-  CDReader cdata(_cycler);
-  return cdata->_modified;
+  return _modified;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -274,40 +263,6 @@ operator < (const CacheEntry &other) const {
   return 0;
   return 0;
 }
 }
 
 
-
-////////////////////////////////////////////////////////////////////
-//     Function: Geom::CData::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE Geom::CData::
-CData() :
-  _primitive_type(PT_none),
-  _shade_model(SM_uniform),
-  _geom_rendering(0),
-  _usage_hint(UH_unspecified),
-  _got_usage_hint(false)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Geom::CData::Copy Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE Geom::CData::
-CData(const Geom::CData &copy) :
-  _data(copy._data),
-  _primitives(copy._primitives),
-  _primitive_type(copy._primitive_type),
-  _shade_model(copy._shade_model),
-  _geom_rendering(copy._geom_rendering),
-  _usage_hint(copy._usage_hint),
-  _got_usage_hint(copy._got_usage_hint),
-  _modified(copy._modified)
-{
-}
-
 INLINE ostream &
 INLINE ostream &
 operator << (ostream &out, const Geom &obj) {
 operator << (ostream &out, const Geom &obj) {
   obj.output(out);
   obj.output(out);

+ 211 - 289
panda/src/gobj/geom.cxx

@@ -35,7 +35,13 @@ TypeHandle Geom::_type_handle;
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 Geom::
 Geom::
-Geom(const GeomVertexData *data) {
+Geom(const GeomVertexData *data) :
+  _primitive_type(PT_none),
+  _shade_model(SM_uniform),
+  _geom_rendering(0),
+  _usage_hint(UH_unspecified),
+  _got_usage_hint(false)
+{
   set_vertex_data(data);
   set_vertex_data(data);
 }
 }
 
 
@@ -48,7 +54,14 @@ Geom::
 Geom(const Geom &copy) :
 Geom(const Geom &copy) :
   TypedWritableReferenceCount(copy),
   TypedWritableReferenceCount(copy),
   BoundedObject(copy),
   BoundedObject(copy),
-  _cycler(copy._cycler)  
+  _data(copy._data),
+  _primitives(copy._primitives),
+  _primitive_type(copy._primitive_type),
+  _shade_model(copy._shade_model),
+  _geom_rendering(copy._geom_rendering),
+  _usage_hint(copy._usage_hint),
+  _got_usage_hint(copy._got_usage_hint),
+  _modified(copy._modified)
 {
 {
 }
 }
 
 
@@ -62,7 +75,16 @@ operator = (const Geom &copy) {
   TypedWritableReferenceCount::operator = (copy);
   TypedWritableReferenceCount::operator = (copy);
   BoundedObject::operator = (copy);
   BoundedObject::operator = (copy);
   clear_cache();
   clear_cache();
-  _cycler = copy._cycler;
+
+  _data = copy._data;
+  _primitives = copy._primitives;
+  _primitive_type = copy._primitive_type;
+  _shade_model = copy._shade_model;
+  _geom_rendering = copy._geom_rendering;
+  _usage_hint = copy._usage_hint;
+  _got_usage_hint = copy._got_usage_hint;
+  _modified = copy._modified;
+
   mark_bound_stale();
   mark_bound_stale();
 }
 }
 
 
@@ -73,23 +95,13 @@ operator = (const Geom &copy) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 Geom::
 Geom::
 ~Geom() {
 ~Geom() {
-  // When we destruct, we should ensure that all of our cached
-  // entries, across all pipeline stages, are properly removed from
-  // the cache manager.
-  int num_stages = _cycler.get_num_stages();
-  for (int i = 0; i < num_stages; i++) {
-    if (_cycler.is_stage_unique(i)) {
-      CData *cdata = _cycler.write_stage(i);
-      for (Cache::iterator ci = cdata->_cache.begin();
-           ci != cdata->_cache.end();
-           ++ci) {
-        CacheEntry *entry = (*ci);
-        entry->erase();
-      }
-      cdata->_cache.clear();
-      _cycler.release_write_stage(i, cdata);
-    }
+  for (Cache::iterator ci = _cache.begin();
+       ci != _cache.end();
+       ++ci) {
+    CacheEntry *entry = (*ci);
+    entry->erase();
   }
   }
+  _cache.clear();
 
 
   release_all();
   release_all();
 }
 }
@@ -117,18 +129,16 @@ make_copy() const {
 void Geom::
 void Geom::
 set_usage_hint(Geom::UsageHint usage_hint) {
 set_usage_hint(Geom::UsageHint usage_hint) {
   clear_cache();
   clear_cache();
-  CDWriter cdata(_cycler);
-  cdata->_usage_hint = usage_hint;
+  _usage_hint = usage_hint;
 
 
   Primitives::iterator pi;
   Primitives::iterator pi;
-  for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
+  for (pi = _primitives.begin(); pi != _primitives.end(); ++pi) {
     if ((*pi)->get_ref_count() > 1) {
     if ((*pi)->get_ref_count() > 1) {
       (*pi) = (*pi)->make_copy();
       (*pi) = (*pi)->make_copy();
     }
     }
     (*pi)->set_usage_hint(usage_hint);
     (*pi)->set_usage_hint(usage_hint);
   }
   }
-
-  cdata->_modified = Geom::get_next_modified();
+  _modified = Geom::get_next_modified();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -144,12 +154,11 @@ modify_vertex_data() {
   // is greater than 1, assume some other Geom has the same pointer,
   // is greater than 1, assume some other Geom has the same pointer,
   // so make a copy of it first.
   // so make a copy of it first.
   clear_cache();
   clear_cache();
-  CDWriter cdata(_cycler);
-  if (cdata->_data->get_ref_count() > 1) {
-    cdata->_data = new GeomVertexData(*cdata->_data);
+  if (_data->get_ref_count() > 1) {
+    _data = new GeomVertexData(*_data);
   }
   }
   mark_bound_stale();
   mark_bound_stale();
-  return cdata->_data;
+  return _data;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -161,11 +170,11 @@ modify_vertex_data() {
 void Geom::
 void Geom::
 set_vertex_data(const GeomVertexData *data) {
 set_vertex_data(const GeomVertexData *data) {
   nassertv(check_will_be_valid(data));
   nassertv(check_will_be_valid(data));
+
   clear_cache();
   clear_cache();
-  CDWriter cdata(_cycler);
-  cdata->_data = (GeomVertexData *)data;
+  _data = (GeomVertexData *)data;
   mark_bound_stale();
   mark_bound_stale();
-  reset_geom_rendering(cdata);
+  reset_geom_rendering();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -182,14 +191,13 @@ set_vertex_data(const GeomVertexData *data) {
 void Geom::
 void Geom::
 offset_vertices(const GeomVertexData *data, int offset) {
 offset_vertices(const GeomVertexData *data, int offset) {
   clear_cache();
   clear_cache();
-  CDWriter cdata(_cycler);
-  cdata->_data = (GeomVertexData *)data;
+  _data = (GeomVertexData *)data;
 
 
 #ifndef NDEBUG
 #ifndef NDEBUG
   bool all_is_valid = true;
   bool all_is_valid = true;
 #endif
 #endif
   Primitives::iterator pi;
   Primitives::iterator pi;
-  for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
+  for (pi = _primitives.begin(); pi != _primitives.end(); ++pi) {
     if ((*pi)->get_ref_count() > 1) {
     if ((*pi)->get_ref_count() > 1) {
       (*pi) = (*pi)->make_copy();
       (*pi) = (*pi)->make_copy();
     }
     }
@@ -202,7 +210,7 @@ offset_vertices(const GeomVertexData *data, int offset) {
 #endif
 #endif
   }
   }
 
 
-  cdata->_modified = Geom::get_next_modified();
+  _modified = Geom::get_next_modified();
   nassertv(all_is_valid);
   nassertv(all_is_valid);
 }
 }
 
 
@@ -220,21 +228,20 @@ make_nonindexed(bool composite_only) {
   int num_changed = 0;
   int num_changed = 0;
 
 
   clear_cache();
   clear_cache();
-  CDWriter cdata(_cycler);
-  CPT(GeomVertexData) orig_data = cdata->_data;
-  PT(GeomVertexData) new_data = new GeomVertexData(*cdata->_data);
+  CPT(GeomVertexData) orig_data = _data;
+  PT(GeomVertexData) new_data = new GeomVertexData(*_data);
   new_data->clear_rows();
   new_data->clear_rows();
-
+  
 #ifndef NDEBUG
 #ifndef NDEBUG
   bool all_is_valid = true;
   bool all_is_valid = true;
 #endif
 #endif
   Primitives::iterator pi;
   Primitives::iterator pi;
   Primitives new_prims;
   Primitives new_prims;
-  new_prims.reserve(cdata->_primitives.size());
-  for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
+  new_prims.reserve(_primitives.size());
+  for (pi = _primitives.begin(); pi != _primitives.end(); ++pi) {
     PT(GeomPrimitive) primitive = (*pi)->make_copy();
     PT(GeomPrimitive) primitive = (*pi)->make_copy();
     new_prims.push_back(primitive);
     new_prims.push_back(primitive);
-
+    
     // GeomPoints are considered "composite" for the purposes of
     // GeomPoints are considered "composite" for the purposes of
     // making nonindexed, since there's no particular advantage to
     // making nonindexed, since there's no particular advantage to
     // having indexed points (as opposed to, say, indexed triangles or
     // having indexed points (as opposed to, say, indexed triangles or
@@ -250,25 +257,25 @@ make_nonindexed(bool composite_only) {
       // the same GeomVertexData.
       // the same GeomVertexData.
       primitive->pack_vertices(new_data, orig_data);
       primitive->pack_vertices(new_data, orig_data);
     }
     }
-
+    
 #ifndef NDEBUG
 #ifndef NDEBUG
     if (!primitive->check_valid(new_data)) {
     if (!primitive->check_valid(new_data)) {
       all_is_valid = false;
       all_is_valid = false;
     }
     }
 #endif
 #endif
   }
   }
-
+  
   nassertr(all_is_valid, 0);
   nassertr(all_is_valid, 0);
-
+  
   if (num_changed != 0) {
   if (num_changed != 0) {
     // If any at all were changed, then keep the result (otherwise,
     // If any at all were changed, then keep the result (otherwise,
     // discard it, since we might have de-optimized the indexed
     // discard it, since we might have de-optimized the indexed
     // geometry a bit).
     // geometry a bit).
-    cdata->_data = new_data;
-    cdata->_primitives.swap(new_prims);
-    cdata->_modified = Geom::get_next_modified();
+    _data = new_data;
+    _primitives.swap(new_prims);
+    _modified = Geom::get_next_modified();
   }
   }
-
+  
   return num_changed;
   return num_changed;
 }
 }
 
 
@@ -281,33 +288,32 @@ make_nonindexed(bool composite_only) {
 void Geom::
 void Geom::
 set_primitive(int i, const GeomPrimitive *primitive) {
 set_primitive(int i, const GeomPrimitive *primitive) {
   clear_cache();
   clear_cache();
-  CDWriter cdata(_cycler);
-  nassertv(i >= 0 && i < (int)cdata->_primitives.size());
-  nassertv(primitive->check_valid(cdata->_data));
+  nassertv(i >= 0 && i < (int)_primitives.size());
+  nassertv(primitive->check_valid(_data));
 
 
   // All primitives within a particular Geom must have the same
   // All primitives within a particular Geom must have the same
   // fundamental primitive type (triangles, points, or lines).
   // fundamental primitive type (triangles, points, or lines).
-  nassertv(cdata->_primitive_type == PT_none ||
-           cdata->_primitive_type == primitive->get_primitive_type());
+  nassertv(_primitive_type == PT_none ||
+           _primitive_type == primitive->get_primitive_type());
 
 
   // They also should have the a compatible shade model.
   // They also should have the a compatible shade model.
-  CPT(GeomPrimitive) compat = primitive->match_shade_model(cdata->_shade_model);
+  CPT(GeomPrimitive) compat = primitive->match_shade_model(_shade_model);
   nassertv_always(compat != (GeomPrimitive *)NULL);
   nassertv_always(compat != (GeomPrimitive *)NULL);
 
 
-  cdata->_primitives[i] = (GeomPrimitive *)compat.p();
+  _primitives[i] = (GeomPrimitive *)compat.p();
   PrimitiveType new_primitive_type = compat->get_primitive_type();
   PrimitiveType new_primitive_type = compat->get_primitive_type();
-  if (new_primitive_type != cdata->_primitive_type) {
-    cdata->_primitive_type = new_primitive_type;
+  if (new_primitive_type != _primitive_type) {
+    _primitive_type = new_primitive_type;
   }
   }
   ShadeModel new_shade_model = compat->get_shade_model();
   ShadeModel new_shade_model = compat->get_shade_model();
-  if (new_shade_model != cdata->_shade_model &&
+  if (new_shade_model != _shade_model &&
       new_shade_model != SM_uniform) {
       new_shade_model != SM_uniform) {
-    cdata->_shade_model = new_shade_model;
+    _shade_model = new_shade_model;
   }
   }
 
 
-  reset_geom_rendering(cdata);
-  cdata->_got_usage_hint = false;
-  cdata->_modified = Geom::get_next_modified();
+  reset_geom_rendering();
+  _got_usage_hint = false;
+  _modified = Geom::get_next_modified();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -321,33 +327,31 @@ set_primitive(int i, const GeomPrimitive *primitive) {
 void Geom::
 void Geom::
 add_primitive(const GeomPrimitive *primitive) {
 add_primitive(const GeomPrimitive *primitive) {
   clear_cache();
   clear_cache();
-  CDWriter cdata(_cycler);
-
-  nassertv(primitive->check_valid(cdata->_data));
+  nassertv(primitive->check_valid(_data));
 
 
   // All primitives within a particular Geom must have the same
   // All primitives within a particular Geom must have the same
   // fundamental primitive type (triangles, points, or lines).
   // fundamental primitive type (triangles, points, or lines).
-  nassertv(cdata->_primitive_type == PT_none ||
-           cdata->_primitive_type == primitive->get_primitive_type());
+  nassertv(_primitive_type == PT_none ||
+           _primitive_type == primitive->get_primitive_type());
 
 
   // They also should have the a compatible shade model.
   // They also should have the a compatible shade model.
-  CPT(GeomPrimitive) compat = primitive->match_shade_model(cdata->_shade_model);
+  CPT(GeomPrimitive) compat = primitive->match_shade_model(_shade_model);
   nassertv_always(compat != (GeomPrimitive *)NULL);
   nassertv_always(compat != (GeomPrimitive *)NULL);
 
 
-  cdata->_primitives.push_back((GeomPrimitive *)compat.p());
+  _primitives.push_back((GeomPrimitive *)compat.p());
   PrimitiveType new_primitive_type = compat->get_primitive_type();
   PrimitiveType new_primitive_type = compat->get_primitive_type();
-  if (new_primitive_type != cdata->_primitive_type) {
-    cdata->_primitive_type = new_primitive_type;
+  if (new_primitive_type != _primitive_type) {
+    _primitive_type = new_primitive_type;
   }
   }
   ShadeModel new_shade_model = compat->get_shade_model();
   ShadeModel new_shade_model = compat->get_shade_model();
-  if (new_shade_model != cdata->_shade_model &&
+  if (new_shade_model != _shade_model &&
       new_shade_model != SM_uniform) {
       new_shade_model != SM_uniform) {
-    cdata->_shade_model = new_shade_model;
+    _shade_model = new_shade_model;
   }
   }
 
 
-  reset_geom_rendering(cdata);
-  cdata->_got_usage_hint = false;
-  cdata->_modified = Geom::get_next_modified();
+  reset_geom_rendering();
+  _got_usage_hint = false;
+  _modified = Geom::get_next_modified();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -358,16 +362,15 @@ add_primitive(const GeomPrimitive *primitive) {
 void Geom::
 void Geom::
 remove_primitive(int i) {
 remove_primitive(int i) {
   clear_cache();
   clear_cache();
-  CDWriter cdata(_cycler);
-  nassertv(i >= 0 && i < (int)cdata->_primitives.size());
-  cdata->_primitives.erase(cdata->_primitives.begin() + i);
-  if (cdata->_primitives.empty()) {
-    cdata->_primitive_type = PT_none;
-    cdata->_shade_model = SM_uniform;
+  nassertv(i >= 0 && i < (int)_primitives.size());
+  _primitives.erase(_primitives.begin() + i);
+  if (_primitives.empty()) {
+    _primitive_type = PT_none;
+    _shade_model = SM_uniform;
   }
   }
-  reset_geom_rendering(cdata);
-  cdata->_got_usage_hint = false;
-  cdata->_modified = Geom::get_next_modified();
+  reset_geom_rendering();
+  _got_usage_hint = false;
+  _modified = Geom::get_next_modified();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -381,11 +384,10 @@ remove_primitive(int i) {
 void Geom::
 void Geom::
 clear_primitives() {
 clear_primitives() {
   clear_cache();
   clear_cache();
-  CDWriter cdata(_cycler);
-  cdata->_primitives.clear();
-  cdata->_primitive_type = PT_none;
-  cdata->_shade_model = SM_uniform;
-  reset_geom_rendering(cdata);
+  _primitives.clear();
+  _primitive_type = PT_none;
+  _shade_model = SM_uniform;
+  reset_geom_rendering();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -398,25 +400,24 @@ clear_primitives() {
 void Geom::
 void Geom::
 decompose_in_place() {
 decompose_in_place() {
   clear_cache();
   clear_cache();
-  CDWriter cdata(_cycler);
 
 
 #ifndef NDEBUG
 #ifndef NDEBUG
   bool all_is_valid = true;
   bool all_is_valid = true;
 #endif
 #endif
   Primitives::iterator pi;
   Primitives::iterator pi;
-  for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
+  for (pi = _primitives.begin(); pi != _primitives.end(); ++pi) {
     CPT(GeomPrimitive) new_prim = (*pi)->decompose();
     CPT(GeomPrimitive) new_prim = (*pi)->decompose();
     (*pi) = (GeomPrimitive *)new_prim.p();
     (*pi) = (GeomPrimitive *)new_prim.p();
 
 
 #ifndef NDEBUG
 #ifndef NDEBUG
-    if (!(*pi)->check_valid(cdata->_data)) {
+    if (!(*pi)->check_valid(_data)) {
       all_is_valid = false;
       all_is_valid = false;
     }
     }
 #endif
 #endif
   }
   }
 
 
-  cdata->_modified = Geom::get_next_modified();
-  reset_geom_rendering(cdata);
+  _modified = Geom::get_next_modified();
+  reset_geom_rendering();
 
 
   nassertv(all_is_valid);
   nassertv(all_is_valid);
 }
 }
@@ -431,24 +432,23 @@ decompose_in_place() {
 void Geom::
 void Geom::
 rotate_in_place() {
 rotate_in_place() {
   clear_cache();
   clear_cache();
-  CDWriter cdata(_cycler);
 
 
 #ifndef NDEBUG
 #ifndef NDEBUG
   bool all_is_valid = true;
   bool all_is_valid = true;
 #endif
 #endif
   Primitives::iterator pi;
   Primitives::iterator pi;
-  for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
+  for (pi = _primitives.begin(); pi != _primitives.end(); ++pi) {
     CPT(GeomPrimitive) new_prim = (*pi)->rotate();
     CPT(GeomPrimitive) new_prim = (*pi)->rotate();
     (*pi) = (GeomPrimitive *)new_prim.p();
     (*pi) = (GeomPrimitive *)new_prim.p();
 
 
 #ifndef NDEBUG
 #ifndef NDEBUG
-    if (!(*pi)->check_valid(cdata->_data)) {
+    if (!(*pi)->check_valid(_data)) {
       all_is_valid = false;
       all_is_valid = false;
     }
     }
 #endif
 #endif
   }
   }
 
 
-  cdata->_modified = Geom::get_next_modified();
+  _modified = Geom::get_next_modified();
 
 
   nassertv(all_is_valid);
   nassertv(all_is_valid);
 }
 }
@@ -471,12 +471,11 @@ unify_in_place() {
   }
   }
 
 
   clear_cache();
   clear_cache();
-  CDWriter cdata(_cycler);
 
 
   PT(GeomPrimitive) new_prim;
   PT(GeomPrimitive) new_prim;
 
 
   Primitives::const_iterator pi;
   Primitives::const_iterator pi;
-  for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) {
+  for (pi = _primitives.begin(); pi != _primitives.end(); ++pi) {
     CPT(GeomPrimitive) primitive = (*pi);
     CPT(GeomPrimitive) primitive = (*pi);
     if (new_prim == (GeomPrimitive *)NULL) {
     if (new_prim == (GeomPrimitive *)NULL) {
       // The first primitive type we come across is copied directly.
       // The first primitive type we come across is copied directly.
@@ -507,17 +506,17 @@ unify_in_place() {
 
 
   // At the end of the day, we have just one primitive, which becomes
   // At the end of the day, we have just one primitive, which becomes
   // the one primitive in our list of primitives.
   // the one primitive in our list of primitives.
-  nassertv(new_prim->check_valid(cdata->_data));
+  nassertv(new_prim->check_valid(_data));
 
 
   // The new primitive, naturally, inherits the Geom's overall shade
   // The new primitive, naturally, inherits the Geom's overall shade
   // model.
   // model.
-  new_prim->set_shade_model(cdata->_shade_model);
+  new_prim->set_shade_model(_shade_model);
 
 
-  cdata->_primitives.clear();
-  cdata->_primitives.push_back(new_prim);
+  _primitives.clear();
+  _primitives.push_back(new_prim);
 
 
-  cdata->_modified = Geom::get_next_modified();
-  reset_geom_rendering(cdata);
+  _modified = Geom::get_next_modified();
+  reset_geom_rendering();
 }
 }
 
 
 
 
@@ -578,12 +577,10 @@ copy_primitives_from(const Geom *other) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int Geom::
 int Geom::
 get_num_bytes() const {
 get_num_bytes() const {
-  CDReader cdata(_cycler);
-
   int num_bytes = sizeof(Geom);
   int num_bytes = sizeof(Geom);
   Primitives::const_iterator pi;
   Primitives::const_iterator pi;
-  for (pi = cdata->_primitives.begin(); 
-       pi != cdata->_primitives.end();
+  for (pi = _primitives.begin(); 
+       pi != _primitives.end();
        ++pi) {
        ++pi) {
     num_bytes += (*pi)->get_num_bytes();
     num_bytes += (*pi)->get_num_bytes();
   }
   }
@@ -638,13 +635,11 @@ transform_vertices(const LMatrix4f &mat) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool Geom::
 bool Geom::
 check_valid() const {
 check_valid() const {
-  CDReader cdata(_cycler);
-
   Primitives::const_iterator pi;
   Primitives::const_iterator pi;
-  for (pi = cdata->_primitives.begin(); 
-       pi != cdata->_primitives.end();
+  for (pi = _primitives.begin(); 
+       pi != _primitives.end();
        ++pi) {
        ++pi) {
-    if (!(*pi)->check_valid(cdata->_data)) {
+    if (!(*pi)->check_valid(_data)) {
       return false;
       return false;
     }
     }
   }
   }
@@ -662,11 +657,9 @@ check_valid() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool Geom::
 bool Geom::
 check_valid(const GeomVertexData *vertex_data) const {
 check_valid(const GeomVertexData *vertex_data) const {
-  CDReader cdata(_cycler);
-
   Primitives::const_iterator pi;
   Primitives::const_iterator pi;
-  for (pi = cdata->_primitives.begin(); 
-       pi != cdata->_primitives.end();
+  for (pi = _primitives.begin(); 
+       pi != _primitives.end();
        ++pi) {
        ++pi) {
     if (!(*pi)->check_valid(vertex_data)) {
     if (!(*pi)->check_valid(vertex_data)) {
       return false;
       return false;
@@ -683,14 +676,12 @@ check_valid(const GeomVertexData *vertex_data) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void Geom::
 void Geom::
 output(ostream &out) const {
 output(ostream &out) const {
-  CDReader cdata(_cycler);
-
   // Get a list of the primitive types contained within this object.
   // Get a list of the primitive types contained within this object.
   int num_faces = 0;
   int num_faces = 0;
   pset<TypeHandle> types;
   pset<TypeHandle> types;
   Primitives::const_iterator pi;
   Primitives::const_iterator pi;
-  for (pi = cdata->_primitives.begin(); 
-       pi != cdata->_primitives.end();
+  for (pi = _primitives.begin(); 
+       pi != _primitives.end();
        ++pi) {
        ++pi) {
     num_faces += (*pi)->get_num_faces();
     num_faces += (*pi)->get_num_faces();
     types.insert((*pi)->get_type());
     types.insert((*pi)->get_type());
@@ -711,12 +702,10 @@ output(ostream &out) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void Geom::
 void Geom::
 write(ostream &out, int indent_level) const {
 write(ostream &out, int indent_level) const {
-  CDReader cdata(_cycler);
-
   // Get a list of the primitive types contained within this object.
   // Get a list of the primitive types contained within this object.
   Primitives::const_iterator pi;
   Primitives::const_iterator pi;
-  for (pi = cdata->_primitives.begin(); 
-       pi != cdata->_primitives.end();
+  for (pi = _primitives.begin(); 
+       pi != _primitives.end();
        ++pi) {
        ++pi) {
     (*pi)->write(out, indent_level);
     (*pi)->write(out, indent_level);
   }
   }
@@ -730,16 +719,13 @@ write(ostream &out, int indent_level) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void Geom::
 void Geom::
 clear_cache() {
 clear_cache() {
-  // Probably we shouldn't do anything at all here unless we are
-  // running in pipeline stage 0.
-  CData *cdata = CDWriter(_cycler);
-  for (Cache::iterator ci = cdata->_cache.begin();
-       ci != cdata->_cache.end();
+  for (Cache::iterator ci = _cache.begin();
+       ci != _cache.end();
        ++ci) {
        ++ci) {
     CacheEntry *entry = (*ci);
     CacheEntry *entry = (*ci);
     entry->erase();
     entry->erase();
   }
   }
-  cdata->_cache.clear();
+  _cache.clear();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -852,28 +838,10 @@ prepare_now(PreparedGraphicsObjects *prepared_objects,
 void Geom::
 void Geom::
 draw(GraphicsStateGuardianBase *gsg, const GeomMunger *munger,
 draw(GraphicsStateGuardianBase *gsg, const GeomMunger *munger,
      const GeomVertexData *vertex_data) const {
      const GeomVertexData *vertex_data) const {
-#ifdef DO_PIPELINING
-  // Make sure the usage_hint is already updated before we start to
-  // draw, so we don't end up with a circular lock if the GSG asks us
-  // to update this while we're holding the read lock.
-  {
-    CDReader cdata(_cycler);
-    if (!cdata->_got_usage_hint) {
-      CDWriter cdataw(((Geom *)this)->_cycler, cdata);
-      ((Geom *)this)->reset_usage_hint(cdataw);
-    }
-  }
-  // TODO: fix up the race condition between this line and the next.
-  // Maybe CDWriter's elevate-to-write should return the read lock to
-  // its original locked state when it's done.
-#endif  // DO_PIPELINING
-
-  CDReader cdata(_cycler);
-  
   if (gsg->begin_draw_primitives(this, munger, vertex_data)) {
   if (gsg->begin_draw_primitives(this, munger, vertex_data)) {
     Primitives::const_iterator pi;
     Primitives::const_iterator pi;
-    for (pi = cdata->_primitives.begin(); 
-         pi != cdata->_primitives.end();
+    for (pi = _primitives.begin(); 
+         pi != _primitives.end();
          ++pi) {
          ++pi) {
       const GeomPrimitive *primitive = (*pi);
       const GeomPrimitive *primitive = (*pi);
       if (primitive->get_num_vertices() != 0) {
       if (primitive->get_num_vertices() != 0) {
@@ -886,7 +854,7 @@ draw(GraphicsStateGuardianBase *gsg, const GeomMunger *munger,
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Geom::calc_tight_bounds
 //     Function: Geom::calc_tight_bounds
-//       Access: Public, Virtual
+//       Access: Public
 //  Description: Expands min_point and max_point to include all of the
 //  Description: Expands min_point and max_point to include all of the
 //               vertices in the Geom, if any.  found_any is set true
 //               vertices in the Geom, if any.  found_any is set true
 //               if any points are found.  It is the caller's
 //               if any points are found.  It is the caller's
@@ -904,12 +872,9 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
                   bool &found_any, 
                   bool &found_any, 
                   const GeomVertexData *vertex_data,
                   const GeomVertexData *vertex_data,
                   bool got_mat, const LMatrix4f &mat) const {
                   bool got_mat, const LMatrix4f &mat) const {
-
-  CDReader cdata(_cycler);
-  
   Primitives::const_iterator pi;
   Primitives::const_iterator pi;
-  for (pi = cdata->_primitives.begin(); 
-       pi != cdata->_primitives.end();
+  for (pi = _primitives.begin(); 
+       pi != _primitives.end();
        ++pi) {
        ++pi) {
     (*pi)->calc_tight_bounds(min_point, max_point, found_any, vertex_data,
     (*pi)->calc_tight_bounds(min_point, max_point, found_any, vertex_data,
                              got_mat, mat);
                              got_mat, mat);
@@ -942,9 +907,9 @@ get_next_modified() {
 //               This includes all of the vertices.
 //               This includes all of the vertices.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 BoundingVolume *Geom::
 BoundingVolume *Geom::
-recompute_bound() {
+recompute_bound(int pipeline_stage) {
   // First, get ourselves a fresh, empty bounding volume.
   // First, get ourselves a fresh, empty bounding volume.
-  BoundingVolume *bound = BoundedObject::recompute_bound();
+  BoundingVolume *bound = BoundedObject::recompute_bound(pipeline_stage);
   nassertr(bound != (BoundingVolume*)0L, bound);
   nassertr(bound != (BoundingVolume*)0L, bound);
 
 
   GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);
   GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);
@@ -953,7 +918,7 @@ recompute_bound() {
   // calc_tight_bounds to determine our minmax first.
   // calc_tight_bounds to determine our minmax first.
   LPoint3f points[2];
   LPoint3f points[2];
   bool found_any = false;
   bool found_any = false;
-  calc_tight_bounds(points[0], points[1], found_any, get_vertex_data(),
+  calc_tight_bounds(points[0], points[1], found_any, _data,
                     false, LMatrix4f::ident_mat());
                     false, LMatrix4f::ident_mat());
   if (found_any) {
   if (found_any) {
     // Then we put the bounding volume around both of those points.
     // Then we put the bounding volume around both of those points.
@@ -1004,11 +969,9 @@ clear_prepared(PreparedGraphicsObjects *prepared_objects) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool Geom::
 bool Geom::
 check_will_be_valid(const GeomVertexData *vertex_data) const {
 check_will_be_valid(const GeomVertexData *vertex_data) const {
-  CDReader cdata(_cycler);
-
   Primitives::const_iterator pi;
   Primitives::const_iterator pi;
-  for (pi = cdata->_primitives.begin(); 
-       pi != cdata->_primitives.end();
+  for (pi = _primitives.begin(); 
+       pi != _primitives.end();
        ++pi) {
        ++pi) {
     if (!(*pi)->check_valid(vertex_data)) {
     if (!(*pi)->check_valid(vertex_data)) {
       return false;
       return false;
@@ -1024,15 +987,15 @@ check_will_be_valid(const GeomVertexData *vertex_data) const {
 //  Description: Recomputes the minimum usage_hint.
 //  Description: Recomputes the minimum usage_hint.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void Geom::
 void Geom::
-reset_usage_hint(Geom::CDWriter &cdata) {
-  cdata->_usage_hint = UH_unspecified;
+reset_usage_hint() {
+  _usage_hint = UH_unspecified;
   Primitives::const_iterator pi;
   Primitives::const_iterator pi;
-  for (pi = cdata->_primitives.begin(); 
-       pi != cdata->_primitives.end();
+  for (pi = _primitives.begin(); 
+       pi != _primitives.end();
        ++pi) {
        ++pi) {
-    cdata->_usage_hint = min(cdata->_usage_hint, (*pi)->get_usage_hint());
+    _usage_hint = min(_usage_hint, (*pi)->get_usage_hint());
   }
   }
-  cdata->_got_usage_hint = true;
+  _got_usage_hint = true;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1041,34 +1004,34 @@ reset_usage_hint(Geom::CDWriter &cdata) {
 //  Description: Rederives the _geom_rendering member.
 //  Description: Rederives the _geom_rendering member.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void Geom::
 void Geom::
-reset_geom_rendering(Geom::CDWriter &cdata) {
-  cdata->_geom_rendering = 0;
+reset_geom_rendering() {
+  _geom_rendering = 0;
   Primitives::const_iterator pi;
   Primitives::const_iterator pi;
-  for (pi = cdata->_primitives.begin(); 
-       pi != cdata->_primitives.end();
+  for (pi = _primitives.begin(); 
+       pi != _primitives.end();
        ++pi) {
        ++pi) {
-    cdata->_geom_rendering |= (*pi)->get_geom_rendering();
+    _geom_rendering |= (*pi)->get_geom_rendering();
   }
   }
 
 
-  if ((cdata->_geom_rendering & GR_point) != 0) {
-    if (cdata->_data->has_column(InternalName::get_size())) {
-      cdata->_geom_rendering |= GR_per_point_size;
+  if ((_geom_rendering & GR_point) != 0) {
+    if (_data->has_column(InternalName::get_size())) {
+      _geom_rendering |= GR_per_point_size;
     }
     }
-    if (cdata->_data->has_column(InternalName::get_aspect_ratio())) {
-      cdata->_geom_rendering |= GR_point_aspect_ratio;
+    if (_data->has_column(InternalName::get_aspect_ratio())) {
+      _geom_rendering |= GR_point_aspect_ratio;
     }
     }
-    if (cdata->_data->has_column(InternalName::get_rotate())) {
-      cdata->_geom_rendering |= GR_point_rotate;
+    if (_data->has_column(InternalName::get_rotate())) {
+      _geom_rendering |= GR_point_rotate;
     }
     }
   }
   }
 
 
   switch (get_shade_model()) {
   switch (get_shade_model()) {
   case SM_flat_first_vertex:
   case SM_flat_first_vertex:
-    cdata->_geom_rendering |= GR_flat_first_vertex;
+    _geom_rendering |= GR_flat_first_vertex;
     break;
     break;
 
 
   case SM_flat_last_vertex:
   case SM_flat_last_vertex:
-    cdata->_geom_rendering |= GR_flat_last_vertex;
+    _geom_rendering |= GR_flat_last_vertex;
     break;
     break;
 
 
   default:
   default:
@@ -1097,7 +1060,20 @@ void Geom::
 write_datagram(BamWriter *manager, Datagram &dg) {
 write_datagram(BamWriter *manager, Datagram &dg) {
   TypedWritable::write_datagram(manager, dg);
   TypedWritable::write_datagram(manager, dg);
 
 
-  manager->write_cdata(dg, _cycler);
+  manager->write_pointer(dg, _data);
+
+  dg.add_uint16(_primitives.size());
+  Primitives::const_iterator pi;
+  for (pi = _primitives.begin(); pi != _primitives.end(); ++pi) {
+    manager->write_pointer(dg, *pi);
+  }
+
+  dg.add_uint8(_primitive_type);
+  dg.add_uint8(_shade_model);
+
+  // Actually, we shouldn't bother writing out _geom_rendering; we'll
+  // just throw it away anyway.
+  dg.add_uint16(_geom_rendering);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1130,16 +1106,36 @@ make_from_bam(const FactoryParams &params) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void Geom::
 void Geom::
 finalize(BamReader *manager) {
 finalize(BamReader *manager) {
-  CDWriter cdata(_cycler);
-
   // Make sure our GeomVertexData is finalized first.  This may result
   // Make sure our GeomVertexData is finalized first.  This may result
   // in the data getting finalized multiple times, but it doesn't mind
   // in the data getting finalized multiple times, but it doesn't mind
   // that.
   // that.
-  if (cdata->_data != (GeomVertexData *)NULL) {
-    cdata->_data->finalize(manager);
+  if (_data != (GeomVertexData *)NULL) {
+    _data->finalize(manager);
   }
   }
 
 
-  reset_geom_rendering(cdata);
+  reset_geom_rendering();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: Geom::complete_pointers
+//       Access: Protected, Virtual
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int Geom::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = TypedWritable::complete_pointers(p_list, manager);
+
+  _data = DCAST(GeomVertexData, p_list[pi++]);
+
+  Primitives::iterator pri;
+  for (pri = _primitives.begin(); pri != _primitives.end(); ++pri) {
+    (*pri) = DCAST(GeomPrimitive, p_list[pi++]);
+  }
+
+  return pi;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1153,7 +1149,24 @@ void Geom::
 fillin(DatagramIterator &scan, BamReader *manager) {
 fillin(DatagramIterator &scan, BamReader *manager) {
   TypedWritable::fillin(scan, manager);
   TypedWritable::fillin(scan, manager);
 
 
-  manager->read_cdata(scan, _cycler);
+  manager->read_pointer(scan);
+
+  int num_primitives = scan.get_uint16();
+  _primitives.reserve(num_primitives);
+  for (int i = 0; i < num_primitives; ++i) {
+    manager->read_pointer(scan);
+    _primitives.push_back(NULL);
+  }
+
+  _primitive_type = (PrimitiveType)scan.get_uint8();
+  _shade_model = (ShadeModel)scan.get_uint8();
+
+  // To be removed: we no longer read _geom_rendering from the bam
+  // file; instead, we rederive it in finalize().
+  scan.get_uint16();
+
+  _got_usage_hint = false;
+  _modified = Geom::get_next_modified();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1176,16 +1189,10 @@ Geom::CacheEntry::
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void Geom::CacheEntry::
 void Geom::CacheEntry::
 evict_callback() {
 evict_callback() {
-  // We have to operate on stage 0 of the pipeline, since that's where
-  // the cache really counts.  Because of the multistage pipeline, we
-  // might not actually have a cache entry there (it might have been
-  // added to stage 1 instead).  No big deal if we don't.
-  CData *cdata = _source->_cycler.write_stage(0);
-  Cache::iterator ci = cdata->_cache.find(this);
-  if (ci != cdata->_cache.end()) {
-    cdata->_cache.erase(ci);
+  Cache::iterator ci = _source->_cache.find(this);
+  if (ci != _source->_cache.end()) {
+    _source->_cache.erase(ci);
   }
   }
-  _source->_cycler.release_write_stage(0, cdata);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1198,88 +1205,3 @@ output(ostream &out) const {
   out << "geom " << (void *)_source << ", " 
   out << "geom " << (void *)_source << ", " 
       << (const void *)_modifier;
       << (const void *)_modifier;
 }
 }
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: Geom::CData::make_copy
-//       Access: Public, Virtual
-//  Description:
-////////////////////////////////////////////////////////////////////
-CycleData *Geom::CData::
-make_copy() const {
-  return new CData(*this);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Geom::CData::write_datagram
-//       Access: Public, Virtual
-//  Description: Writes the contents of this object to the datagram
-//               for shipping out to a Bam file.
-////////////////////////////////////////////////////////////////////
-void Geom::CData::
-write_datagram(BamWriter *manager, Datagram &dg) const {
-  manager->write_pointer(dg, _data);
-
-  dg.add_uint16(_primitives.size());
-  Primitives::const_iterator pi;
-  for (pi = _primitives.begin(); pi != _primitives.end(); ++pi) {
-    manager->write_pointer(dg, *pi);
-  }
-
-  dg.add_uint8(_primitive_type);
-  dg.add_uint8(_shade_model);
-
-  // Actually, we shouldn't bother writing out _geom_rendering; we'll
-  // just throw it away anyway.
-  dg.add_uint16(_geom_rendering);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Geom::CData::complete_pointers
-//       Access: Public, Virtual
-//  Description: Receives an array of pointers, one for each time
-//               manager->read_pointer() was called in fillin().
-//               Returns the number of pointers processed.
-////////////////////////////////////////////////////////////////////
-int Geom::CData::
-complete_pointers(TypedWritable **p_list, BamReader *manager) {
-  int pi = CycleData::complete_pointers(p_list, manager);
-
-  _data = DCAST(GeomVertexData, p_list[pi++]);
-
-  Primitives::iterator pri;
-  for (pri = _primitives.begin(); pri != _primitives.end(); ++pri) {
-    (*pri) = DCAST(GeomPrimitive, p_list[pi++]);
-  }
-
-  return pi;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Geom::CData::fillin
-//       Access: Public, Virtual
-//  Description: This internal function is called by make_from_bam to
-//               read in all of the relevant data from the BamFile for
-//               the new Geom.
-////////////////////////////////////////////////////////////////////
-void Geom::CData::
-fillin(DatagramIterator &scan, BamReader *manager) {
-  manager->read_pointer(scan);
-
-  int num_primitives = scan.get_uint16();
-  _primitives.reserve(num_primitives);
-  for (int i = 0; i < num_primitives; ++i) {
-    manager->read_pointer(scan);
-    _primitives.push_back(NULL);
-  }
-
-  _primitive_type = (PrimitiveType)scan.get_uint8();
-  _shade_model = (ShadeModel)scan.get_uint8();
-
-  // To be removed: we no longer read _geom_rendering from the bam
-  // file; instead, we rederive it in finalize().
-  scan.get_uint16();
-
-  _got_usage_hint = false;
-  _modified = Geom::get_next_modified();
-}

+ 19 - 35
panda/src/gobj/geom.h

@@ -22,10 +22,6 @@
 #include "pandabase.h"
 #include "pandabase.h"
 #include "typedWritableReferenceCount.h"
 #include "typedWritableReferenceCount.h"
 #include "boundedObject.h"
 #include "boundedObject.h"
-#include "cycleData.h"
-#include "cycleDataReader.h"
-#include "cycleDataWriter.h"
-#include "pipelineCycler.h"
 #include "geomVertexData.h"
 #include "geomVertexData.h"
 #include "geomPrimitive.h"
 #include "geomPrimitive.h"
 #include "geomMunger.h"
 #include "geomMunger.h"
@@ -131,7 +127,7 @@ public:
   typedef pset<TextureStage *> NoTexCoordStages;
   typedef pset<TextureStage *> NoTexCoordStages;
 
 
 protected:
 protected:
-  virtual BoundingVolume *recompute_bound();
+  virtual BoundingVolume *recompute_bound(int pipeline_stage);
 
 
 private:
 private:
   void clear_prepared(PreparedGraphicsObjects *prepared_objects);
   void clear_prepared(PreparedGraphicsObjects *prepared_objects);
@@ -145,6 +141,9 @@ private:
   // cache needs to be stored in the CycleData, which makes accurate
   // cache needs to be stored in the CycleData, which makes accurate
   // cleanup more difficult.  We use the GeomCacheManager class to
   // cleanup more difficult.  We use the GeomCacheManager class to
   // avoid cache bloat.
   // avoid cache bloat.
+
+  // Actually, the above is no longer true.  Need to investigate if we
+  // can go back to having explicit cleanup to make this better.
   class CacheEntry : public GeomCacheEntry {
   class CacheEntry : public GeomCacheEntry {
   public:
   public:
     INLINE CacheEntry(const GeomVertexData *source_data,
     INLINE CacheEntry(const GeomVertexData *source_data,
@@ -168,36 +167,20 @@ private:
   };
   };
   typedef pset<PT(CacheEntry), IndirectLess<CacheEntry> > Cache;
   typedef pset<PT(CacheEntry), IndirectLess<CacheEntry> > Cache;
 
 
-  // This is the data that must be cycled between pipeline stages.
-  class EXPCL_PANDA CData : public CycleData {
-  public:
-    INLINE CData();
-    INLINE CData(const CData &copy);
-    virtual CycleData *make_copy() const;
-    virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
-    virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
-    virtual void fillin(DatagramIterator &scan, BamReader *manager);
-    virtual TypeHandle get_parent_type() const {
-      return Geom::get_class_type();
-    }
-
-    PT(GeomVertexData) _data;
-    Primitives _primitives;
-    PrimitiveType _primitive_type;
-    ShadeModel _shade_model;
-    int _geom_rendering;
-    UsageHint _usage_hint;
-    bool _got_usage_hint;
-    UpdateSeq _modified;
-    Cache _cache;
-  };
-
-  PipelineCycler<CData> _cycler;
-  typedef CycleDataReader<CData> CDReader;
-  typedef CycleDataWriter<CData> CDWriter;
-
-  void reset_usage_hint(CDWriter &cdata);
-  void reset_geom_rendering(CDWriter &cdata);
+  // We don't need to cycle any data in the Geom, since the Geom
+  // itself is cycled through the stages.
+  PT(GeomVertexData) _data;
+  Primitives _primitives;
+  PrimitiveType _primitive_type;
+  ShadeModel _shade_model;
+  int _geom_rendering;
+  UsageHint _usage_hint;
+  bool _got_usage_hint;
+  UpdateSeq _modified;
+  Cache _cache;
+
+  void reset_usage_hint();
+  void reset_geom_rendering();
 
 
   // This works just like the Texture contexts: each Geom keeps a
   // This works just like the Texture contexts: each Geom keeps a
   // record of all the PGO objects that hold the Geom, and vice-versa.
   // record of all the PGO objects that hold the Geom, and vice-versa.
@@ -214,6 +197,7 @@ public:
 
 
 protected:
 protected:
   static TypedWritable *make_from_bam(const FactoryParams &params);
   static TypedWritable *make_from_bam(const FactoryParams &params);
+  virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
   void fillin(DatagramIterator &scan, BamReader *manager);
   void fillin(DatagramIterator &scan, BamReader *manager);
 
 
 public:
 public:

+ 4 - 7
panda/src/gobj/geomMunger.cxx

@@ -110,11 +110,10 @@ munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data) {
   // Look up the munger in the geom's cache--maybe we've recently
   // Look up the munger in the geom's cache--maybe we've recently
   // applied it.
   // applied it.
   {
   {
-    Geom::CDReader cdata(geom->_cycler);
     Geom::CacheEntry temp_entry(source_data, this);
     Geom::CacheEntry temp_entry(source_data, this);
     temp_entry.local_object();
     temp_entry.local_object();
-    Geom::Cache::const_iterator ci = cdata->_cache.find(&temp_entry);
-    if (ci != cdata->_cache.end()) {
+    Geom::Cache::const_iterator ci = geom->_cache.find(&temp_entry);
+    if (ci != geom->_cache.end()) {
       Geom::CacheEntry *entry = (*ci);
       Geom::CacheEntry *entry = (*ci);
 
 
       if (geom->get_modified() <= entry->_geom_result->get_modified() &&
       if (geom->get_modified() <= entry->_geom_result->get_modified() &&
@@ -135,8 +134,7 @@ munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data) {
           << "Cache entry " << *entry << " is stale, removing.\n";
           << "Cache entry " << *entry << " is stale, removing.\n";
       }
       }
       entry->erase();
       entry->erase();
-      Geom::CDWriter cdataw(((Geom *)geom.p())->_cycler, cdata);
-      cdataw->_cache.erase(entry);
+      ((Geom *)geom.p())->_cache.erase(entry);
     }
     }
   }
   }
 
 
@@ -151,10 +149,9 @@ munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data) {
     // Record the new result in the cache.
     // Record the new result in the cache.
     Geom::CacheEntry *entry;
     Geom::CacheEntry *entry;
     {
     {
-      Geom::CDWriter cdata(((Geom *)orig_geom.p())->_cycler);
       entry = new Geom::CacheEntry((Geom *)orig_geom.p(), source_data, this,
       entry = new Geom::CacheEntry((Geom *)orig_geom.p(), source_data, this,
                                      geom, data);
                                      geom, data);
-      bool inserted = cdata->_cache.insert(entry).second;
+      bool inserted = ((Geom *)orig_geom.p())->_cache.insert(entry).second;
       nassertv(inserted);
       nassertv(inserted);
     }
     }
     
     

+ 25 - 88
panda/src/gobj/geomPrimitive.I

@@ -26,8 +26,7 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE GeomPrimitive::ShadeModel GeomPrimitive::
 INLINE GeomPrimitive::ShadeModel GeomPrimitive::
 get_shade_model() const {
 get_shade_model() const {
-  CDReader cdata(_cycler);
-  return cdata->_shade_model;
+  return _shade_model;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -43,8 +42,7 @@ get_shade_model() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void GeomPrimitive::
 INLINE void GeomPrimitive::
 set_shade_model(GeomPrimitive::ShadeModel shade_model) {
 set_shade_model(GeomPrimitive::ShadeModel shade_model) {
-  CDWriter cdata(_cycler);
-  cdata->_shade_model = shade_model;
+  _shade_model = shade_model;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -65,8 +63,7 @@ set_shade_model(GeomPrimitive::ShadeModel shade_model) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE GeomPrimitive::UsageHint GeomPrimitive::
 INLINE GeomPrimitive::UsageHint GeomPrimitive::
 get_usage_hint() const {
 get_usage_hint() const {
-  CDReader cdata(_cycler);
-  return cdata->_usage_hint;
+  return _usage_hint;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -77,8 +74,7 @@ get_usage_hint() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE GeomPrimitive::NumericType GeomPrimitive::
 INLINE GeomPrimitive::NumericType GeomPrimitive::
 get_index_type() const {
 get_index_type() const {
-  CDReader cdata(_cycler);
-  return cdata->_index_type;
+  return _index_type;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -107,8 +103,7 @@ is_composite() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool GeomPrimitive::
 INLINE bool GeomPrimitive::
 is_indexed() const {
 is_indexed() const {
-  CDReader cdata(_cycler);
-  return (cdata->_vertices != (GeomVertexArrayData *)NULL);
+  return (_vertices != (GeomVertexArrayData *)NULL);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -119,11 +114,10 @@ is_indexed() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int GeomPrimitive::
 INLINE int GeomPrimitive::
 get_num_vertices() const {
 get_num_vertices() const {
-  CDReader cdata(_cycler);
-  if (cdata->_vertices == (GeomVertexArrayData *)NULL) {
-    return cdata->_num_vertices;
+  if (_vertices == (GeomVertexArrayData *)NULL) {
+    return _num_vertices;
   } else {
   } else {
-    return cdata->_vertices->get_num_rows();
+    return _vertices->get_num_rows();
   }
   }
 }
 }
 
 
@@ -175,14 +169,11 @@ get_primitive_num_faces(int n) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int GeomPrimitive::
 INLINE int GeomPrimitive::
 get_min_vertex() const {
 get_min_vertex() const {
-  CDReader cdata(_cycler);
-  if (!cdata->_got_minmax) {
-    CDWriter cdataw(((GeomPrimitive *)this)->_cycler, cdata);
-    ((GeomPrimitive *)this)->recompute_minmax(cdataw);
-    return cdataw->_min_vertex;
+  if (!_got_minmax) {
+    ((GeomPrimitive *)this)->recompute_minmax();
   }
   }
   
   
-  return cdata->_min_vertex;
+  return _min_vertex;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -193,13 +184,10 @@ get_min_vertex() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int GeomPrimitive::
 INLINE int GeomPrimitive::
 get_max_vertex() const {
 get_max_vertex() const {
-  CDReader cdata(_cycler);
-  if (!cdata->_got_minmax) {
-    CDWriter cdataw(((GeomPrimitive *)this)->_cycler, cdata);
-    ((GeomPrimitive *)this)->recompute_minmax(cdataw);
-    return cdataw->_max_vertex;
+  if (!_got_minmax) {
+    ((GeomPrimitive *)this)->recompute_minmax();
   }
   }
-  return cdata->_max_vertex;
+  return _max_vertex;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -210,8 +198,7 @@ get_max_vertex() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int GeomPrimitive::
 INLINE int GeomPrimitive::
 get_data_size_bytes() const {
 get_data_size_bytes() const {
-  CDReader cdata(_cycler);
-  return cdata->_vertices->get_data_size_bytes();
+  return _vertices->get_data_size_bytes();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -223,8 +210,7 @@ get_data_size_bytes() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE UpdateSeq GeomPrimitive::
 INLINE UpdateSeq GeomPrimitive::
 get_modified() const {
 get_modified() const {
-  CDReader cdata(_cycler);
-  return cdata->_modified;
+  return _modified;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -238,8 +224,7 @@ get_modified() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const GeomVertexArrayData *GeomPrimitive::
 INLINE const GeomVertexArrayData *GeomPrimitive::
 get_vertices() const {
 get_vertices() const {
-  CDReader cdata(_cycler);
-  return cdata->_vertices;
+  return _vertices;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -281,8 +266,7 @@ get_data() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE CPTA_int GeomPrimitive::
 INLINE CPTA_int GeomPrimitive::
 get_ends() const {
 get_ends() const {
-  CDReader cdata(_cycler);
-  return cdata->_ends;
+  return _ends;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -299,13 +283,10 @@ get_ends() const {
 INLINE const GeomVertexArrayData *GeomPrimitive::
 INLINE const GeomVertexArrayData *GeomPrimitive::
 get_mins() const {
 get_mins() const {
   nassertr(is_indexed(), NULL);
   nassertr(is_indexed(), NULL);
-  CDReader cdata(_cycler);
-  if (!cdata->_got_minmax) {
-    CDWriter cdataw(((GeomPrimitive *)this)->_cycler, cdata);
-    ((GeomPrimitive *)this)->recompute_minmax(cdataw);
-    return cdataw->_mins;
+  if (!_got_minmax) {
+    ((GeomPrimitive *)this)->recompute_minmax();
   }
   }
-  return cdata->_mins;
+  return _mins;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -322,13 +303,10 @@ get_mins() const {
 INLINE const GeomVertexArrayData *GeomPrimitive::
 INLINE const GeomVertexArrayData *GeomPrimitive::
 get_maxs() const {
 get_maxs() const {
   nassertr(is_indexed(), NULL);
   nassertr(is_indexed(), NULL);
-  CDReader cdata(_cycler);
-  if (!cdata->_got_minmax) {
-    CDWriter cdataw(((GeomPrimitive *)this)->_cycler, cdata);
-    ((GeomPrimitive *)this)->recompute_minmax(cdataw);
-    return cdataw->_maxs;
+  if (!_got_minmax) {
+    ((GeomPrimitive *)this)->recompute_minmax();
   }
   }
-  return cdata->_maxs;
+  return _maxs;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -341,7 +319,7 @@ INLINE CPT(GeomVertexArrayFormat) GeomPrimitive::
 get_index_format() const {
 get_index_format() const {
   return GeomVertexArrayFormat::register_format
   return GeomVertexArrayFormat::register_format
     (new GeomVertexArrayFormat(InternalName::get_index(), 1, 
     (new GeomVertexArrayFormat(InternalName::get_index(), 1, 
-                                 get_index_type(), C_index));
+                               get_index_type(), C_index));
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -354,47 +332,6 @@ make_index_data() const {
   return new GeomVertexArrayData(get_index_format(), get_usage_hint());
   return new GeomVertexArrayData(get_index_format(), get_usage_hint());
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomPrimitive::CData::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE GeomPrimitive::CData::
-CData() :
-  _shade_model(SM_smooth),
-  _first_vertex(0),
-  _num_vertices(0),
-  _index_type(NT_uint16),
-  _usage_hint(UH_unspecified),
-  _got_minmax(true),
-  _min_vertex(0),
-  _max_vertex(0)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomPrimitive::CData::Copy Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE GeomPrimitive::CData::
-CData(const GeomPrimitive::CData &copy) :
-  _shade_model(copy._shade_model),
-  _first_vertex(copy._first_vertex),
-  _num_vertices(copy._num_vertices),
-  _index_type(copy._index_type),
-  _usage_hint(copy._usage_hint),
-  _vertices(copy._vertices),
-  _ends(copy._ends),
-  _mins(copy._mins),
-  _maxs(copy._maxs),
-  _modified(copy._modified),
-  _got_minmax(copy._got_minmax),
-  _min_vertex(copy._min_vertex),
-  _max_vertex(copy._max_vertex)
-{
-}
-
 INLINE ostream &
 INLINE ostream &
 operator << (ostream &out, const GeomPrimitive &obj) {
 operator << (ostream &out, const GeomPrimitive &obj) {
   obj.output(out);
   obj.output(out);

+ 213 - 239
panda/src/gobj/geomPrimitive.cxx

@@ -52,9 +52,16 @@ GeomPrimitive() {
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 GeomPrimitive::
 GeomPrimitive::
-GeomPrimitive(GeomPrimitive::UsageHint usage_hint) {
-  CDWriter cdata(_cycler);
-  cdata->_usage_hint = usage_hint;
+GeomPrimitive(GeomPrimitive::UsageHint usage_hint) :
+  _shade_model(SM_smooth),
+  _first_vertex(0),
+  _num_vertices(0),
+  _index_type(NT_uint16),
+  _usage_hint(usage_hint),
+  _got_minmax(true),
+  _min_vertex(0),
+  _max_vertex(0)
+{
 }
 }
  
  
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -65,9 +72,45 @@ GeomPrimitive(GeomPrimitive::UsageHint usage_hint) {
 GeomPrimitive::
 GeomPrimitive::
 GeomPrimitive(const GeomPrimitive &copy) :
 GeomPrimitive(const GeomPrimitive &copy) :
   TypedWritableReferenceCount(copy),
   TypedWritableReferenceCount(copy),
-  _cycler(copy._cycler)
+  _shade_model(copy._shade_model),
+  _first_vertex(copy._first_vertex),
+  _num_vertices(copy._num_vertices),
+  _index_type(copy._index_type),
+  _usage_hint(copy._usage_hint),
+  _vertices(copy._vertices),
+  _ends(copy._ends),
+  _mins(copy._mins),
+  _maxs(copy._maxs),
+  _modified(copy._modified),
+  _got_minmax(copy._got_minmax),
+  _min_vertex(copy._min_vertex),
+  _max_vertex(copy._max_vertex)
 {
 {
 }
 }
+ 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitive::Copy Assignment Operator
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void GeomPrimitive::
+operator = (const GeomPrimitive &copy) {
+  TypedWritableReferenceCount::operator = (copy);
+
+  _shade_model = copy._shade_model;
+  _first_vertex = copy._first_vertex;
+  _num_vertices = copy._num_vertices;
+  _index_type = copy._index_type;
+  _usage_hint = copy._usage_hint;
+  _vertices = copy._vertices;
+  _ends = copy._ends;
+  _mins = copy._mins;
+  _maxs = copy._maxs;
+  _modified = copy._modified;
+  _got_minmax = copy._got_minmax;
+  _min_vertex = copy._min_vertex;
+  _max_vertex = copy._max_vertex;
+}
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitive::Destructor
 //     Function: GeomPrimitive::Destructor
@@ -103,16 +146,15 @@ get_geom_rendering() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomPrimitive::
 void GeomPrimitive::
 set_usage_hint(GeomPrimitive::UsageHint usage_hint) {
 set_usage_hint(GeomPrimitive::UsageHint usage_hint) {
-  CDWriter cdata(_cycler);
-  cdata->_usage_hint = usage_hint;
+  _usage_hint = usage_hint;
 
 
-  if (cdata->_vertices != (GeomVertexArrayData *)NULL) {
-    if (cdata->_vertices->get_ref_count() > 1) {
-      cdata->_vertices = new GeomVertexArrayData(*cdata->_vertices);
+  if (_vertices != (GeomVertexArrayData *)NULL) {
+    if (_vertices->get_ref_count() > 1) {
+      _vertices = new GeomVertexArrayData(*_vertices);
     }
     }
     
     
-    cdata->_modified = Geom::get_next_modified();
-    cdata->_usage_hint = usage_hint;
+    _modified = Geom::get_next_modified();
+    _usage_hint = usage_hint;
   }
   }
 }
 }
 
 
@@ -125,24 +167,23 @@ set_usage_hint(GeomPrimitive::UsageHint usage_hint) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomPrimitive::
 void GeomPrimitive::
 set_index_type(GeomPrimitive::NumericType index_type) {
 set_index_type(GeomPrimitive::NumericType index_type) {
-  CDWriter cdata(_cycler);
-  cdata->_index_type = index_type;
+  _index_type = index_type;
 
 
-  if (cdata->_vertices != (GeomVertexArrayData *)NULL) {
+  if (_vertices != (GeomVertexArrayData *)NULL) {
     CPT(GeomVertexArrayFormat) new_format = get_index_format();
     CPT(GeomVertexArrayFormat) new_format = get_index_format();
     
     
-    if (cdata->_vertices->get_array_format() != new_format) {
+    if (_vertices->get_array_format() != new_format) {
       PT(GeomVertexArrayData) new_vertices = make_index_data();
       PT(GeomVertexArrayData) new_vertices = make_index_data();
-      new_vertices->set_num_rows(cdata->_vertices->get_num_rows());
+      new_vertices->set_num_rows(_vertices->get_num_rows());
 
 
-      GeomVertexReader from(cdata->_vertices, 0);
+      GeomVertexReader from(_vertices, 0);
       GeomVertexWriter to(new_vertices, 0);
       GeomVertexWriter to(new_vertices, 0);
       
       
       while (!from.is_at_end()) {
       while (!from.is_at_end()) {
         to.set_data1i(from.get_data1i());
         to.set_data1i(from.get_data1i());
       }
       }
-      cdata->_vertices = new_vertices;
-      cdata->_got_minmax = false;
+      _vertices = new_vertices;
+      _got_minmax = false;
     }
     }
   }
   }
 }
 }
@@ -158,13 +199,12 @@ set_index_type(GeomPrimitive::NumericType index_type) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int GeomPrimitive::
 int GeomPrimitive::
 get_first_vertex() const {
 get_first_vertex() const {
-  CDReader cdata(_cycler);
-  if (cdata->_vertices == (GeomVertexArrayData *)NULL) {
-    return cdata->_first_vertex;
-  } else if (cdata->_vertices->get_num_rows() == 0) {
+  if (_vertices == (GeomVertexArrayData *)NULL) {
+    return _first_vertex;
+  } else if (_vertices->get_num_rows() == 0) {
     return 0;
     return 0;
   } else {
   } else {
-    GeomVertexReader index(cdata->_vertices, 0);
+    GeomVertexReader index(_vertices, 0);
     return index.get_data1i();
     return index.get_data1i();
   }
   }
 }
 }
@@ -176,19 +216,17 @@ get_first_vertex() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int GeomPrimitive::
 int GeomPrimitive::
 get_vertex(int i) const {
 get_vertex(int i) const {
-  CDReader cdata(_cycler);
-
-  if (cdata->_vertices != (GeomVertexArrayData *)NULL) {
+  if (_vertices != (GeomVertexArrayData *)NULL) {
     // The indexed case.
     // The indexed case.
-    nassertr(i >= 0 && i < (int)cdata->_vertices->get_num_rows(), -1);
+    nassertr(i >= 0 && i < (int)_vertices->get_num_rows(), -1);
 
 
-    GeomVertexReader index(cdata->_vertices, 0);
+    GeomVertexReader index(_vertices, 0);
     index.set_row(i);
     index.set_row(i);
     return index.get_data1i();
     return index.get_data1i();
 
 
   } else {
   } else {
     // The nonindexed case.
     // The nonindexed case.
-    return cdata->_first_vertex + i;
+    return _first_vertex + i;
   }
   }
 }
 }
 
 
@@ -204,48 +242,46 @@ get_vertex(int i) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomPrimitive::
 void GeomPrimitive::
 add_vertex(int vertex) {
 add_vertex(int vertex) {
-  CDWriter cdata(_cycler);
-
   int num_primitives = get_num_primitives();
   int num_primitives = get_num_primitives();
   if (num_primitives > 0 &&
   if (num_primitives > 0 &&
       requires_unused_vertices() && 
       requires_unused_vertices() && 
       get_num_vertices() == get_primitive_end(num_primitives - 1)) {
       get_num_vertices() == get_primitive_end(num_primitives - 1)) {
     // If we are beginning a new primitive, give the derived class a
     // If we are beginning a new primitive, give the derived class a
     // chance to insert some degenerate vertices.
     // chance to insert some degenerate vertices.
-    if (cdata->_vertices == (GeomVertexArrayData *)NULL) {
-      do_make_indexed(cdata);
+    if (_vertices == (GeomVertexArrayData *)NULL) {
+      make_indexed();
     }
     }
-    append_unused_vertices(cdata->_vertices, vertex);
+    append_unused_vertices(_vertices, vertex);
   }
   }
 
 
-  if (cdata->_vertices == (GeomVertexArrayData *)NULL) {
+  if (_vertices == (GeomVertexArrayData *)NULL) {
     // The nonindexed case.  We can keep the primitive nonindexed only
     // The nonindexed case.  We can keep the primitive nonindexed only
     // if the vertex number happens to be the next available vertex.
     // if the vertex number happens to be the next available vertex.
-    if (cdata->_num_vertices == 0) {
-      cdata->_first_vertex = vertex;
-      cdata->_num_vertices = 1;
-      cdata->_modified = Geom::get_next_modified();
-      cdata->_got_minmax = false;
+    if (_num_vertices == 0) {
+      _first_vertex = vertex;
+      _num_vertices = 1;
+      _modified = Geom::get_next_modified();
+      _got_minmax = false;
       return;
       return;
 
 
-    } else if (vertex == cdata->_first_vertex + cdata->_num_vertices) {
-      ++cdata->_num_vertices;
-      cdata->_modified = Geom::get_next_modified();
-      cdata->_got_minmax = false;
+    } else if (vertex == _first_vertex + _num_vertices) {
+      ++_num_vertices;
+      _modified = Geom::get_next_modified();
+      _got_minmax = false;
       return;
       return;
     }
     }
     
     
     // Otherwise, we need to suddenly become an indexed primitive.
     // Otherwise, we need to suddenly become an indexed primitive.
-    do_make_indexed(cdata);
+    make_indexed();
   }
   }
 
 
-  GeomVertexWriter index(cdata->_vertices, 0);
-  index.set_row(cdata->_vertices->get_num_rows());
+  GeomVertexWriter index(_vertices, 0);
+  index.set_row(_vertices->get_num_rows());
 
 
   index.add_data1i(vertex);
   index.add_data1i(vertex);
 
 
-  cdata->_modified = Geom::get_next_modified();
-  cdata->_got_minmax = false;
+  _modified = Geom::get_next_modified();
+  _got_minmax = false;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -261,45 +297,43 @@ add_consecutive_vertices(int start, int num_vertices) {
   }
   }
   int end = (start + num_vertices) - 1;
   int end = (start + num_vertices) - 1;
 
 
-  CDWriter cdata(_cycler);
-
   int num_primitives = get_num_primitives();
   int num_primitives = get_num_primitives();
   if (num_primitives > 0 &&
   if (num_primitives > 0 &&
       get_num_vertices() == get_primitive_end(num_primitives - 1)) {
       get_num_vertices() == get_primitive_end(num_primitives - 1)) {
     // If we are beginning a new primitive, give the derived class a
     // If we are beginning a new primitive, give the derived class a
     // chance to insert some degenerate vertices.
     // chance to insert some degenerate vertices.
-    if (cdata->_vertices == (GeomVertexArrayData *)NULL) {
-      do_make_indexed(cdata);
+    if (_vertices == (GeomVertexArrayData *)NULL) {
+      make_indexed();
     }
     }
-    append_unused_vertices(cdata->_vertices, start);
+    append_unused_vertices(_vertices, start);
   }
   }
 
 
-  if (cdata->_vertices == (GeomVertexArrayData *)NULL) {
+  if (_vertices == (GeomVertexArrayData *)NULL) {
     // The nonindexed case.  We can keep the primitive nonindexed only
     // The nonindexed case.  We can keep the primitive nonindexed only
     // if the vertex number happens to be the next available vertex.
     // if the vertex number happens to be the next available vertex.
-    if (cdata->_num_vertices == 0) {
-      cdata->_first_vertex = start;
-      cdata->_num_vertices = num_vertices;
+    if (_num_vertices == 0) {
+      _first_vertex = start;
+      _num_vertices = num_vertices;
       return;
       return;
 
 
-    } else if (start == cdata->_first_vertex + cdata->_num_vertices) {
-      cdata->_num_vertices += num_vertices;
+    } else if (start == _first_vertex + _num_vertices) {
+      _num_vertices += num_vertices;
       return;
       return;
     }
     }
     
     
     // Otherwise, we need to suddenly become an indexed primitive.
     // Otherwise, we need to suddenly become an indexed primitive.
-    do_make_indexed(cdata);
+    make_indexed();
   }
   }
 
 
-  GeomVertexWriter index(cdata->_vertices, 0);
-  index.set_row(cdata->_vertices->get_num_rows());
+  GeomVertexWriter index(_vertices, 0);
+  index.set_row(_vertices->get_num_rows());
 
 
   for (int v = start; v <= end; ++v) {
   for (int v = start; v <= end; ++v) {
     index.add_data1i(v);
     index.add_data1i(v);
   }
   }
 
 
-  cdata->_modified = Geom::get_next_modified();
-  cdata->_got_minmax = false;
+  _modified = Geom::get_next_modified();
+  _got_minmax = false;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -334,26 +368,25 @@ bool GeomPrimitive::
 close_primitive() {
 close_primitive() {
   int num_vertices_per_primitive = get_num_vertices_per_primitive();
   int num_vertices_per_primitive = get_num_vertices_per_primitive();
 
 
-  CDWriter cdata(_cycler);
   if (num_vertices_per_primitive == 0) {
   if (num_vertices_per_primitive == 0) {
     // This is a complex primitive type like a triangle strip: each
     // This is a complex primitive type like a triangle strip: each
     // primitive uses a different number of vertices.
     // primitive uses a different number of vertices.
 #ifndef NDEBUG
 #ifndef NDEBUG
     int num_added;
     int num_added;
-    if (cdata->_ends.empty()) {
+    if (_ends.empty()) {
       num_added = get_num_vertices();
       num_added = get_num_vertices();
     } else {
     } else {
-      num_added = get_num_vertices() - cdata->_ends.back();
+      num_added = get_num_vertices() - _ends.back();
       num_added -= get_num_unused_vertices_per_primitive();
       num_added -= get_num_unused_vertices_per_primitive();
     }
     }
     nassertr(num_added >= get_min_num_vertices_per_primitive(), false);
     nassertr(num_added >= get_min_num_vertices_per_primitive(), false);
 #endif
 #endif
-    if (cdata->_ends.get_ref_count() > 1) {
+    if (_ends.get_ref_count() > 1) {
       PTA_int new_ends;
       PTA_int new_ends;
-      new_ends.v() = cdata->_ends.v();
-      cdata->_ends = new_ends;
+      new_ends.v() = _ends.v();
+      _ends = new_ends;
     }
     }
-    cdata->_ends.push_back(get_num_vertices());
+    _ends.push_back(get_num_vertices());
 
 
   } else {
   } else {
 #ifndef NDEBUG
 #ifndef NDEBUG
@@ -368,7 +401,7 @@ close_primitive() {
 #endif
 #endif
   }
   }
 
 
-  cdata->_modified = Geom::get_next_modified();
+  _modified = Geom::get_next_modified();
 
 
   return true;
   return true;
 }
 }
@@ -381,15 +414,14 @@ close_primitive() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomPrimitive::
 void GeomPrimitive::
 clear_vertices() {
 clear_vertices() {
-  CDWriter cdata(_cycler);
-  cdata->_first_vertex = 0;
-  cdata->_num_vertices = 0;
-  cdata->_vertices.clear();
-  cdata->_ends.clear();
-  cdata->_mins.clear();
-  cdata->_maxs.clear();
-  cdata->_modified = Geom::get_next_modified();
-  cdata->_got_minmax = false;
+  _first_vertex = 0;
+  _num_vertices = 0;
+  _vertices.clear();
+  _ends.clear();
+  _mins.clear();
+  _maxs.clear();
+  _modified = Geom::get_next_modified();
+  _got_minmax = false;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -407,10 +439,9 @@ offset_vertices(int offset) {
     }
     }
 
 
   } else {
   } else {
-    CDWriter cdata(_cycler);
-    cdata->_first_vertex += offset;
-    cdata->_modified = Geom::get_next_modified();
-    cdata->_got_minmax = false;
+    _first_vertex += offset;
+    _modified = Geom::get_next_modified();
+    _got_minmax = false;
   }
   }
 }
 }
 
 
@@ -494,8 +525,13 @@ pack_vertices(GeomVertexData *dest, const GeomVertexData *source) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomPrimitive::
 void GeomPrimitive::
 make_indexed() {
 make_indexed() {
-  CDWriter cdata(_cycler);
-  do_make_indexed(cdata);
+  if (_vertices == (GeomVertexArrayData *)NULL) {
+    _vertices = make_index_data();
+    GeomVertexWriter index(_vertices, 0);
+    for (int i = 0; i < _num_vertices; ++i) {
+      index.add_data1i(i + _first_vertex);
+    }
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -509,11 +545,10 @@ int GeomPrimitive::
 get_num_primitives() const {
 get_num_primitives() const {
   int num_vertices_per_primitive = get_num_vertices_per_primitive();
   int num_vertices_per_primitive = get_num_vertices_per_primitive();
 
 
-  CDReader cdata(_cycler);
   if (num_vertices_per_primitive == 0) {
   if (num_vertices_per_primitive == 0) {
     // This is a complex primitive type like a triangle strip: each
     // This is a complex primitive type like a triangle strip: each
     // primitive uses a different number of vertices.
     // primitive uses a different number of vertices.
-    return cdata->_ends.size();
+    return _ends.size();
 
 
   } else {
   } else {
     // This is a simple primitive type like a triangle: each primitive
     // This is a simple primitive type like a triangle: each primitive
@@ -544,12 +579,11 @@ get_primitive_start(int n) const {
   if (num_vertices_per_primitive == 0) {
   if (num_vertices_per_primitive == 0) {
     // This is a complex primitive type like a triangle strip: each
     // This is a complex primitive type like a triangle strip: each
     // primitive uses a different number of vertices.
     // primitive uses a different number of vertices.
-    CDReader cdata(_cycler);
-    nassertr(n >= 0 && n <= (int)cdata->_ends.size(), -1);
+    nassertr(n >= 0 && n <= (int)_ends.size(), -1);
     if (n == 0) {
     if (n == 0) {
       return 0;
       return 0;
     } else {
     } else {
-      return cdata->_ends[n - 1] + num_unused_vertices_per_primitive;
+      return _ends[n - 1] + num_unused_vertices_per_primitive;
     }
     }
 
 
   } else {
   } else {
@@ -573,9 +607,8 @@ get_primitive_end(int n) const {
   if (num_vertices_per_primitive == 0) {
   if (num_vertices_per_primitive == 0) {
     // This is a complex primitive type like a triangle strip: each
     // This is a complex primitive type like a triangle strip: each
     // primitive uses a different number of vertices.
     // primitive uses a different number of vertices.
-    CDReader cdata(_cycler);
-    nassertr(n >= 0 && n < (int)cdata->_ends.size(), -1);
-    return cdata->_ends[n];
+    nassertr(n >= 0 && n < (int)_ends.size(), -1);
+    return _ends[n];
 
 
   } else {
   } else {
     // This is a simple primitive type like a triangle: each primitive
     // This is a simple primitive type like a triangle: each primitive
@@ -599,13 +632,12 @@ get_primitive_num_vertices(int n) const {
   if (num_vertices_per_primitive == 0) {
   if (num_vertices_per_primitive == 0) {
     // This is a complex primitive type like a triangle strip: each
     // This is a complex primitive type like a triangle strip: each
     // primitive uses a different number of vertices.
     // primitive uses a different number of vertices.
-    CDReader cdata(_cycler);
-    nassertr(n >= 0 && n < (int)cdata->_ends.size(), 0);
+    nassertr(n >= 0 && n < (int)_ends.size(), 0);
     if (n == 0) {
     if (n == 0) {
-      return cdata->_ends[0];
+      return _ends[0];
     } else {
     } else {
       int num_unused_vertices_per_primitive = get_num_unused_vertices_per_primitive();
       int num_unused_vertices_per_primitive = get_num_unused_vertices_per_primitive();
-      return cdata->_ends[n] - cdata->_ends[n - 1] - num_unused_vertices_per_primitive;
+      return _ends[n] - _ends[n - 1] - num_unused_vertices_per_primitive;
     }      
     }      
 
 
   } else {
   } else {
@@ -777,10 +809,9 @@ match_shade_model(GeomPrimitive::ShadeModel shade_model) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int GeomPrimitive::
 int GeomPrimitive::
 get_num_bytes() const {
 get_num_bytes() const {
-  CDReader cdata(_cycler);
-  int num_bytes = cdata->_ends.size() * sizeof(int) + sizeof(GeomPrimitive);
-  if (cdata->_vertices != (GeomVertexArrayData *)NULL) {
-    num_bytes += cdata->_vertices->get_data_size_bytes();
+  int num_bytes = _ends.size() * sizeof(int) + sizeof(GeomPrimitive);
+  if (_vertices != (GeomVertexArrayData *)NULL) {
+    num_bytes += _vertices->get_data_size_bytes();
   }
   }
 
 
   return num_bytes;
   return num_bytes;
@@ -864,19 +895,17 @@ write(ostream &out, int indent_level) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 GeomVertexArrayData *GeomPrimitive::
 GeomVertexArrayData *GeomPrimitive::
 modify_vertices() {
 modify_vertices() {
-  CDWriter cdata(_cycler);
-
-  if (cdata->_vertices == (GeomVertexArrayData *)NULL) {
-    do_make_indexed(cdata);
+  if (_vertices == (GeomVertexArrayData *)NULL) {
+    make_indexed();
   }
   }
 
 
-  if (cdata->_vertices->get_ref_count() > 1) {
-    cdata->_vertices = new GeomVertexArrayData(*cdata->_vertices);
+  if (_vertices->get_ref_count() > 1) {
+    _vertices = new GeomVertexArrayData(*_vertices);
   }
   }
 
 
-  cdata->_modified = Geom::get_next_modified();
-  cdata->_got_minmax = false;
-  return cdata->_vertices;
+  _modified = Geom::get_next_modified();
+  _got_minmax = false;
+  return _vertices;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -888,11 +917,10 @@ modify_vertices() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomPrimitive::
 void GeomPrimitive::
 set_vertices(const GeomVertexArrayData *vertices) {
 set_vertices(const GeomVertexArrayData *vertices) {
-  CDWriter cdata(_cycler);
-  cdata->_vertices = (GeomVertexArrayData *)vertices;
+  _vertices = (GeomVertexArrayData *)vertices;
 
 
-  cdata->_modified = Geom::get_next_modified();
-  cdata->_got_minmax = false;
+  _modified = Geom::get_next_modified();
+  _got_minmax = false;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -903,14 +931,13 @@ set_vertices(const GeomVertexArrayData *vertices) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomPrimitive::
 void GeomPrimitive::
 set_nonindexed_vertices(int first_vertex, int num_vertices) {
 set_nonindexed_vertices(int first_vertex, int num_vertices) {
-  CDWriter cdata(_cycler);
-  cdata->_vertices = (GeomVertexArrayData *)NULL;
-  cdata->_first_vertex = first_vertex;
-  cdata->_num_vertices = num_vertices;
-
-  cdata->_modified = Geom::get_next_modified();
-  cdata->_got_minmax = false;
-  recompute_minmax(cdata);
+  _vertices = (GeomVertexArrayData *)NULL;
+  _first_vertex = first_vertex;
+  _num_vertices = num_vertices;
+
+  _modified = Geom::get_next_modified();
+  _got_minmax = false;
+  recompute_minmax();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -927,11 +954,9 @@ set_nonindexed_vertices(int first_vertex, int num_vertices) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PTA_int GeomPrimitive::
 PTA_int GeomPrimitive::
 modify_ends() {
 modify_ends() {
-  CDWriter cdata(_cycler);
-
-  cdata->_modified = Geom::get_next_modified();
-  cdata->_got_minmax = false;
-  return cdata->_ends;
+  _modified = Geom::get_next_modified();
+  _got_minmax = false;
+  return _ends;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -948,11 +973,10 @@ modify_ends() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomPrimitive::
 void GeomPrimitive::
 set_ends(CPTA_int ends) {
 set_ends(CPTA_int ends) {
-  CDWriter cdata(_cycler);
-  cdata->_ends = (PTA_int &)ends;
+  _ends = (PTA_int &)ends;
 
 
-  cdata->_modified = Geom::get_next_modified();
-  cdata->_got_minmax = false;
+  _modified = Geom::get_next_modified();
+  _got_minmax = false;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1139,13 +1163,11 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
     return;
     return;
   }
   }
 
 
-  CDReader cdata(_cycler);
-
-  if (cdata->_vertices == (GeomVertexArrayData *)NULL) {
+  if (_vertices == (GeomVertexArrayData *)NULL) {
     // Nonindexed case.
     // Nonindexed case.
     if (got_mat) {
     if (got_mat) {
-      for (int i = 0; i < cdata->_num_vertices; i++) {
-        reader.set_row(cdata->_first_vertex + i);
+      for (int i = 0; i < _num_vertices; i++) {
+        reader.set_row(_first_vertex + i);
         LPoint3f vertex = mat.xform_point(reader.get_data3f());
         LPoint3f vertex = mat.xform_point(reader.get_data3f());
         
         
         if (found_any) {
         if (found_any) {
@@ -1162,8 +1184,8 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
         }
         }
       }
       }
     } else {
     } else {
-      for (int i = 0; i < cdata->_num_vertices; i++) {
-        reader.set_row(cdata->_first_vertex + i);
+      for (int i = 0; i < _num_vertices; i++) {
+        reader.set_row(_first_vertex + i);
         const LVecBase3f &vertex = reader.get_data3f();
         const LVecBase3f &vertex = reader.get_data3f();
         
         
         if (found_any) {
         if (found_any) {
@@ -1183,7 +1205,7 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
 
 
   } else {
   } else {
     // Indexed case.
     // Indexed case.
-    GeomVertexReader index(cdata->_vertices, 0);
+    GeomVertexReader index(_vertices, 0);
 
 
     if (got_mat) {
     if (got_mat) {
       while (!index.is_at_end()) {
       while (!index.is_at_end()) {
@@ -1290,49 +1312,49 @@ append_unused_vertices(GeomVertexArrayData *, int) {
 //               necessary.
 //               necessary.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomPrimitive::
 void GeomPrimitive::
-recompute_minmax(GeomPrimitive::CDWriter &cdata) {
-  if (cdata->_vertices == (GeomVertexArrayData *)NULL) {
+recompute_minmax() {
+  if (_vertices == (GeomVertexArrayData *)NULL) {
     // In the nonindexed case, we don't need to do much (the
     // In the nonindexed case, we don't need to do much (the
     // minmax is trivial).
     // minmax is trivial).
-    cdata->_min_vertex = cdata->_first_vertex;
-    cdata->_max_vertex = cdata->_first_vertex + cdata->_num_vertices - 1;
-    cdata->_mins.clear();
-    cdata->_maxs.clear();
+    _min_vertex = _first_vertex;
+    _max_vertex = _first_vertex + _num_vertices - 1;
+    _mins.clear();
+    _maxs.clear();
 
 
   } else if (get_num_vertices() == 0) {
   } else if (get_num_vertices() == 0) {
     // Or if we don't have any vertices, the minmax is also trivial.
     // Or if we don't have any vertices, the minmax is also trivial.
-    cdata->_min_vertex = 0;
-    cdata->_max_vertex = 0;
-    cdata->_mins.clear();
-    cdata->_maxs.clear();
+    _min_vertex = 0;
+    _max_vertex = 0;
+    _mins.clear();
+    _maxs.clear();
 
 
   } else if (get_num_vertices_per_primitive() == 0) {
   } else if (get_num_vertices_per_primitive() == 0) {
     // This is a complex primitive type like a triangle strip; compute
     // This is a complex primitive type like a triangle strip; compute
     // the minmax of each primitive (as well as the overall minmax).
     // the minmax of each primitive (as well as the overall minmax).
-    GeomVertexReader index(cdata->_vertices, 0);
+    GeomVertexReader index(_vertices, 0);
 
 
-    cdata->_mins = make_index_data();
-    cdata->_maxs = make_index_data();
+    _mins = make_index_data();
+    _maxs = make_index_data();
 
 
-    GeomVertexWriter mins(cdata->_mins, 0);
-    GeomVertexWriter maxs(cdata->_maxs, 0);
+    GeomVertexWriter mins(_mins, 0);
+    GeomVertexWriter maxs(_maxs, 0);
 
 
     int pi = 0;
     int pi = 0;
     int vi = 0;
     int vi = 0;
     
     
     unsigned int vertex = index.get_data1i();
     unsigned int vertex = index.get_data1i();
-    cdata->_min_vertex = vertex;
-    cdata->_max_vertex = vertex;
+    _min_vertex = vertex;
+    _max_vertex = vertex;
     unsigned int min_prim = vertex;
     unsigned int min_prim = vertex;
     unsigned int max_prim = vertex;
     unsigned int max_prim = vertex;
     
     
     ++vi;
     ++vi;
     while (!index.is_at_end()) {
     while (!index.is_at_end()) {
       unsigned int vertex = index.get_data1i();
       unsigned int vertex = index.get_data1i();
-      cdata->_min_vertex = min(cdata->_min_vertex, vertex);
-      cdata->_max_vertex = max(cdata->_max_vertex, vertex);
+      _min_vertex = min(_min_vertex, vertex);
+      _max_vertex = max(_max_vertex, vertex);
 
 
-      if (vi == cdata->_ends[pi]) {
+      if (vi == _ends[pi]) {
         mins.add_data1i(min_prim);
         mins.add_data1i(min_prim);
         maxs.add_data1i(max_prim);
         maxs.add_data1i(max_prim);
         min_prim = vertex;
         min_prim = vertex;
@@ -1348,44 +1370,28 @@ recompute_minmax(GeomPrimitive::CDWriter &cdata) {
     }
     }
     mins.add_data1i(min_prim);
     mins.add_data1i(min_prim);
     maxs.add_data1i(max_prim);
     maxs.add_data1i(max_prim);
-    nassertv(cdata->_mins->get_num_rows() == (int)cdata->_ends.size());
+    nassertv(_mins->get_num_rows() == (int)_ends.size());
 
 
   } else {
   } else {
     // This is a simple primitive type like a triangle; just compute
     // This is a simple primitive type like a triangle; just compute
     // the overall minmax.
     // the overall minmax.
-    GeomVertexReader index(cdata->_vertices, 0);
+    GeomVertexReader index(_vertices, 0);
 
 
-    cdata->_mins.clear();
-    cdata->_maxs.clear();
+    _mins.clear();
+    _maxs.clear();
 
 
     unsigned int vertex = index.get_data1i();
     unsigned int vertex = index.get_data1i();
-    cdata->_min_vertex = vertex;
-    cdata->_max_vertex = vertex;
+    _min_vertex = vertex;
+    _max_vertex = vertex;
 
 
     while (!index.is_at_end()) {
     while (!index.is_at_end()) {
       unsigned int vertex = index.get_data1i();
       unsigned int vertex = index.get_data1i();
-      cdata->_min_vertex = min(cdata->_min_vertex, vertex);
-      cdata->_max_vertex = max(cdata->_max_vertex, vertex);
+      _min_vertex = min(_min_vertex, vertex);
+      _max_vertex = max(_max_vertex, vertex);
     }
     }
   }
   }
 
 
-  cdata->_got_minmax = true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomPrimitive::do_make_indexed
-//       Access: Private
-//  Description: The private implementation of make_indexed().
-////////////////////////////////////////////////////////////////////
-void GeomPrimitive::
-do_make_indexed(GeomPrimitive::CDWriter &cdata) {
-  if (cdata->_vertices == (GeomVertexArrayData *)NULL) {
-    cdata->_vertices = make_index_data();
-    GeomVertexWriter index(cdata->_vertices, 0);
-    for (int i = 0; i < cdata->_num_vertices; ++i) {
-      index.add_data1i(i + cdata->_first_vertex);
-    }
-  }
+  _got_minmax = true;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1398,7 +1404,14 @@ void GeomPrimitive::
 write_datagram(BamWriter *manager, Datagram &dg) {
 write_datagram(BamWriter *manager, Datagram &dg) {
   TypedWritable::write_datagram(manager, dg);
   TypedWritable::write_datagram(manager, dg);
 
 
-  manager->write_cdata(dg, _cycler);
+  dg.add_uint8(_shade_model);
+  dg.add_uint32(_first_vertex);
+  dg.add_uint32(_num_vertices);
+  dg.add_uint8(_index_type);
+  dg.add_uint8(_usage_hint);
+
+  manager->write_pointer(dg, _vertices);
+  WRITE_PTA(manager, dg, IPD_int::write_datagram, _ends);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1417,58 +1430,15 @@ finalize(BamReader *manager) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: GeomPrimitive::fillin
-//       Access: Protected
-//  Description: This internal function is called by make_from_bam to
-//               read in all of the relevant data from the BamFile for
-//               the new GeomPrimitive.
-////////////////////////////////////////////////////////////////////
-void GeomPrimitive::
-fillin(DatagramIterator &scan, BamReader *manager) {
-  TypedWritable::fillin(scan, manager);
-
-  manager->read_cdata(scan, _cycler);
-  manager->register_finalize(this);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomPrimitive::CData::make_copy
-//       Access: Public, Virtual
-//  Description:
-////////////////////////////////////////////////////////////////////
-CycleData *GeomPrimitive::CData::
-make_copy() const {
-  return new CData(*this);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomPrimitive::CData::write_datagram
-//       Access: Public, Virtual
-//  Description: Writes the contents of this object to the datagram
-//               for shipping out to a Bam file.
-////////////////////////////////////////////////////////////////////
-void GeomPrimitive::CData::
-write_datagram(BamWriter *manager, Datagram &dg) const {
-  dg.add_uint8(_shade_model);
-  dg.add_uint32(_first_vertex);
-  dg.add_uint32(_num_vertices);
-  dg.add_uint8(_index_type);
-  dg.add_uint8(_usage_hint);
-
-  manager->write_pointer(dg, _vertices);
-  WRITE_PTA(manager, dg, IPD_int::write_datagram, _ends);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomPrimitive::CData::complete_pointers
-//       Access: Public, Virtual
+//     Function: GeomPrimitive::complete_pointers
+//       Access: Protected, Virtual
 //  Description: Receives an array of pointers, one for each time
 //  Description: Receives an array of pointers, one for each time
 //               manager->read_pointer() was called in fillin().
 //               manager->read_pointer() was called in fillin().
 //               Returns the number of pointers processed.
 //               Returns the number of pointers processed.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-int GeomPrimitive::CData::
+int GeomPrimitive::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
-  int pi = CycleData::complete_pointers(p_list, manager);
+  int pi = TypedWritable::complete_pointers(p_list, manager);
 
 
   _vertices = DCAST(GeomVertexArrayData, p_list[pi++]);    
   _vertices = DCAST(GeomVertexArrayData, p_list[pi++]);    
 
 
@@ -1476,14 +1446,16 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: GeomPrimitive::CData::fillin
-//       Access: Public, Virtual
+//     Function: GeomPrimitive::fillin
+//       Access: Protected
 //  Description: This internal function is called by make_from_bam to
 //  Description: This internal function is called by make_from_bam to
 //               read in all of the relevant data from the BamFile for
 //               read in all of the relevant data from the BamFile for
 //               the new GeomPrimitive.
 //               the new GeomPrimitive.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void GeomPrimitive::CData::
+void GeomPrimitive::
 fillin(DatagramIterator &scan, BamReader *manager) {
 fillin(DatagramIterator &scan, BamReader *manager) {
+  TypedWritable::fillin(scan, manager);
+
   _shade_model = (ShadeModel)scan.get_uint8();
   _shade_model = (ShadeModel)scan.get_uint8();
   _first_vertex = scan.get_uint32();
   _first_vertex = scan.get_uint32();
   _num_vertices = scan.get_uint32();
   _num_vertices = scan.get_uint32();
@@ -1495,4 +1467,6 @@ fillin(DatagramIterator &scan, BamReader *manager) {
 
 
   _modified = Geom::get_next_modified();
   _modified = Geom::get_next_modified();
   _got_minmax = false;
   _got_minmax = false;
+
+  manager->register_finalize(this);
 }
 }

+ 20 - 39
panda/src/gobj/geomPrimitive.h

@@ -28,10 +28,6 @@
 #include "pointerTo.h"
 #include "pointerTo.h"
 #include "pta_int.h"
 #include "pta_int.h"
 #include "pStatCollector.h"
 #include "pStatCollector.h"
-#include "cycleData.h"
-#include "cycleDataReader.h"
-#include "cycleDataWriter.h"
-#include "pipelineCycler.h"
 
 
 class GeomVertexData;
 class GeomVertexData;
 class PreparedGraphicsObjects;
 class PreparedGraphicsObjects;
@@ -67,6 +63,7 @@ protected:
 PUBLISHED:
 PUBLISHED:
   GeomPrimitive(UsageHint usage_hint);
   GeomPrimitive(UsageHint usage_hint);
   GeomPrimitive(const GeomPrimitive &copy);
   GeomPrimitive(const GeomPrimitive &copy);
+  void operator = (const GeomPrimitive &copy);
   virtual ~GeomPrimitive();
   virtual ~GeomPrimitive();
 
 
   virtual PT(GeomPrimitive) make_copy() const=0;
   virtual PT(GeomPrimitive) make_copy() const=0;
@@ -200,41 +197,24 @@ private:
   typedef pmap<PreparedGraphicsObjects *, IndexBufferContext *> Contexts;
   typedef pmap<PreparedGraphicsObjects *, IndexBufferContext *> Contexts;
   Contexts _contexts;
   Contexts _contexts;
     
     
-  // This is the data that must be cycled between pipeline stages.
-  class EXPCL_PANDA CData : public CycleData {
-  public:
-    INLINE CData();
-    INLINE CData(const CData &copy);
-    virtual CycleData *make_copy() const;
-    virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
-    virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
-    virtual void fillin(DatagramIterator &scan, BamReader *manager);
-    virtual TypeHandle get_parent_type() const {
-      return GeomPrimitive::get_class_type();
-    }
-
-    ShadeModel _shade_model;
-    int _first_vertex;
-    int _num_vertices;
-    NumericType _index_type;
-    UsageHint _usage_hint;
-    PT(GeomVertexArrayData) _vertices;
-    PTA_int _ends;
-    PT(GeomVertexArrayData) _mins;
-    PT(GeomVertexArrayData) _maxs;
-    UpdateSeq _modified;
-
-    bool _got_minmax;
-    unsigned int _min_vertex;
-    unsigned int _max_vertex;
-  };
-
-  PipelineCycler<CData> _cycler;
-  typedef CycleDataReader<CData> CDReader;
-  typedef CycleDataWriter<CData> CDWriter;
-
-  void recompute_minmax(CDWriter &cdata);
-  void do_make_indexed(CDWriter &cdata);
+  // We don't need to cycle any data in the GeomPrimitive, since the
+  // GeomPrimitive itself is cycled through the stages.
+  ShadeModel _shade_model;
+  int _first_vertex;
+  int _num_vertices;
+  NumericType _index_type;
+  UsageHint _usage_hint;
+  PT(GeomVertexArrayData) _vertices;
+  PTA_int _ends;
+  PT(GeomVertexArrayData) _mins;
+  PT(GeomVertexArrayData) _maxs;
+  UpdateSeq _modified;
+  
+  bool _got_minmax;
+  unsigned int _min_vertex;
+  unsigned int _max_vertex;
+
+  void recompute_minmax();
 
 
   static PStatCollector _decompose_pcollector;
   static PStatCollector _decompose_pcollector;
   static PStatCollector _rotate_pcollector;
   static PStatCollector _rotate_pcollector;
@@ -245,6 +225,7 @@ public:
   virtual void finalize(BamReader *manager);
   virtual void finalize(BamReader *manager);
 
 
 protected:
 protected:
+  virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
   void fillin(DatagramIterator &scan, BamReader *manager);
   void fillin(DatagramIterator &scan, BamReader *manager);
 
 
 public:
 public:

+ 4 - 38
panda/src/gobj/geomVertexArrayData.I

@@ -36,8 +36,7 @@ get_array_format() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE GeomVertexArrayData::UsageHint GeomVertexArrayData::
 INLINE GeomVertexArrayData::UsageHint GeomVertexArrayData::
 get_usage_hint() const {
 get_usage_hint() const {
-  CDReader cdata(_cycler);
-  return cdata->_usage_hint;
+  return _usage_hint;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -83,8 +82,7 @@ clear_rows() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int GeomVertexArrayData::
 INLINE int GeomVertexArrayData::
 get_data_size_bytes() const {
 get_data_size_bytes() const {
-  CDReader cdata(_cycler);
-  return cdata->_data.size();
+  return _data.size();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -96,8 +94,7 @@ get_data_size_bytes() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE UpdateSeq GeomVertexArrayData::
 INLINE UpdateSeq GeomVertexArrayData::
 get_modified() const {
 get_modified() const {
-  CDReader cdata(_cycler);
-  return cdata->_modified;
+  return _modified;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -109,36 +106,5 @@ get_modified() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE CPTA_uchar GeomVertexArrayData::
 INLINE CPTA_uchar GeomVertexArrayData::
 get_data() const {
 get_data() const {
-  CDReader cdata(_cycler);
-  return cdata->_data;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexArrayData::CData::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE GeomVertexArrayData::CData::
-CData() :
-  _usage_hint(UH_unspecified)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexArrayData::CData::Copy Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE GeomVertexArrayData::CData::
-CData(const GeomVertexArrayData::CData &copy) :
-  _usage_hint(copy._usage_hint),
-  _data(copy._data),
-  _modified(copy._modified)
-{
-}
-
-INLINE ostream &
-operator << (ostream &out, const GeomVertexArrayData &obj) {
-  obj.output(out);
-  return out;
+  return _data;
 }
 }

+ 35 - 74
panda/src/gobj/geomVertexArrayData.cxx

@@ -45,7 +45,8 @@ GeomVertexArrayData() {
 GeomVertexArrayData::
 GeomVertexArrayData::
 GeomVertexArrayData(const GeomVertexArrayFormat *array_format,
 GeomVertexArrayData(const GeomVertexArrayFormat *array_format,
                     GeomVertexArrayData::UsageHint usage_hint) :
                     GeomVertexArrayData::UsageHint usage_hint) :
-  _array_format(array_format)
+  _array_format(array_format),
+  _usage_hint(UH_unspecified)
 {
 {
   set_usage_hint(usage_hint);
   set_usage_hint(usage_hint);
   _endian_reversed = false;
   _endian_reversed = false;
@@ -61,7 +62,9 @@ GeomVertexArrayData::
 GeomVertexArrayData(const GeomVertexArrayData &copy) :
 GeomVertexArrayData(const GeomVertexArrayData &copy) :
   TypedWritableReferenceCount(copy),
   TypedWritableReferenceCount(copy),
   _array_format(copy._array_format),
   _array_format(copy._array_format),
-  _cycler(copy._cycler)
+  _usage_hint(copy._usage_hint),
+  _data(copy._data),
+  _modified(copy._modified)
 {
 {
   _endian_reversed = false;
   _endian_reversed = false;
   nassertv(_array_format->is_registered());
   nassertv(_array_format->is_registered());
@@ -76,10 +79,10 @@ void GeomVertexArrayData::
 operator = (const GeomVertexArrayData &copy) {
 operator = (const GeomVertexArrayData &copy) {
   TypedWritableReferenceCount::operator = (copy);
   TypedWritableReferenceCount::operator = (copy);
   _array_format = copy._array_format;
   _array_format = copy._array_format;
-  _cycler = copy._cycler;
 
 
-  CDWriter cdata(_cycler);
-  cdata->_modified = Geom::get_next_modified();
+  _usage_hint = copy._usage_hint;
+  _data = copy._data;
+  _modified = Geom::get_next_modified();
 
 
   nassertv(_array_format->is_registered());
   nassertv(_array_format->is_registered());
 }
 }
@@ -114,35 +117,33 @@ GeomVertexArrayData::
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool GeomVertexArrayData::
 bool GeomVertexArrayData::
 set_num_rows(int n) {
 set_num_rows(int n) {
-  CDWriter cdata(_cycler);
-
   int stride = _array_format->get_stride();
   int stride = _array_format->get_stride();
-  int delta = n - (cdata->_data.size() / stride);
+  int delta = n - (_data.size() / stride);
   
   
   if (delta != 0) {
   if (delta != 0) {
-    if (cdata->_data.get_ref_count() > 1) {
+    if (_data.get_ref_count() > 1) {
       // Copy-on-write: the data is already reffed somewhere else,
       // Copy-on-write: the data is already reffed somewhere else,
       // so we're just going to make a copy.
       // so we're just going to make a copy.
       PTA_uchar new_data;
       PTA_uchar new_data;
       new_data.reserve(n * stride);
       new_data.reserve(n * stride);
       new_data.insert(new_data.end(), n * stride, 0);
       new_data.insert(new_data.end(), n * stride, 0);
-      memcpy(new_data, cdata->_data, 
-             min((size_t)(n * stride), cdata->_data.size()));
-      cdata->_data = new_data;
+      memcpy(new_data, _data, 
+             min((size_t)(n * stride), _data.size()));
+      _data = new_data;
       
       
     } else {
     } else {
       // We've got the only reference to the data, so we can change
       // We've got the only reference to the data, so we can change
       // it directly.
       // it directly.
       if (delta > 0) {
       if (delta > 0) {
-        cdata->_data.insert(cdata->_data.end(), delta * stride, 0);
+        _data.insert(_data.end(), delta * stride, 0);
         
         
       } else {
       } else {
-        cdata->_data.erase(cdata->_data.begin() + n * stride, 
-                           cdata->_data.end());
+        _data.erase(_data.begin() + n * stride, 
+                           _data.end());
       }
       }
     }
     }
 
 
-    cdata->_modified = Geom::get_next_modified();
+    _modified = Geom::get_next_modified();
 
 
     return true;
     return true;
   }
   }
@@ -158,9 +159,8 @@ set_num_rows(int n) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomVertexArrayData::
 void GeomVertexArrayData::
 set_usage_hint(GeomVertexArrayData::UsageHint usage_hint) {
 set_usage_hint(GeomVertexArrayData::UsageHint usage_hint) {
-  CDWriter cdata(_cycler);
-  cdata->_usage_hint = usage_hint;
-  cdata->_modified = Geom::get_next_modified();
+  _usage_hint = usage_hint;
+  _modified = Geom::get_next_modified();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -195,16 +195,14 @@ modify_data() {
   // Perform copy-on-write: if the reference count on the vertex data
   // Perform copy-on-write: if the reference count on the vertex data
   // is greater than 1, assume some other GeomVertexData has the same
   // is greater than 1, assume some other GeomVertexData has the same
   // pointer, so make a copy of it first.
   // pointer, so make a copy of it first.
-  CDWriter cdata(_cycler);
-
-  if (cdata->_data.get_ref_count() > 1) {
-    PTA_uchar orig_data = cdata->_data;
-    cdata->_data = PTA_uchar();
-    cdata->_data.v() = orig_data.v();
+  if (_data.get_ref_count() > 1) {
+    PTA_uchar orig_data = _data;
+    _data = PTA_uchar();
+    _data.v() = orig_data.v();
   }
   }
-  cdata->_modified = Geom::get_next_modified();
+  _modified = Geom::get_next_modified();
 
 
-  return cdata->_data;
+  return _data;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -215,9 +213,8 @@ modify_data() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomVertexArrayData::
 void GeomVertexArrayData::
 set_data(CPTA_uchar array) {
 set_data(CPTA_uchar array) {
-  CDWriter cdata(_cycler);
-  cdata->_data = (PTA_uchar &)array;
-  cdata->_modified = Geom::get_next_modified();
+  _data = (PTA_uchar &)array;
+  _modified = Geom::get_next_modified();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -410,13 +407,15 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   TypedWritableReferenceCount::write_datagram(manager, dg);
   TypedWritableReferenceCount::write_datagram(manager, dg);
 
 
   manager->write_pointer(dg, _array_format);
   manager->write_pointer(dg, _array_format);
-  manager->write_cdata(dg, _cycler, this);
+
+  dg.add_uint8(_usage_hint);
+  WRITE_PTA(manager, dg, write_raw_data, _data);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexArrayData::write_raw_data
 //     Function: GeomVertexArrayData::write_raw_data
 //       Access: Public
 //       Access: Public
-//  Description: Called by CData::write_datagram to write the raw data
+//  Description: Called by write_datagram() to write the raw data
 //               of the array to the indicated datagram.
 //               of the array to the indicated datagram.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomVertexArrayData::
 void GeomVertexArrayData::
@@ -437,7 +436,7 @@ write_raw_data(BamWriter *manager, Datagram &dg, const PTA_uchar &data) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexArrayData::read_raw_data
 //     Function: GeomVertexArrayData::read_raw_data
 //       Access: Public
 //       Access: Public
-//  Description: Called by CData::fillin to read the raw data
+//  Description: Called by fillin() to read the raw data
 //               of the array from the indicated datagram.
 //               of the array from the indicated datagram.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PTA_uchar GeomVertexArrayData::
 PTA_uchar GeomVertexArrayData::
@@ -457,8 +456,7 @@ read_raw_data(BamReader *manager, DatagramIterator &scan) {
       _endian_reversed = true;
       _endian_reversed = true;
     } else {
     } else {
       // Since we have the _array_format pointer now, we can reverse
       // Since we have the _array_format pointer now, we can reverse
-      // it immediately (and we should, to support threaded CData
-      // updates).
+      // it immediately.
       data = reverse_data_endianness(data);
       data = reverse_data_endianness(data);
     }
     }
   }
   }
@@ -498,8 +496,6 @@ finalize(BamReader *manager) {
   // cause the unregistered object to destruct, we have to also tell
   // cause the unregistered object to destruct, we have to also tell
   // the BamReader to return the new object from now on.
   // the BamReader to return the new object from now on.
 
 
-  CDWriter cdata(_cycler);
-
   CPT(GeomVertexArrayFormat) new_array_format = 
   CPT(GeomVertexArrayFormat) new_array_format = 
     GeomVertexArrayFormat::register_format(_array_format);
     GeomVertexArrayFormat::register_format(_array_format);
 
 
@@ -508,7 +504,7 @@ finalize(BamReader *manager) {
 
 
   if (_endian_reversed) {
   if (_endian_reversed) {
     // Now is the time to endian-reverse the data.
     // Now is the time to endian-reverse the data.
-    cdata->_data = reverse_data_endianness(cdata->_data);
+    _data = reverse_data_endianness(_data);
   }
   }
 }
 }
 
 
@@ -545,44 +541,9 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   TypedWritableReferenceCount::fillin(scan, manager);
   TypedWritableReferenceCount::fillin(scan, manager);
 
 
   manager->read_pointer(scan);
   manager->read_pointer(scan);
-  manager->read_cdata(scan, _cycler, this);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexArrayData::CData::make_copy
-//       Access: Public, Virtual
-//  Description:
-////////////////////////////////////////////////////////////////////
-CycleData *GeomVertexArrayData::CData::
-make_copy() const {
-  return new CData(*this);
-}
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexArrayData::CData::write_datagram
-//       Access: Public, Virtual
-//  Description: Writes the contents of this object to the datagram
-//               for shipping out to a Bam file.
-////////////////////////////////////////////////////////////////////
-void GeomVertexArrayData::CData::
-write_datagram(BamWriter *manager, Datagram &dg, void *extra_data) const {
-  GeomVertexArrayData *array_data = (GeomVertexArrayData *)extra_data;
-  dg.add_uint8(_usage_hint);
-  WRITE_PTA(manager, dg, array_data->write_raw_data, _data);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexArrayData::CData::fillin
-//       Access: Public, Virtual
-//  Description: This internal function is called by make_from_bam to
-//               read in all of the relevant data from the BamFile for
-//               the new GeomVertexArrayData.
-////////////////////////////////////////////////////////////////////
-void GeomVertexArrayData::CData::
-fillin(DatagramIterator &scan, BamReader *manager, void *extra_data) {
-  GeomVertexArrayData *array_data = (GeomVertexArrayData *)extra_data;
   _usage_hint = (UsageHint)scan.get_uint8();
   _usage_hint = (UsageHint)scan.get_uint8();
-  READ_PTA(manager, scan, array_data->read_raw_data, _data);
+  READ_PTA(manager, scan, read_raw_data, _data);
 
 
   _modified = Geom::get_next_modified();
   _modified = Geom::get_next_modified();
 }
 }

+ 5 - 26
panda/src/gobj/geomVertexArrayData.h

@@ -25,10 +25,6 @@
 #include "geomEnums.h"
 #include "geomEnums.h"
 #include "pta_uchar.h"
 #include "pta_uchar.h"
 #include "updateSeq.h"
 #include "updateSeq.h"
-#include "cycleData.h"
-#include "cycleDataReader.h"
-#include "cycleDataWriter.h"
-#include "pipelineCycler.h"
 #include "pmap.h"
 #include "pmap.h"
 
 
 class PreparedGraphicsObjects;
 class PreparedGraphicsObjects;
@@ -113,28 +109,11 @@ private:
   // to indicate the data must be endian-reversed in finalize().
   // to indicate the data must be endian-reversed in finalize().
   bool _endian_reversed;
   bool _endian_reversed;
 
 
-  // This is the data that must be cycled between pipeline stages.
-  class EXPCL_PANDA CData : public CycleData {
-  public:
-    INLINE CData();
-    INLINE CData(const CData &copy);
-    virtual CycleData *make_copy() const;
-    virtual void write_datagram(BamWriter *manager, Datagram &dg,
-                                void *extra_data) const;
-    virtual void fillin(DatagramIterator &scan, BamReader *manager,
-                        void *extra_data);
-    virtual TypeHandle get_parent_type() const {
-      return GeomVertexArrayData::get_class_type();
-    }
-
-    UsageHint _usage_hint;
-    PTA_uchar _data;
-    UpdateSeq _modified;
-  };
-
-  PipelineCycler<CData> _cycler;
-  typedef CycleDataReader<CData> CDReader;
-  typedef CycleDataWriter<CData> CDWriter;
+  // We don't need to cycle any data in the GeomVertexArrayData, since
+  // the GeomVertexArrayData itself is cycled through the stages.
+  UsageHint _usage_hint;
+  PTA_uchar _data;
+  UpdateSeq _modified;
 
 
 public:
 public:
   static void register_with_read_factory();
   static void register_with_read_factory();

+ 8 - 83
panda/src/gobj/geomVertexData.I

@@ -56,8 +56,7 @@ get_format() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE GeomVertexData::UsageHint GeomVertexData::
 INLINE GeomVertexData::UsageHint GeomVertexData::
 get_usage_hint() const {
 get_usage_hint() const {
-  CDReader cdata(_cycler);
-  return cdata->_usage_hint;
+  return _usage_hint;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -72,33 +71,6 @@ has_column(const InternalName *name) const {
   return _format->has_column(name);
   return _format->has_column(name);
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::set_num_rows
-//       Access: Published
-//  Description: Sets the length of the array to n rows in all of
-//               the various arrays (presumably by adding rows).
-//
-//               The new vertex data is initialized to 0, except for
-//               the "color" column, which is initialized to (1, 1, 1,
-//               1).
-//
-//               The return value is true if the number of rows
-//               was changed, false if the object already contained n
-//               rows (or if there was some error).
-//
-//               Although this method is Published, application code
-//               only very rarely has any need to call it.  Instead,
-//               you should use the GeomVertexWriter to build up the
-//               rows in a GeomVertexData object automatically,
-//               without need to explicitly set the number of
-//               rows.
-////////////////////////////////////////////////////////////////////
-INLINE bool GeomVertexData::
-set_num_rows(int n) {
-  CDWriter cdata(_cycler);
-  return do_set_num_rows(n, cdata);
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexData::get_num_arrays
 //     Function: GeomVertexData::get_num_arrays
 //       Access: Published
 //       Access: Published
@@ -108,8 +80,7 @@ set_num_rows(int n) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int GeomVertexData::
 INLINE int GeomVertexData::
 get_num_arrays() const {
 get_num_arrays() const {
-  CDReader cdata(_cycler);
-  return cdata->_arrays.size();
+  return _arrays.size();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -121,9 +92,8 @@ get_num_arrays() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const GeomVertexArrayData *GeomVertexData::
 INLINE const GeomVertexArrayData *GeomVertexData::
 get_array(int i) const {
 get_array(int i) const {
-  CDReader cdata(_cycler);
-  nassertr(i >= 0 && i < (int)cdata->_arrays.size(), NULL);
-  return cdata->_arrays[i];
+  nassertr(i >= 0 && i < (int)_arrays.size(), NULL);
+  return _arrays[i];
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -144,8 +114,7 @@ get_array(int i) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const TransformTable *GeomVertexData::
 INLINE const TransformTable *GeomVertexData::
 get_transform_table() const {
 get_transform_table() const {
-  CDReader cdata(_cycler);
-  return cdata->_transform_table;
+  return _transform_table;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -176,8 +145,7 @@ clear_transform_table() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const TransformBlendTable *GeomVertexData::
 INLINE const TransformBlendTable *GeomVertexData::
 get_transform_blend_table() const {
 get_transform_blend_table() const {
-  CDReader cdata(_cycler);
-  return cdata->_transform_blend_table;
+  return _transform_blend_table;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -205,8 +173,7 @@ clear_transform_blend_table() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const SliderTable *GeomVertexData::
 INLINE const SliderTable *GeomVertexData::
 get_slider_table() const {
 get_slider_table() const {
-  CDReader cdata(_cycler);
-  return cdata->_slider_table;
+  return _slider_table;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -230,20 +197,7 @@ clear_slider_table() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE UpdateSeq GeomVertexData::
 INLINE UpdateSeq GeomVertexData::
 get_modified() const {
 get_modified() const {
-  CDReader cdata(_cycler);
-  return cdata->_modified;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::clear_cache
-//       Access: Published
-//  Description: Removes all of the previously-cached results of
-//               convert_to().
-////////////////////////////////////////////////////////////////////
-INLINE void GeomVertexData::
-clear_cache() {
-  CDWriter cdata(_cycler);
-  do_clear_cache(cdata);
+  return _modified;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -412,35 +366,6 @@ operator < (const CacheEntry &other) const {
   return _modifier < other._modifier;
   return _modifier < other._modifier;
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::CData::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE GeomVertexData::CData::
-CData() :
-  _usage_hint(UH_unspecified)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::CData::Copy Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE GeomVertexData::CData::
-CData(const GeomVertexData::CData &copy) :
-  _usage_hint(copy._usage_hint),
-  _arrays(copy._arrays),
-  _transform_table(copy._transform_table),
-  _transform_blend_table(copy._transform_blend_table),
-  _slider_table(copy._slider_table),
-  _animated_vertices(copy._animated_vertices),
-  _animated_vertices_modified(copy._animated_vertices_modified),
-  _modified(copy._modified)
-{
-}
-
 INLINE ostream &
 INLINE ostream &
 operator << (ostream &out, const GeomVertexData &obj) {
 operator << (ostream &out, const GeomVertexData &obj) {
   obj.output(out);
   obj.output(out);

+ 265 - 325
panda/src/gobj/geomVertexData.cxx

@@ -41,6 +41,7 @@ PStatCollector GeomVertexData::_animation_pcollector("*:Animation");
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 GeomVertexData::
 GeomVertexData::
 GeomVertexData() :
 GeomVertexData() :
+  _usage_hint(UH_unspecified),
   _char_pcollector(_animation_pcollector, "unnamed"),
   _char_pcollector(_animation_pcollector, "unnamed"),
   _skinning_pcollector(_char_pcollector, "Skinning"),
   _skinning_pcollector(_char_pcollector, "Skinning"),
   _morphs_pcollector(_char_pcollector, "Morphs")
   _morphs_pcollector(_char_pcollector, "Morphs")
@@ -58,6 +59,7 @@ GeomVertexData(const string &name,
                GeomVertexData::UsageHint usage_hint) :
                GeomVertexData::UsageHint usage_hint) :
   _name(name),
   _name(name),
   _format(format),
   _format(format),
+  _usage_hint(UH_unspecified),
   _char_pcollector(PStatCollector(_animation_pcollector, name)),
   _char_pcollector(PStatCollector(_animation_pcollector, name)),
   _skinning_pcollector(_char_pcollector, "Skinning"),
   _skinning_pcollector(_char_pcollector, "Skinning"),
   _morphs_pcollector(_char_pcollector, "Morphs")
   _morphs_pcollector(_char_pcollector, "Morphs")
@@ -67,13 +69,11 @@ GeomVertexData(const string &name,
   set_usage_hint(usage_hint);
   set_usage_hint(usage_hint);
 
 
   // Create some empty arrays as required by the format.
   // Create some empty arrays as required by the format.
-  CDWriter cdata(_cycler);
-
   int num_arrays = _format->get_num_arrays();
   int num_arrays = _format->get_num_arrays();
   for (int i = 0; i < num_arrays; i++) {
   for (int i = 0; i < num_arrays; i++) {
     PT(GeomVertexArrayData) array = new GeomVertexArrayData
     PT(GeomVertexArrayData) array = new GeomVertexArrayData
       (_format->get_array(i), usage_hint);
       (_format->get_array(i), usage_hint);
-    cdata->_arrays.push_back(array);
+    _arrays.push_back(array);
   }
   }
 }
 }
 
 
@@ -87,15 +87,21 @@ GeomVertexData(const GeomVertexData &copy) :
   TypedWritableReferenceCount(copy),
   TypedWritableReferenceCount(copy),
   _name(copy._name),
   _name(copy._name),
   _format(copy._format),
   _format(copy._format),
-  _cycler(copy._cycler),
+  _usage_hint(copy._usage_hint),
+  _arrays(copy._arrays),
+  _transform_table(copy._transform_table),
+  _transform_blend_table(copy._transform_blend_table),
+  _slider_table(copy._slider_table),
+  _animated_vertices(copy._animated_vertices),
+  _animated_vertices_modified(copy._animated_vertices_modified),
+  _modified(copy._modified),
   _char_pcollector(copy._char_pcollector),
   _char_pcollector(copy._char_pcollector),
   _skinning_pcollector(copy._skinning_pcollector),
   _skinning_pcollector(copy._skinning_pcollector),
   _morphs_pcollector(copy._morphs_pcollector)
   _morphs_pcollector(copy._morphs_pcollector)
 {
 {
-  CDWriter cdata(_cycler);
   // It's important that we *not* copy the animated_vertices pointer.
   // It's important that we *not* copy the animated_vertices pointer.
-  cdata->_animated_vertices = NULL;
-  cdata->_animated_vertices_modified = UpdateSeq();
+  _animated_vertices = NULL;
+  _animated_vertices_modified = UpdateSeq();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -112,7 +118,7 @@ GeomVertexData(const GeomVertexData &copy,
   TypedWritableReferenceCount(copy),
   TypedWritableReferenceCount(copy),
   _name(copy._name),
   _name(copy._name),
   _format(format),
   _format(format),
-  _cycler(copy._cycler),
+  _usage_hint(UH_unspecified),
   _char_pcollector(copy._char_pcollector),
   _char_pcollector(copy._char_pcollector),
   _skinning_pcollector(copy._skinning_pcollector),
   _skinning_pcollector(copy._skinning_pcollector),
   _morphs_pcollector(copy._morphs_pcollector)
   _morphs_pcollector(copy._morphs_pcollector)
@@ -120,20 +126,18 @@ GeomVertexData(const GeomVertexData &copy,
   nassertv(_format->is_registered());
   nassertv(_format->is_registered());
 
 
   // Create some empty arrays as required by the format.
   // Create some empty arrays as required by the format.
-  CDWriter cdata(_cycler);
-
-  UsageHint usage_hint = cdata->_usage_hint;
-  cdata->_arrays.clear();
+  UsageHint usage_hint = _usage_hint;
+  _arrays.clear();
   int num_arrays = _format->get_num_arrays();
   int num_arrays = _format->get_num_arrays();
   for (int i = 0; i < num_arrays; i++) {
   for (int i = 0; i < num_arrays; i++) {
     PT(GeomVertexArrayData) array = new GeomVertexArrayData
     PT(GeomVertexArrayData) array = new GeomVertexArrayData
       (_format->get_array(i), usage_hint);
       (_format->get_array(i), usage_hint);
-    cdata->_arrays.push_back(array);
+    _arrays.push_back(array);
   }
   }
 
 
   // It's important that we *not* copy the animated_vertices pointer.
   // It's important that we *not* copy the animated_vertices pointer.
-  cdata->_animated_vertices = NULL;
-  cdata->_animated_vertices_modified = UpdateSeq();
+  _animated_vertices = NULL;
+  _animated_vertices_modified = UpdateSeq();
 }
 }
   
   
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -146,15 +150,22 @@ operator = (const GeomVertexData &copy) {
   TypedWritableReferenceCount::operator = (copy);
   TypedWritableReferenceCount::operator = (copy);
   _name = copy._name;
   _name = copy._name;
   _format = copy._format;
   _format = copy._format;
-  _cycler = copy._cycler;
   _char_pcollector = copy._char_pcollector;
   _char_pcollector = copy._char_pcollector;
   _skinning_pcollector = copy._skinning_pcollector;
   _skinning_pcollector = copy._skinning_pcollector;
   _morphs_pcollector = copy._morphs_pcollector;
   _morphs_pcollector = copy._morphs_pcollector;
 
 
-  CDWriter cdata(_cycler);
-  do_clear_cache(cdata);
-  cdata->_modified = Geom::get_next_modified();
-  cdata->_animated_vertices_modified = UpdateSeq();
+  _usage_hint = copy._usage_hint;
+  _arrays = copy._arrays;
+  _transform_table = copy._transform_table;
+  _transform_blend_table = copy._transform_blend_table;
+  _slider_table = copy._slider_table;
+  _animated_vertices = copy._animated_vertices;
+  _animated_vertices_modified = copy._animated_vertices_modified;
+
+  clear_cache();
+
+  _modified = Geom::get_next_modified();
+  _animated_vertices_modified = UpdateSeq();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -164,23 +175,13 @@ operator = (const GeomVertexData &copy) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 GeomVertexData::
 GeomVertexData::
 ~GeomVertexData() {
 ~GeomVertexData() {
-  // When we destruct, we should ensure that all of our cached
-  // entries, across all pipeline stages, are properly removed from
-  // the cache manager.
-  int num_stages = _cycler.get_num_stages();
-  for (int i = 0; i < num_stages; i++) {
-    if (_cycler.is_stage_unique(i)) {
-      CData *cdata = _cycler.write_stage(i);
-      for (Cache::iterator ci = cdata->_cache.begin();
-           ci != cdata->_cache.end();
-           ++ci) {
-        CacheEntry *entry = (*ci);
-        entry->erase();
-      }
-      cdata->_cache.clear();
-      _cycler.release_write_stage(i, cdata);
-    }
+  for (Cache::iterator ci = _cache.begin();
+       ci != _cache.end();
+       ++ci) {
+    CacheEntry *entry = (*ci);
+    entry->erase();
   }
   }
+  _cache.clear();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -206,21 +207,20 @@ set_name(const string &name) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomVertexData::
 void GeomVertexData::
 set_usage_hint(GeomVertexData::UsageHint usage_hint) {
 set_usage_hint(GeomVertexData::UsageHint usage_hint) {
-  CDWriter cdata(_cycler);
-  cdata->_usage_hint = usage_hint;
+  _usage_hint = usage_hint;
 
 
   Arrays::iterator ai;
   Arrays::iterator ai;
-  for (ai = cdata->_arrays.begin();
-       ai != cdata->_arrays.end();
+  for (ai = _arrays.begin();
+       ai != _arrays.end();
        ++ai) {
        ++ai) {
     if ((*ai)->get_ref_count() > 1) {
     if ((*ai)->get_ref_count() > 1) {
       (*ai) = new GeomVertexArrayData(*(*ai));
       (*ai) = new GeomVertexArrayData(*(*ai));
     }
     }
     (*ai)->set_usage_hint(usage_hint);
     (*ai)->set_usage_hint(usage_hint);
   }
   }
-  do_clear_cache(cdata);
-  cdata->_modified = Geom::get_next_modified();
-  cdata->_animated_vertices_modified = UpdateSeq();
+  clear_cache();
+  _modified = Geom::get_next_modified();
+  _animated_vertices_modified = UpdateSeq();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -232,8 +232,7 @@ set_usage_hint(GeomVertexData::UsageHint usage_hint) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int GeomVertexData::
 int GeomVertexData::
 get_num_rows() const {
 get_num_rows() const {
-  CDReader cdata(_cycler);
-  nassertr(_format->get_num_arrays() == (int)cdata->_arrays.size(), 0);
+  nassertr(_format->get_num_arrays() == (int)_arrays.size(), 0);
   if (_format->get_num_arrays() == 0) {
   if (_format->get_num_arrays() == 0) {
     // No arrays means no rows.  Weird but legal.
     // No arrays means no rows.  Weird but legal.
     return 0;
     return 0;
@@ -241,7 +240,98 @@ get_num_rows() const {
 
 
   // Look up the answer on the first array (since any array will do).
   // Look up the answer on the first array (since any array will do).
   int stride = _format->get_array(0)->get_stride();
   int stride = _format->get_array(0)->get_stride();
-  return cdata->_arrays[0]->get_data_size_bytes() / stride;
+  return _arrays[0]->get_data_size_bytes() / stride;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexData::set_num_rows
+//       Access: Published
+//  Description: Sets the length of the array to n rows in all of
+//               the various arrays (presumably by adding rows).
+//
+//               The new vertex data is initialized to 0, except for
+//               the "color" column, which is initialized to (1, 1, 1,
+//               1).
+//
+//               The return value is true if the number of rows
+//               was changed, false if the object already contained n
+//               rows (or if there was some error).
+//
+//               Although this method is Published, application code
+//               only very rarely has any need to call it.  Instead,
+//               you should use the GeomVertexWriter to build up the
+//               rows in a GeomVertexData object automatically,
+//               without need to explicitly set the number of
+//               rows.
+////////////////////////////////////////////////////////////////////
+bool GeomVertexData::
+set_num_rows(int n) {
+  nassertr(_format->get_num_arrays() == (int)_arrays.size(), false);
+
+  bool any_changed = false;
+
+  int color_array = -1;
+  int orig_color_rows = -1;
+
+  for (size_t i = 0; i < _arrays.size(); i++) {
+    if (_arrays[i]->get_num_rows() != n) {
+      // Copy-on-write.
+      if (_arrays[i]->get_ref_count() > 1) {
+        _arrays[i] = new GeomVertexArrayData(*_arrays[i]);
+      }
+      if (_arrays[i]->has_column(InternalName::get_color())) {
+        color_array = i;
+        orig_color_rows = _arrays[i]->get_num_rows();
+      }
+      _arrays[i]->set_num_rows(n);
+      any_changed = true;
+    }
+  }
+
+  if (color_array >= 0 && orig_color_rows < n) {
+    // We have just added some rows, fill the "color" column with
+    // (1, 1, 1, 1), for the programmer's convenience.
+    GeomVertexArrayData *array_data = _arrays[color_array];
+    const GeomVertexColumn *column = 
+      array_data->get_array_format()->get_column(InternalName::get_color());
+    int stride = array_data->get_array_format()->get_stride();
+    unsigned char *start = 
+      array_data->modify_data() + column->get_start();
+    unsigned char *stop = start + array_data->get_data_size_bytes();
+    unsigned char *pointer = start + stride * orig_color_rows;
+    int num_values = column->get_num_values();
+
+    switch (column->get_numeric_type()) {
+    case NT_packed_dcba:
+    case NT_packed_dabc:
+    case NT_uint8:
+    case NT_uint16:
+    case NT_uint32:
+      while (pointer < stop) {
+        memset(pointer, 0xff, column->get_total_bytes());
+        pointer += stride;
+      }
+      break;
+
+    case NT_float32:
+      while (pointer < stop) {
+        PN_float32 *pi = (PN_float32 *)pointer;
+        for (int i = 0; i < num_values; i++) {
+          pi[i] = 1.0f;
+        }
+        pointer += stride;
+      }
+      break;
+    }          
+  }
+
+  if (any_changed) {
+    clear_cache();
+    _modified = Geom::get_next_modified();
+    _animated_vertices.clear();
+  }
+
+  return any_changed;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -253,21 +343,20 @@ get_num_rows() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomVertexData::
 void GeomVertexData::
 clear_rows() {
 clear_rows() {
-  CDWriter cdata(_cycler);
-  nassertv(_format->get_num_arrays() == (int)cdata->_arrays.size());
+  nassertv(_format->get_num_arrays() == (int)_arrays.size());
 
 
   Arrays::iterator ai;
   Arrays::iterator ai;
-  for (ai = cdata->_arrays.begin();
-       ai != cdata->_arrays.end();
+  for (ai = _arrays.begin();
+       ai != _arrays.end();
        ++ai) {
        ++ai) {
     if ((*ai)->get_ref_count() > 1) {
     if ((*ai)->get_ref_count() > 1) {
       (*ai) = new GeomVertexArrayData(*(*ai));
       (*ai) = new GeomVertexArrayData(*(*ai));
     }
     }
     (*ai)->clear_rows();
     (*ai)->clear_rows();
   }
   }
-  do_clear_cache(cdata);
-  cdata->_modified = Geom::get_next_modified();
-  cdata->_animated_vertices.clear();
+  clear_cache();
+  _modified = Geom::get_next_modified();
+  _animated_vertices.clear();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -285,17 +374,16 @@ modify_array(int i) {
   // Perform copy-on-write: if the reference count on the vertex data
   // Perform copy-on-write: if the reference count on the vertex data
   // is greater than 1, assume some other GeomVertexData has the same
   // is greater than 1, assume some other GeomVertexData has the same
   // pointer, so make a copy of it first.
   // pointer, so make a copy of it first.
-  CDWriter cdata(_cycler);
-  nassertr(i >= 0 && i < (int)cdata->_arrays.size(), NULL);
+  nassertr(i >= 0 && i < (int)_arrays.size(), NULL);
 
 
-  if (cdata->_arrays[i]->get_ref_count() > 1) {
-    cdata->_arrays[i] = new GeomVertexArrayData(*cdata->_arrays[i]);
+  if (_arrays[i]->get_ref_count() > 1) {
+    _arrays[i] = new GeomVertexArrayData(*_arrays[i]);
   }
   }
-  do_clear_cache(cdata);
-  cdata->_modified = Geom::get_next_modified();
-  cdata->_animated_vertices_modified = UpdateSeq();
+  clear_cache();
+  _modified = Geom::get_next_modified();
+  _animated_vertices_modified = UpdateSeq();
 
 
-  return cdata->_arrays[i];
+  return _arrays[i];
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -308,12 +396,11 @@ modify_array(int i) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomVertexData::
 void GeomVertexData::
 set_array(int i, const GeomVertexArrayData *array) {
 set_array(int i, const GeomVertexArrayData *array) {
-  CDWriter cdata(_cycler);
-  nassertv(i >= 0 && i < (int)cdata->_arrays.size());
-  cdata->_arrays[i] = (GeomVertexArrayData *)array;
-  do_clear_cache(cdata);
-  cdata->_modified = Geom::get_next_modified();
-  cdata->_animated_vertices_modified = UpdateSeq();
+  nassertv(i >= 0 && i < (int)_arrays.size());
+  _arrays[i] = (GeomVertexArrayData *)array;
+  clear_cache();
+  _modified = Geom::get_next_modified();
+  _animated_vertices_modified = UpdateSeq();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -329,11 +416,10 @@ void GeomVertexData::
 set_transform_table(const TransformTable *table) {
 set_transform_table(const TransformTable *table) {
   nassertv(table == (TransformTable *)NULL || table->is_registered());
   nassertv(table == (TransformTable *)NULL || table->is_registered());
 
 
-  CDWriter cdata(_cycler);
-  cdata->_transform_table = (TransformTable *)table;
-  do_clear_cache(cdata);
-  cdata->_modified = Geom::get_next_modified();
-  cdata->_animated_vertices_modified = UpdateSeq();
+  _transform_table = (TransformTable *)table;
+  clear_cache();
+  _modified = Geom::get_next_modified();
+  _animated_vertices_modified = UpdateSeq();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -349,16 +435,14 @@ modify_transform_blend_table() {
   // Perform copy-on-write: if the reference count on the table is
   // Perform copy-on-write: if the reference count on the table is
   // greater than 1, assume some other GeomVertexData has the same
   // greater than 1, assume some other GeomVertexData has the same
   // pointer, so make a copy of it first.
   // pointer, so make a copy of it first.
-  CDWriter cdata(_cycler);
-
-  if (cdata->_transform_blend_table->get_ref_count() > 1) {
-    cdata->_transform_blend_table = new TransformBlendTable(*cdata->_transform_blend_table);
+  if (_transform_blend_table->get_ref_count() > 1) {
+    _transform_blend_table = new TransformBlendTable(*_transform_blend_table);
   }
   }
-  do_clear_cache(cdata);
-  cdata->_modified = Geom::get_next_modified();
-  cdata->_animated_vertices_modified = UpdateSeq();
+  clear_cache();
+  _modified = Geom::get_next_modified();
+  _animated_vertices_modified = UpdateSeq();
 
 
-  return cdata->_transform_blend_table;
+  return _transform_blend_table;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -372,11 +456,10 @@ modify_transform_blend_table() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomVertexData::
 void GeomVertexData::
 set_transform_blend_table(const TransformBlendTable *table) {
 set_transform_blend_table(const TransformBlendTable *table) {
-  CDWriter cdata(_cycler);
-  cdata->_transform_blend_table = (TransformBlendTable *)table;
-  do_clear_cache(cdata);
-  cdata->_modified = Geom::get_next_modified();
-  cdata->_animated_vertices_modified = UpdateSeq();
+  _transform_blend_table = (TransformBlendTable *)table;
+  clear_cache();
+  _modified = Geom::get_next_modified();
+  _animated_vertices_modified = UpdateSeq();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -394,11 +477,10 @@ void GeomVertexData::
 set_slider_table(const SliderTable *table) {
 set_slider_table(const SliderTable *table) {
   nassertv(table == (SliderTable *)NULL || table->is_registered());
   nassertv(table == (SliderTable *)NULL || table->is_registered());
 
 
-  CDWriter cdata(_cycler);
-  cdata->_slider_table = (SliderTable *)table;
-  do_clear_cache(cdata);
-  cdata->_modified = Geom::get_next_modified();
-  cdata->_animated_vertices_modified = UpdateSeq();
+  _slider_table = (SliderTable *)table;
+  clear_cache();
+  _modified = Geom::get_next_modified();
+  _animated_vertices_modified = UpdateSeq();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -409,12 +491,10 @@ set_slider_table(const SliderTable *table) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int GeomVertexData::
 int GeomVertexData::
 get_num_bytes() const {
 get_num_bytes() const {
-  CDReader cdata(_cycler);
-  
   int num_bytes = sizeof(GeomVertexData);
   int num_bytes = sizeof(GeomVertexData);
 
 
   Arrays::const_iterator ai;
   Arrays::const_iterator ai;
-  for (ai = cdata->_arrays.begin(); ai != cdata->_arrays.end(); ++ai) {
+  for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
     num_bytes += (*ai)->get_data_size_bytes();
     num_bytes += (*ai)->get_data_size_bytes();
   }
   }
 
 
@@ -674,11 +754,10 @@ convert_to(const GeomVertexFormat *new_format) const {
   // Look up the new format in our cache--maybe we've recently applied
   // Look up the new format in our cache--maybe we've recently applied
   // it.
   // it.
   {
   {
-    CDReader cdata(_cycler);
     CacheEntry temp_entry(new_format);
     CacheEntry temp_entry(new_format);
     temp_entry.local_object();
     temp_entry.local_object();
-    Cache::const_iterator ci = cdata->_cache.find(&temp_entry);
-    if (ci != cdata->_cache.end()) {
+    Cache::const_iterator ci = _cache.find(&temp_entry);
+    if (ci != _cache.end()) {
       CacheEntry *entry = (*ci);
       CacheEntry *entry = (*ci);
       // Record a cache hit, so this element will stay in the cache a
       // Record a cache hit, so this element will stay in the cache a
       // while longer.
       // while longer.
@@ -706,9 +785,8 @@ convert_to(const GeomVertexFormat *new_format) const {
     // Record the new result in the cache.
     // Record the new result in the cache.
     CacheEntry *entry;
     CacheEntry *entry;
     {
     {
-      CDWriter cdata(((GeomVertexData *)this)->_cycler);
       entry = new CacheEntry((GeomVertexData *)this, new_format, new_data);
       entry = new CacheEntry((GeomVertexData *)this, new_format, new_data);
-      bool inserted = cdata->_cache.insert(entry).second;
+      bool inserted = ((GeomVertexData *)this)->_cache.insert(entry).second;
       nassertr(inserted, new_data);
       nassertr(inserted, new_data);
     }
     }
     
     
@@ -878,24 +956,22 @@ set_color(const Colorf &color, int num_components,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CPT(GeomVertexData) GeomVertexData::
 CPT(GeomVertexData) GeomVertexData::
 animate_vertices() const {
 animate_vertices() const {
-  CDReader cdata(_cycler);
-
   if (_format->get_animation().get_animation_type() != AT_panda) {
   if (_format->get_animation().get_animation_type() != AT_panda) {
     return this;
     return this;
   }
   }
 
 
   UpdateSeq modified;
   UpdateSeq modified;
-  if (cdata->_transform_blend_table != (TransformBlendTable *)NULL) {
-    if (cdata->_slider_table != (SliderTable *)NULL) {
+  if (_transform_blend_table != (TransformBlendTable *)NULL) {
+    if (_slider_table != (SliderTable *)NULL) {
       modified = 
       modified = 
-        max(cdata->_transform_blend_table->get_modified(),
-            cdata->_slider_table->get_modified());
+        max(_transform_blend_table->get_modified(),
+            _slider_table->get_modified());
     } else {
     } else {
-      modified = cdata->_transform_blend_table->get_modified();
+      modified = _transform_blend_table->get_modified();
     }
     }
 
 
-  } else if (cdata->_slider_table != (SliderTable *)NULL) {
-    modified = cdata->_slider_table->get_modified();
+  } else if (_slider_table != (SliderTable *)NULL) {
+    modified = _slider_table->get_modified();
 
 
   } else {
   } else {
     // No transform blend table or slider table--ergo, no vertex
     // No transform blend table or slider table--ergo, no vertex
@@ -903,15 +979,14 @@ animate_vertices() const {
     return this;
     return this;
   }
   }
 
 
-  if (cdata->_animated_vertices_modified == modified &&
-      cdata->_animated_vertices != (GeomVertexData *)NULL) {
+  if (_animated_vertices_modified == modified &&
+      _animated_vertices != (GeomVertexData *)NULL) {
     // No changes.
     // No changes.
-    return cdata->_animated_vertices;
+    return _animated_vertices;
   }
   }
-  CDWriter cdataw(((GeomVertexData *)this)->_cycler, cdata);
-  cdataw->_animated_vertices_modified = modified;
-  ((GeomVertexData *)this)->update_animated_vertices(cdataw);
-  return cdataw->_animated_vertices;
+  ((GeomVertexData *)this)->_animated_vertices_modified = modified;
+  ((GeomVertexData *)this)->update_animated_vertices();
+  return _animated_vertices;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1069,6 +1144,23 @@ write(ostream &out, int indent_level) const {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexData::clear_cache
+//       Access: Published
+//  Description: Removes all of the previously-cached results of
+//               convert_to().
+////////////////////////////////////////////////////////////////////
+void GeomVertexData::
+clear_cache() {
+  for (Cache::iterator ci = _cache.begin();
+       ci != _cache.end();
+       ++ci) {
+    CacheEntry *entry = (*ci);
+    entry->erase();
+  }
+  _cache.clear();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexData::get_array_info
 //     Function: GeomVertexData::get_array_info
 //       Access: Public
 //       Access: Public
@@ -1092,8 +1184,7 @@ get_array_info(const InternalName *name,
   int array_index;
   int array_index;
   const GeomVertexColumn *column;
   const GeomVertexColumn *column;
   if (_format->get_array_info(name, array_index, column)) {
   if (_format->get_array_info(name, array_index, column)) {
-    CDReader cdata(_cycler);
-    array_data = cdata->_arrays[array_index];
+    array_data = _arrays[array_index];
     num_values = column->get_num_values();
     num_values = column->get_num_values();
     numeric_type = column->get_numeric_type();
     numeric_type = column->get_numeric_type();
     start = column->get_start();
     start = column->get_start();
@@ -1118,8 +1209,7 @@ get_vertex_info(const GeomVertexArrayData *&array_data,
   if (array_index >= 0) {
   if (array_index >= 0) {
     const GeomVertexColumn *column = _format->get_vertex_column();
     const GeomVertexColumn *column = _format->get_vertex_column();
 
 
-    CDReader cdata(_cycler);
-    array_data = cdata->_arrays[array_index];
+    array_data = _arrays[array_index];
     num_values = column->get_num_values();
     num_values = column->get_num_values();
     numeric_type = column->get_numeric_type();
     numeric_type = column->get_numeric_type();
     start = column->get_start();
     start = column->get_start();
@@ -1145,8 +1235,7 @@ get_normal_info(const GeomVertexArrayData *&array_data,
     const GeomVertexColumn *column = _format->get_normal_column();
     const GeomVertexColumn *column = _format->get_normal_column();
     nassertr(column->get_num_values() == 3, false);
     nassertr(column->get_num_values() == 3, false);
 
 
-    CDReader cdata(_cycler);
-    array_data = cdata->_arrays[array_index];
+    array_data = _arrays[array_index];
     numeric_type = column->get_numeric_type();
     numeric_type = column->get_numeric_type();
     start = column->get_start();
     start = column->get_start();
     stride = _format->get_array(array_index)->get_stride();
     stride = _format->get_array(array_index)->get_stride();
@@ -1170,8 +1259,7 @@ get_color_info(const GeomVertexArrayData *&array_data,
   if (array_index >= 0) {
   if (array_index >= 0) {
     const GeomVertexColumn *column = _format->get_color_column();
     const GeomVertexColumn *column = _format->get_color_column();
 
 
-    CDReader cdata(_cycler);
-    array_data = cdata->_arrays[array_index];
+    array_data = _arrays[array_index];
     num_values = column->get_num_values();
     num_values = column->get_num_values();
     numeric_type = column->get_numeric_type();
     numeric_type = column->get_numeric_type();
     start = column->get_start();
     start = column->get_start();
@@ -1237,81 +1325,6 @@ uint8_rgba_to_packed_argb(unsigned char *to, int to_stride,
   }
   }
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::do_set_num_rows
-//       Access: Private
-//  Description: The private implementation of set_num_rows().
-////////////////////////////////////////////////////////////////////
-bool GeomVertexData::
-do_set_num_rows(int n, GeomVertexData::CData *cdata) {
-  nassertr(_format->get_num_arrays() == (int)cdata->_arrays.size(), false);
-
-  bool any_changed = false;
-
-  int color_array = -1;
-  int orig_color_rows = -1;
-
-  for (size_t i = 0; i < cdata->_arrays.size(); i++) {
-    if (cdata->_arrays[i]->get_num_rows() != n) {
-      // Copy-on-write.
-      if (cdata->_arrays[i]->get_ref_count() > 1) {
-        cdata->_arrays[i] = new GeomVertexArrayData(*cdata->_arrays[i]);
-      }
-      if (cdata->_arrays[i]->has_column(InternalName::get_color())) {
-        color_array = i;
-        orig_color_rows = cdata->_arrays[i]->get_num_rows();
-      }
-      cdata->_arrays[i]->set_num_rows(n);
-      any_changed = true;
-    }
-  }
-
-  if (color_array >= 0 && orig_color_rows < n) {
-    // We have just added some rows, fill the "color" column with
-    // (1, 1, 1, 1), for the programmer's convenience.
-    GeomVertexArrayData *array_data = cdata->_arrays[color_array];
-    const GeomVertexColumn *column = 
-      array_data->get_array_format()->get_column(InternalName::get_color());
-    int stride = array_data->get_array_format()->get_stride();
-    unsigned char *start = 
-      array_data->modify_data() + column->get_start();
-    unsigned char *stop = start + array_data->get_data_size_bytes();
-    unsigned char *pointer = start + stride * orig_color_rows;
-    int num_values = column->get_num_values();
-
-    switch (column->get_numeric_type()) {
-    case NT_packed_dcba:
-    case NT_packed_dabc:
-    case NT_uint8:
-    case NT_uint16:
-    case NT_uint32:
-      while (pointer < stop) {
-        memset(pointer, 0xff, column->get_total_bytes());
-        pointer += stride;
-      }
-      break;
-
-    case NT_float32:
-      while (pointer < stop) {
-        PN_float32 *pi = (PN_float32 *)pointer;
-        for (int i = 0; i < num_values; i++) {
-          pi[i] = 1.0f;
-        }
-        pointer += stride;
-      }
-      break;
-    }          
-  }
-
-  if (any_changed) {
-    do_clear_cache(cdata);
-    cdata->_modified = Geom::get_next_modified();
-    cdata->_animated_vertices.clear();
-  }
-
-  return any_changed;
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexData::update_animated_vertices
 //     Function: GeomVertexData::update_animated_vertices
 //       Access: Private
 //       Access: Private
@@ -1320,7 +1333,7 @@ do_set_num_rows(int n, GeomVertexData::CData *cdata) {
 //               existing animated_vertices object.
 //               existing animated_vertices object.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomVertexData::
 void GeomVertexData::
-update_animated_vertices(GeomVertexData::CData *cdata) {
+update_animated_vertices() {
   int num_rows = get_num_rows();
   int num_rows = get_num_rows();
 
 
   if (gobj_cat.is_debug()) {
   if (gobj_cat.is_debug()) {
@@ -1331,13 +1344,13 @@ update_animated_vertices(GeomVertexData::CData *cdata) {
 
 
   PStatTimer timer(_char_pcollector);
   PStatTimer timer(_char_pcollector);
 
 
-  if (cdata->_animated_vertices == (GeomVertexData *)NULL) {
+  if (_animated_vertices == (GeomVertexData *)NULL) {
     CPT(GeomVertexFormat) new_format = _format->get_post_animated_format();
     CPT(GeomVertexFormat) new_format = _format->get_post_animated_format();
-    cdata->_animated_vertices = 
+    _animated_vertices = 
       new GeomVertexData(get_name(), new_format,
       new GeomVertexData(get_name(), new_format,
                            min(get_usage_hint(), UH_dynamic));
                            min(get_usage_hint(), UH_dynamic));
   }
   }
-  PT(GeomVertexData) new_data = cdata->_animated_vertices;
+  PT(GeomVertexData) new_data = _animated_vertices;
 
 
   // We have to make a complete copy of the data first so we can
   // We have to make a complete copy of the data first so we can
   // modify it.  If we were clever, we could maybe just figure out the
   // modify it.  If we were clever, we could maybe just figure out the
@@ -1347,7 +1360,7 @@ update_animated_vertices(GeomVertexData::CData *cdata) {
   new_data->copy_from(this, true);
   new_data->copy_from(this, true);
 
 
   // First, apply all of the morphs.
   // First, apply all of the morphs.
-  CPT(SliderTable) slider_table = cdata->_slider_table;
+  CPT(SliderTable) slider_table = _slider_table;
   if (slider_table != (SliderTable *)NULL) {
   if (slider_table != (SliderTable *)NULL) {
     PStatTimer timer2(_morphs_pcollector);
     PStatTimer timer2(_morphs_pcollector);
     int num_morphs = _format->get_num_morphs();
     int num_morphs = _format->get_num_morphs();
@@ -1398,7 +1411,7 @@ update_animated_vertices(GeomVertexData::CData *cdata) {
   }
   }
 
 
   // Then apply the transforms.
   // Then apply the transforms.
-  CPT(TransformBlendTable) tb_table = cdata->_transform_blend_table;
+  CPT(TransformBlendTable) tb_table = _transform_blend_table;
   if (tb_table != (TransformBlendTable *)NULL) {
   if (tb_table != (TransformBlendTable *)NULL) {
     PStatTimer timer3(_skinning_pcollector);
     PStatTimer timer3(_skinning_pcollector);
 
 
@@ -1454,24 +1467,6 @@ update_animated_vertices(GeomVertexData::CData *cdata) {
   }
   }
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::do_clear_cache
-//       Access: Private
-//  Description: The private implementation of clear_cache().
-////////////////////////////////////////////////////////////////////
-void GeomVertexData::
-do_clear_cache(GeomVertexData::CData *cdata) {
-  // Probably we shouldn't do anything at all here unless we are
-  // running in pipeline stage 0.
-  for (Cache::iterator ci = cdata->_cache.begin();
-       ci != cdata->_cache.end();
-       ++ci) {
-    CacheEntry *entry = (*ci);
-    entry->erase();
-  }
-  cdata->_cache.clear();
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexData::register_with_read_factory
 //     Function: GeomVertexData::register_with_read_factory
 //       Access: Public, Static
 //       Access: Public, Static
@@ -1496,7 +1491,17 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   dg.add_string(_name);
   dg.add_string(_name);
   manager->write_pointer(dg, _format);
   manager->write_pointer(dg, _format);
 
 
-  manager->write_cdata(dg, _cycler);
+  dg.add_uint8(_usage_hint);
+
+  dg.add_uint16(_arrays.size());
+  Arrays::const_iterator ai;
+  for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
+    manager->write_pointer(dg, *ai);
+  }
+
+  manager->write_pointer(dg, _transform_table);
+  manager->write_pointer(dg, _transform_blend_table);
+  manager->write_pointer(dg, _slider_table);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1533,6 +1538,17 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
 
 
   _format = DCAST(GeomVertexFormat, p_list[pi++]);
   _format = DCAST(GeomVertexFormat, p_list[pi++]);
 
 
+  Arrays::iterator ai;
+  for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
+    (*ai) = DCAST(GeomVertexArrayData, p_list[pi++]);    
+  }
+
+  _transform_table = DCAST(TransformTable, p_list[pi++]);
+  _transform_blend_table = DCAST(TransformBlendTable, p_list[pi++]);
+  _slider_table = DCAST(SliderTable, p_list[pi++]);
+
+  _modified = Geom::get_next_modified();
+
   return pi;
   return pi;
 }
 }
 
 
@@ -1559,34 +1575,32 @@ finalize(BamReader *manager) {
   // This extends to the nested array datas, as well as the transform
   // This extends to the nested array datas, as well as the transform
   // table and slider tables, as well.
   // table and slider tables, as well.
 
 
-  CDWriter cdata(_cycler);
-
   CPT(GeomVertexFormat) new_format = 
   CPT(GeomVertexFormat) new_format = 
     GeomVertexFormat::register_format(_format);
     GeomVertexFormat::register_format(_format);
 
 
-  for (size_t i = 0; i < cdata->_arrays.size(); ++i) {
+  for (size_t i = 0; i < _arrays.size(); ++i) {
     CPT(GeomVertexArrayFormat) new_array_format = new_format->get_array(i);
     CPT(GeomVertexArrayFormat) new_array_format = new_format->get_array(i);
-    nassertv(cdata->_arrays[i]->_array_format->compare_to(*new_array_format) == 0);
+    nassertv(_arrays[i]->_array_format->compare_to(*new_array_format) == 0);
 
 
-    manager->change_pointer(cdata->_arrays[i]->_array_format, new_array_format);
-    cdata->_arrays[i]->_array_format = new_array_format;
+    manager->change_pointer(_arrays[i]->_array_format, new_array_format);
+    _arrays[i]->_array_format = new_array_format;
   }
   }
 
 
   manager->change_pointer(_format, new_format);
   manager->change_pointer(_format, new_format);
   _format = new_format;
   _format = new_format;
 
 
-  if (cdata->_transform_table != (TransformTable *)NULL) {
+  if (_transform_table != (TransformTable *)NULL) {
     CPT(TransformTable) new_transform_table = 
     CPT(TransformTable) new_transform_table = 
-      TransformTable::register_table(cdata->_transform_table);
-    manager->change_pointer(cdata->_transform_table, new_transform_table);
-    cdata->_transform_table = new_transform_table;
+      TransformTable::register_table(_transform_table);
+    manager->change_pointer(_transform_table, new_transform_table);
+    _transform_table = new_transform_table;
   }
   }
 
 
-  if (cdata->_slider_table != (SliderTable *)NULL) {
+  if (_slider_table != (SliderTable *)NULL) {
     CPT(SliderTable) new_slider_table = 
     CPT(SliderTable) new_slider_table = 
-      SliderTable::register_table(cdata->_slider_table);
-    manager->change_pointer(cdata->_slider_table, new_slider_table);
-    cdata->_slider_table = new_slider_table;
+      SliderTable::register_table(_slider_table);
+    manager->change_pointer(_slider_table, new_slider_table);
+    _slider_table = new_slider_table;
   }
   }
 }
 }
 
 
@@ -1604,7 +1618,18 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   set_name(scan.get_string());
   set_name(scan.get_string());
   manager->read_pointer(scan);
   manager->read_pointer(scan);
 
 
-  manager->read_cdata(scan, _cycler);
+  _usage_hint = (UsageHint)scan.get_uint8();
+
+  size_t num_arrays = scan.get_uint16();
+  _arrays.reserve(num_arrays);
+  for (size_t i = 0; i < num_arrays; ++i) {
+    manager->read_pointer(scan);
+    _arrays.push_back(NULL);
+  }
+
+  manager->read_pointer(scan);
+  manager->read_pointer(scan);
+  manager->read_pointer(scan);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1615,16 +1640,10 @@ fillin(DatagramIterator &scan, BamReader *manager) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomVertexData::CacheEntry::
 void GeomVertexData::CacheEntry::
 evict_callback() {
 evict_callback() {
-  // We have to operate on stage 0 of the pipeline, since that's where
-  // the cache really counts.  Because of the multistage pipeline, we
-  // might not actually have a cache entry there (it might have been
-  // added to stage 1 instead).  No big deal if we don't.
-  CData *cdata = _source->_cycler.write_stage(0);
-  Cache::iterator ci = cdata->_cache.find(this);
-  if (ci != cdata->_cache.end()) {
-    cdata->_cache.erase(ci);
+  Cache::iterator ci = _source->_cache.find(this);
+  if (ci != _source->_cache.end()) {
+    _source->_cache.erase(ci);
   }
   }
-  _source->_cycler.release_write_stage(0, cdata);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1637,82 +1656,3 @@ output(ostream &out) const {
   out << "vertex data " << (void *)_source << " to " 
   out << "vertex data " << (void *)_source << " to " 
       << *_modifier;
       << *_modifier;
 }
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::CData::make_copy
-//       Access: Public, Virtual
-//  Description:
-////////////////////////////////////////////////////////////////////
-CycleData *GeomVertexData::CData::
-make_copy() const {
-  return new CData(*this);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::CData::write_datagram
-//       Access: Public, Virtual
-//  Description: Writes the contents of this object to the datagram
-//               for shipping out to a Bam file.
-////////////////////////////////////////////////////////////////////
-void GeomVertexData::CData::
-write_datagram(BamWriter *manager, Datagram &dg) const {
-  dg.add_uint8(_usage_hint);
-
-  dg.add_uint16(_arrays.size());
-  Arrays::const_iterator ai;
-  for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
-    manager->write_pointer(dg, *ai);
-  }
-
-  manager->write_pointer(dg, _transform_table);
-  manager->write_pointer(dg, _transform_blend_table);
-  manager->write_pointer(dg, _slider_table);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::CData::complete_pointers
-//       Access: Public, Virtual
-//  Description: Receives an array of pointers, one for each time
-//               manager->read_pointer() was called in fillin().
-//               Returns the number of pointers processed.
-////////////////////////////////////////////////////////////////////
-int GeomVertexData::CData::
-complete_pointers(TypedWritable **p_list, BamReader *manager) {
-  int pi = CycleData::complete_pointers(p_list, manager);
-
-  Arrays::iterator ai;
-  for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
-    (*ai) = DCAST(GeomVertexArrayData, p_list[pi++]);    
-  }
-
-  _transform_table = DCAST(TransformTable, p_list[pi++]);
-  _transform_blend_table = DCAST(TransformBlendTable, p_list[pi++]);
-  _slider_table = DCAST(SliderTable, p_list[pi++]);
-
-  _modified = Geom::get_next_modified();
-
-  return pi;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::CData::fillin
-//       Access: Public, Virtual
-//  Description: This internal function is called by make_from_bam to
-//               read in all of the relevant data from the BamFile for
-//               the new GeomVertexData.
-////////////////////////////////////////////////////////////////////
-void GeomVertexData::CData::
-fillin(DatagramIterator &scan, BamReader *manager) {
-  _usage_hint = (UsageHint)scan.get_uint8();
-
-  size_t num_arrays = scan.get_uint16();
-  _arrays.reserve(num_arrays);
-  for (size_t i = 0; i < num_arrays; ++i) {
-    manager->read_pointer(scan);
-    _arrays.push_back(NULL);
-  }
-
-  manager->read_pointer(scan);
-  manager->read_pointer(scan);
-  manager->read_pointer(scan);
-}

+ 14 - 36
panda/src/gobj/geomVertexData.h

@@ -30,10 +30,6 @@
 #include "transformBlendTable.h"
 #include "transformBlendTable.h"
 #include "sliderTable.h"
 #include "sliderTable.h"
 #include "internalName.h"
 #include "internalName.h"
-#include "cycleData.h"
-#include "cycleDataReader.h"
-#include "cycleDataWriter.h"
-#include "pipelineCycler.h"
 #include "pStatCollector.h"
 #include "pStatCollector.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
 #include "pmap.h"
 #include "pmap.h"
@@ -94,7 +90,7 @@ PUBLISHED:
   INLINE bool has_column(const InternalName *name) const;
   INLINE bool has_column(const InternalName *name) const;
 
 
   int get_num_rows() const;
   int get_num_rows() const;
-  INLINE bool set_num_rows(int n);
+  bool set_num_rows(int n);
   void clear_rows();
   void clear_rows();
 
 
   INLINE int get_num_arrays() const;
   INLINE int get_num_arrays() const;
@@ -142,7 +138,7 @@ PUBLISHED:
   void output(ostream &out) const;
   void output(ostream &out) const;
   void write(ostream &out, int indent_level = 0) const;
   void write(ostream &out, int indent_level = 0) const;
 
 
-  INLINE void clear_cache();
+  void clear_cache();
 
 
 public:
 public:
   bool get_array_info(const InternalName *name, 
   bool get_array_info(const InternalName *name, 
@@ -215,38 +211,20 @@ private:
   };
   };
   typedef pset<PT(CacheEntry), IndirectLess<CacheEntry> > Cache;
   typedef pset<PT(CacheEntry), IndirectLess<CacheEntry> > Cache;
 
 
-  // This is the data that must be cycled between pipeline stages.
-  class EXPCL_PANDA CData : public CycleData {
-  public:
-    INLINE CData();
-    INLINE CData(const CData &copy);
-    virtual CycleData *make_copy() const;
-    virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
-    virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
-    virtual void fillin(DatagramIterator &scan, BamReader *manager);
-    virtual TypeHandle get_parent_type() const {
-      return GeomVertexData::get_class_type();
-    }
-
-    UsageHint _usage_hint;
-    Arrays _arrays;
-    CPT(TransformTable) _transform_table;
-    PT(TransformBlendTable) _transform_blend_table;
-    CPT(SliderTable) _slider_table;
-    PT(GeomVertexData) _animated_vertices;
-    UpdateSeq _animated_vertices_modified;
-    UpdateSeq _modified;
-    Cache _cache;
-  };
-
-  PipelineCycler<CData> _cycler;
-  typedef CycleDataReader<CData> CDReader;
-  typedef CycleDataWriter<CData> CDWriter;
+  // We don't need to cycle any data in the GeomVertexData, since the
+  // GeomVertexData itself is cycled through the stages.
+  UsageHint _usage_hint;
+  Arrays _arrays;
+  CPT(TransformTable) _transform_table;
+  PT(TransformBlendTable) _transform_blend_table;
+  CPT(SliderTable) _slider_table;
+  PT(GeomVertexData) _animated_vertices;
+  UpdateSeq _animated_vertices_modified;
+  UpdateSeq _modified;
+  Cache _cache;
 
 
 private:
 private:
-  bool do_set_num_rows(int n, CData *cdata);
-  void update_animated_vertices(CData *cdata);
-  void do_clear_cache(CData *cdata);
+  void update_animated_vertices();
 
 
   static PStatCollector _convert_pcollector;
   static PStatCollector _convert_pcollector;
   static PStatCollector _scale_color_pcollector;
   static PStatCollector _scale_color_pcollector;

+ 11 - 6
panda/src/parametrics/ropeNode.cxx

@@ -233,8 +233,9 @@ write(ostream &out, int indent_level) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void RopeNode::
 void RopeNode::
 reset_bound(const NodePath &rel_to) {
 reset_bound(const NodePath &rel_to) {
-  do_recompute_bound(rel_to);
-  changed_internal_bound();
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  do_recompute_bound(rel_to, pipeline_stage);
+  changed_internal_bound(pipeline_stage);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -246,8 +247,8 @@ reset_bound(const NodePath &rel_to) {
 //               thing.
 //               thing.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 BoundingVolume *RopeNode::
 BoundingVolume *RopeNode::
-recompute_internal_bound() {
-  return do_recompute_bound(NodePath(this));
+recompute_internal_bound(int pipeline_stage) {
+  return do_recompute_bound(NodePath(this), pipeline_stage);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -288,9 +289,13 @@ get_format(bool support_normals) const {
 //  Description: Does the actual internal recompute.
 //  Description: Does the actual internal recompute.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 BoundingVolume *RopeNode::
 BoundingVolume *RopeNode::
-do_recompute_bound(const NodePath &rel_to) {
+do_recompute_bound(const NodePath &rel_to, int pipeline_stage) {
+  // TODO: fix the bounds so that it properly reflects the indicated
+  // pipeline stage.  At the moment, we cheat and get some of the
+  // properties from the current pipeline stage, the lazy way.
+
   // First, get ourselves a fresh, empty bounding volume.
   // First, get ourselves a fresh, empty bounding volume.
-  BoundingVolume *bound = PandaNode::recompute_internal_bound();
+  BoundingVolume *bound = PandaNode::recompute_internal_bound(pipeline_stage);
   nassertr(bound != (BoundingVolume *)NULL, bound);
   nassertr(bound != (BoundingVolume *)NULL, bound);
   
   
   NurbsCurveEvaluator *curve = get_curve();
   NurbsCurveEvaluator *curve = get_curve();

+ 3 - 2
panda/src/parametrics/ropeNode.h

@@ -143,12 +143,13 @@ PUBLISHED:
   void reset_bound(const NodePath &rel_to);
   void reset_bound(const NodePath &rel_to);
 
 
 protected:
 protected:
-  virtual BoundingVolume *recompute_internal_bound();
+  virtual BoundingVolume *recompute_internal_bound(int pipeline_stage);
 
 
 private:
 private:
   CPT(GeomVertexFormat) get_format(bool support_normals) const;
   CPT(GeomVertexFormat) get_format(bool support_normals) const;
 
 
-  BoundingVolume *do_recompute_bound(const NodePath &rel_to);
+  BoundingVolume *do_recompute_bound(const NodePath &rel_to,
+                                     int pipeline_stage);
   void render_thread(CullTraverser *trav, CullTraverserData &data, 
   void render_thread(CullTraverser *trav, CullTraverserData &data, 
                      NurbsCurveResult *result) const;
                      NurbsCurveResult *result) const;
   void render_tape(CullTraverser *trav, CullTraverserData &data, 
   void render_tape(CullTraverser *trav, CullTraverserData &data, 

+ 11 - 6
panda/src/parametrics/sheetNode.cxx

@@ -215,8 +215,9 @@ write(ostream &out, int indent_level) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void SheetNode::
 void SheetNode::
 reset_bound(const NodePath &rel_to) {
 reset_bound(const NodePath &rel_to) {
-  do_recompute_bound(rel_to);
-  changed_internal_bound();
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  do_recompute_bound(rel_to, pipeline_stage);
+  changed_internal_bound(pipeline_stage);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -228,8 +229,8 @@ reset_bound(const NodePath &rel_to) {
 //               thing.
 //               thing.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 BoundingVolume *SheetNode::
 BoundingVolume *SheetNode::
-recompute_internal_bound() {
-  return do_recompute_bound(NodePath(this));
+recompute_internal_bound(int pipeline_stage) {
+  return do_recompute_bound(NodePath(this), pipeline_stage);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -238,9 +239,13 @@ recompute_internal_bound() {
 //  Description: Does the actual internal recompute.
 //  Description: Does the actual internal recompute.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 BoundingVolume *SheetNode::
 BoundingVolume *SheetNode::
-do_recompute_bound(const NodePath &rel_to) {
+do_recompute_bound(const NodePath &rel_to, int pipeline_stage) {
+  // TODO: fix the bounds so that it properly reflects the indicated
+  // pipeline stage.  At the moment, we cheat and get some of the
+  // properties from the current pipeline stage, the lazy way.
+
   // First, get ourselves a fresh, empty bounding volume.
   // First, get ourselves a fresh, empty bounding volume.
-  BoundingVolume *bound = PandaNode::recompute_internal_bound();
+  BoundingVolume *bound = PandaNode::recompute_internal_bound(pipeline_stage);
   nassertr(bound != (BoundingVolume *)NULL, bound);
   nassertr(bound != (BoundingVolume *)NULL, bound);
   
   
   NurbsSurfaceEvaluator *surface = get_surface();
   NurbsSurfaceEvaluator *surface = get_surface();

+ 3 - 2
panda/src/parametrics/sheetNode.h

@@ -68,10 +68,11 @@ PUBLISHED:
   void reset_bound(const NodePath &rel_to);
   void reset_bound(const NodePath &rel_to);
 
 
 protected:
 protected:
-  virtual BoundingVolume *recompute_internal_bound();
+  virtual BoundingVolume *recompute_internal_bound(int pipeline_stage);
 
 
 private:
 private:
-  BoundingVolume *do_recompute_bound(const NodePath &rel_to);
+  BoundingVolume *do_recompute_bound(const NodePath &rel_to,
+                                     int pipeline_stage);
   void render_sheet(CullTraverser *trav, CullTraverserData &data, 
   void render_sheet(CullTraverser *trav, CullTraverserData &data, 
                     NurbsSurfaceResult *result);
                     NurbsSurfaceResult *result);
 
 

+ 1 - 2
panda/src/pgraph/accumulatedAttribs.cxx

@@ -18,7 +18,6 @@
 
 
 #include "accumulatedAttribs.h"
 #include "accumulatedAttribs.h"
 #include "sceneGraphReducer.h"
 #include "sceneGraphReducer.h"
-#include "geomTransformer.h"
 #include "pandaNode.h"
 #include "pandaNode.h"
 #include "colorAttrib.h"
 #include "colorAttrib.h"
 #include "colorScaleAttrib.h"
 #include "colorScaleAttrib.h"
@@ -77,7 +76,7 @@ collect(PandaNode *node, int attrib_types) {
     nassertv(_transform != (TransformState *)NULL);
     nassertv(_transform != (TransformState *)NULL);
     _transform = _transform->compose(node->get_transform());
     _transform = _transform->compose(node->get_transform());
     node->set_transform(TransformState::make_identity());
     node->set_transform(TransformState::make_identity());
-    node->reset_prev_transform();
+    node->set_prev_transform(TransformState::make_identity());
   }
   }
 
 
   if ((attrib_types & SceneGraphReducer::TT_color) != 0) {
   if ((attrib_types & SceneGraphReducer::TT_color) != 0) {

+ 4 - 4
panda/src/pgraph/cullBinBackToFront.cxx

@@ -51,12 +51,12 @@ CullBinBackToFront::
 void CullBinBackToFront::
 void CullBinBackToFront::
 add_object(CullableObject *object) {
 add_object(CullableObject *object) {
   // Determine the center of the bounding volume.
   // Determine the center of the bounding volume.
-  const BoundingVolume &volume = object->_geom->get_bound();
+  const BoundingVolume *volume = object->_geom->get_bound();
 
 
-  if (!volume.is_empty() &&
-      volume.is_of_type(GeometricBoundingVolume::get_class_type())) {
+  if (!volume->is_empty() &&
+      volume->is_of_type(GeometricBoundingVolume::get_class_type())) {
     const GeometricBoundingVolume *gbv;
     const GeometricBoundingVolume *gbv;
-    DCAST_INTO_V(gbv, &volume);
+    DCAST_INTO_V(gbv, volume);
     
     
     LPoint3f center = gbv->get_approx_center();
     LPoint3f center = gbv->get_approx_center();
     nassertv(object->_modelview_transform != (const TransformState *)NULL);
     nassertv(object->_modelview_transform != (const TransformState *)NULL);

+ 4 - 4
panda/src/pgraph/cullBinFrontToBack.cxx

@@ -51,12 +51,12 @@ CullBinFrontToBack::
 void CullBinFrontToBack::
 void CullBinFrontToBack::
 add_object(CullableObject *object) {
 add_object(CullableObject *object) {
   // Determine the center of the bounding volume.
   // Determine the center of the bounding volume.
-  const BoundingVolume &volume = object->_geom->get_bound();
+  const BoundingVolume *volume = object->_geom->get_bound();
 
 
-  if (!volume.is_empty() &&
-      volume.is_of_type(GeometricBoundingVolume::get_class_type())) {
+  if (!volume->is_empty() &&
+      volume->is_of_type(GeometricBoundingVolume::get_class_type())) {
     const GeometricBoundingVolume *gbv;
     const GeometricBoundingVolume *gbv;
-    DCAST_INTO_V(gbv, &volume);
+    DCAST_INTO_V(gbv, volume);
     
     
     LPoint3f center = gbv->get_approx_center();
     LPoint3f center = gbv->get_approx_center();
     nassertv(object->_modelview_transform != (const TransformState *)NULL);
     nassertv(object->_modelview_transform != (const TransformState *)NULL);

+ 5 - 5
panda/src/pgraph/cullTraverser.cxx

@@ -305,13 +305,13 @@ show_bounds(CullTraverserData &data, bool tight) {
 //               bounding volume.
 //               bounding volume.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PT(Geom) CullTraverser::
 PT(Geom) CullTraverser::
-make_bounds_viz(const BoundingVolume &vol) {
+make_bounds_viz(const BoundingVolume *vol) {
   PT(Geom) geom;
   PT(Geom) geom;
-  if (vol.is_infinite()) {
+  if (vol->is_infinite()) {
     // No way to draw an infinite bounding volume.
     // No way to draw an infinite bounding volume.
 
 
-  } else if (vol.is_of_type(BoundingSphere::get_class_type())) {
-    const BoundingSphere *sphere = DCAST(BoundingSphere, &vol);
+  } else if (vol->is_of_type(BoundingSphere::get_class_type())) {
+    const BoundingSphere *sphere = DCAST(BoundingSphere, vol);
 
 
     static const int num_slices = 16;
     static const int num_slices = 16;
     static const int num_stacks = 8;
     static const int num_stacks = 8;
@@ -343,7 +343,7 @@ make_bounds_viz(const BoundingVolume &vol) {
   } else {
   } else {
     pgraph_cat.warning()
     pgraph_cat.warning()
       << "Don't know how to draw a representation of "
       << "Don't know how to draw a representation of "
-      << vol.get_class_type() << "\n";
+      << vol->get_class_type() << "\n";
   }
   }
 
 
   return geom;
   return geom;

+ 1 - 1
panda/src/pgraph/cullTraverser.h

@@ -96,7 +96,7 @@ public:
 
 
 private:
 private:
   void show_bounds(CullTraverserData &data, bool tight);
   void show_bounds(CullTraverserData &data, bool tight);
-  PT(Geom) make_bounds_viz(const BoundingVolume &vol);
+  PT(Geom) make_bounds_viz(const BoundingVolume *vol);
   PT(Geom) make_tight_bounds_viz(PandaNode *node);
   PT(Geom) make_tight_bounds_viz(PandaNode *node);
   static Vertexf compute_point(const BoundingSphere *sphere, 
   static Vertexf compute_point(const BoundingSphere *sphere, 
                                float latitude, float longitude);
                                float latitude, float longitude);

+ 6 - 6
panda/src/pgraph/cullTraverserData.cxx

@@ -135,10 +135,10 @@ apply_transform_and_state(CullTraverser *trav,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool CullTraverserData::
 bool CullTraverserData::
 is_in_view_impl() {
 is_in_view_impl() {
-  const BoundingVolume &node_volume = node()->get_bound();
-  nassertr(node_volume.is_of_type(GeometricBoundingVolume::get_class_type()), false);
+  const BoundingVolume *node_volume = node()->get_bound();
+  nassertr(node_volume->is_of_type(GeometricBoundingVolume::get_class_type()), false);
   const GeometricBoundingVolume *node_gbv =
   const GeometricBoundingVolume *node_gbv =
-    DCAST(GeometricBoundingVolume, &node_volume);
+    DCAST(GeometricBoundingVolume, node_volume);
 
 
   if (_view_frustum != (GeometricBoundingVolume *)NULL) {
   if (_view_frustum != (GeometricBoundingVolume *)NULL) {
     int result = _view_frustum->contains(node_gbv);
     int result = _view_frustum->contains(node_gbv);
@@ -227,10 +227,10 @@ test_within_clip_planes_impl(const CullTraverser *trav,
   int result = BoundingVolume::IF_all | BoundingVolume::IF_possible | BoundingVolume::IF_some;
   int result = BoundingVolume::IF_all | BoundingVolume::IF_possible | BoundingVolume::IF_some;
 
 
 
 
-  const BoundingVolume &node_volume = node()->get_bound();
-  nassertr(node_volume.is_of_type(GeometricBoundingVolume::get_class_type()), result);
+  const BoundingVolume *node_volume = node()->get_bound();
+  nassertr(node_volume->is_of_type(GeometricBoundingVolume::get_class_type()), result);
   const GeometricBoundingVolume *node_gbv =
   const GeometricBoundingVolume *node_gbv =
-    DCAST(GeometricBoundingVolume, &node_volume);
+    DCAST(GeometricBoundingVolume, node_volume);
 
 
   CPT(TransformState) net_transform = get_net_transform(trav);
   CPT(TransformState) net_transform = get_net_transform(trav);
 
 

+ 18 - 2
panda/src/pgraph/geomNode.I

@@ -72,6 +72,8 @@ get_geom(int n) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE Geom *GeomNode::
 INLINE Geom *GeomNode::
 get_unique_geom(int n) {
 get_unique_geom(int n) {
+  pgraph_cat.warning() 
+    << "Deprecated method GeomNode::get_unique_geom() called.  Use modify_geom() instead.\n";
   return modify_geom(n);
   return modify_geom(n);
 }
 }
 
 
@@ -85,10 +87,17 @@ get_unique_geom(int n) {
 //               "copy on write" that ensures that the Geom that is
 //               "copy on write" that ensures that the Geom that is
 //               returned is unique to this GeomNode and is not shared
 //               returned is unique to this GeomNode and is not shared
 //               with any other GeomNodes.
 //               with any other GeomNodes.
+//
+//               Note that if this method is called in a downstream
+//               stage (for instance, during cull or draw), then it
+//               will propagate the new list of Geoms upstream all the
+//               way to pipeline stage 0, which may step on changes
+//               that were made independently in pipeline stage 0.
+//               Use with caution.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE Geom *GeomNode::
 INLINE Geom *GeomNode::
 modify_geom(int n) {
 modify_geom(int n) {
-  CDWriter cdata(_cycler);
+  CDWriter cdata(_cycler, true);
   nassertr(n >= 0 && n < (int)cdata->_geoms.size(), NULL);
   nassertr(n >= 0 && n < (int)cdata->_geoms.size(), NULL);
   Geom *geom = cdata->_geoms[n]._geom;
   Geom *geom = cdata->_geoms[n]._geom;
   if (geom->get_ref_count() > 1) {
   if (geom->get_ref_count() > 1) {
@@ -124,10 +133,17 @@ get_geom_state(int n) const {
 //               the Geom is rendered will also be affected by
 //               the Geom is rendered will also be affected by
 //               RenderStates that appear on the scene graph in nodes
 //               RenderStates that appear on the scene graph in nodes
 //               above this GeomNode.
 //               above this GeomNode.
+//
+//               Note that if this method is called in a downstream
+//               stage (for instance, during cull or draw), then it
+//               will propagate the new list of Geoms upstream all the
+//               way to pipeline stage 0, which may step on changes
+//               that were made independently in pipeline stage 0.
+//               Use with caution.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void GeomNode::
 INLINE void GeomNode::
 set_geom_state(int n, const RenderState *state) {
 set_geom_state(int n, const RenderState *state) {
-  CDWriter cdata(_cycler);
+  CDWriter cdata(_cycler, true);
   nassertv(n >= 0 && n < (int)cdata->_geoms.size());
   nassertv(n >= 0 && n < (int)cdata->_geoms.size());
   cdata->_geoms[n]._state = state;
   cdata->_geoms[n]._state = state;
 }
 }

+ 152 - 135
panda/src/pgraph/geomNode.cxx

@@ -190,94 +190,97 @@ apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
     }
     }
   }
   }
 
 
-  GeomNode::CDWriter cdata(_cycler);
-  GeomNode::Geoms::iterator gi;
-  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
-    GeomEntry &entry = (*gi);
-    PT(Geom) new_geom = entry._geom->make_copy();
-
-    AccumulatedAttribs geom_attribs = attribs;
-    entry._state = geom_attribs.collect(entry._state, attrib_types);
-
-    bool any_changed = false;
-    
-    if ((attrib_types & SceneGraphReducer::TT_color) != 0) {
-      if (geom_attribs._color != (const RenderAttrib *)NULL) {
-        const ColorAttrib *ca = DCAST(ColorAttrib, geom_attribs._color);
-        if (ca->get_color_type() == ColorAttrib::T_flat) {
-          if (transformer.set_color(new_geom, ca->get_color())) {
-            any_changed = true;
+  OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler) {
+    CDStageWriter cdata(_cycler, pipeline_stage);
+    Geoms::iterator gi;
+    for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+      GeomEntry &entry = (*gi);
+      PT(Geom) new_geom = entry._geom->make_copy();
+      
+      AccumulatedAttribs geom_attribs = attribs;
+      entry._state = geom_attribs.collect(entry._state, attrib_types);
+      
+      bool any_changed = false;
+      
+      if ((attrib_types & SceneGraphReducer::TT_color) != 0) {
+        if (geom_attribs._color != (const RenderAttrib *)NULL) {
+          const ColorAttrib *ca = DCAST(ColorAttrib, geom_attribs._color);
+          if (ca->get_color_type() == ColorAttrib::T_flat) {
+            if (transformer.set_color(new_geom, ca->get_color())) {
+              any_changed = true;
+            }
           }
           }
         }
         }
       }
       }
-    }
-    if ((attrib_types & SceneGraphReducer::TT_color_scale) != 0) {
-      if (geom_attribs._color_scale != (const RenderAttrib *)NULL) {
-        const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, geom_attribs._color_scale);
-        if (csa->get_scale() != LVecBase4f(1.0f, 1.0f, 1.0f, 1.0f)) {
-          if (transformer.transform_colors(new_geom, csa->get_scale())) {
-            any_changed = true;
+      if ((attrib_types & SceneGraphReducer::TT_color_scale) != 0) {
+        if (geom_attribs._color_scale != (const RenderAttrib *)NULL) {
+          const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, geom_attribs._color_scale);
+          if (csa->get_scale() != LVecBase4f(1.0f, 1.0f, 1.0f, 1.0f)) {
+            if (transformer.transform_colors(new_geom, csa->get_scale())) {
+              any_changed = true;
+            }
           }
           }
         }
         }
       }
       }
-    }
-    if ((attrib_types & SceneGraphReducer::TT_tex_matrix) != 0) {
-      if (geom_attribs._tex_matrix != (const RenderAttrib *)NULL) {
-        // Determine which texture coordinate names are used more than
-        // once.  This assumes we have discovered all of the textures
-        // that are in effect on the geomNode; this may not be true if
-        // there is a texture that has been applied at a node above
-        // that from which we started the flatten operation, but
-        // caveat programmer.
-        NameCount name_count;
-
-        if (geom_attribs._texture != (RenderAttrib *)NULL) {
-          const TextureAttrib *ta = DCAST(TextureAttrib, geom_attribs._texture);
-          int num_on_stages = ta->get_num_on_stages();
-          for (int si = 0; si < num_on_stages; si++) {
-            TextureStage *stage = ta->get_on_stage(si);
-            const InternalName *name = stage->get_texcoord_name();
-            count_name(name_count, name);
+      if ((attrib_types & SceneGraphReducer::TT_tex_matrix) != 0) {
+        if (geom_attribs._tex_matrix != (const RenderAttrib *)NULL) {
+          // Determine which texture coordinate names are used more than
+          // once.  This assumes we have discovered all of the textures
+          // that are in effect on the geomNode; this may not be true if
+          // there is a texture that has been applied at a node above
+          // that from which we started the flatten operation, but
+          // caveat programmer.
+          NameCount name_count;
+          
+          if (geom_attribs._texture != (RenderAttrib *)NULL) {
+            const TextureAttrib *ta = DCAST(TextureAttrib, geom_attribs._texture);
+            int num_on_stages = ta->get_num_on_stages();
+            for (int si = 0; si < num_on_stages; si++) {
+              TextureStage *stage = ta->get_on_stage(si);
+              const InternalName *name = stage->get_texcoord_name();
+              count_name(name_count, name);
+            }
           }
           }
-        }
-
-        const TexMatrixAttrib *tma = 
-          DCAST(TexMatrixAttrib, geom_attribs._tex_matrix);
-
-        CPT(TexMatrixAttrib) new_tma = DCAST(TexMatrixAttrib, TexMatrixAttrib::make());
-
-        int num_stages = tma->get_num_stages();
-        for (int i = 0; i < num_stages; i++) {
-          TextureStage *stage = tma->get_stage(i);
-          InternalName *name = stage->get_texcoord_name();
-          if (get_name_count(name_count, name) > 1) {
-            // We can't transform these texcoords, since the name is
-            // used by more than one active stage.
-            new_tma = DCAST(TexMatrixAttrib, new_tma->add_stage(stage, tma->get_transform(stage)));
-
-          } else {
-            // It's safe to transform these texcoords; the name is
-            // used by no more than one active stage.
-            if (transformer.transform_texcoords(new_geom, name, name, tma->get_mat(stage))) {
-              any_changed = true;
+          
+          const TexMatrixAttrib *tma = 
+            DCAST(TexMatrixAttrib, geom_attribs._tex_matrix);
+          
+          CPT(TexMatrixAttrib) new_tma = DCAST(TexMatrixAttrib, TexMatrixAttrib::make());
+          
+          int num_stages = tma->get_num_stages();
+          for (int i = 0; i < num_stages; i++) {
+            TextureStage *stage = tma->get_stage(i);
+            InternalName *name = stage->get_texcoord_name();
+            if (get_name_count(name_count, name) > 1) {
+              // We can't transform these texcoords, since the name is
+              // used by more than one active stage.
+              new_tma = DCAST(TexMatrixAttrib, new_tma->add_stage(stage, tma->get_transform(stage)));
+              
+            } else {
+              // It's safe to transform these texcoords; the name is
+              // used by no more than one active stage.
+              if (transformer.transform_texcoords(new_geom, name, name, tma->get_mat(stage))) {
+                any_changed = true;
+              }
             }
             }
           }
           }
-        }
-
-        if (!new_tma->is_empty()) {
-          entry._state = entry._state->add_attrib(new_tma);
+          
+          if (!new_tma->is_empty()) {
+            entry._state = entry._state->add_attrib(new_tma);
+          }
         }
         }
       }
       }
-    }
-
-    if (any_changed) {
-      entry._geom = new_geom;
-    }
 
 
-    if ((attrib_types & SceneGraphReducer::TT_other) != 0) {
-      entry._state = geom_attribs._other->compose(entry._state);
+      if (any_changed) {
+        entry._geom = new_geom;
+      }
+      
+      if ((attrib_types & SceneGraphReducer::TT_other) != 0) {
+        entry._state = geom_attribs._other->compose(entry._state);
+      }
     }
     }
   }
   }
+  CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -383,19 +386,20 @@ get_legal_collide_mask() const {
 //               indicated state (which may be
 //               indicated state (which may be
 //               RenderState::make_empty(), to completely inherit its
 //               RenderState::make_empty(), to completely inherit its
 //               state from the scene graph).
 //               state from the scene graph).
-//
-//               The return value is the index number of the new Geom.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-int GeomNode::
+void GeomNode::
 add_geom(Geom *geom, const RenderState *state) {
 add_geom(Geom *geom, const RenderState *state) {
-  nassertr(geom != (Geom *)NULL, -1);
-  nassertr(geom->check_valid(), -1);
-  nassertr(state != (RenderState *)NULL, -1);
-  CDWriter cdata(_cycler);
+  nassertv(geom != (Geom *)NULL);
+  nassertv(geom->check_valid());
+  nassertv(state != (RenderState *)NULL);
 
 
-  cdata->_geoms.push_back(GeomEntry(geom, state));
-  mark_bound_stale();
-  return cdata->_geoms.size() - 1;
+  OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler) {
+    CDStageWriter cdata(_cycler, pipeline_stage);
+
+    cdata->_geoms.push_back(GeomEntry(geom, state));
+    mark_bound_stale(pipeline_stage);
+  }
+  CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -406,18 +410,21 @@ add_geom(Geom *geom, const RenderState *state) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomNode::
 void GeomNode::
 add_geoms_from(const GeomNode *other) {
 add_geoms_from(const GeomNode *other) {
-  CDReader cdata_other(other->_cycler);
-  CDWriter cdata(_cycler);
-  mark_bound_stale();
-
-  Geoms::const_iterator gi;
-  for (gi = cdata_other->_geoms.begin(); 
-       gi != cdata_other->_geoms.end(); 
-       ++gi) {
-    const GeomEntry &entry = (*gi);
-    nassertv(entry._geom->check_valid());
-    cdata->_geoms.push_back(entry);
+  OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler) {
+    CDStageWriter cdata(_cycler, pipeline_stage);
+    CDStageReader cdata_other(other->_cycler, pipeline_stage);
+    mark_bound_stale(pipeline_stage);
+
+    Geoms::const_iterator gi;
+    for (gi = cdata_other->_geoms.begin(); 
+         gi != cdata_other->_geoms.end(); 
+         ++gi) {
+      const GeomEntry &entry = (*gi);
+      nassertv(entry._geom->check_valid());
+      cdata->_geoms.push_back(entry);
+    }
   }
   }
+  CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -425,13 +432,20 @@ add_geoms_from(const GeomNode *other) {
 //       Access: Public
 //       Access: Public
 //  Description: Replaces the nth Geom of the node with a new pointer.
 //  Description: Replaces the nth Geom of the node with a new pointer.
 //               There must already be a Geom in this slot.
 //               There must already be a Geom in this slot.
+//
+//               Note that if this method is called in a downstream
+//               stage (for instance, during cull or draw), then it
+//               will propagate the new list of Geoms upstream all the
+//               way to pipeline stage 0, which may step on changes
+//               that were made independently in pipeline stage 0.
+//               Use with caution.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomNode::
 void GeomNode::
 set_geom(int n, Geom *geom) {
 set_geom(int n, Geom *geom) {
   nassertv(geom != (Geom *)NULL);
   nassertv(geom != (Geom *)NULL);
   nassertv(geom->check_valid());
   nassertv(geom->check_valid());
 
 
-  CDWriter cdata(_cycler);
+  CDWriter cdata(_cycler, true);
   nassertv(n >= 0 && n < (int)cdata->_geoms.size());
   nassertv(n >= 0 && n < (int)cdata->_geoms.size());
   cdata->_geoms[n]._geom = geom;
   cdata->_geoms[n]._geom = geom;
 
 
@@ -479,46 +493,49 @@ check_valid() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomNode::
 void GeomNode::
 unify() {
 unify() {
-  CDWriter cdata(_cycler);
-
-  Geoms new_geoms;
-
-  // Try to unify each Geom with each preceding Geom.  This is an n^2
-  // operation, but usually there are only a handful of Geoms to
-  // consider, so that's not a big deal.
-  Geoms::iterator gi;
-  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
-    const GeomEntry &entry = (*gi);
-    
-    bool unified = false;
-    Geoms::iterator gj;
-    for (gj = new_geoms.begin(); gj != new_geoms.end() && !unified; ++gj) {
-      GeomEntry &new_entry = (*gj);
-      if (entry._state == new_entry._state) {
-        // Both states match, so try to combine the primitives.
-        if (new_entry._geom->copy_primitives_from(entry._geom)) {
-          // Successfully combined!
-          unified = true;
+  OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler) {
+    CDStageWriter cdata(_cycler, pipeline_stage);
+
+    Geoms new_geoms;
+
+    // Try to unify each Geom with each preceding Geom.  This is an n^2
+    // operation, but usually there are only a handful of Geoms to
+    // consider, so that's not a big deal.
+    Geoms::iterator gi;
+    for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+      const GeomEntry &entry = (*gi);
+      
+      bool unified = false;
+      Geoms::iterator gj;
+      for (gj = new_geoms.begin(); gj != new_geoms.end() && !unified; ++gj) {
+        GeomEntry &new_entry = (*gj);
+        if (entry._state == new_entry._state) {
+          // Both states match, so try to combine the primitives.
+          if (new_entry._geom->copy_primitives_from(entry._geom)) {
+            // Successfully combined!
+            unified = true;
+          }
         }
         }
       }
       }
+      
+      if (!unified) {
+        // Couldn't unify this Geom with anything, so just add it to the
+        // output list.
+        new_geoms.push_back(entry);
+      }
     }
     }
-
-    if (!unified) {
-      // Couldn't unify this Geom with anything, so just add it to the
-      // output list.
-      new_geoms.push_back(entry);
+    
+    // Done!  We'll keep whatever's left in the output list.
+    cdata->_geoms.swap(new_geoms);
+    new_geoms.clear();
+
+    // Finally, go back through and unify the resulting geom(s).
+    for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+      const GeomEntry &entry = (*gi);
+      entry._geom->unify_in_place();
     }
     }
   }
   }
-
-  // Done!  We'll keep whatever's left in the output list.
-  cdata->_geoms.swap(new_geoms);
-  new_geoms.clear();
-
-  // Finally, go back through and unify the resulting geom(s).
-  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
-    const GeomEntry &entry = (*gi);
-    entry._geom->unify_in_place();
-  }
+  CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -622,20 +639,20 @@ is_geom_node() const {
 //               thing.
 //               thing.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 BoundingVolume *GeomNode::
 BoundingVolume *GeomNode::
-recompute_internal_bound() {
+recompute_internal_bound(int pipeline_stage) {
   // First, get ourselves a fresh, empty bounding volume.
   // First, get ourselves a fresh, empty bounding volume.
-  BoundingVolume *bound = PandaNode::recompute_internal_bound();
+  BoundingVolume *bound = PandaNode::recompute_internal_bound(pipeline_stage);
   nassertr(bound != (BoundingVolume *)NULL, bound);
   nassertr(bound != (BoundingVolume *)NULL, bound);
 
 
   // Now actually compute the bounding volume by putting it around all
   // Now actually compute the bounding volume by putting it around all
   // of our geoms' bounding volumes.
   // of our geoms' bounding volumes.
   pvector<const BoundingVolume *> child_volumes;
   pvector<const BoundingVolume *> child_volumes;
 
 
-  CDReader cdata(_cycler);
+  CDStageReader cdata(_cycler, pipeline_stage);
   Geoms::const_iterator gi;
   Geoms::const_iterator gi;
   for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
   for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
     const GeomEntry &entry = (*gi);
     const GeomEntry &entry = (*gi);
-    child_volumes.push_back(&entry._geom->get_bound());
+    child_volumes.push_back(entry._geom->get_bound(pipeline_stage));
   }
   }
 
 
   const BoundingVolume **child_begin = &child_volumes[0];
   const BoundingVolume **child_begin = &child_volumes[0];

+ 5 - 3
panda/src/pgraph/geomNode.h

@@ -20,7 +20,7 @@
 #define GEOMNODE_H
 #define GEOMNODE_H
 
 
 #include "pandabase.h"
 #include "pandabase.h"
-
+#include "config_pgraph.h"
 #include "pandaNode.h"
 #include "pandaNode.h"
 #include "pointerToArray.h"
 #include "pointerToArray.h"
 #include "geom.h"
 #include "geom.h"
@@ -63,7 +63,7 @@ PUBLISHED:
   INLINE const RenderState *get_geom_state(int n) const;
   INLINE const RenderState *get_geom_state(int n) const;
   INLINE void set_geom_state(int n, const RenderState *state);
   INLINE void set_geom_state(int n, const RenderState *state);
 
 
-  int add_geom(Geom *geom, const RenderState *state = RenderState::make_empty());
+  void add_geom(Geom *geom, const RenderState *state = RenderState::make_empty());
   void add_geoms_from(const GeomNode *other);
   void add_geoms_from(const GeomNode *other);
   void set_geom(int n, Geom *geom);
   void set_geom(int n, Geom *geom);
   INLINE void remove_geom(int n);
   INLINE void remove_geom(int n);
@@ -83,7 +83,7 @@ public:
   virtual bool is_geom_node() const;
   virtual bool is_geom_node() const;
 
 
 protected:
 protected:
-  virtual BoundingVolume *recompute_internal_bound();
+  virtual BoundingVolume *recompute_internal_bound(int pipeline_stage);
 
 
 public:
 public:
   // This must be declared public so that VC6 will allow the nested
   // This must be declared public so that VC6 will allow the nested
@@ -121,6 +121,8 @@ private:
   PipelineCycler<CData> _cycler;
   PipelineCycler<CData> _cycler;
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataWriter<CData> CDWriter;
   typedef CycleDataWriter<CData> CDWriter;
+  typedef CycleDataStageReader<CData> CDStageReader;
+  typedef CycleDataStageWriter<CData> CDStageWriter;
 
 
 public:
 public:
   static void register_with_read_factory();
   static void register_with_read_factory();

+ 12 - 9
panda/src/pgraph/geomTransformer.cxx

@@ -127,17 +127,20 @@ bool GeomTransformer::
 transform_vertices(GeomNode *node, const LMatrix4f &mat) {
 transform_vertices(GeomNode *node, const LMatrix4f &mat) {
   bool any_changed = false;
   bool any_changed = false;
 
 
-  GeomNode::CDWriter cdata(node->_cycler);
-  GeomNode::Geoms::iterator gi;
-  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
-    GeomNode::GeomEntry &entry = (*gi);
-    PT(Geom) new_geom = entry._geom->make_copy();
-    if (transform_vertices(new_geom, mat)) {
-      node->mark_bound_stale();
-      entry._geom = new_geom;
-      any_changed = true;
+  OPEN_ITERATE_CURRENT_AND_UPSTREAM(node->_cycler) {
+    GeomNode::CDStageWriter cdata(node->_cycler, pipeline_stage);
+    GeomNode::Geoms::iterator gi;
+    for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+      GeomNode::GeomEntry &entry = (*gi);
+      PT(Geom) new_geom = entry._geom->make_copy();
+      if (transform_vertices(new_geom, mat)) {
+        node->mark_bound_stale(pipeline_stage);
+        entry._geom = new_geom;
+        any_changed = true;
+      }
     }
     }
   }
   }
+  CLOSE_ITERATE_CURRENT_AND_UPSTREAM(node->_cycler);
 
 
   return any_changed;
   return any_changed;
 }
 }

+ 1 - 1
panda/src/pgraph/nodePath.cxx

@@ -5313,7 +5313,7 @@ hide_bounds() {
 PT(BoundingVolume) NodePath::
 PT(BoundingVolume) NodePath::
 get_bounds() const {
 get_bounds() const {
   nassertr_always(!is_empty(), new BoundingSphere);
   nassertr_always(!is_empty(), new BoundingSphere);
-  return node()->get_bound().make_copy();
+  return node()->get_bound()->make_copy();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 113 - 329
panda/src/pgraph/pandaNode.I

@@ -122,6 +122,7 @@ CData() {
   _draw_mask = DrawMask::all_on();
   _draw_mask = DrawMask::all_on();
   _into_collide_mask = CollideMask::all_off();
   _into_collide_mask = CollideMask::all_off();
   _net_collide_mask = CollideMask::all_off();
   _net_collide_mask = CollideMask::all_off();
+  _stale_child_cache = true;
   _fixed_internal_bound = false;
   _fixed_internal_bound = false;
 }
 }
 
 
@@ -257,11 +258,7 @@ get_parent(int n) const {
 INLINE int PandaNode::
 INLINE int PandaNode::
 find_parent(PandaNode *node) const {
 find_parent(PandaNode *node) const {
   CDReader cdata(_cycler);
   CDReader cdata(_cycler);
-  Up::const_iterator ui = cdata->_up.find(UpConnection(node));
-  if (ui == cdata->_up.end()) {
-    return -1;
-  }
-  return ui - cdata->_up.begin();
+  return do_find_parent(node, cdata);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -305,6 +302,36 @@ get_child_sort(int n) const {
   return cdata->_down[n].get_sort();
   return cdata->_down[n].get_sort();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::find_child
+//       Access: Published
+//  Description: Returns the index of the indicated child node, if it
+//               is a child, or -1 if it is not.
+////////////////////////////////////////////////////////////////////
+INLINE int PandaNode::
+find_child(PandaNode *node) const {
+  CDReader cdata(_cycler);
+  return do_find_child(node, cdata);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::remove_child
+//       Access: Published
+//  Description: Removes the nth child from the node.
+////////////////////////////////////////////////////////////////////
+INLINE void PandaNode::
+remove_child(int n) {
+  CDWriter cdata(_cycler);
+  nassertv(n >= 0 && n < (int)cdata->_down.size());
+  
+  PT(PandaNode) child_node = cdata->_down[n].get_child();
+  CDWriter cdata_child(child_node->_cycler);
+
+  do_remove_child(n, child_node, 
+                  Thread::get_current_pipeline_stage(),
+                  cdata, cdata_child);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::stash_child
 //     Function: PandaNode::stash_child
 //       Access: Published
 //       Access: Published
@@ -397,27 +424,33 @@ get_stashed_sort(int n) const {
   return cdata->_stashed[n].get_sort();
   return cdata->_stashed[n].get_sort();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::find_stashed
+//       Access: Published
+//  Description: Returns the index of the indicated stashed node, if
+//               it is a stashed child, or -1 if it is not.
+////////////////////////////////////////////////////////////////////
+INLINE int PandaNode::
+find_stashed(PandaNode *node) const {
+  CDReader cdata(_cycler);
+  return do_find_stashed(node, cdata);
+}
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::set_attrib
+//     Function: PandaNode::remove_stashed
 //       Access: Published
 //       Access: Published
-//  Description: Adds the indicated render attribute to the scene
-//               graph on this node.  This attribute will now apply to
-//               this node and everything below.  If there was already
-//               an attribute of the same type, it is replaced.
+//  Description: Removes the nth stashed child from the node.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PandaNode::
 INLINE void PandaNode::
-set_attrib(const RenderAttrib *attrib, int override) {
+remove_stashed(int n) {
   CDWriter cdata(_cycler);
   CDWriter cdata(_cycler);
-  CPT(RenderState) new_state = cdata->_state->add_attrib(attrib, override);
-  if (cdata->_state != new_state) {
-    cdata->_state = new_state;
-
-    // We mark the bound stale when the state changes, in case we have
-    // changed a ClipPlaneAttrib.
-    mark_bound_stale();
-    state_changed();
-  }
+  nassertv(n >= 0 && n < (int)cdata->_stashed.size());
+  
+  PT(PandaNode) child_node = cdata->_stashed[n].get_child();
+  CDWriter cdata_child(child_node->_cycler);
+
+  do_remove_stashed(n, child_node, Thread::get_current_pipeline_stage(),
+                    cdata, cdata_child);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -453,41 +486,6 @@ has_attrib(TypeHandle type) const {
   return (index >= 0);
   return (index >= 0);
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::clear_attrib
-//       Access: Published
-//  Description: Removes the render attribute of the given type from
-//               this node.  This node, and the subgraph below, will
-//               now inherit the indicated render attribute from the
-//               nodes above this one.
-////////////////////////////////////////////////////////////////////
-INLINE void PandaNode::
-clear_attrib(TypeHandle type) {
-  CDWriter cdata(_cycler);
-  CPT(RenderState) new_state = cdata->_state->remove_attrib(type);
-  if (cdata->_state != new_state) {
-    cdata->_state = new_state;
-  
-    // We mark the bound stale when the state changes, in case we have
-    // changed a ClipPlaneAttrib.
-    mark_bound_stale();
-    state_changed();
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::set_effect
-//       Access: Published
-//  Description: Adds the indicated render effect to the scene
-//               graph on this node.  If there was already an effect
-//               of the same type, it is replaced.
-////////////////////////////////////////////////////////////////////
-INLINE void PandaNode::
-set_effect(const RenderEffect *effect) {
-  CDWriter cdata(_cycler);
-  cdata->_effects = cdata->_effects->add_effect(effect);
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::get_effect
 //     Function: PandaNode::get_effect
 //       Access: Published
 //       Access: Published
@@ -518,41 +516,6 @@ has_effect(TypeHandle type) const {
   return (index >= 0);
   return (index >= 0);
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::clear_effect
-//       Access: Published
-//  Description: Removes the render effect of the given type from
-//               this node.
-////////////////////////////////////////////////////////////////////
-INLINE void PandaNode::
-clear_effect(TypeHandle type) {
-  CDWriter cdata(_cycler);
-  cdata->_effects = cdata->_effects->remove_effect(type);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::set_state
-//       Access: Published
-//  Description: Sets the complete RenderState that will be applied to
-//               all nodes at this level and below.  (The actual state
-//               that will be applied to lower nodes is based on the
-//               composition of RenderStates from above this node as
-//               well).  This completely replaces whatever has been
-//               set on this node via repeated calls to set_attrib().
-////////////////////////////////////////////////////////////////////
-INLINE void PandaNode::
-set_state(const RenderState *state) {
-  CDWriter cdata(_cycler);
-  if (cdata->_state != state) {
-    cdata->_state = state;
-
-    // We mark the bound stale when the state changes, in case we have
-    // changed a ClipPlaneAttrib.
-    mark_bound_stale();
-    state_changed();
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::get_state
 //     Function: PandaNode::get_state
 //       Access: Published
 //       Access: Published
@@ -568,41 +531,6 @@ get_state() const {
   return cdata->_state;
   return cdata->_state;
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::clear_state
-//       Access: Published
-//  Description: Resets this node to leave the render state alone.
-//               Nodes at this level and below will once again inherit
-//               their render state unchanged from the nodes above
-//               this level.
-////////////////////////////////////////////////////////////////////
-INLINE void PandaNode::
-clear_state() {
-  CDWriter cdata(_cycler);
-  if (!cdata->_state->is_empty()) {
-    cdata->_state = RenderState::make_empty();
-
-    // We mark the bound stale when the state changes, in case we have
-    // changed a ClipPlaneAttrib.
-    mark_bound_stale();
-    state_changed();
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::set_effects
-//       Access: Published
-//  Description: Sets the complete RenderEffects that will be applied
-//               this node.  This completely replaces whatever has
-//               been set on this node via repeated calls to
-//               set_attrib().
-////////////////////////////////////////////////////////////////////
-INLINE void PandaNode::
-set_effects(const RenderEffects *effects) {
-  CDWriter cdata(_cycler);
-  cdata->_effects = effects;
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::get_effects
 //     Function: PandaNode::get_effects
 //       Access: Published
 //       Access: Published
@@ -615,34 +543,6 @@ get_effects() const {
   return cdata->_effects;
   return cdata->_effects;
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::clear_effects
-//       Access: Published
-//  Description: Resets this node to have no render effects.
-////////////////////////////////////////////////////////////////////
-INLINE void PandaNode::
-clear_effects() {
-  CDWriter cdata(_cycler);
-  cdata->_effects = RenderEffects::make_empty();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::set_transform
-//       Access: Published
-//  Description: Sets the transform that will be applied to this node
-//               and below.  This defines a new coordinate space at
-//               this point in the scene graph and below.
-////////////////////////////////////////////////////////////////////
-INLINE void PandaNode::
-set_transform(const TransformState *transform) {
-  CDWriter cdata(_cycler);
-  if (cdata->_transform != transform) {
-    cdata->_transform = transform;
-    mark_bound_stale();
-    transform_changed();
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::get_transform
 //     Function: PandaNode::get_transform
 //       Access: Published
 //       Access: Published
@@ -657,36 +557,6 @@ get_transform() const {
   return cdata->_transform;
   return cdata->_transform;
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::clear_transform
-//       Access: Published
-//  Description: Resets the transform on this node to the identity
-//               transform.
-////////////////////////////////////////////////////////////////////
-INLINE void PandaNode::
-clear_transform() {
-  CDWriter cdata(_cycler);
-  if (!cdata->_transform->is_identity()) {
-    cdata->_transform = TransformState::make_identity();
-    mark_bound_stale();
-    transform_changed();
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::set_prev_transform
-//       Access: Published
-//  Description: Sets the transform that represents this node's
-//               "previous" position, one frame ago, for the purposes
-//               of detecting motion for accurate collision
-//               calculations.
-////////////////////////////////////////////////////////////////////
-INLINE void PandaNode::
-set_prev_transform(const TransformState *transform) {
-  CDWriter cdata(_cycler);
-  cdata->_prev_transform = transform;
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::get_prev_transform
 //     Function: PandaNode::get_prev_transform
 //       Access: Published
 //       Access: Published
@@ -709,27 +579,10 @@ get_prev_transform() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PandaNode::
 INLINE void PandaNode::
 reset_prev_transform() {
 reset_prev_transform() {
-  CDWriter cdata(_cycler);
-  cdata->_prev_transform = cdata->_transform;
-}
+  nassertv(Thread::get_current_pipeline_stage() == 0);
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::set_tag
-//       Access: Published
-//  Description: Associates a user-defined value with a user-defined
-//               key which is stored on the node.  This value has no
-//               meaning to Panda; but it is stored indefinitely on
-//               the node until it is requested again.
-//
-//               Each unique key stores a different string value.
-//               There is no effective limit on the number of
-//               different keys that may be stored or on the length of
-//               any one key's value.
-////////////////////////////////////////////////////////////////////
-INLINE void PandaNode::
-set_tag(const string &key, const string &value) {
   CDWriter cdata(_cycler);
   CDWriter cdata(_cycler);
-  cdata->_tag_data[key] = value;
+  cdata->_prev_transform = cdata->_transform;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -766,19 +619,6 @@ has_tag(const string &key) const {
   return (ti != cdata->_tag_data.end());
   return (ti != cdata->_tag_data.end());
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::clear_tag
-//       Access: Published
-//  Description: Removes the value defined for this key on this
-//               particular node.  After a call to clear_tag(),
-//               has_tag() will return false for the indicated key.
-////////////////////////////////////////////////////////////////////
-INLINE void PandaNode::
-clear_tag(const string &key) {
-  CDWriter cdata(_cycler);
-  cdata->_tag_data.erase(key);
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::has_tags
 //     Function: PandaNode::has_tags
 //       Access: Published
 //       Access: Published
@@ -810,28 +650,6 @@ ls(ostream &out, int indent_level) const {
   r_list_descendants(out, indent_level);
   r_list_descendants(out, indent_level);
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::set_draw_mask
-//       Access: Published
-//  Description: Sets the hide/show bits of this particular node.
-//
-//               During the cull traversal, a node is not visited if
-//               none of its draw mask bits intersect with the
-//               camera's draw mask bits.  These masks can be used to
-//               selectively hide and show different parts of the
-//               scene graph from different cameras that are otherwise
-//               viewing the same scene.  See
-//               Camera::set_camera_mask().
-////////////////////////////////////////////////////////////////////
-INLINE void PandaNode::
-set_draw_mask(DrawMask mask) {
-  CDWriter cdata(_cycler);
-  if (cdata->_draw_mask != mask) {
-    cdata->_draw_mask = mask;
-    draw_mask_changed();
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::get_draw_mask
 //     Function: PandaNode::get_draw_mask
 //       Access: Published
 //       Access: Published
@@ -844,34 +662,6 @@ get_draw_mask() const {
   return cdata->_draw_mask;
   return cdata->_draw_mask;
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::set_into_collide_mask
-//       Access: Published
-//  Description: Sets the "into" CollideMask.  
-//
-//               This specifies the set of bits that must be shared
-//               with a CollisionNode's "from" CollideMask in order
-//               for the CollisionNode to detect a collision with this
-//               particular node.
-//
-//               The actual CollideMask that will be set is masked by
-//               the return value from get_legal_collide_mask().
-//               Thus, the into_collide_mask cannot be set to anything
-//               other than nonzero except for those types of nodes
-//               that can be collided into, such as CollisionNodes and
-//               GeomNodes.
-////////////////////////////////////////////////////////////////////
-INLINE void PandaNode::
-set_into_collide_mask(CollideMask mask) {
-  CDWriter cdata(_cycler);
-  cdata->_into_collide_mask = mask & get_legal_collide_mask();
-
-  // We mark the bound stale when this changes, not because the actual
-  // bounding volume changes, but rather because we piggyback the
-  // computing of the _net_collide_mask on the bounding volume.
-  mark_bound_stale();
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::get_into_collide_mask
 //     Function: PandaNode::get_into_collide_mask
 //       Access: Published
 //       Access: Published
@@ -884,62 +674,15 @@ get_into_collide_mask() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::get_net_collide_mask
-//       Access: Published
-//  Description: Returns the union of all into_collide_mask() values
-//               set at CollisionNodes at this level and below.
-////////////////////////////////////////////////////////////////////
-INLINE CollideMask PandaNode::
-get_net_collide_mask() const {
-  // Call get_bound() first to ensure the mask is recomputed.
-  BoundedObject::get_bound();
-  CDReader cdata(_cycler);
-  return cdata->_net_collide_mask;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::get_off_clip_planes
-//       Access: Published
-//  Description: Returns a ClipPlaneAttrib which represents the union
-//               of all of the clip planes that have been turned *off*
-//               at this level and below.
-////////////////////////////////////////////////////////////////////
-INLINE const RenderAttrib *PandaNode::
-get_off_clip_planes() const {
-  // Call get_bound() first to ensure the attrib is recomputed.
-  BoundedObject::get_bound();
-  CDReader cdata(_cycler);
-  return cdata->_off_clip_planes;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::set_bound
-//       Access: Published
-//  Description: Sets the type of the external bounding volume that is
-//               placed around this node and all of its children.
-////////////////////////////////////////////////////////////////////
-INLINE void PandaNode::
-set_bound(BoundingVolumeType type) {
-  CDWriter cdata(_cycler);
-  cdata->_fixed_internal_bound = false;
-  BoundedObject::set_bound(type);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::set_bound
+//     Function: PandaNode::get_bound
 //       Access: Published
 //       Access: Published
-//  Description: Resets the internal bounding volume so that it is the
-//               indicated volume.  The external bounding volume as
-//               returned by get_bound() (which includes all of the
-//               node's children) will be adjusted to include this
-//               internal volume.
+//  Description: Returns the node's external bounding volume.  This is
+//               the bounding volume around the node and all of its
+//               children.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE void PandaNode::
-set_bound(const BoundingVolume &volume) {
-  CDWriter cdata(_cycler);
-  cdata->_fixed_internal_bound = true;
-  _internal_bound.set_bound(volume);
-  changed_internal_bound();
+INLINE const BoundingVolume *PandaNode::
+get_bound() const {
+  return BoundedObject::get_bound();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -949,9 +692,9 @@ set_bound(const BoundingVolume &volume) {
 //               the bounding volume around the node and all of its
 //               the bounding volume around the node and all of its
 //               children.
 //               children.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const BoundingVolume &PandaNode::
-get_bound() const {
-  return BoundedObject::get_bound();
+INLINE const BoundingVolume *PandaNode::
+get_bound(int pipeline_stage) const {
+  return BoundedObject::get_bound(pipeline_stage);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -961,14 +704,28 @@ get_bound() const {
 //               the bounding volume around the node alone, without
 //               the bounding volume around the node alone, without
 //               including children.
 //               including children.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE const BoundingVolume &PandaNode::
+INLINE const BoundingVolume *PandaNode::
 get_internal_bound() const {
 get_internal_bound() const {
-  CDReader cdata(_cycler);
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  return get_internal_bound(pipeline_stage);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::get_internal_bound
+//       Access: Published
+//  Description: Returns the node's internal bounding volume.  This is
+//               the bounding volume around the node alone, without
+//               including children.
+////////////////////////////////////////////////////////////////////
+INLINE const BoundingVolume *PandaNode::
+get_internal_bound(int pipeline_stage) const {
+  CDStageReader cdata(_cycler, pipeline_stage);
   if (!cdata->_fixed_internal_bound && 
   if (!cdata->_fixed_internal_bound && 
-      (is_bound_stale() || _internal_bound.is_bound_stale())) {
-    ((PandaNode *)this)->recompute_internal_bound();
+      (is_bound_stale(pipeline_stage) || 
+       _internal_bound.is_bound_stale(pipeline_stage))) {
+    ((PandaNode *)this)->recompute_internal_bound(pipeline_stage);
   }
   }
-  return _internal_bound.get_bound();
+  return _internal_bound.get_bound(pipeline_stage);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -979,8 +736,8 @@ get_internal_bound() const {
 //               bounding volume to be recomputed.
 //               bounding volume to be recomputed.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PandaNode::
 INLINE void PandaNode::
-changed_internal_bound() {
-  BoundedObject::mark_bound_stale();
+changed_internal_bound(int pipeline_stage) {
+  BoundedObject::mark_bound_stale(pipeline_stage);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1019,3 +776,30 @@ get_children_copy() const {
   return ChildrenCopy(cdata);
   return ChildrenCopy(cdata);
 }
 }
 
 
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::mark_child_cache_stale
+//       Access: Private
+//  Description: Indicates that the _child_cache value that is
+//               propagated upwards has changed for this node.
+////////////////////////////////////////////////////////////////////
+INLINE void PandaNode::
+mark_child_cache_stale(int pipeline_stage, CData *cdata) {
+  if (!cdata->_stale_child_cache) {
+    force_child_cache_stale(pipeline_stage, cdata);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::do_find_parent
+//       Access: Private
+//  Description: The private implementation of find_parent().
+////////////////////////////////////////////////////////////////////
+INLINE int PandaNode::
+do_find_parent(PandaNode *node, const CData *cdata) const {
+  Up::const_iterator ui = cdata->_up.find(UpConnection(node));
+  if (ui == cdata->_up.end()) {
+    return -1;
+  }
+  return ui - cdata->_up.begin();
+}

File diff suppressed because it is too large
+ 573 - 256
panda/src/pgraph/pandaNode.cxx


+ 66 - 45
panda/src/pgraph/pandaNode.h

@@ -24,6 +24,8 @@
 #include "cycleData.h"
 #include "cycleData.h"
 #include "cycleDataReader.h"
 #include "cycleDataReader.h"
 #include "cycleDataWriter.h"
 #include "cycleDataWriter.h"
+#include "cycleDataStageReader.h"
+#include "cycleDataStageWriter.h"
 #include "pipelineCycler.h"
 #include "pipelineCycler.h"
 #include "renderState.h"
 #include "renderState.h"
 #include "renderEffects.h"
 #include "renderEffects.h"
@@ -113,10 +115,10 @@ PUBLISHED:
   INLINE int get_num_children() const;
   INLINE int get_num_children() const;
   INLINE PandaNode *get_child(int n) const;
   INLINE PandaNode *get_child(int n) const;
   INLINE int get_child_sort(int n) const;
   INLINE int get_child_sort(int n) const;
-  int find_child(PandaNode *node) const;
+  INLINE int find_child(PandaNode *node) const;
 
 
   void add_child(PandaNode *child_node, int sort = 0);
   void add_child(PandaNode *child_node, int sort = 0);
-  void remove_child(int n);
+  INLINE void remove_child(int n);
   bool remove_child(PandaNode *child_node);
   bool remove_child(PandaNode *child_node);
   bool replace_child(PandaNode *orig_child, PandaNode *new_child);
   bool replace_child(PandaNode *orig_child, PandaNode *new_child);
 
 
@@ -128,45 +130,45 @@ PUBLISHED:
   INLINE int get_num_stashed() const;
   INLINE int get_num_stashed() const;
   INLINE PandaNode *get_stashed(int n) const;
   INLINE PandaNode *get_stashed(int n) const;
   INLINE int get_stashed_sort(int n) const;
   INLINE int get_stashed_sort(int n) const;
-  int find_stashed(PandaNode *node) const;
+  INLINE int find_stashed(PandaNode *node) const;
 
 
   void add_stashed(PandaNode *child_node, int sort = 0);
   void add_stashed(PandaNode *child_node, int sort = 0);
-  void remove_stashed(int n);
+  INLINE void remove_stashed(int n);
 
 
   void remove_all_children();
   void remove_all_children();
   void steal_children(PandaNode *other);
   void steal_children(PandaNode *other);
   void copy_children(PandaNode *other);
   void copy_children(PandaNode *other);
 
 
-  INLINE void set_attrib(const RenderAttrib *attrib, int override = 0);
+  void set_attrib(const RenderAttrib *attrib, int override = 0);
   INLINE const RenderAttrib *get_attrib(TypeHandle type) const;
   INLINE const RenderAttrib *get_attrib(TypeHandle type) const;
   INLINE bool has_attrib(TypeHandle type) const;
   INLINE bool has_attrib(TypeHandle type) const;
-  INLINE void clear_attrib(TypeHandle type);
+  void clear_attrib(TypeHandle type);
 
 
-  INLINE void set_effect(const RenderEffect *effect);
+  void set_effect(const RenderEffect *effect);
   INLINE const RenderEffect *get_effect(TypeHandle type) const;
   INLINE const RenderEffect *get_effect(TypeHandle type) const;
   INLINE bool has_effect(TypeHandle type) const;
   INLINE bool has_effect(TypeHandle type) const;
-  INLINE void clear_effect(TypeHandle type);
+  void clear_effect(TypeHandle type);
 
 
-  INLINE void set_state(const RenderState *state);
+  void set_state(const RenderState *state);
   INLINE const RenderState *get_state() const;
   INLINE const RenderState *get_state() const;
-  INLINE void clear_state();
+  void clear_state();
 
 
-  INLINE void set_effects(const RenderEffects *effects);
+  void set_effects(const RenderEffects *effects);
   INLINE const RenderEffects *get_effects() const;
   INLINE const RenderEffects *get_effects() const;
-  INLINE void clear_effects();
+  void clear_effects();
 
 
-  INLINE void set_transform(const TransformState *transform);
+  void set_transform(const TransformState *transform);
   INLINE const TransformState *get_transform() const;
   INLINE const TransformState *get_transform() const;
-  INLINE void clear_transform();
+  void clear_transform();
 
 
-  INLINE void set_prev_transform(const TransformState *transform);
+  void set_prev_transform(const TransformState *transform);
   INLINE const TransformState *get_prev_transform() const;
   INLINE const TransformState *get_prev_transform() const;
   INLINE void reset_prev_transform();
   INLINE void reset_prev_transform();
 
 
-  INLINE void set_tag(const string &key, const string &value);
+  void set_tag(const string &key, const string &value);
   INLINE string get_tag(const string &key) const;
   INLINE string get_tag(const string &key) const;
   INLINE bool has_tag(const string &key) const;
   INLINE bool has_tag(const string &key) const;
-  INLINE void clear_tag(const string &key);
+  void clear_tag(const string &key);
 
 
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
   void set_python_tag(const string &key, PyObject *value);
   void set_python_tag(const string &key, PyObject *value);
@@ -179,15 +181,15 @@ PUBLISHED:
   void copy_tags(PandaNode *other);
   void copy_tags(PandaNode *other);
   void list_tags(ostream &out, const string &separator = "\n") const;
   void list_tags(ostream &out, const string &separator = "\n") const;
 
 
-  INLINE void set_draw_mask(DrawMask mask);
+  void set_draw_mask(DrawMask mask);
   INLINE DrawMask get_draw_mask() const;
   INLINE DrawMask get_draw_mask() const;
 
 
-  INLINE void set_into_collide_mask(CollideMask mask);
+  void set_into_collide_mask(CollideMask mask);
   INLINE CollideMask get_into_collide_mask() const;
   INLINE CollideMask get_into_collide_mask() const;
   virtual CollideMask get_legal_collide_mask() const;
   virtual CollideMask get_legal_collide_mask() const;
 
 
-  INLINE CollideMask get_net_collide_mask() const;
-  INLINE const RenderAttrib *get_off_clip_planes() const;
+  CollideMask get_net_collide_mask() const;
+  const RenderAttrib *get_off_clip_planes() const;
 
 
   virtual void output(ostream &out) const;
   virtual void output(ostream &out) const;
   virtual void write(ostream &out, int indent_level) const;
   virtual void write(ostream &out, int indent_level) const;
@@ -206,10 +208,12 @@ PUBLISHED:
   // the external bound.  Although it might seem strange and confusing
   // the external bound.  Although it might seem strange and confusing
   // to do this, this is actually the natural way the user thinks
   // to do this, this is actually the natural way the user thinks
   // about nodes and bounding volumes.
   // about nodes and bounding volumes.
-  INLINE void set_bound(BoundingVolumeType type);
-  INLINE void set_bound(const BoundingVolume &volume);
-  INLINE const BoundingVolume &get_bound() const;
-  INLINE const BoundingVolume &get_internal_bound() const;
+  void set_bound(BoundingVolumeType type);
+  void set_bound(const BoundingVolume &volume);
+  INLINE const BoundingVolume *get_bound() const;
+  INLINE const BoundingVolume *get_bound(int pipeline_stage) const;
+  INLINE const BoundingVolume *get_internal_bound() const;
+  INLINE const BoundingVolume *get_internal_bound(int pipeline_stage) const;
 
 
   virtual bool is_geom_node() const;
   virtual bool is_geom_node() const;
   virtual bool is_lod_node() const;
   virtual bool is_lod_node() const;
@@ -217,17 +221,17 @@ PUBLISHED:
 
 
 protected:
 protected:
   // Inherited from BoundedObject
   // Inherited from BoundedObject
-  virtual void propagate_stale_bound();
-  virtual BoundingVolume *recompute_bound();
+  virtual void propagate_stale_bound(int pipeline_stage);
+  virtual BoundingVolume *recompute_bound(int pipeline_stage);
 
 
   // Local to PandaNode
   // Local to PandaNode
-  virtual BoundingVolume *recompute_internal_bound();
-  INLINE void changed_internal_bound();
-  virtual void parents_changed();
-  virtual void children_changed();
-  virtual void transform_changed();
-  virtual void state_changed();
-  virtual void draw_mask_changed();
+  virtual BoundingVolume *recompute_internal_bound(int pipeline_stage);
+  INLINE void changed_internal_bound(int pipeline_stage);
+  virtual void parents_changed(int pipeline_stage);
+  virtual void children_changed(int pipeline_stage);
+  virtual void transform_changed(int pipeline_stage);
+  virtual void state_changed(int pipeline_stage);
+  virtual void draw_mask_changed(int pipeline_stage);
   INLINE void add_net_collide_mask(CollideMask mask);
   INLINE void add_net_collide_mask(CollideMask mask);
 
 
   typedef pmap<PandaNode *, PandaNode *> InstanceMap;
   typedef pmap<PandaNode *, PandaNode *> InstanceMap;
@@ -242,6 +246,22 @@ protected:
 private:
 private:
   class CData;
   class CData;
 
 
+  void update_child_cache();
+  void do_update_child_cache(int pipeline_stage, CData *cdata);
+  INLINE void mark_child_cache_stale(int pipeline_stage, CData *cdata);
+  void force_child_cache_stale(int pipeline_stage, CData *cdata);
+
+  INLINE int do_find_parent(PandaNode *node, const CData *cdata) const;
+  int do_find_child(PandaNode *node, const CData *cdata) const;
+  int do_find_stashed(PandaNode *node, const CData *cdata) const;
+  bool stage_remove_child(PandaNode *child_node, int pipeline_stage);
+  bool stage_replace_child(PandaNode *orig_child, PandaNode *new_child,
+                           int pipeline_stage);
+  void do_remove_child(int n, PandaNode *child_node, int pipeline_stage,
+                       CData *cdata, CData *cdata_child);
+  void do_remove_stashed(int n, PandaNode *child_node, int pipeline_stage,
+                         CData *cdata, CData *cdata_child);
+
   // parent-child manipulation for NodePath support.  Don't try to
   // parent-child manipulation for NodePath support.  Don't try to
   // call these directly.
   // call these directly.
   static PT(NodePathComponent) attach(NodePathComponent *parent, 
   static PT(NodePathComponent) attach(NodePathComponent *parent, 
@@ -256,8 +276,10 @@ private:
   PT(NodePathComponent) get_generic_component(bool accept_ambiguity);
   PT(NodePathComponent) get_generic_component(bool accept_ambiguity);
   PT(NodePathComponent) r_get_generic_component(bool accept_ambiguity, bool &ambiguity_detected);
   PT(NodePathComponent) r_get_generic_component(bool accept_ambiguity, bool &ambiguity_detected);
   void delete_component(NodePathComponent *component);
   void delete_component(NodePathComponent *component);
-  static void sever_connection(PandaNode *parent_node, PandaNode *child_node);
-  static void new_connection(PandaNode *parent_node, PandaNode *child_node);
+  static void sever_connection(PandaNode *parent_node, PandaNode *child_node,
+                               CData *cdata_child);
+  static void new_connection(PandaNode *parent_node, PandaNode *child_node,
+                             CData *cdata_child);
   void fix_path_lengths(const CData *cdata);
   void fix_path_lengths(const CData *cdata);
   void r_list_descendants(ostream &out, int indent_level) const;
   void r_list_descendants(ostream &out, int indent_level) const;
 
 
@@ -369,26 +391,25 @@ private:
     CollideMask _into_collide_mask;
     CollideMask _into_collide_mask;
 
 
     // This is the union of all into_collide_mask bits for any nodes
     // This is the union of all into_collide_mask bits for any nodes
-    // at and below this level.  It's conceptually similar to a
-    // bounding volume--it represents the bounding volume of this node
-    // in the space of collision bits--and it needs to be updated for
-    // the same reasons the bounding volume needs to be updated.  So
-    // we update them together.
+    // at and below this level.  It's updated automatically whenever
+    // _stale_child_cache is true.
     CollideMask _net_collide_mask;
     CollideMask _net_collide_mask;
 
 
     // This is a ClipPlaneAttrib that represents the union of all clip
     // This is a ClipPlaneAttrib that represents the union of all clip
-    // planes that have been turned *off* at and below this level.  As
-    // above, it's similar to a bounding volume, and is updated at the
-    // same time.  TODO: fix the circular reference counts involved
-    // here.
+    // planes that have been turned *off* at and below this level.  We
+    // piggyback this automatic update on _stale_child_cache.  TODO:
+    // fix the circular reference counts involved here.
     CPT(RenderAttrib) _off_clip_planes;
     CPT(RenderAttrib) _off_clip_planes;
 
 
+    bool _stale_child_cache;
     bool _fixed_internal_bound;
     bool _fixed_internal_bound;
   };
   };
 
 
   PipelineCycler<CData> _cycler;
   PipelineCycler<CData> _cycler;
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataWriter<CData> CDWriter;
   typedef CycleDataWriter<CData> CDWriter;
+  typedef CycleDataStageReader<CData> CDStageReader;
+  typedef CycleDataStageWriter<CData> CDStageWriter;
 
 
 public:
 public:
   // Use this interface when you want to walk through the list of
   // Use this interface when you want to walk through the list of

+ 1 - 1
panda/src/pgraph/portalClipper.I

@@ -150,7 +150,7 @@ is_facing_view(Planef portal_plane) {
 INLINE bool PortalClipper::
 INLINE bool PortalClipper::
 is_whole_portal_in_view(LMatrix4f cmat) {
 is_whole_portal_in_view(LMatrix4f cmat) {
   // I am about to xform this gbv, so lets make a copy
   // I am about to xform this gbv, so lets make a copy
-  const BoundingVolume *bv = &_portal_node->get_bound();
+  const BoundingVolume *bv = _portal_node->get_bound();
   BoundingVolume *cbv = bv->make_copy();
   BoundingVolume *cbv = bv->make_copy();
   GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, cbv);
   GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, cbv);
 
 

+ 4 - 4
panda/src/pgraph/portalNode.cxx

@@ -309,8 +309,8 @@ draw() const {
 //               setting the _net_portal_mask bits.
 //               setting the _net_portal_mask bits.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 BoundingVolume *PortalNode::
 BoundingVolume *PortalNode::
-recompute_bound() {
-  BoundingVolume *result = PandaNode::recompute_bound();
+recompute_bound(int pipeline_stage) {
+  BoundingVolume *result = PandaNode::recompute_bound(pipeline_stage);
   return result;
   return result;
 }
 }
 
 
@@ -323,9 +323,9 @@ recompute_bound() {
 //               thing.
 //               thing.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 BoundingVolume *PortalNode::
 BoundingVolume *PortalNode::
-recompute_internal_bound() {
+recompute_internal_bound(int pipeline_stage) {
   // First, get ourselves a fresh, empty bounding volume.
   // First, get ourselves a fresh, empty bounding volume.
-  BoundingVolume *bound = PandaNode::recompute_internal_bound();
+  BoundingVolume *bound = PandaNode::recompute_internal_bound(pipeline_stage);
   nassertr(bound != (BoundingVolume *)NULL, bound);
   nassertr(bound != (BoundingVolume *)NULL, bound);
 
 
   GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);
   GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);

+ 2 - 2
panda/src/pgraph/portalNode.h

@@ -85,8 +85,8 @@ PUBLISHED:
   //  void draw () const;
   //  void draw () const;
 
 
 protected:
 protected:
-  virtual BoundingVolume *recompute_bound();
-  virtual BoundingVolume *recompute_internal_bound();
+  virtual BoundingVolume *recompute_bound(int pipeline_stage);
+  virtual BoundingVolume *recompute_internal_bound(int pipeline_stage);
 
 
 private:
 private:
   CPT(RenderState) get_last_pos_state();
   CPT(RenderState) get_last_pos_state();

+ 1 - 1
panda/src/pgraph/sceneGraphReducer.cxx

@@ -232,7 +232,7 @@ r_flatten(PandaNode *grandparent_node, PandaNode *parent_node,
 
 
   if (parent_node->safe_to_flatten_below()) {
   if (parent_node->safe_to_flatten_below()) {
     if ((combine_siblings_bits & CS_within_radius) != 0) {
     if ((combine_siblings_bits & CS_within_radius) != 0) {
-      const BoundingVolume *bv = &parent_node->get_bound();
+      const BoundingVolume *bv = parent_node->get_bound();
       if (bv->is_of_type(BoundingSphere::get_class_type())) {
       if (bv->is_of_type(BoundingSphere::get_class_type())) {
         const BoundingSphere *bs = DCAST(BoundingSphere, bv);
         const BoundingSphere *bs = DCAST(BoundingSphere, bv);
         if (pgraph_cat.is_spam()) {
         if (pgraph_cat.is_spam()) {

+ 7 - 7
panda/src/pgui/pgItem.cxx

@@ -127,8 +127,8 @@ make_copy() const {
 //               classes can do something special in this case.
 //               classes can do something special in this case.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
-transform_changed() {
-  PandaNode::transform_changed();
+transform_changed(int pipeline_stage) {
+  PandaNode::transform_changed(pipeline_stage);
   if (has_notify()) {
   if (has_notify()) {
     get_notify()->item_transform_changed(this);
     get_notify()->item_transform_changed(this);
   }
   }
@@ -142,8 +142,8 @@ transform_changed() {
 //               classes can do something special in this case.
 //               classes can do something special in this case.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
-draw_mask_changed() {
-  PandaNode::draw_mask_changed();
+draw_mask_changed(int pipeline_stage) {
+  PandaNode::draw_mask_changed(pipeline_stage);
   if (has_notify()) {
   if (has_notify()) {
     get_notify()->item_draw_mask_changed(this);
     get_notify()->item_draw_mask_changed(this);
   }
   }
@@ -261,9 +261,9 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
 //               thing.
 //               thing.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 BoundingVolume *PGItem::
 BoundingVolume *PGItem::
-recompute_internal_bound() {
+recompute_internal_bound(int pipeline_stage) {
   // First, get ourselves a fresh, empty bounding volume.
   // First, get ourselves a fresh, empty bounding volume.
-  BoundingVolume *bound = PandaNode::recompute_internal_bound();
+  BoundingVolume *bound = PandaNode::recompute_internal_bound(pipeline_stage);
   nassertr(bound != (BoundingVolume *)NULL, bound);
   nassertr(bound != (BoundingVolume *)NULL, bound);
 
 
   // Now actually compute the bounding volume by putting it around all
   // Now actually compute the bounding volume by putting it around all
@@ -276,7 +276,7 @@ recompute_internal_bound() {
   for (int i = 0; i < (int)_state_defs.size(); i++) {
   for (int i = 0; i < (int)_state_defs.size(); i++) {
     NodePath &root = get_state_def(i);
     NodePath &root = get_state_def(i);
     if (!root.is_empty()) {
     if (!root.is_empty()) {
-      child_volumes.push_back(&root.node()->get_bound());
+      child_volumes.push_back(root.node()->get_bound());
     }
     }
   }
   }
 
 

+ 3 - 3
panda/src/pgui/pgItem.h

@@ -62,13 +62,13 @@ protected:
   PGItem(const PGItem &copy);
   PGItem(const PGItem &copy);
 
 
   virtual PandaNode *make_copy() const;
   virtual PandaNode *make_copy() const;
-  virtual void transform_changed();
-  virtual void draw_mask_changed();
+  virtual void transform_changed(int pipeline_stage);
+  virtual void draw_mask_changed(int pipeline_stage);
 
 
   virtual bool has_cull_callback() const;
   virtual bool has_cull_callback() const;
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
 
 
-  virtual BoundingVolume *recompute_internal_bound();
+  virtual BoundingVolume *recompute_internal_bound(int pipeline_stage);
 
 
 public:
 public:
   virtual void xform(const LMatrix4f &mat);
   virtual void xform(const LMatrix4f &mat);

+ 4 - 2
panda/src/physics/actorNode.cxx

@@ -90,9 +90,11 @@ update_transform() {
 //                i.e. copy from PandaNode to PhysicsObject
 //                i.e. copy from PandaNode to PhysicsObject
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void ActorNode::
 void ActorNode::
-transform_changed() {
+transform_changed(int pipeline_stage) {
+  PandaNode::transform_changed(pipeline_stage);
+
   // this callback could be triggered by update_transform, BAD.
   // this callback could be triggered by update_transform, BAD.
-  if (_ok_to_callback == false) {
+  if (!_ok_to_callback) {
     return;
     return;
   }
   }
 
 

+ 1 - 1
panda/src/physics/actorNode.h

@@ -55,7 +55,7 @@ private:
 
 
   // node hook if the client changes the node's transform.
   // node hook if the client changes the node's transform.
   // i.e. copy from PandaNode to PhysicsObject
   // i.e. copy from PandaNode to PhysicsObject
-  virtual void transform_changed();
+  virtual void transform_changed(int pipeline_stage);
 
 
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {

+ 9 - 0
panda/src/putil/Sources.pp

@@ -24,7 +24,10 @@
     config_util.N config_util.h configurable.h \
     config_util.N config_util.h configurable.h \
     cycleData.h cycleData.I \
     cycleData.h cycleData.I \
     cycleDataReader.h cycleDataReader.I \
     cycleDataReader.h cycleDataReader.I \
+    cycleDataStageReader.h cycleDataStageReader.I \
+    cycleDataStageWriter.h cycleDataStageWriter.I \
     cycleDataWriter.h cycleDataWriter.I \
     cycleDataWriter.h cycleDataWriter.I \
+    cyclerHolder.h cyclerHolder.I \
     datagramInputFile.I datagramInputFile.h \
     datagramInputFile.I datagramInputFile.h \
     datagramOutputFile.I datagramOutputFile.h \
     datagramOutputFile.I datagramOutputFile.h \
     drawMask.h \
     drawMask.h \
@@ -75,7 +78,10 @@
     config_util.cxx configurable.cxx \
     config_util.cxx configurable.cxx \
     cycleData.cxx \
     cycleData.cxx \
     cycleDataReader.cxx \
     cycleDataReader.cxx \
+    cycleDataStageReader.cxx \
+    cycleDataStageWriter.cxx \
     cycleDataWriter.cxx \
     cycleDataWriter.cxx \
+    cyclerHolder.cxx \
     datagramInputFile.cxx datagramOutputFile.cxx \
     datagramInputFile.cxx datagramOutputFile.cxx \
     factoryBase.cxx \
     factoryBase.cxx \
     factoryParam.cxx factoryParams.cxx \
     factoryParam.cxx factoryParams.cxx \
@@ -119,7 +125,10 @@
     config_util.h configurable.h factory.I factory.h \
     config_util.h configurable.h factory.I factory.h \
     cycleData.h cycleData.I \
     cycleData.h cycleData.I \
     cycleDataReader.h cycleDataReader.I \
     cycleDataReader.h cycleDataReader.I \
+    cycleDataStageReader.h cycleDataStageReader.I \
+    cycleDataStageWriter.h cycleDataStageWriter.I \
     cycleDataWriter.h cycleDataWriter.I \
     cycleDataWriter.h cycleDataWriter.I \
+    cyclerHolder.h cyclerHolder.I \
     datagramInputFile.I datagramInputFile.h \
     datagramInputFile.I datagramInputFile.h \
     datagramOutputFile.I datagramOutputFile.h \
     datagramOutputFile.I datagramOutputFile.h \
     drawMask.h \
     drawMask.h \

+ 2 - 50
panda/src/putil/cycleDataReader.I

@@ -31,7 +31,6 @@ CycleDataReader(const PipelineCycler<CycleDataType> &cycler) :
   _cycler(&cycler)
   _cycler(&cycler)
 {
 {
   _pointer = _cycler->read();
   _pointer = _cycler->read();
-  _write_pointer = (CycleDataType *)NULL;
   nassertv(_pointer != (const CycleDataType *)NULL);
   nassertv(_pointer != (const CycleDataType *)NULL);
 }
 }
 
 
@@ -44,13 +43,9 @@ template<class CycleDataType>
 INLINE CycleDataReader<CycleDataType>::
 INLINE CycleDataReader<CycleDataType>::
 CycleDataReader(const CycleDataReader<CycleDataType> &copy) :
 CycleDataReader(const CycleDataReader<CycleDataType> &copy) :
   _cycler(copy._cycler),
   _cycler(copy._cycler),
-  _pointer(copy._pointer),
-  _write_pointer(copy._write_pointer)
+  _pointer(copy._pointer)
 {
 {
   nassertv(_pointer != (const CycleDataType *)NULL);
   nassertv(_pointer != (const CycleDataType *)NULL);
-  // We cannot copy a CycleDataReader that has elevated itself to a
-  // write pointer.
-  nassertv(_write_pointer == (const CycleDataType *)NULL);
   _cycler->increment_read(_pointer);
   _cycler->increment_read(_pointer);
 }
 }
 
 
@@ -64,12 +59,8 @@ INLINE void CycleDataReader<CycleDataType>::
 operator = (const CycleDataReader<CycleDataType> &copy) {
 operator = (const CycleDataReader<CycleDataType> &copy) {
   _cycler = copy._cycler;
   _cycler = copy._cycler;
   _pointer = copy._pointer;
   _pointer = copy._pointer;
-  _write_pointer = copy._write_pointer;
 
 
   nassertv(_pointer != (const CycleDataType *)NULL);
   nassertv(_pointer != (const CycleDataType *)NULL);
-  // We cannot copy a CycleDataReader that has elevated itself to a
-  // write pointer.
-  nassertv(_write_pointer == (const CycleDataType *)NULL);
   _cycler->increment_read(_pointer);
   _cycler->increment_read(_pointer);
 }
 }
 
 
@@ -81,12 +72,7 @@ operator = (const CycleDataReader<CycleDataType> &copy) {
 template<class CycleDataType>
 template<class CycleDataType>
 INLINE CycleDataReader<CycleDataType>::
 INLINE CycleDataReader<CycleDataType>::
 ~CycleDataReader() {
 ~CycleDataReader() {
-  if (_write_pointer != (CycleDataType *)NULL) {
-    // If the _write_pointer is non-NULL, then someone called
-    // elevate_to_write() at some point, and we now actually hold a
-    // write pointer, not a read pointer.
-    ((PipelineCycler<CycleDataType> *)_cycler)->release_write(_write_pointer);
-  } else if (_pointer != (CycleDataType *)NULL) {
+  if (_pointer != NULL) {
     _cycler->release_read(_pointer);
     _cycler->release_read(_pointer);
   }
   }
 }
 }
@@ -132,30 +118,10 @@ INLINE const CycleDataType *CycleDataReader<CycleDataType>::
 take_pointer() {
 take_pointer() {
   const CycleDataType *pointer = _pointer;
   const CycleDataType *pointer = _pointer;
   _pointer = (CycleDataType *)NULL;
   _pointer = (CycleDataType *)NULL;
-  _write_pointer = (CycleDataType *)NULL;
   nassertr(pointer != (const CycleDataType *)NULL, _cycler->cheat());
   nassertr(pointer != (const CycleDataType *)NULL, _cycler->cheat());
   return pointer;
   return pointer;
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: CycleDataReader::elevate_to_write (full)
-//       Access: Public
-//  Description: Call this to permanently elevate the readable pointer
-//               to a writable pointer.  This returns a writable
-//               pointer; subsequent calls to the same function will
-//               trivially return the same writable pointer.
-////////////////////////////////////////////////////////////////////
-template<class CycleDataType>
-INLINE CycleDataType *CycleDataReader<CycleDataType>::
-elevate_to_write(PipelineCycler<CycleDataType> &cycler) {
-  nassertr(&cycler = &_cycler, cycler.cheat());
-  if (_write_pointer == (CycleDataType *)NULL) {
-    _write_pointer = cycler.elevate_read(_pointer);
-    _pointer = _write_pointer;
-  }
-  return _write_pointer;
-}
-
 #else  // !DO_PIPELINING
 #else  // !DO_PIPELINING
 // This is the trivial, do-nothing implementation.
 // This is the trivial, do-nothing implementation.
 
 
@@ -243,18 +209,4 @@ take_pointer() {
   return _pointer;
   return _pointer;
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: CycleDataReader::elevate_to_write (trivial)
-//       Access: Public
-//  Description: Call this to permanently elevate the readable pointer
-//               to a writable pointer.  This returns a writable
-//               pointer; subsequent calls to the same function will
-//               trivially return the same writable pointer.
-////////////////////////////////////////////////////////////////////
-template<class CycleDataType>
-INLINE CycleDataType *CycleDataReader<CycleDataType>::
-elevate_to_write(PipelineCycler<CycleDataType> &cycler) {
-  return (CycleDataType *)_pointer;
-}
-
 #endif  // DO_PIPELINING
 #endif  // DO_PIPELINING

+ 0 - 1
panda/src/putil/cycleDataReader.h

@@ -49,7 +49,6 @@ public:
   INLINE operator const CycleDataType * () const;
   INLINE operator const CycleDataType * () const;
 
 
   INLINE const CycleDataType *take_pointer();
   INLINE const CycleDataType *take_pointer();
-  INLINE CycleDataType *elevate_to_write(PipelineCycler<CycleDataType> &cycler);
 
 
 private:
 private:
 #ifdef DO_PIPELINING
 #ifdef DO_PIPELINING

+ 217 - 0
panda/src/putil/cycleDataStageReader.I

@@ -0,0 +1,217 @@
+// Filename: cycleDataStageReader.I
+// Created by:  drose (08Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+#ifdef DO_PIPELINING
+// This is the implementation for full support of pipelining (as well
+// as the sanity-check only implementation).
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageReader::Constructor (full)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataStageReader<CycleDataType>::
+CycleDataStageReader(const PipelineCycler<CycleDataType> &cycler,
+                     int stage) :
+  _cycler(&cycler),
+  _stage(stage)
+{
+  _pointer = _cycler->read_stage(_stage);
+  nassertv(_pointer != (const CycleDataType *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageReader::Copy Constructor (full)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataStageReader<CycleDataType>::
+CycleDataStageReader(const CycleDataStageReader<CycleDataType> &copy) :
+  _cycler(copy._cycler),
+  _pointer(copy._pointer),
+  _stage(copy._stage)
+{
+  nassertv(_pointer != (const CycleDataType *)NULL);
+  _cycler->increment_read(_pointer);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageReader::Copy Assignment (full)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE void CycleDataStageReader<CycleDataType>::
+operator = (const CycleDataStageReader<CycleDataType> &copy) {
+  _cycler = copy._cycler;
+  _pointer = copy._pointer;
+  _stage = copy._stage;
+
+  nassertv(_pointer != (const CycleDataType *)NULL);
+  _cycler->increment_read(_pointer);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageReader::Destructor (full)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataStageReader<CycleDataType>::
+~CycleDataStageReader() {
+  if (_pointer != NULL) {
+    _cycler->release_read_stage(_stage, _pointer);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageReader::operator -> (full)
+//       Access: Public
+//  Description: This provides an indirect member access to the actual
+//               CycleData data.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *CycleDataStageReader<CycleDataType>::
+operator -> () const {
+  nassertr(_pointer != (const CycleDataType *)NULL, _cycler->cheat());
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageReader::Typecast pointer (full)
+//       Access: Public
+//  Description: This allows the CycleDataStageReader to be passed to any
+//               function that expects a const CycleDataType pointer.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataStageReader<CycleDataType>::
+operator const CycleDataType * () const {
+  nassertr(_pointer != (const CycleDataType *)NULL, _cycler->cheat());
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageReader::take_pointer (full)
+//       Access: Public
+//  Description: This is intended to be called only from
+//               CycleDataStageWriter when it elevates the pointer from
+//               read to write status.  This function returns the
+//               reader's pointer and relinquishes ownership of the
+//               pointer, rendering the reader invalid for future
+//               reads.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *CycleDataStageReader<CycleDataType>::
+take_pointer() {
+  const CycleDataType *pointer = _pointer;
+  _pointer = (CycleDataType *)NULL;
+  nassertr(pointer != (const CycleDataType *)NULL, _cycler->cheat());
+  return pointer;
+}
+
+#else  // !DO_PIPELINING
+// This is the trivial, do-nothing implementation.
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageReader::Constructor (trivial)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataStageReader<CycleDataType>::
+CycleDataStageReader(const PipelineCycler<CycleDataType> &cycler, int) {
+  _pointer = cycler.read();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageReader::Copy Constructor (trivial)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataStageReader<CycleDataType>::
+CycleDataStageReader(const CycleDataStageReader<CycleDataType> &copy) :
+  _pointer(copy._pointer)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageReader::Copy Assignment (trivial)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE void CycleDataStageReader<CycleDataType>::
+operator = (const CycleDataStageReader<CycleDataType> &copy) {
+  _pointer = copy._pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageReader::Destructor (trivial)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataStageReader<CycleDataType>::
+~CycleDataStageReader() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageReader::operator -> (trivial)
+//       Access: Public
+//  Description: This provides an indirect member access to the actual
+//               CycleData data.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *CycleDataStageReader<CycleDataType>::
+operator -> () const {
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageReader::Typecast pointer (trivial)
+//       Access: Public
+//  Description: This allows the CycleDataStageReader to be passed to any
+//               function that expects a const CycleDataType pointer.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataStageReader<CycleDataType>::
+operator const CycleDataType * () const {
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageReader::take_pointer (trivial)
+//       Access: Public
+//  Description: This is intended to be called only from
+//               CycleDataStageWriter when it elevates the pointer from
+//               read to write status.  This function returns the
+//               reader's pointer and relinquishes ownership of the
+//               pointer, rendering the reader invalid for future
+//               reads.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *CycleDataStageReader<CycleDataType>::
+take_pointer() {
+  return _pointer;
+}
+
+#endif  // DO_PIPELINING

+ 19 - 0
panda/src/putil/cycleDataStageReader.cxx

@@ -0,0 +1,19 @@
+// Filename: cycleDataStageReader.cxx
+// Created by:  drose (08Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "cycleDataStageReader.h"

+ 61 - 0
panda/src/putil/cycleDataStageReader.h

@@ -0,0 +1,61 @@
+// Filename: cycleDataStageReader.h
+// Created by:  drose (08Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CYCLEDATASTAGEREADER_H
+#define CYCLEDATASTAGEREADER_H
+
+#include "pandabase.h"
+
+#include "cycleData.h"
+#include "pipelineCycler.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : CycleDataStageReader
+// Description : This class is similar to CycleDataReader, except it
+//               allows reading from a particular stage of the
+//               pipeline.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+class CycleDataStageReader {
+public:
+  INLINE CycleDataStageReader(const PipelineCycler<CycleDataType> &cycler, int stage);
+  INLINE CycleDataStageReader(const CycleDataStageReader<CycleDataType> &copy);
+  INLINE void operator = (const CycleDataStageReader<CycleDataType> &copy);
+
+  INLINE ~CycleDataStageReader();
+
+  INLINE const CycleDataType *operator -> () const;
+  INLINE operator const CycleDataType * () const;
+
+  INLINE const CycleDataType *take_pointer();
+
+private:
+#ifdef DO_PIPELINING
+  // This is the data stored for a real pipelining implementation.
+  const PipelineCycler<CycleDataType> *_cycler;
+  const CycleDataType *_pointer;
+  int _stage;
+#else  // !DO_PIPELINING
+  // This is all we need for the trivial, do-nothing implementation.
+  const CycleDataType *_pointer;
+#endif  // DO_PIPELINING
+};
+
+#include "cycleDataStageReader.I"
+
+#endif

+ 222 - 0
panda/src/putil/cycleDataStageWriter.I

@@ -0,0 +1,222 @@
+// Filename: cycleDataStageWriter.I
+// Created by:  drose (06Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifdef DO_PIPELINING
+// This is the implementation for full support of pipelining (as well
+// as the sanity-check only implementation).
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageWriter::Constructor (full)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataStageWriter<CycleDataType>::
+CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int stage) :
+  _cycler(&cycler),
+  _stage(stage)
+{
+  _pointer = _cycler->write_stage(_stage);
+  nassertv(_pointer != (CycleDataType *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageWriter::Copy Constructor (full)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataStageWriter<CycleDataType>::
+CycleDataStageWriter(const CycleDataStageWriter<CycleDataType> &copy) :
+  _cycler(copy._cycler),
+  _pointer(copy._pointer),
+  _stage(copy._stage)
+{
+  nassertv(_pointer != (CycleDataType *)NULL);
+  _cycler->increment_write(_pointer);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageWriter::Copy Assigment (full)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE void CycleDataStageWriter<CycleDataType>::
+operator = (const CycleDataStageWriter<CycleDataType> &copy) {
+  _cycler = copy._cycler;
+  _pointer = copy._pointer;
+  _stage = copy._stage;
+
+  nassertv(_pointer != (CycleDataType *)NULL);
+  _cycler->increment_write(_pointer);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageWriter::Constructor (full)
+//       Access: Public
+//  Description: This flavor of the constructor elevates the pointer
+//               from the CycleDataStageReader from a read to a write
+//               pointer (and invalidates the reader).
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataStageWriter<CycleDataType>::
+CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int stage,
+                     CycleDataStageReader<CycleDataType> &take_from) :
+  _cycler(&cycler),
+  _stage(stage)
+{
+  _pointer = _cycler->elevate_read_stage(_stage, take_from.take_pointer());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageWriter::Destructor (full)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataStageWriter<CycleDataType>::
+~CycleDataStageWriter() {
+  if (_pointer != (CycleDataType *)NULL) {
+    _cycler->release_write_stage(_stage, _pointer);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageWriter::operator -> (full)
+//       Access: Public
+//  Description: This provides an indirect member access to the actual
+//               CycleData data.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *CycleDataStageWriter<CycleDataType>::
+operator -> () {
+  nassertr(_pointer != (CycleDataType *)NULL, _cycler->cheat());
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageWriter::operator -> (full)
+//       Access: Public
+//  Description: This provides an indirect member access to the actual
+//               CycleData data.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *CycleDataStageWriter<CycleDataType>::
+operator -> () const {
+  nassertr(_pointer != (CycleDataType *)NULL, _cycler->cheat());
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageWriter::Typecast pointer (full)
+//       Access: Public
+//  Description: This allows the CycleDataStageWriter to be passed to any
+//               function that expects a CycleDataType pointer.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataStageWriter<CycleDataType>::
+operator CycleDataType * () {
+  nassertr(_pointer != (CycleDataType *)NULL, _cycler->cheat());
+  return _pointer;
+}
+
+#else  // !DO_PIPELINING
+// This is the trivial, do-nothing implementation.
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageWriter::Constructor (trivial)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataStageWriter<CycleDataType>::
+CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int) {
+  _pointer = cycler.write();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageWriter::Copy Constructor (trivial)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataStageWriter<CycleDataType>::
+CycleDataStageWriter(const CycleDataStageWriter<CycleDataType> &copy) :
+  _pointer(copy._pointer)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageWriter::Copy Assigment (trivial)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE void CycleDataStageWriter<CycleDataType>::
+operator = (const CycleDataStageWriter<CycleDataType> &copy) {
+  _pointer = copy._pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageWriter::Destructor (trivial)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataStageWriter<CycleDataType>::
+~CycleDataStageWriter() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageWriter::operator -> (trivial)
+//       Access: Public
+//  Description: This provides an indirect member access to the actual
+//               CycleData data.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *CycleDataStageWriter<CycleDataType>::
+operator -> () {
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageWriter::operator -> (trivial)
+//       Access: Public
+//  Description: This provides an indirect member access to the actual
+//               CycleData data.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *CycleDataStageWriter<CycleDataType>::
+operator -> () const {
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageWriter::Typecast pointer (trivial)
+//       Access: Public
+//  Description: This allows the CycleDataStageWriter to be passed to any
+//               function that expects a CycleDataType pointer.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataStageWriter<CycleDataType>::
+operator CycleDataType * () {
+  return _pointer;
+}
+
+#endif  // DO_PIPELINING

+ 19 - 0
panda/src/putil/cycleDataStageWriter.cxx

@@ -0,0 +1,19 @@
+// Filename: cycleDataStageWriter.cxx
+// Created by:  drose (06Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "cycleDataStageWriter.h"

+ 68 - 0
panda/src/putil/cycleDataStageWriter.h

@@ -0,0 +1,68 @@
+// Filename: cycleDataStageWriter.h
+// Created by:  drose (06Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CYCLEDATASTAGEWRITER_H
+#define CYCLEDATASTAGEWRITER_H
+
+#include "pandabase.h"
+
+#include "cycleData.h"
+#include "pipelineCycler.h"
+#include "cycleDataStageReader.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : CycleDataStageWriter
+// Description : This class is similar to CycleDataWriter, except it
+//               allows writing to a particular stage of the pipeline.
+//               Usually this is used to implement writing directly to
+//               an upstream pipeline value, to recompute a cached
+//               value there (otherwise, the cached value would go
+//               away with the next pipeline cycle).
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+class CycleDataStageWriter {
+public:
+  INLINE CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int stage);
+  INLINE CycleDataStageWriter(const CycleDataStageWriter<CycleDataType> &copy);
+  INLINE void operator = (const CycleDataStageWriter<CycleDataType> &copy);
+
+  INLINE CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int stage,
+                              CycleDataStageReader<CycleDataType> &take_from);
+
+  INLINE ~CycleDataStageWriter();
+
+  INLINE CycleDataType *operator -> ();
+  INLINE const CycleDataType *operator -> () const;
+
+  INLINE operator CycleDataType * ();
+
+private:
+#ifdef DO_PIPELINING
+  // This is the data stored for a real pipelining implementation.
+  PipelineCycler<CycleDataType> *_cycler;
+  CycleDataType *_pointer;
+  int _stage;
+#else  // !DO_PIPELINING
+  // This is all we need for the trivial, do-nothing implementation.
+  CycleDataType *_pointer;
+#endif  // DO_PIPELINING
+};
+
+#include "cycleDataStageWriter.I"
+
+#endif

+ 37 - 0
panda/src/putil/cycleDataWriter.I

@@ -34,6 +34,26 @@ CycleDataWriter(PipelineCycler<CycleDataType> &cycler) :
   nassertv(_pointer != (CycleDataType *)NULL);
   nassertv(_pointer != (CycleDataType *)NULL);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::Constructor (full)
+//       Access: Public
+//  Description: This two-parameter constructor, with a bool parameter
+//               for the second parameter, automatically propagates
+//               the CycleData pointer upstream from the current
+//               stage, either stopping at the first pointer
+//               encountered that's different, going or all the way to
+//               stage 0, according to force_to_0.  See
+//               PipelineCycler::write_upstream().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataWriter<CycleDataType>::
+CycleDataWriter(PipelineCycler<CycleDataType> &cycler, bool force_to_0) :
+  _cycler(&cycler)
+{
+  _pointer = _cycler->write_upstream(force_to_0);
+  nassertv(_pointer != (CycleDataType *)NULL);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CycleDataWriter::Copy Constructor (full)
 //     Function: CycleDataWriter::Copy Constructor (full)
 //       Access: Public
 //       Access: Public
@@ -166,6 +186,23 @@ CycleDataWriter(PipelineCycler<CycleDataType> &cycler) {
   _pointer = cycler.write();
   _pointer = cycler.write();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::Constructor (full)
+//       Access: Public
+//  Description: This two-parameter constructor, with a bool parameter
+//               for the second parameter, automatically propagates
+//               the CycleData pointer upstream from the current
+//               stage, either stopping at the first pointer
+//               encountered that's different, going or all the way to
+//               stage 0, according to force_to_0.  See
+//               PipelineCycler::write_upstream().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataWriter<CycleDataType>::
+CycleDataWriter(PipelineCycler<CycleDataType> &cycler, bool) {
+  _pointer = _cycler->write();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CycleDataWriter::Copy Constructor (trivial)
 //     Function: CycleDataWriter::Copy Constructor (trivial)
 //       Access: Public
 //       Access: Public

+ 2 - 0
panda/src/putil/cycleDataWriter.h

@@ -23,6 +23,7 @@
 
 
 #include "cycleData.h"
 #include "cycleData.h"
 #include "pipelineCycler.h"
 #include "pipelineCycler.h"
+#include "cycleDataReader.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : CycleDataWriter
 //       Class : CycleDataWriter
@@ -40,6 +41,7 @@ template<class CycleDataType>
 class CycleDataWriter {
 class CycleDataWriter {
 public:
 public:
   INLINE CycleDataWriter(PipelineCycler<CycleDataType> &cycler);
   INLINE CycleDataWriter(PipelineCycler<CycleDataType> &cycler);
+  INLINE CycleDataWriter(PipelineCycler<CycleDataType> &cycler, bool force_to_0);
   INLINE CycleDataWriter(const CycleDataWriter<CycleDataType> &copy);
   INLINE CycleDataWriter(const CycleDataWriter<CycleDataType> &copy);
   INLINE void operator = (const CycleDataWriter<CycleDataType> &copy);
   INLINE void operator = (const CycleDataWriter<CycleDataType> &copy);
 
 

+ 63 - 0
panda/src/putil/cyclerHolder.I

@@ -0,0 +1,63 @@
+// Filename: cyclerHolder.I
+// Created by:  drose (09Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CyclerHolder::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE CyclerHolder::
+CyclerHolder(PipelineCyclerBase &cycler) {
+#ifdef DO_PIPELINING
+  _cycler = &cycler;
+  _cycler->lock();
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CyclerHolder::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE CyclerHolder::
+~CyclerHolder() {
+#ifdef DO_PIPELINING
+  _cycler->release();
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CyclerHolder::Copy Constructor
+//       Access: Private
+//  Description: Do not attempt to copy CyclerHolders.
+////////////////////////////////////////////////////////////////////
+INLINE CyclerHolder::
+CyclerHolder(const CyclerHolder &copy) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CyclerHolder::Copy Assignment Operator
+//       Access: Private
+//  Description: Do not attempt to copy CyclerHolders.
+////////////////////////////////////////////////////////////////////
+INLINE void CyclerHolder::
+operator = (const CyclerHolder &copy) {
+  nassertv(false);
+}

+ 19 - 0
panda/src/putil/cyclerHolder.cxx

@@ -0,0 +1,19 @@
+// Filename: cyclerHolder.cxx
+// Created by:  drose (09Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "cyclerHolder.h"

+ 48 - 0
panda/src/putil/cyclerHolder.h

@@ -0,0 +1,48 @@
+// Filename: cyclerHolder.h
+// Created by:  drose (09Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CYCLERHOLDER_H
+#define CYCLERHOLDER_H
+
+#include "pandabase.h"
+#include "pipelineCyclerBase.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : CyclerHolder
+// Description : A lightweight C++ object whose constructor calls
+//               lock() and whose destructor calls release() on a
+//               PipelineCyclerBase object.  This is similar to a
+//               MutexHolder.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS CyclerHolder {
+public:
+  INLINE CyclerHolder(PipelineCyclerBase &cycler);
+  INLINE ~CyclerHolder();
+private:
+  INLINE CyclerHolder(const CyclerHolder &copy);
+  INLINE void operator = (const CyclerHolder &copy);
+
+private:
+#ifdef DO_PIPELINING
+  PipelineCyclerBase *_cycler;
+#endif
+};
+
+#include "cyclerHolder.I"
+
+#endif

+ 11 - 0
panda/src/putil/pipeline.I

@@ -40,3 +40,14 @@ INLINE void Pipeline::
 set_min_stages(int min_stages) {
 set_min_stages(int min_stages) {
   set_num_stages(max(min_stages, get_num_stages()));
   set_num_stages(max(min_stages, get_num_stages()));
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: Pipeline::get_num_stages
+//       Access: Public
+//  Description: Returns the number of stages required for the
+//               pipeline.
+////////////////////////////////////////////////////////////////////
+INLINE int Pipeline::
+get_num_stages() const {
+  return _num_stages;
+}

+ 0 - 11
panda/src/putil/pipeline.cxx

@@ -172,17 +172,6 @@ set_num_stages(int num_stages) {
 #endif  // DO_PIPELINING && HAVE_THREADS
 #endif  // DO_PIPELINING && HAVE_THREADS
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: Pipeline::get_num_stages
-//       Access: Public
-//  Description: Returns the number of stages required for the
-//               pipeline.
-////////////////////////////////////////////////////////////////////
-int Pipeline::
-get_num_stages() const {
-  return _num_stages;
-}
-
 
 
 #if defined(DO_PIPELINING) && defined(HAVE_THREADS)
 #if defined(DO_PIPELINING) && defined(HAVE_THREADS)
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/putil/pipeline.h

@@ -50,7 +50,7 @@ public:
 
 
   void set_num_stages(int num_stages);
   void set_num_stages(int num_stages);
   INLINE void set_min_stages(int min_stages);
   INLINE void set_min_stages(int min_stages);
-  int get_num_stages() const;
+  INLINE int get_num_stages() const;
 
 
 #if defined(DO_PIPELINING) && defined(HAVE_THREADS)
 #if defined(DO_PIPELINING) && defined(HAVE_THREADS)
   void add_cycler(PipelineCyclerTrueImpl *cycler);
   void add_cycler(PipelineCyclerTrueImpl *cycler);

+ 44 - 0
panda/src/putil/pipelineCycler.I

@@ -67,6 +67,17 @@ read() const {
   return (const CycleDataType *)PipelineCyclerBase::read();
   return (const CycleDataType *)PipelineCyclerBase::read();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::read (dummy or true)
+//       Access: Public
+//  Description: See PipelineCyclerBase::read_stage().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *PipelineCycler<CycleDataType>::
+read_stage(int n) const {
+  return (const CycleDataType *)PipelineCyclerBase::read_stage(n);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCycler::write (dummy or true)
 //     Function: PipelineCycler::write (dummy or true)
 //       Access: Public
 //       Access: Public
@@ -89,6 +100,28 @@ elevate_read(const CycleDataType *pointer) {
   return (CycleDataType *)PipelineCyclerBase::elevate_read(pointer);
   return (CycleDataType *)PipelineCyclerBase::elevate_read(pointer);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::elevate_read_stage (dummy or true)
+//       Access: Public
+//  Description: See PipelineCyclerBase::elevate_read_stage().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+elevate_read_stage(int n, const CycleDataType *pointer) {
+  return (CycleDataType *)PipelineCyclerBase::elevate_read_stage(n, pointer);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::write_upstream (dummy or true)
+//       Access: Public
+//  Description: See PipelineCyclerBase::write_upstream().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+write_upstream(bool force_to_0) {
+  return (CycleDataType *)PipelineCyclerBase::write_upstream(force_to_0);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCycler::write_stage (dummy or true)
 //     Function: PipelineCycler::write_stage (dummy or true)
 //       Access: Public
 //       Access: Public
@@ -190,6 +223,17 @@ elevate_read(const CycleDataType *) {
   return &_typed_data;
   return &_typed_data;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::write_upstream (trivial)
+//       Access: Public
+//  Description: See PipelineCyclerBase::write_upstream().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+write_upstream(bool) {
+  return &_typed_data;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCycler::write_stage (trivial)
 //     Function: PipelineCycler::write_stage (trivial)
 //       Access: Public
 //       Access: Public

+ 72 - 1
panda/src/putil/pipelineCycler.h

@@ -20,8 +20,8 @@
 #define PIPELINECYCLER_H
 #define PIPELINECYCLER_H
 
 
 #include "pandabase.h"
 #include "pandabase.h"
-
 #include "pipelineCyclerBase.h"
 #include "pipelineCyclerBase.h"
+#include "cyclerHolder.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : PipelineCycler
 //       Class : PipelineCycler
@@ -63,8 +63,11 @@ public:
   INLINE void operator = (const PipelineCycler<CycleDataType> &copy);
   INLINE void operator = (const PipelineCycler<CycleDataType> &copy);
 
 
   INLINE const CycleDataType *read() const;
   INLINE const CycleDataType *read() const;
+  INLINE const CycleDataType *read_stage(int n) const;
   INLINE CycleDataType *write();
   INLINE CycleDataType *write();
   INLINE CycleDataType *elevate_read(const CycleDataType *pointer);
   INLINE CycleDataType *elevate_read(const CycleDataType *pointer);
+  INLINE CycleDataType *elevate_read_stage(int n, const CycleDataType *pointer);
+  INLINE CycleDataType *write_upstream(bool force_to_0);
   INLINE CycleDataType *write_stage(int n);
   INLINE CycleDataType *write_stage(int n);
 
 
   INLINE CycleDataType *cheat() const;
   INLINE CycleDataType *cheat() const;
@@ -77,6 +80,74 @@ private:
 #endif  // !DO_PIPELINING
 #endif  // !DO_PIPELINING
 };
 };
 
 
+// These macros are handy for iterating through the set of pipeline
+// stages.  They're particularly useful for updating cache values
+// upstream of the current stage, or for removing bad pointers from
+// all stages of the pipeline.  In each case, the variable
+// pipeline_stage is defined within the loop to be the current stage
+// of the pipeline traversed by the loop.
+#ifdef DO_PIPELINING
+
+// Iterates through all of the pipeline stages upstream of the current
+// stage, but not including the current stage.
+#define OPEN_ITERATE_UPSTREAM_ONLY(cycler) {                        \
+    CyclerHolder cholder(cycler);                                   \
+    int pipeline_stage;                                             \
+    for (pipeline_stage = Thread::get_current_pipeline_stage() - 1; \
+         pipeline_stage >= 0;                                       \
+         --pipeline_stage)
+
+#define CLOSE_ITERATE_UPSTREAM_ONLY(cycler)     \
+  }
+
+// Iterates through all of the pipeline stages upstream of the current
+// stage, and including the current stage.
+#define OPEN_ITERATE_CURRENT_AND_UPSTREAM(cycler) {                 \
+    CyclerHolder cholder(cycler);                                   \
+    int pipeline_stage;                                             \
+    for (pipeline_stage = Thread::get_current_pipeline_stage() ;    \
+         pipeline_stage >= 0;                                       \
+         --pipeline_stage)
+
+#define CLOSE_ITERATE_CURRENT_AND_UPSTREAM(cycler)      \
+  }
+
+// Iterates through all of the pipeline stages.
+#define OPEN_ITERATE_ALL_STAGES(cycler) {                           \
+    int pipeline_stage;                                             \
+    for (pipeline_stage = (cycler).get_num_stages() - 1;            \
+         pipeline_stage >= 0;                                       \
+         --pipeline_stage)
+
+#define CLOSE_ITERATE_ALL_STAGES(cycler)        \
+  }
+
+#else  // DO_PIPELINING
+
+// These are trivial implementations of the above macros, defined when
+// pipelining is not enabled, that simply operate on stage 0 without
+// bothering to create a for loop.
+#define OPEN_ITERATE_UPSTREAM_ONLY(cycler)      \
+  if (false) {                                  \
+    const int pipeline_stage = -1;                   
+
+#define CLOSE_ITERATE_UPSTREAM_ONLY(cycler)     \
+  }
+
+#define OPEN_ITERATE_CURRENT_AND_UPSTREAM(cycler) {                     \
+    const int pipeline_stage = 0;                                       \
+    
+#define CLOSE_ITERATE_CURRENT_AND_UPSTREAM(cycler)      \
+  }
+
+#define OPEN_ITERATE_ALL_STAGES(cycler) {                               \
+    const int pipeline_stage = 0;                                       \
+    
+#define CLOSE_ITERATE_ALL_STAGES(cycler)        \
+  }
+
+#endif  // DO_PIPELINING
+
 #include "pipelineCycler.I"
 #include "pipelineCycler.I"
 
 
 #endif
 #endif

+ 132 - 28
panda/src/putil/pipelineCyclerDummyImpl.I

@@ -18,7 +18,7 @@
 
 
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerDummyImpl::Constructor (sanity-check)
+//     Function: PipelineCyclerDummyImpl::Constructor
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -27,7 +27,8 @@ PipelineCyclerDummyImpl(CycleData *initial_data, Pipeline *pipeline) :
   _data(initial_data),
   _data(initial_data),
   _pipeline(pipeline),
   _pipeline(pipeline),
   _read_count(0),
   _read_count(0),
-  _write_count(0)
+  _write_count(0),
+  _locked(false)
 {
 {
   if (_pipeline == (Pipeline *)NULL) {
   if (_pipeline == (Pipeline *)NULL) {
     _pipeline = Pipeline::get_render_pipeline();
     _pipeline = Pipeline::get_render_pipeline();
@@ -35,7 +36,7 @@ PipelineCyclerDummyImpl(CycleData *initial_data, Pipeline *pipeline) :
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerDummyImpl::Copy Constructor (sanity-check)
+//     Function: PipelineCyclerDummyImpl::Copy Constructor
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -44,12 +45,13 @@ PipelineCyclerDummyImpl(const PipelineCyclerDummyImpl &copy) :
   _data(copy._data->make_copy()),
   _data(copy._data->make_copy()),
   _pipeline(copy._pipeline),
   _pipeline(copy._pipeline),
   _read_count(0),
   _read_count(0),
-  _write_count(0)
+  _write_count(0),
+  _locked(false)
 {
 {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerDummyImpl::Copy Assignment (sanity-check)
+//     Function: PipelineCyclerDummyImpl::Copy Assignment
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -61,17 +63,42 @@ operator = (const PipelineCyclerDummyImpl &copy) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerDummyImpl::Destructor (sanity-check)
+//     Function: PipelineCyclerDummyImpl::Destructor
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PipelineCyclerDummyImpl::
 INLINE PipelineCyclerDummyImpl::
 ~PipelineCyclerDummyImpl() {
 ~PipelineCyclerDummyImpl() {
-  nassertv(_read_count == 0 && _write_count == 0);
+  nassertv(_read_count == 0 && _write_count == 0 && !_locked);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::lock
+//       Access: Public
+//  Description: Grabs an overall lock on the cycler.  Release it with
+//               a call to release().  This lock should be held while
+//               walking the list of stages.
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerDummyImpl::
+lock() {
+  nassertv(!_locked);
+  _locked = true;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerDummyImpl::read (sanity-check)
+//     Function: PipelineCyclerDummyImpl::release
+//       Access: Public
+//  Description: Release the overall lock on the cycler that was
+//               grabbed via lock().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerDummyImpl::
+release() {
+  nassertv(_locked);
+  _locked = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::read
 //       Access: Public
 //       Access: Public
 //  Description: Returns a const CycleData pointer, filled with the
 //  Description: Returns a const CycleData pointer, filled with the
 //               data for the current stage of the pipeline as seen by
 //               data for the current stage of the pipeline as seen by
@@ -93,7 +120,7 @@ read() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerDummyImpl::increment_read (sanity-check)
+//     Function: PipelineCyclerDummyImpl::increment_read
 //       Access: Public
 //       Access: Public
 //  Description: Increments the count on a pointer previously
 //  Description: Increments the count on a pointer previously
 //               retrieved by read(); now the pointer will need to be
 //               retrieved by read(); now the pointer will need to be
@@ -109,7 +136,7 @@ increment_read(const CycleData *pointer) const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerDummyImpl::release_read (sanity-check)
+//     Function: PipelineCyclerDummyImpl::release_read
 //       Access: Public
 //       Access: Public
 //  Description: Releases a pointer previously obtained via a call to
 //  Description: Releases a pointer previously obtained via a call to
 //               read().
 //               read().
@@ -124,7 +151,7 @@ release_read(const CycleData *pointer) const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerDummyImpl::write (sanity-check)
+//     Function: PipelineCyclerDummyImpl::write
 //       Access: Public
 //       Access: Public
 //  Description: Returns a non-const CycleData pointer, filled with a
 //  Description: Returns a non-const CycleData pointer, filled with a
 //               unique copy of the data for the current stage of the
 //               unique copy of the data for the current stage of the
@@ -154,7 +181,7 @@ write() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerDummyImpl::elevate_read (sanity-check)
+//     Function: PipelineCyclerDummyImpl::elevate_read
 //       Access: Public
 //       Access: Public
 //  Description: Elevates a currently-held read pointer into a write
 //  Description: Elevates a currently-held read pointer into a write
 //               pointer.  This may or may not change the value of the
 //               pointer.  This may or may not change the value of the
@@ -169,7 +196,7 @@ elevate_read(const CycleData *pointer) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerDummyImpl::release_write (sanity-check)
+//     Function: PipelineCyclerDummyImpl::release_write
 //       Access: Public
 //       Access: Public
 //  Description: Releases a pointer previously obtained via a call to
 //  Description: Releases a pointer previously obtained via a call to
 //               write().
 //               write().
@@ -182,7 +209,7 @@ release_write(CycleData *pointer) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerDummyImpl::get_num_stages (sanity-check)
+//     Function: PipelineCyclerDummyImpl::get_num_stages
 //       Access: Public
 //       Access: Public
 //  Description: Returns the number of stages in the pipeline.
 //  Description: Returns the number of stages in the pipeline.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -192,20 +219,81 @@ get_num_stages() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerDummyImpl::is_stage_unique (sanity-check)
+//     Function: PipelineCyclerDummyImpl::read_stage
+//       Access: Public
+//  Description: Returns a const CycleData pointer, filled with the
+//               data for the indicated stage of the pipeline.  This
+//               pointer should eventually be released by calling
+//               release_read_stage().
+//
+//               There should be no outstanding write pointers on the
+//               data when this function is called.
+////////////////////////////////////////////////////////////////////
+INLINE const CycleData *PipelineCyclerDummyImpl::
+read_stage(int n) const {
+  // This function isn't truly const, but it doesn't change the data
+  // in any meaningful way, so we pretend it is.
+  nassertr(n == 0, NULL);
+  ((PipelineCyclerDummyImpl *)this)->_read_count++;
+
+  // It's not an error to grab a read pointer while someone else holds
+  // a read or a write pointer.
+  return _data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::release_read_stage
 //       Access: Public
 //       Access: Public
-//  Description: Returns true if the nth stage is a different pointer
-//               than the previous stage, or false if its pointer is
-//               shared with the previous one.
-////////////////////////////////////////////////////////////////////
-INLINE bool PipelineCyclerDummyImpl::
-is_stage_unique(int n) const {
-  nassertr(n == 0, false);
-  return true;
+//  Description: Releases a pointer previously obtained via a call to
+//               read_stage().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerDummyImpl::
+release_read_stage(int n, const CycleData *pointer) const {
+  // This function isn't truly const, but it doesn't change the data
+  // in any meaningful way, so we pretend it is.
+  nassertv(n == 0);
+  nassertv(pointer == _data);
+  nassertv(_read_count > 0);
+  ((PipelineCyclerDummyImpl *)this)->_read_count--;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerDummyImpl::write_stage (sanity-check)
+//     Function: PipelineCyclerDummyImpl::write_upstream
+//       Access: Public
+//  Description: This special variant on write() will automatically
+//               propagate changes back to upstream pipeline stages.
+//               If force_to_0 is false, then it propagates back only
+//               as long as the CycleData pointers are equivalent,
+//               guaranteeing that it does not modify upstream data
+//               (other than the modification that will be performed
+//               by the code that returns this pointer).  This is
+//               particularly appropriate for minor updates, where it
+//               doesn't matter much if the update is lost, such as
+//               storing a cached value.
+//
+//               If force_to_0 is dummy, then the CycleData pointer for
+//               the current pipeline stage is propagated all the way
+//               back up to stage 0; after this call, there will be
+//               only one CycleData pointer that is duplicated in all
+//               stages between stage 0 and the current stage.  This
+//               may undo some recent changes that were made
+//               independently at pipeline stage 0 (or any other
+//               upstream stage).  However, it guarantees that the
+//               change that is to be applied at this pipeline stage
+//               will stick.  This is slightly dangerous because of
+//               the risk of losing upstream changes; generally, this
+//               should only be done when you are confident that there
+//               are no upstream changes to be lost (for instance, for
+//               an object that has been recently created).
+////////////////////////////////////////////////////////////////////
+CycleData *PipelineCyclerDummyImpl::
+write_upstream(bool) {
+  _write_count++;
+  return _data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::write_stage
 //       Access: Public
 //       Access: Public
 //  Description: Returns a pointer suitable for writing to the nth
 //  Description: Returns a pointer suitable for writing to the nth
 //               stage of the pipeline.  This is for special
 //               stage of the pipeline.  This is for special
@@ -222,7 +310,23 @@ write_stage(int n) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerDummyImpl::release_write_stage (sanity-check)
+//     Function: PipelineCyclerDummyImpl::elevate_read_stage
+//       Access: Public
+//  Description: Elevates a currently-held read pointer into a write
+//               pointer.  This may or may not change the value of the
+//               pointer.  It is only valid to do this if this is the
+//               only currently-outstanding read pointer on the
+//               current stage.
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerDummyImpl::
+elevate_read_stage(int n, const CycleData *pointer) {
+  nassertr(n == 0, NULL);
+  release_read(pointer);
+  return write();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::release_write_stage
 //       Access: Public
 //       Access: Public
 //  Description: Releases a pointer previously obtained via a call to
 //  Description: Releases a pointer previously obtained via a call to
 //               write_stage().
 //               write_stage().
@@ -235,7 +339,7 @@ release_write_stage(int n, CycleData *pointer) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerDummyImpl::cheat (sanity-check)
+//     Function: PipelineCyclerDummyImpl::cheat
 //       Access: Public
 //       Access: Public
 //  Description: Returns a pointer without counting it.  This is only
 //  Description: Returns a pointer without counting it.  This is only
 //               intended for use as the return value for certain
 //               intended for use as the return value for certain
@@ -250,7 +354,7 @@ cheat() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerDummyImpl::get_read_count (sanity-check)
+//     Function: PipelineCyclerDummyImpl::get_read_count
 //       Access: Public
 //       Access: Public
 //  Description: Returns the number of handles currently outstanding
 //  Description: Returns the number of handles currently outstanding
 //               to read the current stage of the data.  This should
 //               to read the current stage of the data.  This should
@@ -262,7 +366,7 @@ get_read_count() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerDummyImpl::get_write_count (sanity-check)
+//     Function: PipelineCyclerDummyImpl::get_write_count
 //       Access: Public
 //       Access: Public
 //  Description: Returns the number of handles currently outstanding
 //  Description: Returns the number of handles currently outstanding
 //               to read the current stage of the data.  This will
 //               to read the current stage of the data.  This will

+ 8 - 1
panda/src/putil/pipelineCyclerDummyImpl.h

@@ -52,6 +52,9 @@ public:
   INLINE void operator = (const PipelineCyclerDummyImpl &copy);
   INLINE void operator = (const PipelineCyclerDummyImpl &copy);
   INLINE ~PipelineCyclerDummyImpl();
   INLINE ~PipelineCyclerDummyImpl();
 
 
+  INLINE void lock();
+  INLINE void release();
+
   INLINE const CycleData *read() const;
   INLINE const CycleData *read() const;
   INLINE void increment_read(const CycleData *pointer) const;
   INLINE void increment_read(const CycleData *pointer) const;
   INLINE void release_read(const CycleData *pointer) const;
   INLINE void release_read(const CycleData *pointer) const;
@@ -61,8 +64,11 @@ public:
   INLINE void release_write(CycleData *pointer);
   INLINE void release_write(CycleData *pointer);
 
 
   INLINE int get_num_stages();
   INLINE int get_num_stages();
-  INLINE bool is_stage_unique(int n) const;
+  INLINE const CycleData *read_stage(int n) const;
+  INLINE void release_read_stage(int n, const CycleData *pointer) const;
+  INLINE CycleData *write_upstream(bool force_to_0);
   INLINE CycleData *write_stage(int n);
   INLINE CycleData *write_stage(int n);
+  INLINE CycleData *elevate_read_stage(int n, const CycleData *pointer);
   INLINE void release_write_stage(int n, CycleData *pointer);
   INLINE void release_write_stage(int n, CycleData *pointer);
 
 
   INLINE CycleData *cheat() const;
   INLINE CycleData *cheat() const;
@@ -73,6 +79,7 @@ private:
   PT(CycleData) _data;
   PT(CycleData) _data;
   Pipeline *_pipeline;
   Pipeline *_pipeline;
   short _read_count, _write_count;
   short _read_count, _write_count;
+  short _locked;
 };
 };
 
 
 #include "pipelineCyclerDummyImpl.I"
 #include "pipelineCyclerDummyImpl.I"

+ 119 - 23
panda/src/putil/pipelineCyclerTrivialImpl.I

@@ -18,7 +18,7 @@
 
 
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerTrivialImpl::Constructor (trivial)
+//     Function: PipelineCyclerTrivialImpl::Constructor
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -41,7 +41,7 @@ PipelineCyclerTrivialImpl(CycleData *initial_data, Pipeline *) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerTrivialImpl::Copy Constructor (trivial)
+//     Function: PipelineCyclerTrivialImpl::Copy Constructor
 //       Access: Private
 //       Access: Private
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -55,7 +55,7 @@ PipelineCyclerTrivialImpl(const PipelineCyclerTrivialImpl &) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerTrivialImpl::Copy Assignment (trivial)
+//     Function: PipelineCyclerTrivialImpl::Copy Assignment
 //       Access: Private
 //       Access: Private
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -69,7 +69,7 @@ operator = (const PipelineCyclerTrivialImpl &) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerTrivialImpl::Destructor (trivial)
+//     Function: PipelineCyclerTrivialImpl::Destructor
 //       Access: Public
 //       Access: Public
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -78,7 +78,28 @@ INLINE PipelineCyclerTrivialImpl::
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerTrivialImpl::read (trivial)
+//     Function: PipelineCyclerTrivialImpl::lock
+//       Access: Public
+//  Description: Grabs an overall lock on the cycler.  Release it with
+//               a call to release().  This lock should be held while
+//               walking the list of stages.
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrivialImpl::
+lock() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::release
+//       Access: Public
+//  Description: Release the overall lock on the cycler that was
+//               grabbed via lock().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrivialImpl::
+release() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::read
 //       Access: Public
 //       Access: Public
 //  Description: Returns a const CycleData pointer, filled with the
 //  Description: Returns a const CycleData pointer, filled with the
 //               data for the current stage of the pipeline as seen by
 //               data for the current stage of the pipeline as seen by
@@ -95,7 +116,7 @@ read() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerTrivialImpl::increment_read (trivial)
+//     Function: PipelineCyclerTrivialImpl::increment_read
 //       Access: Public
 //       Access: Public
 //  Description: Increments the count on a pointer previously
 //  Description: Increments the count on a pointer previously
 //               retrieved by read(); now the pointer will need to be
 //               retrieved by read(); now the pointer will need to be
@@ -106,7 +127,7 @@ increment_read(const CycleData *) const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerTrivialImpl::release_read (trivial)
+//     Function: PipelineCyclerTrivialImpl::release_read
 //       Access: Public
 //       Access: Public
 //  Description: Releases a pointer previously obtained via a call to
 //  Description: Releases a pointer previously obtained via a call to
 //               read().
 //               read().
@@ -116,7 +137,7 @@ release_read(const CycleData *) const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerTrivialImpl::write (trivial)
+//     Function: PipelineCyclerTrivialImpl::write
 //       Access: Public
 //       Access: Public
 //  Description: Returns a non-const CycleData pointer, filled with a
 //  Description: Returns a non-const CycleData pointer, filled with a
 //               unique copy of the data for the current stage of the
 //               unique copy of the data for the current stage of the
@@ -141,7 +162,7 @@ write() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerTrivialImpl::elevate_read (trivial)
+//     Function: PipelineCyclerTrivialImpl::elevate_read
 //       Access: Public
 //       Access: Public
 //  Description: Elevates a currently-held read pointer into a write
 //  Description: Elevates a currently-held read pointer into a write
 //               pointer.  This may or may not change the value of the
 //               pointer.  This may or may not change the value of the
@@ -159,7 +180,7 @@ elevate_read(const CycleData *) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerTrivialImpl::release_write (trivial)
+//     Function: PipelineCyclerTrivialImpl::release_write
 //       Access: Public
 //       Access: Public
 //  Description: Releases a pointer previously obtained via a call to
 //  Description: Releases a pointer previously obtained via a call to
 //               write().
 //               write().
@@ -169,7 +190,7 @@ release_write(CycleData *) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerTrivialImpl::get_num_stages (trivial)
+//     Function: PipelineCyclerTrivialImpl::get_num_stages
 //       Access: Public
 //       Access: Public
 //  Description: Returns the number of stages in the pipeline.
 //  Description: Returns the number of stages in the pipeline.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -179,19 +200,72 @@ get_num_stages() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerTrivialImpl::is_stage_unique (trivial)
+//     Function: PipelineCyclerTrivialImpl::read_stage
+//       Access: Public
+//  Description: Returns a const CycleData pointer, filled with the
+//               data for the indicated pipeline stage.  This pointer
+//               should eventually be released by calling
+//               release_read().
+////////////////////////////////////////////////////////////////////
+INLINE const CycleData *PipelineCyclerTrivialImpl::
+read_stage(int) const {
+#ifdef SIMPLE_STRUCT_POINTERS
+  return (const CycleData *)this;
+#else
+  return _data;
+#endif  // SIMPLE_STRUCT_POINTERS
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::release_read_stage
 //       Access: Public
 //       Access: Public
-//  Description: Returns true if the nth stage is a different pointer
-//               than the previous stage, or false if its pointer is
-//               shared with the previous one.
+//  Description: Releases a pointer previously obtained via a call to
+//               read_stage().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrivialImpl::
+release_read_stage(int, const CycleData *) const {
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE bool PipelineCyclerTrivialImpl::
-is_stage_unique(int n) const {
-  return true;
+//     Function: PipelineCyclerTrivialImpl::write_upstream
+//       Access: Public
+//  Description: This special variant on write() will automatically
+//               propagate changes back to upstream pipeline stages.
+//               If force_to_0 is false, then it propagates back only
+//               as long as the CycleData pointers are equivalent,
+//               guaranteeing that it does not modify upstream data
+//               (other than the modification that will be performed
+//               by the code that returns this pointer).  This is
+//               particularly appropriate for minor updates, where it
+//               doesn't matter much if the update is lost, such as
+//               storing a cached value.
+//
+//               If force_to_0 is trivial, then the CycleData pointer for
+//               the current pipeline stage is propagated all the way
+//               back up to stage 0; after this call, there will be
+//               only one CycleData pointer that is duplicated in all
+//               stages between stage 0 and the current stage.  This
+//               may undo some recent changes that were made
+//               independently at pipeline stage 0 (or any other
+//               upstream stage).  However, it guarantees that the
+//               change that is to be applied at this pipeline stage
+//               will stick.  This is slightly dangerous because of
+//               the risk of losing upstream changes; generally, this
+//               should only be done when you are confident that there
+//               are no upstream changes to be lost (for instance, for
+//               an object that has been recently created).
+////////////////////////////////////////////////////////////////////
+CycleData *PipelineCyclerTrivialImpl::
+write_upstream(bool) {
+#ifdef SIMPLE_STRUCT_POINTERS
+  return (CycleData *)this;
+#else
+  return _data;
+#endif  // SIMPLE_STRUCT_POINTERS
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerTrivialImpl::write_stage (trivial)
+//     Function: PipelineCyclerTrivialImpl::write_stage
 //       Access: Public
 //       Access: Public
 //  Description: Returns a pointer suitable for writing to the nth
 //  Description: Returns a pointer suitable for writing to the nth
 //               stage of the pipeline.  This is for special
 //               stage of the pipeline.  This is for special
@@ -202,11 +276,33 @@ is_stage_unique(int n) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE CycleData *PipelineCyclerTrivialImpl::
 INLINE CycleData *PipelineCyclerTrivialImpl::
 write_stage(int) {
 write_stage(int) {
+#ifdef SIMPLE_STRUCT_POINTERS
+  return (CycleData *)this;
+#else
+  return _data;
+#endif  // SIMPLE_STRUCT_POINTERS
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::elevate_read_stage
+//       Access: Public
+//  Description: Elevates a currently-held read pointer into a write
+//               pointer.  This may or may not change the value of the
+//               pointer.  It is only valid to do this if this is the
+//               only currently-outstanding read pointer on the
+//               current stage.
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerTrivialImpl::
+elevate_read_stage(int, const CycleData *) {
+#ifdef SIMPLE_STRUCT_POINTERS
   return (CycleData *)this;
   return (CycleData *)this;
+#else
+  return _data;
+#endif  // SIMPLE_STRUCT_POINTERS
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerTrivialImpl::release_write_stage (trivial)
+//     Function: PipelineCyclerTrivialImpl::release_write_stage
 //       Access: Public
 //       Access: Public
 //  Description: Releases a pointer previously obtained via a call to
 //  Description: Releases a pointer previously obtained via a call to
 //               write_stage().
 //               write_stage().
@@ -216,7 +312,7 @@ release_write_stage(int, CycleData *) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerTrivialImpl::cheat (trivial)
+//     Function: PipelineCyclerTrivialImpl::cheat
 //       Access: Public
 //       Access: Public
 //  Description: Returns a pointer without counting it.  This is only
 //  Description: Returns a pointer without counting it.  This is only
 //               intended for use as the return value for certain
 //               intended for use as the return value for certain
@@ -235,7 +331,7 @@ cheat() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerTrivialImpl::get_read_count (trivial)
+//     Function: PipelineCyclerTrivialImpl::get_read_count
 //       Access: Public
 //       Access: Public
 //  Description: Returns the number of handles currently outstanding
 //  Description: Returns the number of handles currently outstanding
 //               to read the current stage of the data.  This should
 //               to read the current stage of the data.  This should
@@ -247,7 +343,7 @@ get_read_count() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerTrivialImpl::get_write_count (trivial)
+//     Function: PipelineCyclerTrivialImpl::get_write_count
 //       Access: Public
 //       Access: Public
 //  Description: Returns the number of handles currently outstanding
 //  Description: Returns the number of handles currently outstanding
 //               to read the current stage of the data.  This will
 //               to read the current stage of the data.  This will

+ 7 - 1
panda/src/putil/pipelineCyclerTrivialImpl.h

@@ -55,6 +55,9 @@ private:
 public:
 public:
   INLINE ~PipelineCyclerTrivialImpl();
   INLINE ~PipelineCyclerTrivialImpl();
 
 
+  INLINE void lock();
+  INLINE void release();
+
   INLINE const CycleData *read() const;
   INLINE const CycleData *read() const;
   INLINE void increment_read(const CycleData *pointer) const;
   INLINE void increment_read(const CycleData *pointer) const;
   INLINE void release_read(const CycleData *pointer) const;
   INLINE void release_read(const CycleData *pointer) const;
@@ -64,8 +67,11 @@ public:
   INLINE void release_write(CycleData *pointer);
   INLINE void release_write(CycleData *pointer);
 
 
   INLINE int get_num_stages();
   INLINE int get_num_stages();
-  INLINE bool is_stage_unique(int n) const;
+  INLINE const CycleData *read_stage(int n) const;
+  INLINE void release_read_stage(int n, const CycleData *pointer) const;
+  INLINE CycleData *write_upstream(bool force_to_0);
   INLINE CycleData *write_stage(int n);
   INLINE CycleData *write_stage(int n);
+  INLINE CycleData *elevate_read_stage(int n, const CycleData *pointer);
   INLINE void release_write_stage(int n, CycleData *pointer);
   INLINE void release_write_stage(int n, CycleData *pointer);
 
 
   INLINE CycleData *cheat() const;
   INLINE CycleData *cheat() const;

+ 83 - 19
panda/src/putil/pipelineCyclerTrueImpl.I

@@ -17,6 +17,29 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::lock
+//       Access: Public
+//  Description: Grabs an overall lock on the cycler.  Release it with
+//               a call to release().  This lock should be held while
+//               walking the list of stages.
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrueImpl::
+lock() {
+  _lock.lock();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::release
+//       Access: Public
+//  Description: Release the overall lock on the cycler that was
+//               grabbed via lock().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrueImpl::
+release() {
+  _lock.release();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCyclerTrueImpl::read
 //     Function: PipelineCyclerTrueImpl::read
 //       Access: Public
 //       Access: Public
@@ -30,7 +53,7 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const CycleData *PipelineCyclerTrueImpl::
 INLINE const CycleData *PipelineCyclerTrueImpl::
 read() const {
 read() const {
-  int pipeline_stage = Thread::get_current_thread()->get_pipeline_stage();
+  int pipeline_stage = Thread::get_current_pipeline_stage();
   nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
   nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
   _lock.lock();
   _lock.lock();
   return _data[pipeline_stage];
   return _data[pipeline_stage];
@@ -46,7 +69,7 @@ read() const {
 INLINE void PipelineCyclerTrueImpl::
 INLINE void PipelineCyclerTrueImpl::
 increment_read(const CycleData *pointer) const {
 increment_read(const CycleData *pointer) const {
 #ifndef NDEBUG
 #ifndef NDEBUG
-  int pipeline_stage = Thread::get_current_thread()->get_pipeline_stage();
+  int pipeline_stage = Thread::get_current_pipeline_stage();
   nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages);
   nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages);
   nassertv(_data[pipeline_stage] == pointer);
   nassertv(_data[pipeline_stage] == pointer);
 #endif
 #endif
@@ -62,7 +85,7 @@ increment_read(const CycleData *pointer) const {
 INLINE void PipelineCyclerTrueImpl::
 INLINE void PipelineCyclerTrueImpl::
 release_read(const CycleData *pointer) const {
 release_read(const CycleData *pointer) const {
 #ifndef NDEBUG
 #ifndef NDEBUG
-  int pipeline_stage = Thread::get_current_thread()->get_pipeline_stage();
+  int pipeline_stage = Thread::get_current_pipeline_stage();
   nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages);
   nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages);
   nassertv(_data[pipeline_stage] == pointer);
   nassertv(_data[pipeline_stage] == pointer);
 #endif
 #endif
@@ -87,7 +110,7 @@ release_read(const CycleData *pointer) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE CycleData *PipelineCyclerTrueImpl::
 INLINE CycleData *PipelineCyclerTrueImpl::
 write() {
 write() {
-  int pipeline_stage = Thread::get_current_thread()->get_pipeline_stage();
+  int pipeline_stage = Thread::get_current_pipeline_stage();
   return write_stage(pipeline_stage);
   return write_stage(pipeline_stage);
 }
 }
 
 
@@ -102,6 +125,11 @@ write() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE CycleData *PipelineCyclerTrueImpl::
 INLINE CycleData *PipelineCyclerTrueImpl::
 elevate_read(const CycleData *pointer) {
 elevate_read(const CycleData *pointer) {
+#ifndef NDEBUG
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
+  nassertr(_data[pipeline_stage] == pointer, NULL);
+#endif
   CycleData *new_pointer = write();
   CycleData *new_pointer = write();
   _lock.release();
   _lock.release();
   return new_pointer;
   return new_pointer;
@@ -116,7 +144,7 @@ elevate_read(const CycleData *pointer) {
 INLINE void PipelineCyclerTrueImpl::
 INLINE void PipelineCyclerTrueImpl::
 release_write(CycleData *pointer) {
 release_write(CycleData *pointer) {
 #ifdef NDEBUG
 #ifdef NDEBUG
-  int pipeline_stage = Thread::get_current_thread()->get_pipeline_stage();
+  int pipeline_stage = Thread::get_current_pipeline_stage();
   return release_write_stage(pipeline_stage, pointer);
   return release_write_stage(pipeline_stage, pointer);
 #else
 #else
   _lock.release();
   _lock.release();
@@ -134,20 +162,56 @@ get_num_stages() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerTrueImpl::is_stage_unique
+//     Function: PipelineCyclerTrueImpl::read_stage
 //       Access: Public
 //       Access: Public
-//  Description: Returns true if the nth stage is a different pointer
-//               than the previous stage, or false if its pointer is
-//               shared with the previous one.
-////////////////////////////////////////////////////////////////////
-INLINE bool PipelineCyclerTrueImpl::
-is_stage_unique(int n) const {
-  nassertr(n >= 0 && n < _num_stages, false);
-  if (n == 0) {
-    return true;
-  } else {
-    return _data[n] == _data[n - 1];
-  }
+//  Description: Returns a const CycleData pointer, filled with the
+//               data for the indicated stage of the pipeline.  This
+//               pointer should eventually be released by calling
+//               release_read_stage().
+//
+//               There should be no outstanding write pointers on the
+//               data when this function is called.
+////////////////////////////////////////////////////////////////////
+INLINE const CycleData *PipelineCyclerTrueImpl::
+read_stage(int n) const {
+  nassertr(n >= 0 && n < _num_stages, NULL);
+  _lock.lock();
+  return _data[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::release_read_stage
+//       Access: Public
+//  Description: Releases a pointer previously obtained via a call to
+//               read_stage().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrueImpl::
+release_read_stage(int n, const CycleData *pointer) const {
+#ifndef NDEBUG
+  nassertv(n >= 0 && n < _num_stages);
+  nassertv(_data[n] == pointer);
+#endif
+  _lock.release();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::elevate_read_stage
+//       Access: Public
+//  Description: Elevates a currently-held read pointer into a write
+//               pointer.  This may or may not change the value of the
+//               pointer.  It is only valid to do this if this is the
+//               only currently-outstanding read pointer on the
+//               indicated stage.
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerTrueImpl::
+elevate_read_stage(int n, const CycleData *pointer) {
+#ifndef NDEBUG
+  nassertr(n >= 0 && n < _num_stages, NULL);
+  nassertr(_data[n] == pointer, NULL);
+#endif
+  CycleData *new_pointer = write_stage(n);
+  _lock.release();
+  return new_pointer;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -177,7 +241,7 @@ release_write_stage(int n, CycleData *pointer) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE CycleData *PipelineCyclerTrueImpl::
 INLINE CycleData *PipelineCyclerTrueImpl::
 cheat() const {
 cheat() const {
-  int pipeline_stage = Thread::get_current_thread()->get_pipeline_stage();
+  int pipeline_stage = Thread::get_current_pipeline_stage();
   nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
   nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
   return _data[pipeline_stage];
   return _data[pipeline_stage];
 }
 }

+ 114 - 50
panda/src/putil/pipelineCyclerTrueImpl.cxx

@@ -66,13 +66,10 @@ PipelineCyclerTrueImpl(const PipelineCyclerTrueImpl &copy) :
   nassertv(_num_stages == copy._num_stages);
   nassertv(_num_stages == copy._num_stages);
   _data = new PT(CycleData)[_num_stages];
   _data = new PT(CycleData)[_num_stages];
 
 
-  // It's important that we preserve pointerwise equivalence in the
-  // copy: if a and b of the original pipeline are the same pointer,
-  // then a' and b' of the copied pipeline should be the same pointer
-  // (but a' must be a different pointer than a).  This is important
-  // because we rely on pointer equivalence to determine whether an
-  // adjustment at a later stage in the pipeline is automatically
-  // propagated backwards.
+  // It's no longer critically important that we preserve pointerwise
+  // equivalence between different stages in the copy, but it doesn't
+  // cost much and might be a little more efficient, so we do it
+  // anyway.
   typedef pmap<CycleData *, PT(CycleData) > Pointers;
   typedef pmap<CycleData *, PT(CycleData) > Pointers;
   Pointers pointers;
   Pointers pointers;
 
 
@@ -96,8 +93,20 @@ operator = (const PipelineCyclerTrueImpl &copy) {
   ReMutexHolder holder2(copy._lock);
   ReMutexHolder holder2(copy._lock);
 
 
   nassertv(_num_stages == copy._num_stages);
   nassertv(_num_stages == copy._num_stages);
+
+  typedef pmap<CycleData *, PT(CycleData) > Pointers;
+  Pointers pointers;
+
   for (int i = 0; i < _num_stages; ++i) {
   for (int i = 0; i < _num_stages; ++i) {
-    _data[i] = copy._data[i];
+    PT(CycleData) &new_pt = pointers[copy._data[i]];
+    if (new_pt == NULL) {
+      new_pt = copy._data[i]->make_copy();
+    }
+    _data[i] = new_pt;
+  }
+
+  if (copy._dirty && !_dirty) {
+    _pipeline->add_dirty_cycler(this);
   }
   }
 }
 }
 
 
@@ -117,74 +126,129 @@ PipelineCyclerTrueImpl::
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerTrueImpl::write_stage
+//     Function: PipelineCyclerTrueImpl::write_upstream
 //       Access: Public
 //       Access: Public
-//  Description: Returns a pointer suitable for writing to the nth
-//               stage of the pipeline.  This is for special
-//               applications that need to update the entire pipeline
-//               at once (for instance, to remove an invalid pointer).
-//               This pointer should later be released with
-//               release_write_stage().
+//  Description: This special variant on write() will automatically
+//               propagate changes back to upstream pipeline stages.
+//               If force_to_0 is false, then it propagates back only
+//               as long as the CycleData pointers are equivalent,
+//               guaranteeing that it does not modify upstream data
+//               (other than the modification that will be performed
+//               by the code that returns this pointer).  This is
+//               particularly appropriate for minor updates, where it
+//               doesn't matter much if the update is lost, such as
+//               storing a cached value.
+//
+//               If force_to_0 is true, then the CycleData pointer for
+//               the current pipeline stage is propagated all the way
+//               back up to stage 0; after this call, there will be
+//               only one CycleData pointer that is duplicated in all
+//               stages between stage 0 and the current stage.  This
+//               may undo some recent changes that were made
+//               independently at pipeline stage 0 (or any other
+//               upstream stage).  However, it guarantees that the
+//               change that is to be applied at this pipeline stage
+//               will stick.  This is slightly dangerous because of
+//               the risk of losing upstream changes; generally, this
+//               should only be done when you are confident that there
+//               are no upstream changes to be lost (for instance, for
+//               an object that has been recently created).
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CycleData *PipelineCyclerTrueImpl::
 CycleData *PipelineCyclerTrueImpl::
-write_stage(int n) {
+write_upstream(bool force_to_0) {
   _lock.lock();
   _lock.lock();
+  
+  int pipeline_stage = Thread::get_current_pipeline_stage();
 
 
 #ifndef NDEBUG
 #ifndef NDEBUG
-  nassertd(n >= 0 && n < _num_stages) {
+  nassertd(pipeline_stage >= 0 && pipeline_stage < _num_stages) {
     _lock.release();
     _lock.release();
     return NULL;
     return NULL;
   }
   }
 #endif  // NDEBUG
 #endif  // NDEBUG
 
 
-  CycleData *old_data = _data[n];
+  CycleData *old_data = _data[pipeline_stage];
 
 
   if (old_data->get_ref_count() != 1) {
   if (old_data->get_ref_count() != 1) {
-    // Copy-on-write.
-
-    // There's a special problem that happens when we write to a stage
-    // other than stage 0.  If we do this, when the next frame cycles,
-    // the changes that we record to stage n will be lost when the
-    // data from stage (n - 1) is cycled into place.  This can be
-    // wasteful, especially if we are updating a cached value (which
-    // is generally the case when we are writing to stages other than
-    // stage 0).
-
-    // To minimize this, we make a special exception: whenever we
-    // write to stage n, if stage (n - 1) has the same pointer, we
-    // will write to stage (n - 1) at the same time, and so on all the
-    // way back to stage 0 or the last different stage.
-
-    // On the other hand, if *all* of the instances of this pointer
-    // are found in stages k .. n, then we don't need to do anything
-    // at all.
-    int count = old_data->get_ref_count() - 1;
-    int k = n - 1;
+    // Count the number of references before the current stage, and
+    // the number of references remaining other than those.
+    int external_count = old_data->get_ref_count() - 1;
+    int k = pipeline_stage - 1;
     while (k >= 0 && _data[k] == old_data) {
     while (k >= 0 && _data[k] == old_data) {
       --k;
       --k;
-      --count;
+      --external_count;
     }
     }
-
-    if (count > 0) {
+    
+    if (external_count > 0) {
+      // There are references other than the ones before this stage in
+      // the pipeline; perform a copy-on-write.
       PT(CycleData) new_data = old_data->make_copy();
       PT(CycleData) new_data = old_data->make_copy();
-
-      int k = n - 1;
-      while (k >= 0 && _data[k] == old_data) {
+      
+      int k = pipeline_stage - 1;
+      while (k >= 0 && (_data[k] == old_data || force_to_0)) {
         _data[k] = new_data;
         _data[k] = new_data;
         --k;
         --k;
       }
       }
       
       
-      _data[n] = new_data;
+      _data[pipeline_stage] = new_data;
+      
+      if (k >= 0 || pipeline_stage + 1 < _num_stages) {
+        // Now we have differences between some of the data pointers,
+        // which makes us "dirty".
+        if (!_dirty) {
+          _pipeline->add_dirty_cycler(this);
+        }
+      }
 
 
-      // Now we have differences between some of the data pointers, so
-      // we're "dirty".  Mark it so.
-      if (!_dirty) {
-        _pipeline->add_dirty_cycler(this);
+    } else if (k >= 0 && force_to_0) {
+      // There are no external pointers, so no need to copy-on-write,
+      // but the current pointer doesn't go all the way back.  Make it
+      // do so.
+      while (k >= 0) {
+        _data[k] = old_data;
+        --k;
       }
       }
     }
     }
   }
   }
 
 
-  return _data[n];
+  return _data[pipeline_stage];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::write_stage
+//       Access: Public
+//  Description: Returns a pointer suitable for writing to the nth
+//               stage of the pipeline.  This is for special
+//               applications that need to update the entire pipeline
+//               at once (for instance, to remove an invalid pointer).
+//               This pointer should later be released with
+//               release_write_stage().
+////////////////////////////////////////////////////////////////////
+CycleData *PipelineCyclerTrueImpl::
+write_stage(int pipeline_stage) {
+  _lock.lock();
+
+#ifndef NDEBUG
+  nassertd(pipeline_stage >= 0 && pipeline_stage < _num_stages) {
+    _lock.release();
+    return NULL;
+  }
+#endif  // NDEBUG
+
+  CycleData *old_data = _data[pipeline_stage];
+
+  if (old_data->get_ref_count() != 1) {
+    // Copy-on-write.
+    _data[pipeline_stage] = old_data->make_copy();
+
+    // Now we have differences between some of the data pointers, so
+    // we're "dirty".  Mark it so.
+    if (!_dirty) {
+      _pipeline->add_dirty_cycler(this);
+    }
+  }
+
+  return _data[pipeline_stage];
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 8 - 2
panda/src/putil/pipelineCyclerTrueImpl.h

@@ -53,6 +53,9 @@ public:
   void operator = (const PipelineCyclerTrueImpl &copy);
   void operator = (const PipelineCyclerTrueImpl &copy);
   ~PipelineCyclerTrueImpl();
   ~PipelineCyclerTrueImpl();
 
 
+  INLINE void lock();
+  INLINE void release();
+
   INLINE const CycleData *read() const;
   INLINE const CycleData *read() const;
   INLINE void increment_read(const CycleData *pointer) const;
   INLINE void increment_read(const CycleData *pointer) const;
   INLINE void release_read(const CycleData *pointer) const;
   INLINE void release_read(const CycleData *pointer) const;
@@ -62,8 +65,11 @@ public:
   INLINE void release_write(CycleData *pointer);
   INLINE void release_write(CycleData *pointer);
 
 
   INLINE int get_num_stages();
   INLINE int get_num_stages();
-  INLINE bool is_stage_unique(int n) const;
-  CycleData *write_stage(int n);
+  INLINE const CycleData *read_stage(int n) const;
+  INLINE void release_read_stage(int n, const CycleData *pointer) const;
+  CycleData *write_upstream(bool force_to_0);
+  CycleData *write_stage(int pipeline_stage);
+  INLINE CycleData *elevate_read_stage(int n, const CycleData *pointer);
   INLINE void release_write_stage(int n, CycleData *pointer);
   INLINE void release_write_stage(int n, CycleData *pointer);
 
 
   INLINE CycleData *cheat() const;
   INLINE CycleData *cheat() const;

+ 3 - 1
panda/src/putil/putil_composite1.cxx

@@ -12,7 +12,10 @@
 #include "configurable.cxx"
 #include "configurable.cxx"
 #include "cycleData.cxx"
 #include "cycleData.cxx"
 #include "cycleDataReader.cxx"
 #include "cycleDataReader.cxx"
+#include "cycleDataStageReader.cxx"
+#include "cycleDataStageWriter.cxx"
 #include "cycleDataWriter.cxx"
 #include "cycleDataWriter.cxx"
+#include "cyclerHolder.cxx"
 #include "datagramInputFile.cxx"
 #include "datagramInputFile.cxx"
 #include "datagramOutputFile.cxx"
 #include "datagramOutputFile.cxx"
 #include "factoryBase.cxx"
 #include "factoryBase.cxx"
@@ -26,4 +29,3 @@
 #include "lineStream.cxx"
 #include "lineStream.cxx"
 #include "lineStreamBuf.cxx"
 #include "lineStreamBuf.cxx"
 #include "load_prc_file.cxx"
 #include "load_prc_file.cxx"
-#include "modifierButtons.cxx"

+ 1 - 0
panda/src/putil/putil_composite2.cxx

@@ -1,3 +1,4 @@
+#include "modifierButtons.cxx"
 #include "mouseButton.cxx"
 #include "mouseButton.cxx"
 #include "mouseData.cxx"
 #include "mouseData.cxx"
 #include "nameUniquifier.cxx"
 #include "nameUniquifier.cxx"

+ 2 - 2
panda/src/text/textNode.cxx

@@ -578,9 +578,9 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
 //               thing.
 //               thing.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 BoundingVolume *TextNode::
 BoundingVolume *TextNode::
-recompute_internal_bound() {
+recompute_internal_bound(int pipeline_stage) {
   // First, get ourselves a fresh, empty bounding volume.
   // First, get ourselves a fresh, empty bounding volume.
-  BoundingVolume *bound = PandaNode::recompute_internal_bound();
+  BoundingVolume *bound = PandaNode::recompute_internal_bound(pipeline_stage);
   nassertr(bound != (BoundingVolume *)NULL, bound);
   nassertr(bound != (BoundingVolume *)NULL, bound);
 
 
   GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);
   GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);

+ 1 - 1
panda/src/text/textNode.h

@@ -237,7 +237,7 @@ public:
   virtual bool has_cull_callback() const;
   virtual bool has_cull_callback() const;
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
 
 
-  virtual BoundingVolume *recompute_internal_bound();
+  virtual BoundingVolume *recompute_internal_bound(int pipeline_stage);
 
 
 private:
 private:
   INLINE void invalidate_no_measure();
   INLINE void invalidate_no_measure();

Some files were not shown because too many files changed in this diff