Explorar el Código

factor out more calls to Thread::get_current_thread()

David Rose hace 19 años
padre
commit
9808294212
Se han modificado 95 ficheros con 924 adiciones y 570 borrados
  1. 1 1
      panda/src/cull/cullBinBackToFront.cxx
  2. 2 2
      panda/src/cull/cullBinOcclusionTest.cxx
  3. 3 2
      panda/src/cull/drawCullHandler.cxx
  4. 2 1
      panda/src/device/analogNode.cxx
  5. 2 1
      panda/src/device/analogNode.h
  6. 2 1
      panda/src/device/buttonNode.cxx
  7. 2 1
      panda/src/device/buttonNode.h
  8. 2 1
      panda/src/device/dialNode.cxx
  9. 2 1
      panda/src/device/dialNode.h
  10. 2 1
      panda/src/device/mouseAndKeyboard.cxx
  11. 2 1
      panda/src/device/mouseAndKeyboard.h
  12. 2 1
      panda/src/device/trackerNode.cxx
  13. 2 1
      panda/src/device/trackerNode.h
  14. 2 1
      panda/src/device/virtualMouse.cxx
  15. 2 1
      panda/src/device/virtualMouse.h
  16. 11 0
      panda/src/dgraph/dataGraphTraverser.I
  17. 1 1
      panda/src/dgraph/dataGraphTraverser.cxx
  18. 2 0
      panda/src/dgraph/dataGraphTraverser.h
  19. 5 3
      panda/src/dgraph/dataNode.cxx
  20. 5 2
      panda/src/dgraph/dataNode.h
  21. 2 2
      panda/src/display/displayRegion.I
  22. 1 1
      panda/src/display/displayRegion.h
  23. 12 11
      panda/src/display/graphicsEngine.cxx
  24. 4 4
      panda/src/display/graphicsStateGuardian.cxx
  25. 4 2
      panda/src/display/graphicsStateGuardian.h
  26. 31 29
      panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx
  27. 6 3
      panda/src/dxgsg8/dxGraphicsStateGuardian8.h
  28. 20 11
      panda/src/dxgsg8/dxIndexBufferContext8.cxx
  29. 3 2
      panda/src/dxgsg8/dxIndexBufferContext8.h
  30. 18 10
      panda/src/dxgsg8/dxVertexBufferContext8.cxx
  31. 3 2
      panda/src/dxgsg8/dxVertexBufferContext8.h
  32. 40 33
      panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx
  33. 6 3
      panda/src/dxgsg9/dxGraphicsStateGuardian9.h
  34. 22 13
      panda/src/dxgsg9/dxIndexBufferContext9.cxx
  35. 4 4
      panda/src/dxgsg9/dxIndexBufferContext9.h
  36. 20 12
      panda/src/dxgsg9/dxVertexBufferContext9.cxx
  37. 4 4
      panda/src/dxgsg9/dxVertexBufferContext9.h
  38. 8 9
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  39. 2 1
      panda/src/glstuff/glGraphicsStateGuardian_src.h
  40. 5 4
      panda/src/gobj/geom.I
  41. 38 26
      panda/src/gobj/geom.cxx
  42. 2 2
      panda/src/gobj/geom.h
  43. 4 4
      panda/src/gobj/geomCacheEntry.cxx
  44. 2 2
      panda/src/gobj/geomCacheEntry.h
  45. 2 2
      panda/src/gobj/geomMunger.I
  46. 8 8
      panda/src/gobj/geomMunger.cxx
  47. 3 3
      panda/src/gobj/geomMunger.h
  48. 10 0
      panda/src/gobj/geomPrimitive.I
  49. 1 0
      panda/src/gobj/geomPrimitive.h
  50. 2 2
      panda/src/gobj/geomVertexData.I
  51. 4 2
      panda/src/gobj/geomVertexData.cxx
  52. 1 1
      panda/src/gobj/geomVertexData.h
  53. 14 10
      panda/src/gobj/indexBufferContext.I
  54. 4 4
      panda/src/gobj/indexBufferContext.h
  55. 14 10
      panda/src/gobj/vertexBufferContext.I
  56. 4 4
      panda/src/gobj/vertexBufferContext.h
  57. 4 2
      panda/src/grutil/frameRateMeter.I
  58. 11 7
      panda/src/grutil/frameRateMeter.cxx
  59. 1 1
      panda/src/grutil/frameRateMeter.h
  60. 3 1
      panda/src/gsgbase/graphicsStateGuardianBase.h
  61. 2 2
      panda/src/pgraph/camera.cxx
  62. 1 1
      panda/src/pgraph/camera.h
  63. 2 2
      panda/src/pgraph/cullResult.cxx
  64. 14 12
      panda/src/pgraph/cullTraverser.cxx
  65. 1 1
      panda/src/pgraph/cullTraverserData.I
  66. 13 11
      panda/src/pgraph/cullTraverserData.cxx
  67. 1 1
      panda/src/pgraph/cullTraverserData.h
  68. 3 3
      panda/src/pgraph/cullableObject.I
  69. 3 2
      panda/src/pgraph/cullableObject.cxx
  70. 1 2
      panda/src/pgraph/cullableObject.h
  71. 181 34
      panda/src/pgraph/geomNode.I
  72. 122 113
      panda/src/pgraph/geomNode.cxx
  73. 29 2
      panda/src/pgraph/geomNode.h
  74. 18 12
      panda/src/pgraph/geomTransformer.cxx
  75. 10 10
      panda/src/pgraph/nodePath.cxx
  76. 1 1
      panda/src/pgraph/nodePath.h
  77. 13 12
      panda/src/pgraph/pandaNode.I
  78. 15 21
      panda/src/pgraph/pandaNode.cxx
  79. 17 12
      panda/src/pgraph/pandaNode.h
  80. 2 1
      panda/src/recorder/mouseRecorder.cxx
  81. 2 1
      panda/src/recorder/mouseRecorder.h
  82. 2 1
      panda/src/tform/buttonThrower.cxx
  83. 2 1
      panda/src/tform/buttonThrower.h
  84. 2 1
      panda/src/tform/driveInterface.cxx
  85. 2 1
      panda/src/tform/driveInterface.h
  86. 2 1
      panda/src/tform/mouseSubregion.cxx
  87. 2 1
      panda/src/tform/mouseSubregion.h
  88. 8 4
      panda/src/tform/mouseWatcher.cxx
  89. 2 1
      panda/src/tform/mouseWatcher.h
  90. 35 24
      panda/src/tform/mouseWatcherGroup.cxx
  91. 3 0
      panda/src/tform/mouseWatcherGroup.h
  92. 2 1
      panda/src/tform/trackball.cxx
  93. 2 1
      panda/src/tform/trackball.h
  94. 6 2
      panda/src/tform/transform2sg.cxx
  95. 2 1
      panda/src/tform/transform2sg.h

+ 1 - 1
panda/src/cull/cullBinBackToFront.cxx

@@ -61,7 +61,7 @@ make_bin(const string &name, GraphicsStateGuardianBase *gsg) {
 void CullBinBackToFront::
 add_object(CullableObject *object, Thread *current_thread) {
   // Determine the center of the bounding volume.
-  CPT(BoundingVolume) volume = object->_geom->get_bounds();
+  CPT(BoundingVolume) volume = object->_geom->get_bounds(current_thread);
 
   if (!volume->is_empty()) {
     const GeometricBoundingVolume *gbv;

+ 2 - 2
panda/src/cull/cullBinOcclusionTest.cxx

@@ -490,7 +490,7 @@ occlusion_test(CullBinOcclusionTest &bin, Thread *current_thread) {
   CPT(TransformState) internal_transform = bin._gsg->get_cs_transform()->compose(modelview_transform);
   
   CPT(RenderState) state = get_octree_solid_test_state();
-  PT(GeomMunger) munger = bin._gsg->get_geom_munger(state);
+  PT(GeomMunger) munger = bin._gsg->get_geom_munger(state, current_thread);
   
   CPT(Geom) viz = get_octree_solid_test();
   CPT(GeomVertexData) munged_data = viz->get_vertex_data();
@@ -628,7 +628,7 @@ draw_wireframe(CullBinOcclusionTest &bin, Thread *current_thread) {
   CPT(TransformState) internal_transform = bin._gsg->get_cs_transform()->compose(modelview_transform);
   
   CPT(RenderState) state = RenderState::make_empty();
-  PT(GeomMunger) munger = bin._gsg->get_geom_munger(state);
+  PT(GeomMunger) munger = bin._gsg->get_geom_munger(state, current_thread);
   
   CPT(Geom) viz = get_octree_wireframe_viz();
   CPT(GeomVertexData) munged_data = viz->get_vertex_data();

+ 3 - 2
panda/src/cull/drawCullHandler.cxx

@@ -35,9 +35,10 @@ void DrawCullHandler::
 record_object(CullableObject *object, const CullTraverser *traverser) {
   // Munge vertices as needed for the GSG's requirements, and the
   // object's current state.
-  object->munge_geom(_gsg, _gsg->get_geom_munger(object->_state), traverser);
+  Thread *current_thread = traverser->get_current_thread();
+  object->munge_geom(_gsg, _gsg->get_geom_munger(object->_state, current_thread), traverser);
 
   // And draw the object, then dispense with it.
-  draw(object, _gsg, traverser->get_current_thread());
+  draw(object, _gsg, current_thread);
   delete object;
 }

+ 2 - 1
panda/src/device/analogNode.cxx

@@ -98,7 +98,8 @@ write(ostream &out, int indent_level) const {
 //               calls.
 ////////////////////////////////////////////////////////////////////
 void AnalogNode::
-do_transmit_data(const DataNodeTransmit &, DataNodeTransmit &output) {
+do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &, 
+                 DataNodeTransmit &output) {
   if (is_valid()) {
     _analog->poll();
 

+ 2 - 1
panda/src/device/analogNode.h

@@ -81,7 +81,8 @@ private:
 
 protected:
   // Inherited from DataNode
-  virtual void do_transmit_data(const DataNodeTransmit &input,
+  virtual void do_transmit_data(DataGraphTraverser *trav,
+                                const DataNodeTransmit &input,
                                 DataNodeTransmit &output);
 
 private:

+ 2 - 1
panda/src/device/buttonNode.cxx

@@ -116,7 +116,8 @@ write(ostream &out, int indent_level) const {
 //               calls.
 ////////////////////////////////////////////////////////////////////
 void ButtonNode::
-do_transmit_data(const DataNodeTransmit &, DataNodeTransmit &output) {
+do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &, 
+                 DataNodeTransmit &output) {
   if (is_valid()) {
     _button->poll();
     _button->lock();

+ 2 - 1
panda/src/device/buttonNode.h

@@ -66,7 +66,8 @@ private:
 
 protected:
   // Inherited from DataNode
-  virtual void do_transmit_data(const DataNodeTransmit &input,
+  virtual void do_transmit_data(DataGraphTraverser *trav,
+                                const DataNodeTransmit &input,
                                 DataNodeTransmit &output);
 
 private:

+ 2 - 1
panda/src/device/dialNode.cxx

@@ -78,7 +78,8 @@ DialNode::
 //               calls.
 ////////////////////////////////////////////////////////////////////
 void DialNode::
-do_transmit_data(const DataNodeTransmit &, DataNodeTransmit &output) {
+do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &,
+                 DataNodeTransmit &output) {
   if (is_valid()) {
     _dial->poll();
 

+ 2 - 1
panda/src/device/dialNode.h

@@ -57,7 +57,8 @@ private:
 
 protected:
   // Inherited from DataNode
-  virtual void do_transmit_data(const DataNodeTransmit &input,
+  virtual void do_transmit_data(DataGraphTraverser *trav,
+                                const DataNodeTransmit &input,
                                 DataNodeTransmit &output);
 
 private:

+ 2 - 1
panda/src/device/mouseAndKeyboard.cxx

@@ -74,7 +74,8 @@ set_source(GraphicsWindow *window, int device) {
 //               calls.
 ////////////////////////////////////////////////////////////////////
 void MouseAndKeyboard::
-do_transmit_data(const DataNodeTransmit &, DataNodeTransmit &output) {
+do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &,
+                 DataNodeTransmit &output) {
   if (_window->has_button_event(_device)) {
     // Fill up the button events.
     _button_events->clear();

+ 2 - 1
panda/src/device/mouseAndKeyboard.h

@@ -54,7 +54,8 @@ PUBLISHED:
 
 protected:
   // Inherited from DataNode
-  virtual void do_transmit_data(const DataNodeTransmit &input,
+  virtual void do_transmit_data(DataGraphTraverser *trav,
+                                const DataNodeTransmit &input,
                                 DataNodeTransmit &output);
 
 private:

+ 2 - 1
panda/src/device/trackerNode.cxx

@@ -84,7 +84,8 @@ TrackerNode::
 //               calls.
 ////////////////////////////////////////////////////////////////////
 void TrackerNode::
-do_transmit_data(const DataNodeTransmit &, DataNodeTransmit &output) {
+do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &,
+                 DataNodeTransmit &output) {
   if (is_valid()) {
     _tracker->poll();
     _tracker->lock();

+ 2 - 1
panda/src/device/trackerNode.h

@@ -58,7 +58,8 @@ PUBLISHED:
 
 protected:
   // Inherited from DataNode
-  virtual void do_transmit_data(const DataNodeTransmit &input,
+  virtual void do_transmit_data(DataGraphTraverser *trav,
+                                const DataNodeTransmit &input,
                                 DataNodeTransmit &output);
 
 private:

+ 2 - 1
panda/src/device/virtualMouse.cxx

@@ -123,7 +123,8 @@ release_button(ButtonHandle button) {
 //               calls.
 ////////////////////////////////////////////////////////////////////
 void VirtualMouse::
-do_transmit_data(const DataNodeTransmit &, DataNodeTransmit &output) {
+do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &,
+                 DataNodeTransmit &output) {
   // Swap in the button events, and clear them for next time.
   PT(ButtonEventList) events = _button_events;
   _button_events = _next_button_events;

+ 2 - 1
panda/src/device/virtualMouse.h

@@ -55,7 +55,8 @@ private:
 
 protected:
   // Inherited from DataNode
-  virtual void do_transmit_data(const DataNodeTransmit &input,
+  virtual void do_transmit_data(DataGraphTraverser *trav,
+                                const DataNodeTransmit &input,
                                 DataNodeTransmit &output);
 
 private:

+ 11 - 0
panda/src/dgraph/dataGraphTraverser.I

@@ -17,6 +17,17 @@
 ////////////////////////////////////////////////////////////////////
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: DataGraphTraverser::get_current_thread
+//       Access: Public
+//  Description: Returns the currently-executing thread object, as
+//               passed to the DataGraphTraverser constructor.
+////////////////////////////////////////////////////////////////////
+INLINE Thread *DataGraphTraverser::
+get_current_thread() const {
+  return _current_thread;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DataGraphTraverser::CollectedData::Constructor
 //       Access: Public

+ 1 - 1
panda/src/dgraph/dataGraphTraverser.cxx

@@ -170,7 +170,7 @@ void DataGraphTraverser::
 r_transmit(DataNode *data_node, const DataNodeTransmit inputs[]) {
   DataNodeTransmit output;
   output.reserve(data_node->get_num_outputs());
-  data_node->transmit_data(inputs, output);
+  data_node->transmit_data(this, inputs, output);
 
   traverse_below(data_node, output);
 }

+ 2 - 0
panda/src/dgraph/dataGraphTraverser.h

@@ -41,6 +41,8 @@ PUBLISHED:
   DataGraphTraverser(Thread *current_thread = Thread::get_current_thread());
   ~DataGraphTraverser();
 
+  INLINE Thread *get_current_thread() const;
+
   void traverse(PandaNode *node);
   void traverse_below(PandaNode *node, const DataNodeTransmit &output);
   void collect_leftovers();

+ 5 - 3
panda/src/dgraph/dataNode.cxx

@@ -45,7 +45,8 @@ make_copy() const {
 //               inputs and put the result into the indicated output.
 ////////////////////////////////////////////////////////////////////
 void DataNode::
-transmit_data(const DataNodeTransmit inputs[],
+transmit_data(DataGraphTraverser *trav,
+              const DataNodeTransmit inputs[],
               DataNodeTransmit &output) {
   DataNodeTransmit new_input;
   new_input.reserve(get_num_inputs());
@@ -79,7 +80,7 @@ transmit_data(const DataNodeTransmit inputs[],
   }
   #endif  // NDEBUG
 
-  do_transmit_data(new_input, output);
+  do_transmit_data(trav, new_input, output);
 
   #ifndef NDEBUG
   if (dgraph_cat.is_spam()) {
@@ -273,7 +274,8 @@ parents_changed() {
 //               calls.
 ////////////////////////////////////////////////////////////////////
 void DataNode::
-do_transmit_data(const DataNodeTransmit &, DataNodeTransmit &) {
+do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &, 
+                 DataNodeTransmit &) {
 }
 
 ////////////////////////////////////////////////////////////////////

+ 5 - 2
panda/src/dgraph/dataNode.h

@@ -50,6 +50,7 @@
 #include "pandaNode.h"
 #include "pointerTo.h"
 
+class DataGraphTraverser;
 class DataNodeTransmit;
 
 ////////////////////////////////////////////////////////////////////
@@ -73,7 +74,8 @@ protected:
 public:
   virtual PandaNode *make_copy() const;
 
-  void transmit_data(const DataNodeTransmit inputs[],
+  void transmit_data(DataGraphTraverser *trav,
+                     const DataNodeTransmit inputs[],
                      DataNodeTransmit &output);
 
   INLINE int get_num_inputs() const;
@@ -93,7 +95,8 @@ protected:
   virtual void parents_changed();
 
   // Local to DataNode
-  virtual void do_transmit_data(const DataNodeTransmit &input,
+  virtual void do_transmit_data(DataGraphTraverser *trav,
+                                const DataNodeTransmit &input,
                                 DataNodeTransmit &output);
 
 private:

+ 2 - 2
panda/src/display/displayRegion.I

@@ -116,8 +116,8 @@ get_window() const {
 //               associated.
 ////////////////////////////////////////////////////////////////////
 INLINE NodePath DisplayRegion::
-get_camera() const {
-  CDReader cdata(_cycler);
+get_camera(Thread *current_thread) const {
+  CDReader cdata(_cycler, current_thread);
   return cdata->_camera;
 }
 

+ 1 - 1
panda/src/display/displayRegion.h

@@ -81,7 +81,7 @@ PUBLISHED:
   GraphicsPipe *get_pipe() const;
 
   void set_camera(const NodePath &camera);
-  INLINE NodePath get_camera() const;
+  INLINE NodePath get_camera(Thread *current_thread = Thread::get_current_thread()) const;
 
   void set_active(bool active);
   INLINE bool is_active() const;

+ 12 - 11
panda/src/display/graphicsEngine.cxx

@@ -600,15 +600,15 @@ render_frame() {
       for (int i = 0; i < num_drs; ++i) {
         DisplayRegion *dr = win->get_active_display_region(i);
         if (dr != (DisplayRegion *)NULL) {
-          NodePath camera_np = dr->get_camera();
+          NodePath camera_np = dr->get_camera(current_thread);
           if (!camera_np.is_empty()) {
             Camera *camera = DCAST(Camera, camera_np.node());
             NodePath scene = camera->get_scene();
             if (scene.is_empty()) {
-              scene = camera_np.get_top();
+              scene = camera_np.get_top(current_thread);
             }
             if (!scene.is_empty()) {
-              scene.get_bounds();
+              scene.get_bounds(current_thread);
             }
           }
         }
@@ -1348,7 +1348,8 @@ do_flip_frame(Thread *current_thread) {
 ////////////////////////////////////////////////////////////////////
 PT(SceneSetup) GraphicsEngine::
 setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) {
-  PStatTimer timer(_cull_setup_pcollector, dr->get_current_thread());
+  Thread *current_thread = dr->get_current_thread();
+  PStatTimer timer(_cull_setup_pcollector, current_thread);
 
   GraphicsOutput *window = dr->get_window();
   // The window pointer shouldn't be NULL, since we presumably got to
@@ -1369,7 +1370,7 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) {
     // Camera inactive, no draw.
     return NULL;
   }
-  camera_node->cleanup_aux_scene_data();
+  camera_node->cleanup_aux_scene_data(current_thread);
 
   Lens *lens = camera_node->get_lens();
   if (lens == (Lens *)NULL) {
@@ -1382,7 +1383,7 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) {
     // If there's no explicit scene specified, use whatever scene the
     // camera is parented within.  This is the normal and preferred
     // case; the use of an explicit scene is now deprecated.
-    scene_root = camera.get_top();
+    scene_root = camera.get_top(current_thread);
   }
 
   PT(SceneSetup) scene_setup = new SceneSetup;
@@ -1394,9 +1395,9 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) {
   // scene_root, because the scene_root's own transform is immediately
   // applied to these during rendering.  (Normally, the parent of the
   // scene_root is the empty NodePath, although it need not be.)
-  NodePath scene_parent = scene_root.get_parent();
-  CPT(TransformState) camera_transform = camera.get_transform(scene_parent);
-  CPT(TransformState) world_transform = scene_parent.get_transform(camera);
+  NodePath scene_parent = scene_root.get_parent(current_thread);
+  CPT(TransformState) camera_transform = camera.get_transform(scene_parent, current_thread);
+  CPT(TransformState) world_transform = scene_parent.get_transform(camera, current_thread);
 
   CPT(RenderState) initial_state = camera_node->get_initial_state();
 
@@ -1451,9 +1452,9 @@ do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
       PT(GeometricBoundingVolume) local_frustum;
       local_frustum = DCAST(GeometricBoundingVolume, bv->make_copy());
 
-      NodePath scene_parent = scene_setup->get_scene_root().get_parent();
+      NodePath scene_parent = scene_setup->get_scene_root().get_parent(current_thread);
       CPT(TransformState) cull_center_transform = 
-        scene_setup->get_cull_center().get_transform(scene_parent);
+        scene_setup->get_cull_center().get_transform(scene_parent, current_thread);
       local_frustum->xform(cull_center_transform->get_mat());
 
       trav.set_view_frustum(local_frustum);

+ 4 - 4
panda/src/display/graphicsStateGuardian.cxx

@@ -578,13 +578,13 @@ end_occlusion_query() {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::get_geom_munger
-//       Access: Public
+//       Access: Public, Virtual
 //  Description: Looks up or creates a GeomMunger object to munge
 //               vertices appropriate to this GSG for the indicated
 //               state.
 ////////////////////////////////////////////////////////////////////
 PT(GeomMunger) GraphicsStateGuardian::
-get_geom_munger(const RenderState *state) {
+get_geom_munger(const RenderState *state, Thread *current_thread) {
   // Before we even look up the map, see if the _last_mi value points
   // to this GSG.  This is likely because we tend to visit the same
   // state multiple times during a frame.  Also, this might well be
@@ -604,7 +604,7 @@ get_geom_munger(const RenderState *state) {
   }
 
   // Nothing in the map; create a new entry.
-  PT(GeomMunger) munger = make_geom_munger(state);
+  PT(GeomMunger) munger = make_geom_munger(state, current_thread);
 
   // Cast the RenderState to a non-const object.  We can do this
   // because we are only updating a cache within the RenderState, not
@@ -623,7 +623,7 @@ get_geom_munger(const RenderState *state) {
 //               appropriate to this GSG for the indicated state.
 ////////////////////////////////////////////////////////////////////
 PT(GeomMunger) GraphicsStateGuardian::
-make_geom_munger(const RenderState *state) {
+make_geom_munger(const RenderState *state, Thread *current_thread) {
   // The default implementation returns no munger at all, but
   // presumably, every kind of GSG needs some special munging action,
   // so real GSG's will override this to return something more

+ 4 - 2
panda/src/display/graphicsStateGuardian.h

@@ -158,8 +158,10 @@ public:
   virtual void begin_occlusion_query();
   virtual PT(OcclusionQueryContext) end_occlusion_query();
 
-  PT(GeomMunger) get_geom_munger(const RenderState *state);
-  virtual PT(GeomMunger) make_geom_munger(const RenderState *state);
+  virtual PT(GeomMunger) get_geom_munger(const RenderState *state, 
+                                         Thread *current_thread);
+  virtual PT(GeomMunger) make_geom_munger(const RenderState *state,
+                                          Thread *current_thread);
 
   virtual void set_state_and_transform(const RenderState *state,
                                        const TransformState *transform);

+ 31 - 29
panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx

@@ -303,23 +303,24 @@ prepare_vertex_buffer(GeomVertexArrayData *data) {
 //               makes it the current vertex buffer for rendering.
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian8::
-apply_vertex_buffer(VertexBufferContext *vbc) {
+apply_vertex_buffer(VertexBufferContext *vbc,
+                    const GeomVertexArrayDataPipelineReader *reader) {
   DXVertexBufferContext8 *dvbc = DCAST(DXVertexBufferContext8, vbc);
 
   if (dvbc->_vbuffer == NULL) {
     // Attempt to create a new vertex buffer.
     if (vertex_buffers &&
-        dvbc->get_data()->get_usage_hint() != Geom::UH_client) {
-      dvbc->create_vbuffer(*_screen);
+        reader->get_usage_hint() != Geom::UH_client) {
+      dvbc->create_vbuffer(*_screen, reader);
     }
 
     if (dvbc->_vbuffer != NULL) {
-      dvbc->upload_data();
+      dvbc->upload_data(reader);
 
-      dvbc->mark_loaded();
+      dvbc->mark_loaded(reader);
 
       _d3d_device->SetStreamSource
-        (0, dvbc->_vbuffer, dvbc->get_data()->get_array_format()->get_stride());
+        (0, dvbc->_vbuffer, reader->get_array_format()->get_stride());
       _active_vbuffer = dvbc;
       _active_ibuffer = NULL;
       dvbc->set_active(true);
@@ -329,21 +330,21 @@ apply_vertex_buffer(VertexBufferContext *vbc) {
     }
 
   } else {
-    if (dvbc->was_modified()) {
-      if (dvbc->changed_size()) {
+    if (dvbc->was_modified(reader)) {
+      if (dvbc->changed_size(reader)) {
         // We have to destroy the old vertex buffer and create a new
         // one.
-        dvbc->create_vbuffer(*_screen);
+        dvbc->create_vbuffer(*_screen, reader);
       }
 
-      dvbc->upload_data();
-      dvbc->mark_loaded();
+      dvbc->upload_data(reader);
+      dvbc->mark_loaded(reader);
       _active_vbuffer = NULL;
     }
 
     if (_active_vbuffer != dvbc) {
       _d3d_device->SetStreamSource
-        (0, dvbc->_vbuffer, dvbc->get_data()->get_array_format()->get_stride());
+        (0, dvbc->_vbuffer, reader->get_array_format()->get_stride());
       _active_vbuffer = dvbc;
       _active_ibuffer = NULL;
       dvbc->set_active(true);
@@ -400,16 +401,17 @@ prepare_index_buffer(GeomPrimitive *data) {
 //               makes it the current index buffer for rendering.
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian8::
-apply_index_buffer(IndexBufferContext *ibc) {
+apply_index_buffer(IndexBufferContext *ibc,
+                   const GeomPrimitivePipelineReader *reader) {
   DXIndexBufferContext8 *dibc = DCAST(DXIndexBufferContext8, ibc);
 
   if (dibc->_ibuffer == NULL) {
     // Attempt to create a new index buffer.
-    dibc->create_ibuffer(*_screen);
+    dibc->create_ibuffer(*_screen, reader);
 
     if (dibc->_ibuffer != NULL) {
-      dibc->upload_data();
-      dibc->mark_loaded();
+      dibc->upload_data(reader);
+      dibc->mark_loaded(reader);
 
       _d3d_device->SetIndices(dibc->_ibuffer, 0);
       _active_ibuffer = dibc;
@@ -421,16 +423,16 @@ apply_index_buffer(IndexBufferContext *ibc) {
     }
 
   } else {
-    if (dibc->was_modified()) {
-      if (dibc->changed_size()) {
+    if (dibc->was_modified(reader)) {
+      if (dibc->changed_size(reader)) {
         // We have to destroy the old index buffer and create a new
         // one.
-        dibc->create_ibuffer(*_screen);
+        dibc->create_ibuffer(*_screen, reader);
       }
 
-      dibc->upload_data();
+      dibc->upload_data(reader);
 
-      dibc->mark_loaded();
+      dibc->mark_loaded(reader);
       _active_ibuffer = NULL;
     }
 
@@ -463,9 +465,9 @@ release_index_buffer(IndexBufferContext *ibc) {
 //               appropriate to this GSG for the indicated state.
 ////////////////////////////////////////////////////////////////////
 PT(GeomMunger) DXGraphicsStateGuardian8::
-make_geom_munger(const RenderState *state) {
+make_geom_munger(const RenderState *state, Thread *current_thread) {
   PT(DXGeomMunger8) munger = new DXGeomMunger8(this, state);
-  return GeomMunger::register_munger(munger);
+  return GeomMunger::register_munger(munger, current_thread);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -805,7 +807,7 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader,
 
   VertexBufferContext *vbc = ((GeomVertexArrayData *)(data->get_object()))->prepare_now(get_prepared_objects(), this);
   nassertr(vbc != (VertexBufferContext *)NULL, false);
-  apply_vertex_buffer(vbc);
+  apply_vertex_buffer(vbc, data);
 
   const GeomVertexAnimationSpec &animation =
     data_reader->get_format()->get_animation();
@@ -910,7 +912,7 @@ draw_triangles(const GeomPrimitivePipelineReader *reader) {
       // Indexed, vbuffers.
       IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
       nassertv(ibc != (IndexBufferContext *)NULL);
-      apply_index_buffer(ibc);
+      apply_index_buffer(ibc, reader);
 
       _d3d_device->DrawIndexedPrimitive
         (D3DPT_TRIANGLELIST,
@@ -970,7 +972,7 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader) {
         // Indexed, vbuffers, one line triangle strip.
         IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
         nassertv(ibc != (IndexBufferContext *)NULL);
-        apply_index_buffer(ibc);
+        apply_index_buffer(ibc, reader);
 
         _d3d_device->DrawIndexedPrimitive
           (D3DPT_TRIANGLESTRIP,
@@ -1027,7 +1029,7 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader) {
         // Indexed, vbuffers, individual triangle strips.
         IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
         nassertv(ibc != (IndexBufferContext *)NULL);
-        apply_index_buffer(ibc);
+        apply_index_buffer(ibc, reader);
 
         unsigned int start = 0;
         for (size_t i = 0; i < ends.size(); i++) {
@@ -1127,7 +1129,7 @@ draw_trifans(const GeomPrimitivePipelineReader *reader) {
       // Indexed, vbuffers.
       IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
       nassertv(ibc != (IndexBufferContext *)NULL);
-      apply_index_buffer(ibc);
+      apply_index_buffer(ibc, reader);
 
       unsigned int start = 0;
       for (size_t i = 0; i < ends.size(); i++) {
@@ -1217,7 +1219,7 @@ draw_lines(const GeomPrimitivePipelineReader *reader) {
       // Indexed, vbuffers.
       IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
       nassertv(ibc != (IndexBufferContext *)NULL);
-      apply_index_buffer(ibc);
+      apply_index_buffer(ibc, reader);
 
       _d3d_device->DrawIndexedPrimitive
         (D3DPT_LINELIST,

+ 6 - 3
panda/src/dxgsg8/dxGraphicsStateGuardian8.h

@@ -53,14 +53,17 @@ public:
   virtual void release_texture(TextureContext *tc);
 
   virtual VertexBufferContext *prepare_vertex_buffer(GeomVertexArrayData *data);
-  void apply_vertex_buffer(VertexBufferContext *vbc);
+  void apply_vertex_buffer(VertexBufferContext *vbc,
+                           const GeomVertexArrayDataPipelineReader *reader);
   virtual void release_vertex_buffer(VertexBufferContext *vbc);
 
   virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data);
-  void apply_index_buffer(IndexBufferContext *ibc);
+  void apply_index_buffer(IndexBufferContext *ibc,
+                          const GeomPrimitivePipelineReader *reader);
   virtual void release_index_buffer(IndexBufferContext *ibc);
 
-  virtual PT(GeomMunger) make_geom_munger(const RenderState *state);
+  virtual PT(GeomMunger) make_geom_munger(const RenderState *state,
+                                          Thread *current_thread);
 
   virtual void set_color_clear_value(const Colorf &value);
 

+ 20 - 11
panda/src/dxgsg8/dxIndexBufferContext8.cxx

@@ -62,21 +62,26 @@ DXIndexBufferContext8::
 //               to it).
 ////////////////////////////////////////////////////////////////////
 void DXIndexBufferContext8::
-create_ibuffer(DXScreenData &scrn) {
+create_ibuffer(DXScreenData &scrn, 
+               const GeomPrimitivePipelineReader *reader) {
+  nassertv(reader->get_object() == get_data());
+  Thread *current_thread = reader->get_current_thread();
+
   if (_ibuffer != NULL) {
     RELEASE(_ibuffer, dxgsg8, "index buffer", RELEASE_ONCE);
     _ibuffer = NULL;
   }
 
-  PStatTimer timer(GraphicsStateGuardian::_create_index_buffer_pcollector);
+  PStatTimer timer(GraphicsStateGuardian::_create_index_buffer_pcollector,
+                   current_thread);
 
   D3DFORMAT index_type =
-    DXGraphicsStateGuardian8::get_index_type(get_data()->get_index_type());
+    DXGraphicsStateGuardian8::get_index_type(reader->get_index_type());
 
   HRESULT hr = scrn._d3d_device->CreateIndexBuffer
-//    (get_data()->get_data_size_bytes(), D3DUSAGE_WRITEONLY,
+//    (reader->get_data_size_bytes(), D3DUSAGE_WRITEONLY,
 //     index_type, D3DPOOL_MANAGED, &_ibuffer);
-    (get_data()->get_data_size_bytes(), D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC,
+    (reader->get_data_size_bytes(), D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC,
      index_type, D3DPOOL_DEFAULT, &_ibuffer);
   if (FAILED(hr)) {
     dxgsg8_cat.warning()
@@ -86,8 +91,8 @@ create_ibuffer(DXScreenData &scrn) {
     if (dxgsg8_cat.is_debug()) {
       dxgsg8_cat.debug()
         << "creating index buffer " << _ibuffer << ": "
-        << get_data()->get_num_vertices() << " indices ("
-        << get_data()->get_vertices()->get_array_format()->get_column(0)->get_numeric_type()
+        << reader->get_num_vertices() << " indices ("
+        << reader->get_vertices_reader()->get_array_format()->get_column(0)->get_numeric_type()
         << ")\n";
     }
   }
@@ -100,11 +105,15 @@ create_ibuffer(DXScreenData &scrn) {
 //               DirectX.
 ////////////////////////////////////////////////////////////////////
 void DXIndexBufferContext8::
-upload_data() {
+upload_data(const GeomPrimitivePipelineReader *reader) {
+  nassertv(reader->get_object() == get_data());
+  Thread *current_thread = reader->get_current_thread();
+
   nassertv(_ibuffer != NULL);
-  PStatTimer timer(GraphicsStateGuardian::_load_index_buffer_pcollector);
+  PStatTimer timer(GraphicsStateGuardian::_load_index_buffer_pcollector,
+                   current_thread);
 
-  int data_size = get_data()->get_data_size_bytes();
+  int data_size = reader->get_data_size_bytes();
 
   if (dxgsg8_cat.is_spam()) {
     dxgsg8_cat.spam()
@@ -122,7 +131,7 @@ upload_data() {
   }
 
   GraphicsStateGuardian::_data_transferred_pcollector.add_level(data_size);
-  memcpy(local_pointer, get_data()->get_data(), data_size);
+  memcpy(local_pointer, reader->get_data(), data_size);
 
   _ibuffer->Unlock();
 }

+ 3 - 2
panda/src/dxgsg8/dxIndexBufferContext8.h

@@ -33,8 +33,9 @@ public:
   DXIndexBufferContext8(PreparedGraphicsObjects *pgo, GeomPrimitive *data);
   virtual ~DXIndexBufferContext8();
 
-  void create_ibuffer(DXScreenData &scrn);
-  void upload_data();
+  void create_ibuffer(DXScreenData &scrn,
+                      const GeomPrimitivePipelineReader *reader);
+  void upload_data(const GeomPrimitivePipelineReader *reader);
 
   IDirect3DIndexBuffer8 *_ibuffer;
 

+ 18 - 10
panda/src/dxgsg8/dxVertexBufferContext8.cxx

@@ -188,7 +188,11 @@ DXVertexBufferContext8::
 //               to it).
 ////////////////////////////////////////////////////////////////////
 void DXVertexBufferContext8::
-create_vbuffer(DXScreenData &scrn) {
+create_vbuffer(DXScreenData &scrn,
+               const GeomVertexArrayDataPipelineReader *reader) {
+  nassertv(reader->get_object() == get_data());
+  Thread *current_thread = reader->get_current_thread();
+
   if (_vbuffer != NULL) {
     if (dxgsg8_cat.is_debug()) {
       dxgsg8_cat.debug()
@@ -199,12 +203,13 @@ create_vbuffer(DXScreenData &scrn) {
     _vbuffer = NULL;
   }
 
-  PStatTimer timer(GraphicsStateGuardian::_create_vertex_buffer_pcollector);
+  PStatTimer timer(GraphicsStateGuardian::_create_vertex_buffer_pcollector,
+                   current_thread);
 
   HRESULT hr = scrn._d3d_device->CreateVertexBuffer
-//    (get_data()->get_data_size_bytes(), D3DUSAGE_WRITEONLY,
+//    (reader->get_data_size_bytes(), D3DUSAGE_WRITEONLY,
 //     _fvf, D3DPOOL_MANAGED, &_vbuffer);
-     (get_data()->get_data_size_bytes(), D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC,
+     (reader->get_data_size_bytes(), D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC,
      _fvf, D3DPOOL_DEFAULT, &_vbuffer);
   if (FAILED(hr)) {
     dxgsg8_cat.warning()
@@ -214,8 +219,8 @@ create_vbuffer(DXScreenData &scrn) {
     if (dxgsg8_cat.is_debug()) {
       dxgsg8_cat.debug()
         << "created vertex buffer " << _vbuffer << ": "
-        << get_data()->get_num_rows() << " vertices "
-        << *get_data()->get_array_format() << "\n";
+        << reader->get_num_rows() << " vertices "
+        << *reader->get_array_format() << "\n";
     }
   }
 }
@@ -227,11 +232,14 @@ create_vbuffer(DXScreenData &scrn) {
 //               DirectX.
 ////////////////////////////////////////////////////////////////////
 void DXVertexBufferContext8::
-upload_data() {
+upload_data(const GeomVertexArrayDataPipelineReader *reader) {
+  nassertv(reader->get_object() == get_data());
   nassertv(_vbuffer != NULL);
-  PStatTimer timer(GraphicsStateGuardian::_load_vertex_buffer_pcollector);
+  Thread *current_thread = reader->get_current_thread();
+  PStatTimer timer(GraphicsStateGuardian::_load_vertex_buffer_pcollector,
+                   current_thread);
 
-  int data_size = get_data()->get_data_size_bytes();
+  int data_size = reader->get_data_size_bytes();
 
   if (dxgsg8_cat.is_spam()) {
     dxgsg8_cat.spam()
@@ -249,7 +257,7 @@ upload_data() {
   }
 
   GraphicsStateGuardian::_data_transferred_pcollector.add_level(data_size);
-  memcpy(local_pointer, get_data()->get_data(), data_size);
+  memcpy(local_pointer, reader->get_data(), data_size);
 
   _vbuffer->Unlock();
 }

+ 3 - 2
panda/src/dxgsg8/dxVertexBufferContext8.h

@@ -33,8 +33,9 @@ public:
   DXVertexBufferContext8(PreparedGraphicsObjects *pgo, GeomVertexArrayData *data);
   virtual ~DXVertexBufferContext8();
 
-  void create_vbuffer(DXScreenData &scrn);
-  void upload_data();
+  void create_vbuffer(DXScreenData &scrn,
+                      const GeomVertexArrayDataPipelineReader *reader);
+  void upload_data(const GeomVertexArrayDataPipelineReader *reader);
 
   IDirect3DVertexBuffer8 *_vbuffer;
   int _fvf;

+ 40 - 33
panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx

@@ -393,7 +393,9 @@ prepare_vertex_buffer(GeomVertexArrayData *data) {
 //               makes it the current vertex buffer for rendering.
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian9::
-apply_vertex_buffer(VertexBufferContext *vbc, CLP(ShaderContext) *shader_context) {
+apply_vertex_buffer(VertexBufferContext *vbc, 
+                    CLP(ShaderContext) *shader_context,
+                    const GeomVertexArrayDataPipelineReader *reader) {
   DXVertexBufferContext9 *dvbc = DCAST(DXVertexBufferContext9, vbc);
 
   DBG_SH3 dxgsg9_cat.debug ( ) << "apply_vertex_buffer\n"; DBG_E
@@ -414,14 +416,14 @@ apply_vertex_buffer(VertexBufferContext *vbc, CLP(ShaderContext) *shader_context
   if (dvbc->_vbuffer == NULL) {
     // Attempt to create a new vertex buffer.
     if (vertex_buffers &&
-        dvbc->get_data()->get_usage_hint() != Geom::UH_client) {
-      dvbc->create_vbuffer(*_screen);
+        reader->get_usage_hint() != Geom::UH_client) {
+      dvbc->create_vbuffer(*_screen, reader);
     }
 
     if (dvbc->_vbuffer != NULL) {
-      dvbc->upload_data();
+      dvbc->upload_data(reader);
 
-      dvbc->mark_loaded();
+      dvbc->mark_loaded(reader);
 
       set_stream_source = true;
 
@@ -430,16 +432,16 @@ apply_vertex_buffer(VertexBufferContext *vbc, CLP(ShaderContext) *shader_context
     }
 
   } else {
-    if (dvbc->was_modified()) {
-      if (dvbc->changed_size()) {
+    if (dvbc->was_modified(reader)) {
+      if (dvbc->changed_size(reader)) {
         // We have to destroy the old vertex buffer and create a new
         // one.
-        dvbc->create_vbuffer(*_screen);
+        dvbc->create_vbuffer(*_screen, reader);
       }
 
-      dvbc->upload_data();
+      dvbc->upload_data(reader);
 
-      dvbc->mark_loaded();
+      dvbc->mark_loaded(reader);
       _active_vbuffer = NULL;
     }
 
@@ -452,7 +454,7 @@ apply_vertex_buffer(VertexBufferContext *vbc, CLP(ShaderContext) *shader_context
     // FVF MODE
     if (set_stream_source) {
       hr = _d3d_device->SetStreamSource
-        (stream, dvbc->_vbuffer, offset, dvbc->get_data()->get_array_format()->get_stride());
+        (stream, dvbc->_vbuffer, offset, reader->get_array_format()->get_stride());
       if (FAILED(hr)) {
         dxgsg9_cat.error()
           << "SetStreamSource failed" << D3DERRORSTRING(hr);
@@ -543,7 +545,7 @@ apply_vertex_buffer(VertexBufferContext *vbc, CLP(ShaderContext) *shader_context
 
       offset = 0;
       hr = _d3d_device->SetStreamSource
-        (stream, dvbc->_vbuffer, offset, dvbc->get_data()->get_array_format()->get_stride());
+        (stream, dvbc->_vbuffer, offset, reader->get_array_format()->get_stride());
       if (FAILED(hr)) {
         dxgsg9_cat.error()
           << "SetStreamSource failed" << D3DERRORSTRING(hr);
@@ -606,7 +608,8 @@ prepare_index_buffer(GeomPrimitive *data) {
 //               makes it the current index buffer for rendering.
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian9::
-apply_index_buffer(IndexBufferContext *ibc) {
+apply_index_buffer(IndexBufferContext *ibc,
+                   const GeomPrimitivePipelineReader *reader) {
   DXIndexBufferContext9 *dibc = DCAST(DXIndexBufferContext9, ibc);
 
   if (_lru) {
@@ -615,11 +618,11 @@ apply_index_buffer(IndexBufferContext *ibc) {
 
   if (dibc->_ibuffer == NULL) {
     // Attempt to create a new index buffer.
-    dibc->create_ibuffer(*_screen);
+    dibc->create_ibuffer(*_screen, reader);
 
     if (dibc->_ibuffer != NULL) {
-      dibc->upload_data();
-      dibc->mark_loaded();
+      dibc->upload_data(reader);
+      dibc->mark_loaded(reader);
 
       _d3d_device->SetIndices(dibc->_ibuffer);
       _active_ibuffer = dibc;
@@ -631,16 +634,16 @@ apply_index_buffer(IndexBufferContext *ibc) {
     }
 
   } else {
-    if (dibc->was_modified()) {
-      if (dibc->changed_size()) {
+    if (dibc->was_modified(reader)) {
+      if (dibc->changed_size(reader)) {
         // We have to destroy the old index buffer and create a new
         // one.
-        dibc->create_ibuffer(*_screen);
+        dibc->create_ibuffer(*_screen, reader);
       }
 
-      dibc->upload_data();
+      dibc->upload_data(reader);
 
-      dibc->mark_loaded();
+      dibc->mark_loaded(reader);
       _active_ibuffer = NULL;
     }
 
@@ -673,9 +676,9 @@ release_index_buffer(IndexBufferContext *ibc) {
 //               appropriate to this GSG for the indicated state.
 ////////////////////////////////////////////////////////////////////
 PT(GeomMunger) DXGraphicsStateGuardian9::
-make_geom_munger(const RenderState *state) {
+make_geom_munger(const RenderState *state, Thread *current_thread) {
   PT(DXGeomMunger9) munger = new DXGeomMunger9(this, state);
-  return GeomMunger::register_munger(munger);
+  return GeomMunger::register_munger(munger, current_thread);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1286,7 +1289,7 @@ vertex_element_array -> vertex_element_type_array;
 
   VertexBufferContext *vbc = ((GeomVertexArrayData *)(data->get_object()))->prepare_now(get_prepared_objects(), this);
   nassertr(vbc != (VertexBufferContext *)NULL, false);
-  apply_vertex_buffer(vbc, _current_shader_context);
+  apply_vertex_buffer(vbc, _current_shader_context, data);
 
   const GeomVertexAnimationSpec &animation =
     data_reader->get_format()->get_animation();
@@ -1394,7 +1397,7 @@ draw_triangles(const GeomPrimitivePipelineReader *reader) {
       // Indexed, vbuffers.
       IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
       nassertv(ibc != (IndexBufferContext *)NULL);
-      apply_index_buffer(ibc);
+      apply_index_buffer(ibc, reader);
 
 //DBG_SH2 dxgsg9_cat.debug ( ) << "DrawIndexedPrimitive \n"; DBG_E
 
@@ -1469,7 +1472,7 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader) {
         // Indexed, vbuffers, one line triangle strip.
         IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
         nassertv(ibc != (IndexBufferContext *)NULL);
-        apply_index_buffer(ibc);
+        apply_index_buffer(ibc, reader);
 
 //dxgsg9_cat.error ( ) << "DrawIndexedPrimitive D3DPT_TRIANGLESTRIP VERTICES: " << reader->get_num_vertices ( ) << "\n";
 
@@ -1534,7 +1537,7 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader) {
         // Indexed, vbuffers, individual triangle strips.
         IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
         nassertv(ibc != (IndexBufferContext *)NULL);
-        apply_index_buffer(ibc);
+        apply_index_buffer(ibc, reader);
 
         unsigned int start = 0;
         for (size_t i = 0; i < ends.size(); i++) {
@@ -1638,7 +1641,7 @@ draw_trifans(const GeomPrimitivePipelineReader *reader) {
       // Indexed, vbuffers.
       IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
       nassertv(ibc != (IndexBufferContext *)NULL);
-      apply_index_buffer(ibc);
+      apply_index_buffer(ibc, reader);
 
       unsigned int start = 0;
       for (size_t i = 0; i < ends.size(); i++) {
@@ -1728,7 +1731,7 @@ draw_lines(const GeomPrimitivePipelineReader *reader) {
       // Indexed, vbuffers.
       IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
       nassertv(ibc != (IndexBufferContext *)NULL);
-      apply_index_buffer(ibc);
+      apply_index_buffer(ibc, reader);
 
       _d3d_device->DrawIndexedPrimitive
         (D3DPT_LINELIST,
@@ -2211,10 +2214,12 @@ bool vertex_buffer_page_in_function (LruPage *lru_page)
   vertex_buffer = (DXVertexBufferContext9 *) lru_page -> _m.lru_page_type.pointer;
 
   // allocate vertex buffer
-  vertex_buffer -> allocate_vbuffer (*(gsg->_screen));
+  Thread *current_thread = Thread::get_current_thread();
+  GeomVertexArrayDataPipelineReader reader(vertex_buffer->get_data(), current_thread);  
+  vertex_buffer -> allocate_vbuffer (*(gsg->_screen), &reader);
 
   // update vertex buffer
-  vertex_buffer -> upload_data ( );
+  vertex_buffer -> upload_data(&reader);
   vertex_buffer -> set_resident(true);
 
   if (DEBUG_LRU && dxgsg9_cat.is_debug())
@@ -2250,10 +2255,12 @@ bool index_buffer_page_in_function (LruPage *lru_page)
   index_buffer = (DXIndexBufferContext9 *) lru_page -> _m.lru_page_type.pointer;
 
   // allocate vertex buffer
-  index_buffer -> allocate_ibuffer (*(gsg->_screen));
+  Thread *current_thread = Thread::get_current_thread();
+  GeomPrimitivePipelineReader reader(index_buffer->get_data(), current_thread);  
+  index_buffer -> allocate_ibuffer (*(gsg->_screen), &reader);
 
   // update vertex buffer
-  index_buffer -> upload_data ( );
+  index_buffer -> upload_data (&reader);
   index_buffer -> set_resident(true);
 
   if (DEBUG_LRU && dxgsg9_cat.is_debug())

+ 6 - 3
panda/src/dxgsg9/dxGraphicsStateGuardian9.h

@@ -89,14 +89,17 @@ public:
   void release_shader(ShaderContext *sc);
 
   virtual VertexBufferContext *prepare_vertex_buffer(GeomVertexArrayData *data);
-  void apply_vertex_buffer(VertexBufferContext *vbc, CLP(ShaderContext) *shader_context);
+  void apply_vertex_buffer(VertexBufferContext *vbc, CLP(ShaderContext) *shader_context,
+                           const GeomVertexArrayDataPipelineReader *reader);
   virtual void release_vertex_buffer(VertexBufferContext *vbc);
 
   virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data);
-  void apply_index_buffer(IndexBufferContext *ibc);
+  void apply_index_buffer(IndexBufferContext *ibc,
+                          const GeomPrimitivePipelineReader *reader);
   virtual void release_index_buffer(IndexBufferContext *ibc);
 
-  virtual PT(GeomMunger) make_geom_munger(const RenderState *state);
+  virtual PT(GeomMunger) make_geom_munger(const RenderState *state,
+                                          Thread *current_thread);
 
   virtual void set_color_clear_value(const Colorf &value);
 

+ 22 - 13
panda/src/dxgsg9/dxIndexBufferContext9.cxx

@@ -90,16 +90,17 @@ free_ibuffer(void) {
 //  Description: Allocates index buffer memory.
 ////////////////////////////////////////////////////////////////////
 void DXIndexBufferContext9::
-allocate_ibuffer(DXScreenData &scrn) {
+allocate_ibuffer(DXScreenData &scrn,
+                 const GeomPrimitivePipelineReader *reader) {
 
   D3DFORMAT index_type =
-    DXGraphicsStateGuardian9::get_index_type(get_data()->get_index_type());
+    DXGraphicsStateGuardian9::get_index_type(reader->get_index_type());
 
   int data_size;
   DWORD usage;
   D3DPOOL pool;
 
-  data_size = get_data()->get_data_size_bytes();
+  data_size = reader->get_data_size_bytes();
 
   _managed = scrn._managed_index_buffers;
   if (_managed)
@@ -133,8 +134,8 @@ allocate_ibuffer(DXScreenData &scrn) {
     if (DEBUG_INDEX_BUFFER && dxgsg9_cat.is_debug()) {
       dxgsg9_cat.debug()
         << "creating index buffer " << _ibuffer << ": "
-        << get_data()->get_num_vertices() << " indices ("
-        << get_data()->get_vertices()->get_array_format()->get_column(0)->get_numeric_type()
+        << reader->get_num_vertices() << " indices ("
+        << reader->get_vertices_reader()->get_array_format()->get_column(0)->get_numeric_type()
         << ")\n";
     }
   }
@@ -147,7 +148,10 @@ allocate_ibuffer(DXScreenData &scrn) {
 //               to it).
 ////////////////////////////////////////////////////////////////////
 void DXIndexBufferContext9::
-create_ibuffer(DXScreenData &scrn) {
+create_ibuffer(DXScreenData &scrn, 
+               const GeomPrimitivePipelineReader *reader) {
+  nassertv(reader->get_object() == get_data());
+  Thread *current_thread = reader->get_current_thread();
 
   this -> free_ibuffer ( );
 
@@ -158,13 +162,14 @@ create_ibuffer(DXScreenData &scrn) {
     _lru_page = 0;
   }
 
-  PStatTimer timer(GraphicsStateGuardian::_create_index_buffer_pcollector);
+  PStatTimer timer(GraphicsStateGuardian::_create_index_buffer_pcollector,
+                   current_thread);
 
   int data_size;
 
-  data_size = get_data()->get_data_size_bytes();
+  data_size = reader->get_data_size_bytes();
 
-  this -> allocate_ibuffer(scrn);
+  this -> allocate_ibuffer(scrn, reader);
 
   if (_ibuffer)
   {
@@ -198,11 +203,15 @@ create_ibuffer(DXScreenData &scrn) {
 //               DirectX.
 ////////////////////////////////////////////////////////////////////
 void DXIndexBufferContext9::
-upload_data() {
+upload_data(const GeomPrimitivePipelineReader *reader) {
+  nassertv(reader->get_object() == get_data());
+  Thread *current_thread = reader->get_current_thread();
+
   nassertv(_ibuffer != NULL);
-  PStatTimer timer(GraphicsStateGuardian::_load_index_buffer_pcollector);
+  PStatTimer timer(GraphicsStateGuardian::_load_index_buffer_pcollector,
+                   current_thread);
 
-  int data_size = get_data()->get_data_size_bytes();
+  int data_size = reader->get_data_size_bytes();
 
   if (dxgsg9_cat.is_spam()) {
     dxgsg9_cat.spam()
@@ -228,7 +237,7 @@ upload_data() {
   }
 
   GraphicsStateGuardian::_data_transferred_pcollector.add_level(data_size);
-  memcpy(local_pointer, get_data()->get_data(), data_size);
+  memcpy(local_pointer, reader->get_data(), data_size);
 
   _ibuffer->Unlock();
 }

+ 4 - 4
panda/src/dxgsg9/dxIndexBufferContext9.h

@@ -33,10 +33,10 @@ public:
   DXIndexBufferContext9(PreparedGraphicsObjects *pgo, GeomPrimitive *data);
   virtual ~DXIndexBufferContext9();
 
-  void free_ibuffer(void);
-  void allocate_ibuffer(DXScreenData &scrn);
-  void create_ibuffer(DXScreenData &scrn);
-  void upload_data();
+  void free_ibuffer();
+  void allocate_ibuffer(DXScreenData &scrn, const GeomPrimitivePipelineReader *reader);
+  void create_ibuffer(DXScreenData &scrn, const GeomPrimitivePipelineReader *reader);
+  void upload_data(const GeomPrimitivePipelineReader *reader);
 
   IDirect3DIndexBuffer9 *_ibuffer;
   int _managed;

+ 20 - 12
panda/src/dxgsg9/dxVertexBufferContext9.cxx

@@ -340,14 +340,15 @@ free_vbuffer(void) {
 //  Description: Allocates vertex buffer memory.
 ////////////////////////////////////////////////////////////////////
 void DXVertexBufferContext9::
-allocate_vbuffer(DXScreenData &scrn) {
+allocate_vbuffer(DXScreenData &scrn,
+                 const GeomVertexArrayDataPipelineReader *reader) {
 
   int data_size;
   HRESULT hr;
   DWORD usage;
   D3DPOOL pool;
 
-  data_size = get_data()->get_data_size_bytes();
+  data_size = reader->get_data_size_bytes();
 
   _managed = scrn._managed_vertex_buffers;
   if (_managed) {
@@ -378,8 +379,8 @@ allocate_vbuffer(DXScreenData &scrn) {
     if (DEBUG_VERTEX_BUFFER && dxgsg9_cat.is_debug()) {
       dxgsg9_cat.debug()
         << "created vertex buffer " << _vbuffer << ": "
-        << get_data()->get_num_rows() << " vertices "
-        << *get_data()->get_array_format() << "\n";
+        << reader->get_num_rows() << " vertices "
+        << *reader->get_array_format() << "\n";
     }
   }
 }
@@ -391,7 +392,10 @@ allocate_vbuffer(DXScreenData &scrn) {
 //               to it).
 ////////////////////////////////////////////////////////////////////
 void DXVertexBufferContext9::
-create_vbuffer(DXScreenData &scrn) {
+create_vbuffer(DXScreenData &scrn,
+               const GeomVertexArrayDataPipelineReader *reader) {
+  nassertv(reader->get_object() == get_data());
+  Thread *current_thread = reader->get_current_thread();
 
   free_vbuffer ( );
 
@@ -401,13 +405,14 @@ create_vbuffer(DXScreenData &scrn) {
     _lru_page = 0;
   }
 
-  PStatTimer timer(GraphicsStateGuardian::_create_vertex_buffer_pcollector);
+  PStatTimer timer(GraphicsStateGuardian::_create_vertex_buffer_pcollector,
+                   current_thread);
 
   int data_size;
 
-  data_size = get_data()->get_data_size_bytes();
+  data_size = reader->get_data_size_bytes();
 
-  this -> allocate_vbuffer(scrn);
+  this -> allocate_vbuffer(scrn, reader);
 
   if (_vbuffer) {
     if (_managed == false) {
@@ -437,11 +442,14 @@ create_vbuffer(DXScreenData &scrn) {
 //               DirectX.
 ////////////////////////////////////////////////////////////////////
 void DXVertexBufferContext9::
-upload_data() {
+upload_data(const GeomVertexArrayDataPipelineReader *reader) {
+  nassertv(reader->get_object() == get_data());
   nassertv(_vbuffer != NULL);
-  PStatTimer timer(GraphicsStateGuardian::_load_vertex_buffer_pcollector);
+  Thread *current_thread = reader->get_current_thread();
+  PStatTimer timer(GraphicsStateGuardian::_load_vertex_buffer_pcollector,
+                   current_thread);
 
-  int data_size = get_data()->get_data_size_bytes();
+  int data_size = reader->get_data_size_bytes();
 
   if (dxgsg9_cat.is_spam()) {
     dxgsg9_cat.spam()
@@ -465,7 +473,7 @@ upload_data() {
   }
 
   GraphicsStateGuardian::_data_transferred_pcollector.add_level(data_size);
-  memcpy(local_pointer, get_data()->get_data(), data_size);
+  memcpy(local_pointer, reader->get_data(), data_size);
 
   _vbuffer->Unlock();
 }

+ 4 - 4
panda/src/dxgsg9/dxVertexBufferContext9.h

@@ -33,10 +33,10 @@ public:
   DXVertexBufferContext9(PreparedGraphicsObjects *pgo, GeomVertexArrayData *data, DXScreenData &scrn);
   virtual ~DXVertexBufferContext9();
 
-  void free_vbuffer(void);
-  void allocate_vbuffer(DXScreenData &scrn);
-  void create_vbuffer(DXScreenData &scrn);
-  void upload_data();
+  void free_vbuffer();
+  void allocate_vbuffer(DXScreenData &scrn, const GeomVertexArrayDataPipelineReader *reader);
+  void create_vbuffer(DXScreenData &scrn, const GeomVertexArrayDataPipelineReader *reader);
+  void upload_data(const GeomVertexArrayDataPipelineReader *reader);
 
   IDirect3DVertexBuffer9 *_vbuffer;
   int _fvf;

+ 8 - 9
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -2634,7 +2634,7 @@ apply_vertex_buffer(VertexBufferContext *vbc,
     gvbc->set_active(true);
   }
 
-  if (gvbc->was_modified()) {
+  if (gvbc->was_modified(reader)) {
     PStatTimer timer(_load_vertex_buffer_pcollector, reader->get_current_thread());
     int num_bytes = reader->get_data_size_bytes();
     if (GLCAT.is_spam()) {
@@ -2643,7 +2643,7 @@ apply_vertex_buffer(VertexBufferContext *vbc,
         << " bytes into vertex buffer " << gvbc->_index << "\n";
     }
     if (num_bytes != 0) {
-      if (gvbc->changed_size() || gvbc->changed_usage_hint()) {
+      if (gvbc->changed_size(reader) || gvbc->changed_usage_hint(reader)) {
         _glBufferData(GL_ARRAY_BUFFER, num_bytes,
                       reader->get_data(),
                       get_usage(reader->get_usage_hint()));
@@ -2655,7 +2655,7 @@ apply_vertex_buffer(VertexBufferContext *vbc,
       _data_transferred_pcollector.add_level(num_bytes);
     }
 
-    gvbc->mark_loaded();
+    gvbc->mark_loaded(reader);
   }
 
   report_my_gl_errors();
@@ -2803,7 +2803,7 @@ apply_index_buffer(IndexBufferContext *ibc,
     gibc->set_active(true);
   }
 
-  if (gibc->was_modified()) {
+  if (gibc->was_modified(reader)) {
     PStatTimer timer(_load_index_buffer_pcollector, reader->get_current_thread());
     int num_bytes = reader->get_data_size_bytes();
     if (GLCAT.is_spam()) {
@@ -2812,7 +2812,7 @@ apply_index_buffer(IndexBufferContext *ibc,
         << " bytes into index buffer " << gibc->_index << "\n";
     }
     if (num_bytes != 0) {
-      if (gibc->changed_size() || gibc->changed_usage_hint()) {
+      if (gibc->changed_size(reader) || gibc->changed_usage_hint(reader)) {
         _glBufferData(GL_ELEMENT_ARRAY_BUFFER, num_bytes,
                       reader->get_data(),
                       get_usage(reader->get_usage_hint()));
@@ -2823,7 +2823,7 @@ apply_index_buffer(IndexBufferContext *ibc,
       }
       _data_transferred_pcollector.add_level(num_bytes);
     }
-    gibc->mark_loaded();
+    gibc->mark_loaded(reader);
   }
 
   report_my_gl_errors();
@@ -2967,9 +2967,9 @@ end_occlusion_query() {
 //               appropriate to this GSG for the indicated state.
 ////////////////////////////////////////////////////////////////////
 PT(GeomMunger) CLP(GraphicsStateGuardian)::
-make_geom_munger(const RenderState *state) {
+make_geom_munger(const RenderState *state, Thread *current_thread) {
   PT(CLP(GeomMunger)) munger = new CLP(GeomMunger)(this, state);
-  return GeomMunger::register_munger(munger);
+  return GeomMunger::register_munger(munger, current_thread);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -3035,7 +3035,6 @@ framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr,
 
   TextureContext *tc = tex->prepare_now(get_prepared_objects(), this);
   nassertv(tc != (TextureContext *)NULL);
-  CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
   apply_texture(tc);
 
   if (z >= 0) {

+ 2 - 1
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -143,7 +143,8 @@ public:
   virtual void begin_occlusion_query();
   virtual PT(OcclusionQueryContext) end_occlusion_query();
 
-  virtual PT(GeomMunger) make_geom_munger(const RenderState *state);
+  virtual PT(GeomMunger) make_geom_munger(const RenderState *state,
+                                          Thread *current_thread);
 
   virtual void framebuffer_copy_to_texture
     (Texture *tex, int z, const DisplayRegion *dr, const RenderBuffer &rb);

+ 5 - 4
panda/src/gobj/geom.I

@@ -133,11 +133,12 @@ get_primitive(int i) const {
 ////////////////////////////////////////////////////////////////////
 INLINE GeomPrimitive *Geom::
 modify_primitive(int i) {
-  CDWriter cdata(_cycler, true);
+  Thread *current_thread = Thread::get_current_thread();
+  CDWriter cdata(_cycler, true, current_thread);
   nassertr(i >= 0 && i < (int)cdata->_primitives.size(), NULL);
   cdata->_got_usage_hint = false;
   cdata->_modified = Geom::get_next_modified();
-  clear_cache_stage();
+  clear_cache_stage(current_thread);
   if (cdata->_primitives[i]->get_ref_count() > 1) {
     cdata->_primitives[i] = cdata->_primitives[i]->make_copy();
   }
@@ -199,8 +200,8 @@ unify() const {
 //               tested separately.
 ////////////////////////////////////////////////////////////////////
 INLINE UpdateSeq Geom::
-get_modified() const {
-  CDReader cdata(_cycler);
+get_modified(Thread *current_thread) const {
+  CDReader cdata(_cycler, current_thread);
   return cdata->_modified;
 }
 

+ 38 - 26
panda/src/gobj/geom.cxx

@@ -119,7 +119,8 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 void Geom::
 set_usage_hint(Geom::UsageHint usage_hint) {
-  CDWriter cdata(_cycler, true);
+  Thread *current_thread = Thread::get_current_thread();
+  CDWriter cdata(_cycler, true, current_thread);
   cdata->_usage_hint = usage_hint;
 
   Primitives::iterator pi;
@@ -130,7 +131,7 @@ set_usage_hint(Geom::UsageHint usage_hint) {
     (*pi)->set_usage_hint(usage_hint);
   }
 
-  clear_cache_stage();
+  clear_cache_stage(current_thread);
   cdata->_modified = Geom::get_next_modified();
 }
 
@@ -147,14 +148,15 @@ set_usage_hint(Geom::UsageHint usage_hint) {
 ////////////////////////////////////////////////////////////////////
 PT(GeomVertexData) Geom::
 modify_vertex_data() {
+  Thread *current_thread = Thread::get_current_thread();
   // Perform copy-on-write: if the reference count on the vertex data
   // is greater than 1, assume some other Geom has the same pointer,
   // so make a copy of it first.
-  CDWriter cdata(_cycler, true);
+  CDWriter cdata(_cycler, true, current_thread);
   if (cdata->_data->get_ref_count() > 1) {
     cdata->_data = new GeomVertexData(*cdata->_data);
   }
-  clear_cache_stage();
+  clear_cache_stage(current_thread);
   mark_internal_bounds_stale(cdata);
   return cdata->_data;
 }
@@ -171,10 +173,11 @@ modify_vertex_data() {
 ////////////////////////////////////////////////////////////////////
 void Geom::
 set_vertex_data(const GeomVertexData *data) {
+  Thread *current_thread = Thread::get_current_thread();
   nassertv(check_will_be_valid(data));
-  CDWriter cdata(_cycler, true);
+  CDWriter cdata(_cycler, true, current_thread);
   cdata->_data = (GeomVertexData *)data;
-  clear_cache_stage();
+  clear_cache_stage(current_thread);
   mark_internal_bounds_stale(cdata);
   reset_geom_rendering(cdata);
 }
@@ -196,7 +199,8 @@ set_vertex_data(const GeomVertexData *data) {
 ////////////////////////////////////////////////////////////////////
 void Geom::
 offset_vertices(const GeomVertexData *data, int offset) {
-  CDWriter cdata(_cycler, true);
+  Thread *current_thread = Thread::get_current_thread();
+  CDWriter cdata(_cycler, true, current_thread);
   cdata->_data = (GeomVertexData *)data;
 
 #ifndef NDEBUG
@@ -217,7 +221,7 @@ offset_vertices(const GeomVertexData *data, int offset) {
   }
 
   cdata->_modified = Geom::get_next_modified();
-  clear_cache_stage();
+  clear_cache_stage(current_thread);
   nassertv(all_is_valid);
 }
 
@@ -236,9 +240,10 @@ offset_vertices(const GeomVertexData *data, int offset) {
 ////////////////////////////////////////////////////////////////////
 int Geom::
 make_nonindexed(bool composite_only) {
+  Thread *current_thread = Thread::get_current_thread();
   int num_changed = 0;
 
-  CDWriter cdata(_cycler, true);
+  CDWriter cdata(_cycler, true, current_thread);
   CPT(GeomVertexData) orig_data = cdata->_data;
   PT(GeomVertexData) new_data = new GeomVertexData(*cdata->_data);
   new_data->clear_rows();
@@ -285,7 +290,7 @@ make_nonindexed(bool composite_only) {
     cdata->_data = new_data;
     cdata->_primitives.swap(new_prims);
     cdata->_modified = Geom::get_next_modified();
-    clear_cache_stage();
+    clear_cache_stage(current_thread);
   }
 
   return num_changed;
@@ -303,7 +308,8 @@ make_nonindexed(bool composite_only) {
 ////////////////////////////////////////////////////////////////////
 void Geom::
 set_primitive(int i, const GeomPrimitive *primitive) {
-  CDWriter cdata(_cycler, true);
+  Thread *current_thread = Thread::get_current_thread();
+  CDWriter cdata(_cycler, true, current_thread);
   nassertv(i >= 0 && i < (int)cdata->_primitives.size());
   nassertv(primitive->check_valid(cdata->_data));
 
@@ -330,7 +336,7 @@ set_primitive(int i, const GeomPrimitive *primitive) {
   reset_geom_rendering(cdata);
   cdata->_got_usage_hint = false;
   cdata->_modified = Geom::get_next_modified();
-  clear_cache_stage();
+  clear_cache_stage(current_thread);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -347,7 +353,8 @@ set_primitive(int i, const GeomPrimitive *primitive) {
 ////////////////////////////////////////////////////////////////////
 void Geom::
 add_primitive(const GeomPrimitive *primitive) {
-  CDWriter cdata(_cycler, true);
+  Thread *current_thread = Thread::get_current_thread();
+  CDWriter cdata(_cycler, true, current_thread);
 
   nassertv(primitive->check_valid(cdata->_data));
 
@@ -374,7 +381,7 @@ add_primitive(const GeomPrimitive *primitive) {
   reset_geom_rendering(cdata);
   cdata->_got_usage_hint = false;
   cdata->_modified = Geom::get_next_modified();
-  clear_cache_stage();
+  clear_cache_stage(current_thread);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -388,7 +395,8 @@ add_primitive(const GeomPrimitive *primitive) {
 ////////////////////////////////////////////////////////////////////
 void Geom::
 remove_primitive(int i) {
-  CDWriter cdata(_cycler, true);
+  Thread *current_thread = Thread::get_current_thread();
+  CDWriter cdata(_cycler, true, current_thread);
   nassertv(i >= 0 && i < (int)cdata->_primitives.size());
   cdata->_primitives.erase(cdata->_primitives.begin() + i);
   if (cdata->_primitives.empty()) {
@@ -398,7 +406,7 @@ remove_primitive(int i) {
   reset_geom_rendering(cdata);
   cdata->_got_usage_hint = false;
   cdata->_modified = Geom::get_next_modified();
-  clear_cache_stage();
+  clear_cache_stage(current_thread);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -415,12 +423,13 @@ remove_primitive(int i) {
 ////////////////////////////////////////////////////////////////////
 void Geom::
 clear_primitives() {
-  CDWriter cdata(_cycler, true);
+  Thread *current_thread = Thread::get_current_thread();
+  CDWriter cdata(_cycler, true, current_thread);
   cdata->_primitives.clear();
   cdata->_primitive_type = PT_none;
   cdata->_shade_model = SM_uniform;
   reset_geom_rendering(cdata);
-  clear_cache_stage();
+  clear_cache_stage(current_thread);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -436,7 +445,8 @@ clear_primitives() {
 ////////////////////////////////////////////////////////////////////
 void Geom::
 decompose_in_place() {
-  CDWriter cdata(_cycler, true);
+  Thread *current_thread = Thread::get_current_thread();
+  CDWriter cdata(_cycler, true, current_thread);
 
 #ifndef NDEBUG
   bool all_is_valid = true;
@@ -455,7 +465,7 @@ decompose_in_place() {
 
   cdata->_modified = Geom::get_next_modified();
   reset_geom_rendering(cdata);
-  clear_cache_stage();
+  clear_cache_stage(current_thread);
 
   nassertv(all_is_valid);
 }
@@ -473,7 +483,8 @@ decompose_in_place() {
 ////////////////////////////////////////////////////////////////////
 void Geom::
 rotate_in_place() {
-  CDWriter cdata(_cycler, true);
+  Thread *current_thread = Thread::get_current_thread();
+  CDWriter cdata(_cycler, true, current_thread);
 
 #ifndef NDEBUG
   bool all_is_valid = true;
@@ -491,7 +502,7 @@ rotate_in_place() {
   }
 
   cdata->_modified = Geom::get_next_modified();
-  clear_cache_stage();
+  clear_cache_stage(current_thread);
 
   nassertv(all_is_valid);
 }
@@ -511,13 +522,14 @@ rotate_in_place() {
 ////////////////////////////////////////////////////////////////////
 void Geom::
 unify_in_place() {
+  Thread *current_thread = Thread::get_current_thread();
   if (get_num_primitives() <= 1) {
     // If we don't have more than one primitive to start with, no need
     // to do anything.
     return;
   }
 
-  CDWriter cdata(_cycler, true);
+  CDWriter cdata(_cycler, true, current_thread);
 
   PT(GeomPrimitive) new_prim;
 
@@ -563,7 +575,7 @@ unify_in_place() {
   cdata->_primitives.push_back(new_prim);
 
   cdata->_modified = Geom::get_next_modified();
-  clear_cache_stage();
+  clear_cache_stage(current_thread);
   reset_geom_rendering(cdata);
 }
 
@@ -810,12 +822,12 @@ clear_cache() {
 //               have recently made in an upstream thread.
 ////////////////////////////////////////////////////////////////////
 void Geom::
-clear_cache_stage() {
+clear_cache_stage(Thread *current_thread) {
   for (Cache::iterator ci = _cache.begin();
        ci != _cache.end();
        ++ci) {
     CacheEntry *entry = (*ci);
-    CDCacheWriter cdata(entry->_cycler);
+    CDCacheWriter cdata(entry->_cycler, current_thread);
     cdata->set_result(NULL, NULL);
   }
 }

+ 2 - 2
panda/src/gobj/geom.h

@@ -99,7 +99,7 @@ PUBLISHED:
   virtual bool copy_primitives_from(const Geom *other);
 
   int get_num_bytes() const;
-  INLINE UpdateSeq get_modified() const;
+  INLINE UpdateSeq get_modified(Thread *current_thread = Thread::get_current_thread()) const;
 
   void transform_vertices(const LMatrix4f &mat);
   bool check_valid() const;
@@ -114,7 +114,7 @@ PUBLISHED:
   virtual void write(ostream &out, int indent_level = 0) const;
 
   void clear_cache();
-  void clear_cache_stage();
+  void clear_cache_stage(Thread *current_thread);
 
   void prepare(PreparedGraphicsObjects *prepared_objects);
   bool release(PreparedGraphicsObjects *prepared_objects);

+ 4 - 4
panda/src/gobj/geomCacheEntry.cxx

@@ -38,7 +38,7 @@ GeomCacheEntry::
 //               time.
 ////////////////////////////////////////////////////////////////////
 PT(GeomCacheEntry) GeomCacheEntry::
-record() {
+record(Thread *current_thread) {
   nassertr(_next == (GeomCacheEntry *)NULL && _prev == (GeomCacheEntry *)NULL, NULL);
   PT(GeomCacheEntry) keepme = this;
 
@@ -55,7 +55,7 @@ record() {
   ++cache_mgr->_total_size;
   cache_mgr->_geom_cache_size_pcollector.set_level(cache_mgr->_total_size);
   cache_mgr->_geom_cache_record_pcollector.add_level(1);
-  _last_frame_used = ClockObject::get_global_clock()->get_frame_count();
+  _last_frame_used = ClockObject::get_global_clock()->get_frame_count(current_thread);
 
   if (PStatClient::is_connected()) {
     GeomCacheManager::_geom_cache_active_pcollector.add_level(1);
@@ -81,7 +81,7 @@ record() {
 //               be evicted for a while.
 ////////////////////////////////////////////////////////////////////
 void GeomCacheEntry::
-refresh() {
+refresh(Thread *current_thread) {
   nassertv(_next != (GeomCacheEntry *)NULL && _prev != (GeomCacheEntry *)NULL);
 
   GeomCacheManager *cache_mgr = GeomCacheManager::get_global_ptr();
@@ -90,7 +90,7 @@ refresh() {
   remove_from_list();
   insert_before(cache_mgr->_list);
 
-  int current_frame = ClockObject::get_global_clock()->get_frame_count();
+  int current_frame = ClockObject::get_global_clock()->get_frame_count(current_thread);
   if (PStatClient::is_connected()) {
     if (_last_frame_used != current_frame) {
       GeomCacheManager::_geom_cache_active_pcollector.add_level(1);

+ 2 - 2
panda/src/gobj/geomCacheEntry.h

@@ -40,8 +40,8 @@ public:
   INLINE GeomCacheEntry();
   virtual ~GeomCacheEntry();
 
-  PT(GeomCacheEntry) record();
-  void refresh();
+  PT(GeomCacheEntry) record(Thread *current_thread);
+  void refresh(Thread *current_thread);
   PT(GeomCacheEntry) erase();
 
   virtual void evict_callback();

+ 2 - 2
panda/src/gobj/geomMunger.I

@@ -46,8 +46,8 @@ is_registered() const {
 //               this point on.
 ////////////////////////////////////////////////////////////////////
 INLINE PT(GeomMunger) GeomMunger::
-register_munger(GeomMunger *munger) {
-  return get_registry()->register_munger(munger);
+register_munger(GeomMunger *munger, Thread *current_thread) {
+  return get_registry()->register_munger(munger, current_thread);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 8 - 8
panda/src/gobj/geomMunger.cxx

@@ -121,14 +121,14 @@ munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data,
     // Here's an element in the cache for this computation.  Record a
     // cache hit, so this element will stay in the cache a while
     // longer.
-    entry->refresh();
+    entry->refresh(current_thread);
 
     // Now check that it's fresh.
     Geom::CDCacheReader cdata(entry->_cycler, current_thread);
     nassertv(cdata->_source == geom);
     if (cdata->_geom_result != (Geom *)NULL &&
-	geom->get_modified() <= cdata->_geom_result->get_modified() &&
-	data->get_modified() <= cdata->_data_result->get_modified()) {
+	geom->get_modified(current_thread) <= cdata->_geom_result->get_modified(current_thread) &&
+	data->get_modified(current_thread) <= cdata->_data_result->get_modified(current_thread)) {
       // The cache entry is still good; use it.
       
       geom = cdata->_geom_result;
@@ -159,7 +159,7 @@ munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data,
     // And tell the cache manager about the new entry.  (It might
     // immediately request a delete from the cache of the thing we
     // just added.)
-    entry->record();
+    entry->record(current_thread);
   }
 
   // Finally, store the cached result on the entry.
@@ -292,7 +292,7 @@ make_registry() {
 //  Description: Called internally when the munger is registered.
 ////////////////////////////////////////////////////////////////////
 void GeomMunger::
-do_register() {
+do_register(Thread *current_thread) {
   if (gobj_cat.is_debug()) {
     gobj_cat.debug()
       << "GeomMunger::do_register(): " << (void *)this << "\n";
@@ -305,7 +305,7 @@ do_register() {
   // over again.
   CacheEntry *entry = new CacheEntry;
   entry->_munger = this;
-  entry->record();
+  entry->record(current_thread);
 
   _is_registered = true;
 }
@@ -363,7 +363,7 @@ Registry() {
 //               this point on.
 ////////////////////////////////////////////////////////////////////
 PT(GeomMunger) GeomMunger::Registry::
-register_munger(GeomMunger *munger) {
+register_munger(GeomMunger *munger, Thread *current_thread) {
   if (munger->is_registered()) {
     return munger;
   }
@@ -377,7 +377,7 @@ register_munger(GeomMunger *munger) {
   GeomMunger *new_munger = (*mi);
   if (!new_munger->is_registered()) {
     new_munger->_registered_key = mi;
-    new_munger->do_register();
+    new_munger->do_register(current_thread);
   }
 
   return new_munger;

+ 3 - 3
panda/src/gobj/geomMunger.h

@@ -64,7 +64,7 @@ public:
   virtual ~GeomMunger();
 
   INLINE bool is_registered() const;
-  INLINE static PT(GeomMunger) register_munger(GeomMunger *munger);
+  INLINE static PT(GeomMunger) register_munger(GeomMunger *munger, Thread *current_thread);
 
   INLINE CPT(GeomVertexFormat) munge_format(const GeomVertexFormat *format,
                                               const GeomVertexAnimationSpec &animation) const;
@@ -96,7 +96,7 @@ private:
   INLINE static Registry *get_registry();
   static void make_registry();
 
-  void do_register();
+  void do_register(Thread *current_thread);
   void do_unregister();
 
 private:
@@ -116,7 +116,7 @@ private:
   class EXPCL_PANDA Registry {
   public:
     Registry();
-    PT(GeomMunger) register_munger(GeomMunger *munger);
+    PT(GeomMunger) register_munger(GeomMunger *munger, Thread *current_thread);
     void unregister_munger(GeomMunger *munger);
 
     Mungers _mungers;

+ 10 - 0
panda/src/gobj/geomPrimitive.I

@@ -679,6 +679,16 @@ get_index_stride() const {
   return _cdata->_vertices->get_array_format()->get_stride();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::get_vertices_reader
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE const GeomVertexArrayDataPipelineReader *GeomPrimitivePipelineReader::
+get_vertices_reader() const {
+  return _vertices_reader;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitivePipelineReader::get_data
 //       Access: Public

+ 1 - 0
panda/src/gobj/geomPrimitive.h

@@ -325,6 +325,7 @@ public:
   INLINE UpdateSeq get_modified() const;
   bool check_valid(const GeomVertexDataPipelineReader *data_reader) const;
   INLINE int get_index_stride() const;
+  INLINE const GeomVertexArrayDataPipelineReader *get_vertices_reader() const;
   INLINE CPTA_uchar get_data() const;
   INLINE CPTA_int get_ends() const;
   INLINE const GeomVertexArrayData *get_mins() const;

+ 2 - 2
panda/src/gobj/geomVertexData.I

@@ -300,8 +300,8 @@ get_num_bytes() const {
 //               modified.
 ////////////////////////////////////////////////////////////////////
 INLINE UpdateSeq GeomVertexData::
-get_modified() const {
-  CDReader cdata(_cycler);
+get_modified(Thread *current_thread) const {
+  CDReader cdata(_cycler, current_thread);
   return cdata->_modified;
 }
 

+ 4 - 2
panda/src/gobj/geomVertexData.cxx

@@ -660,6 +660,8 @@ copy_row_from(int dest_row, const GeomVertexData *source,
 ////////////////////////////////////////////////////////////////////
 CPT(GeomVertexData) GeomVertexData::
 convert_to(const GeomVertexFormat *new_format) const {
+  Thread *current_thread = Thread::get_current_thread();
+
   if (new_format == get_format()) {
     // Trivial case: no change is needed.
     return this;
@@ -679,7 +681,7 @@ convert_to(const GeomVertexFormat *new_format) const {
     // Here's an element in the cache for this computation.  Record a
     // cache hit, so this element will stay in the cache a while
     // longer.
-    entry->refresh();
+    entry->refresh(current_thread);
 
     CDCacheReader cdata(entry->_cycler);
     if (cdata->_result != (GeomVertexData *)NULL) {
@@ -717,7 +719,7 @@ convert_to(const GeomVertexFormat *new_format) const {
     // And tell the cache manager about the new entry.  (It might
     // immediately request a delete from the cache of the thing we
     // just added.)
-    entry->record();
+    entry->record(current_thread);
   }
 
   // Finally, store the cached result on the entry.

+ 1 - 1
panda/src/gobj/geomVertexData.h

@@ -122,7 +122,7 @@ PUBLISHED:
   INLINE void clear_slider_table();
 
   INLINE int get_num_bytes() const;
-  INLINE UpdateSeq get_modified() const;
+  INLINE UpdateSeq get_modified(Thread *current_thread = Thread::get_current_thread()) const;
 
   void copy_from(const GeomVertexData *source, bool keep_data_objects);
   void copy_row_from(int dest_row, const GeomVertexData *source, 

+ 14 - 10
panda/src/gobj/indexBufferContext.I

@@ -47,8 +47,9 @@ get_data() const {
 //               last time mark_loaded() was called.
 ////////////////////////////////////////////////////////////////////
 INLINE bool IndexBufferContext::
-changed_size() const {
-  return get_data_size_bytes() != (size_t)_data->get_data_size_bytes();
+changed_size(const GeomPrimitivePipelineReader *reader) const {
+  nassertr(reader->get_object() == _data, false);
+  return get_data_size_bytes() != (size_t)reader->get_data_size_bytes();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -58,8 +59,9 @@ changed_size() const {
 //               since the last time mark_loaded() was called.
 ////////////////////////////////////////////////////////////////////
 INLINE bool IndexBufferContext::
-changed_usage_hint() const {
-  return _usage_hint != _data->get_usage_hint();
+changed_usage_hint(const GeomPrimitivePipelineReader *reader) const {
+  nassertr(reader->get_object() == _data, false);
+  return _usage_hint != reader->get_usage_hint();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -69,8 +71,9 @@ changed_usage_hint() const {
 //               last time mark_loaded() was called.
 ////////////////////////////////////////////////////////////////////
 INLINE bool IndexBufferContext::
-was_modified() const {
-  return get_modified() != _data->get_modified();
+was_modified(const GeomPrimitivePipelineReader *reader) const {
+  nassertr(reader->get_object() == _data, false);
+  return get_modified() != reader->get_modified();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -81,10 +84,11 @@ was_modified() const {
 //               internal flags for changed_size() and modified().
 ////////////////////////////////////////////////////////////////////
 INLINE void IndexBufferContext::
-mark_loaded() {
-  update_data_size_bytes(_data->get_data_size_bytes());
-  update_modified(_data->get_modified());
-  _usage_hint = _data->get_usage_hint();
+mark_loaded(const GeomPrimitivePipelineReader *reader) {
+  nassertv(reader->get_object() == _data);
+  update_data_size_bytes(reader->get_data_size_bytes());
+  update_modified(reader->get_modified());
+  _usage_hint = reader->get_usage_hint();
 
   // Assume the buffer is now resident.
   set_resident(true);

+ 4 - 4
panda/src/gobj/indexBufferContext.h

@@ -42,11 +42,11 @@ public:
 
   INLINE GeomPrimitive *get_data() const;
 
-  INLINE bool changed_size() const;
-  INLINE bool changed_usage_hint() const;
-  INLINE bool was_modified() const;
+  INLINE bool changed_size(const GeomPrimitivePipelineReader *reader) const;
+  INLINE bool changed_usage_hint(const GeomPrimitivePipelineReader *reader) const;
+  INLINE bool was_modified(const GeomPrimitivePipelineReader *reader) const;
 
-  INLINE void mark_loaded();
+  INLINE void mark_loaded(const GeomPrimitivePipelineReader *reader);
 
 private:
   // This cannot be a PT(GeomPrimitive), because the data and

+ 14 - 10
panda/src/gobj/vertexBufferContext.I

@@ -47,8 +47,9 @@ get_data() const {
 //               last time mark_loaded() was called.
 ////////////////////////////////////////////////////////////////////
 INLINE bool VertexBufferContext::
-changed_size() const {
-  return get_data_size_bytes() != (size_t)_data->get_data_size_bytes();
+changed_size(const GeomVertexArrayDataPipelineReader *reader) const {
+  nassertr(reader->get_object() == _data, false);
+  return get_data_size_bytes() != (size_t)reader->get_data_size_bytes();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -58,8 +59,9 @@ changed_size() const {
 //               since the last time mark_loaded() was called.
 ////////////////////////////////////////////////////////////////////
 INLINE bool VertexBufferContext::
-changed_usage_hint() const {
-  return _usage_hint != _data->get_usage_hint();
+changed_usage_hint(const GeomVertexArrayDataPipelineReader *reader) const {
+  nassertr(reader->get_object() == _data, false);
+  return _usage_hint != reader->get_usage_hint();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -69,8 +71,9 @@ changed_usage_hint() const {
 //               last time mark_loaded() was called.
 ////////////////////////////////////////////////////////////////////
 INLINE bool VertexBufferContext::
-was_modified() const {
-  return get_modified() != _data->get_modified();
+was_modified(const GeomVertexArrayDataPipelineReader *reader) const {
+  nassertr(reader->get_object() == _data, false);
+  return get_modified() != reader->get_modified();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -81,10 +84,11 @@ was_modified() const {
 //               internal flags for changed_size() and modified().
 ////////////////////////////////////////////////////////////////////
 INLINE void VertexBufferContext::
-mark_loaded() {
-  update_data_size_bytes(_data->get_data_size_bytes());
-  update_modified(_data->get_modified());
-  _usage_hint = _data->get_usage_hint();
+mark_loaded(const GeomVertexArrayDataPipelineReader *reader) {
+  nassertv(reader->get_object() == _data);
+  update_data_size_bytes(reader->get_data_size_bytes());
+  update_modified(reader->get_modified());
+  _usage_hint = reader->get_usage_hint();
 
   // Assume the buffer is now resident.
   set_resident(true);

+ 4 - 4
panda/src/gobj/vertexBufferContext.h

@@ -43,11 +43,11 @@ public:
 
   INLINE GeomVertexArrayData *get_data() const;
 
-  INLINE bool changed_size() const;
-  INLINE bool changed_usage_hint() const;
-  INLINE bool was_modified() const;
+  INLINE bool changed_size(const GeomVertexArrayDataPipelineReader *reader) const;
+  INLINE bool changed_usage_hint(const GeomVertexArrayDataPipelineReader *reader) const;
+  INLINE bool was_modified(const GeomVertexArrayDataPipelineReader *reader) const;
 
-  INLINE void mark_loaded();
+  INLINE void mark_loaded(const GeomVertexArrayDataPipelineReader *reader);
 
 private:
   // This cannot be a PT(GeomVertexArrayData), because the data and

+ 4 - 2
panda/src/grutil/frameRateMeter.I

@@ -77,7 +77,8 @@ get_update_interval() const {
 INLINE void FrameRateMeter::
 set_text_pattern(const string &text_pattern) {
   _text_pattern = text_pattern;
-  do_update();
+  Thread *current_thread = Thread::get_current_thread();
+  do_update(current_thread);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -125,5 +126,6 @@ get_clock_object() const {
 ////////////////////////////////////////////////////////////////////
 INLINE void FrameRateMeter::
 update() {
-  do_update();
+  Thread *current_thread = Thread::get_current_thread();
+  do_update(current_thread);
 }

+ 11 - 7
panda/src/grutil/frameRateMeter.cxx

@@ -38,6 +38,8 @@ TypeHandle FrameRateMeter::_type_handle;
 ////////////////////////////////////////////////////////////////////
 FrameRateMeter::
 FrameRateMeter(const string &name) : TextNode(name) {
+  Thread *current_thread = Thread::get_current_thread();
+
   _update_interval = frame_rate_meter_update_interval;
   _last_update = 0.0f;
   _text_pattern = frame_rate_meter_text_pattern;
@@ -50,7 +52,7 @@ FrameRateMeter(const string &name) : TextNode(name) {
   set_card_as_margin(frame_rate_meter_side_margins, frame_rate_meter_side_margins, 0.1f, 0.0f);
   set_usage_hint(Geom::UH_client);
 
-  do_update();
+  do_update(current_thread);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -146,14 +148,16 @@ clear_window() {
 ////////////////////////////////////////////////////////////////////
 bool FrameRateMeter::
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
+  Thread *current_thread = trav->get_current_thread();
+
   // Statistics
-  PStatTimer timer(_show_fps_pcollector);
+  PStatTimer timer(_show_fps_pcollector, current_thread);
   
   // Check to see if it's time to update.
-  double now = _clock_object->get_frame_time();
+  double now = _clock_object->get_frame_time(current_thread);
   double elapsed = now - _last_update;
   if (elapsed < 0.0 || elapsed >= _update_interval) {
-    do_update();
+    do_update(current_thread);
   }
 
   return TextNode::cull_callback(trav, data);
@@ -165,10 +169,10 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
 //  Description: Resets the text according to the current frame rate.
 ////////////////////////////////////////////////////////////////////
 void FrameRateMeter::
-do_update() {
-  _last_update = _clock_object->get_frame_time();
+do_update(Thread *current_thread) {
+  _last_update = _clock_object->get_frame_time(current_thread);
 
-  double frame_rate = _clock_object->get_average_frame_rate();
+  double frame_rate = _clock_object->get_average_frame_rate(current_thread);
 
   static const size_t buffer_size = 1024;
   char buffer[buffer_size];

+ 1 - 1
panda/src/grutil/frameRateMeter.h

@@ -68,7 +68,7 @@ protected:
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
 
 private:
-  void do_update();
+  void do_update(Thread *current_thread);
 
 private:
   PT(GraphicsOutput) _window;

+ 3 - 1
panda/src/gsgbase/graphicsStateGuardianBase.h

@@ -26,6 +26,7 @@
 
 // A handful of forward references.
 
+class Thread;
 class RenderBuffer;
 class GraphicsWindow;
 class NodePath;
@@ -139,7 +140,8 @@ public:
   virtual void begin_occlusion_query()=0;
   virtual PT(OcclusionQueryContext) end_occlusion_query()=0;
 
-  virtual PT(GeomMunger) get_geom_munger(const RenderState *state)=0;
+  virtual PT(GeomMunger) get_geom_munger(const RenderState *state,
+                                         Thread *current_thread)=0;
 
   virtual void set_state_and_transform(const RenderState *state,
                                        const TransformState *transform)=0;

+ 2 - 2
panda/src/pgraph/camera.cxx

@@ -247,10 +247,10 @@ list_aux_scene_data(ostream &out) const {
 //               elements released.
 ////////////////////////////////////////////////////////////////////
 int Camera::
-cleanup_aux_scene_data() {
+cleanup_aux_scene_data(Thread *current_thread) {
   int num_deleted = 0;
 
-  double now = ClockObject::get_global_clock()->get_frame_time();
+  double now = ClockObject::get_global_clock()->get_frame_time(current_thread);
 
   AuxData::iterator ai;
   ai = _aux_data.begin();

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

@@ -82,7 +82,7 @@ PUBLISHED:
   bool clear_aux_scene_data(const NodePath &node_path);
   AuxSceneData *get_aux_scene_data(const NodePath &node_path) const;
   void list_aux_scene_data(ostream &out) const;
-  int cleanup_aux_scene_data();
+  int cleanup_aux_scene_data(Thread *current_thread = Thread::get_current_thread());
 
 private:
   void add_display_region(DisplayRegion *display_region);

+ 2 - 2
panda/src/pgraph/cullResult.cxx

@@ -167,7 +167,7 @@ add_object(CullableObject *object, const CullTraverser *traverser) {
                 get_dual_transparent_state();
               transparent_part->_state = state->compose(transparent_state);
               transparent_part->munge_geom
-                (_gsg, _gsg->get_geom_munger(transparent_part->_state),
+                (_gsg, _gsg->get_geom_munger(transparent_part->_state, current_thread),
                  traverser);
               CullBin *bin = get_bin(transparent_part->_state->get_bin_index());
               nassertv(bin != (CullBin *)NULL);
@@ -207,7 +207,7 @@ add_object(CullableObject *object, const CullTraverser *traverser) {
 
   // Munge vertices as needed for the GSG's requirements, and the
   // object's current state.
-  object->munge_geom(_gsg, _gsg->get_geom_munger(object->_state), traverser);
+  object->munge_geom(_gsg, _gsg->get_geom_munger(object->_state, current_thread), traverser);
   bin->add_object(object, current_thread);
 }
 

+ 14 - 12
panda/src/pgraph/cullTraverser.cxx

@@ -162,7 +162,7 @@ traverse(CullTraverserData &data) {
 
     PandaNode *node = data.node();
 
-    const RenderEffects *node_effects = node->get_effects();
+    const RenderEffects *node_effects = node->get_effects(_current_thread);
     if (node_effects->has_show_bounds()) {
       // If we should show the bounding volume for this node, make it
       // up now.
@@ -171,7 +171,7 @@ traverse(CullTraverserData &data) {
 
     data.apply_transform_and_state(this);
 
-    const FogAttrib *fog = node->get_state()->get_fog();
+    const FogAttrib *fog = node->get_state(_current_thread)->get_fog();
     if (fog != (const FogAttrib *)NULL && fog->get_fog() != (Fog *)NULL) {
       // If we just introduced a FogAttrib here, call adjust_to_camera()
       // now.  This maybe isn't the perfect time to call it, but it's
@@ -204,7 +204,7 @@ traverse_below(CullTraverserData &data) {
 
   bool this_node_hidden = data.is_this_node_hidden(this);
 
-  const RenderEffects *node_effects = node->get_effects();
+  const RenderEffects *node_effects = node->get_effects(_current_thread);
   bool has_decal = !this_node_hidden && node_effects->has_decal();
   if (has_decal && !_depth_offset_decals) {
     // Start the three-pass decal rendering if we're not using
@@ -223,10 +223,11 @@ traverse_below(CullTraverserData &data) {
       }
       
       // Get all the Geoms, with no decalling.
-      int num_geoms = geom_node->get_num_geoms();
+      GeomNode::Geoms geoms = geom_node->get_geoms(_current_thread);
+      int num_geoms = geoms.get_num_geoms();
       _geoms_pcollector.add_level(num_geoms);
       for (int i = 0; i < num_geoms; i++) {
-        CullableObject *object = new CullableObject(this, data, geom_node, i);
+        CullableObject *object = new CullableObject(this, data, geoms, i);
         if (object->_state->has_cull_callback() &&
             !object->_state->cull_callback(this, data)) {
           delete object;
@@ -251,7 +252,7 @@ traverse_below(CullTraverserData &data) {
     }
 
     // Now visit all the node's children.
-    PandaNode::Children children = node->get_children();
+    PandaNode::Children children = node->get_children(_current_thread);
     int num_children = children.get_num_children();
     if (node->has_selective_visibility()) {
       int i = node->get_first_visible_child();
@@ -530,7 +531,7 @@ start_decal(const CullTraverserData &data) {
   // Since the CullableObject is a linked list which gets built in
   // LIFO order, we start with the decals.
   CullableObject *decals = (CullableObject *)NULL;
-  PandaNode::Children cr = node->get_children();
+  PandaNode::Children cr = node->get_children(_current_thread);
   int num_children = cr.get_num_children();
   if (node->has_selective_visibility()) {
     int i = node->get_first_visible_child();
@@ -554,11 +555,12 @@ start_decal(const CullTraverserData &data) {
   // And now get the base Geoms, again in reverse order.
   CullableObject *object = separator;
   GeomNode *geom_node = DCAST(GeomNode, node);
-  int num_geoms = geom_node->get_num_geoms();
+  GeomNode::Geoms geoms = geom_node->get_geoms();
+  int num_geoms = geoms.get_num_geoms();
   _geoms_pcollector.add_level(num_geoms);
   for (int i = num_geoms - 1; i >= 0; i--) {
     CullableObject *next_object = 
-      new CullableObject(this, data, geom_node, i, object);
+      new CullableObject(this, data, geoms, i, object);
     if (next_object->_state->has_cull_callback() &&
         !next_object->_state->cull_callback(this, data)) {
       next_object->_next = NULL;
@@ -622,12 +624,12 @@ r_get_decals(CullTraverserData &data, CullableObject *decals) {
     // Now, tack on any geoms within the node.
     if (node->is_geom_node()) {
       GeomNode *geom_node = DCAST(GeomNode, node);
-      
-      int num_geoms = geom_node->get_num_geoms();
+      GeomNode::Geoms geoms = geom_node->get_geoms();
+      int num_geoms = geoms.get_num_geoms();
       _geoms_pcollector.add_level(num_geoms);
       for (int i = num_geoms - 1; i >= 0; i--) {
         CullableObject *next_decals = 
-          new CullableObject(this, data, geom_node, i, decals);
+          new CullableObject(this, data, geoms, i, decals);
         if (next_decals->_state->has_cull_callback() &&
             !next_decals->_state->cull_callback(this, data)) {
           next_decals->_next = NULL;

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

@@ -148,7 +148,7 @@ is_in_view(const DrawMask &camera_mask, Thread *current_thread) {
   }
 
   // Otherwise, compare the bounding volume to the frustum.
-  return is_in_view_impl();
+  return is_in_view_impl(current_thread);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 13 - 11
panda/src/pgraph/cullTraverserData.cxx

@@ -51,22 +51,24 @@ get_modelview_transform(const CullTraverser *trav) const {
 ////////////////////////////////////////////////////////////////////
 void CullTraverserData::
 apply_transform_and_state(CullTraverser *trav) {
+  Thread *current_thread = trav->get_current_thread();
   PandaNode *node = _node_path.node();
-  CPT(RenderState) node_state = node->get_state();
+  CPT(RenderState) node_state = node->get_state(current_thread);
 
-  if (trav->has_tag_state_key() && node->has_tag(trav->get_tag_state_key())) {
+  if (trav->has_tag_state_key() && 
+      node->has_tag(trav->get_tag_state_key(), current_thread)) {
     // Here's a node that has been tagged with the special key for our
     // current camera.  This indicates some special state transition
     // for this node, which is unique to this camera.
     const Camera *camera = trav->get_scene()->get_camera_node();
-    string tag_state = node->get_tag(trav->get_tag_state_key());
+    string tag_state = node->get_tag(trav->get_tag_state_key(), current_thread);
     node_state = node_state->compose(camera->get_tag_state(tag_state));
   }
-  node->compose_draw_mask(_draw_mask);
+  node->compose_draw_mask(_draw_mask, current_thread);
 
-  apply_transform_and_state(trav, node->get_transform(),
-                            node_state, node->get_effects(),
-                            node->get_off_clip_planes());
+  apply_transform_and_state(trav, node->get_transform(current_thread),
+                            node_state, node->get_effects(current_thread),
+                            node->get_off_clip_planes(current_thread));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -135,8 +137,8 @@ apply_transform_and_state(CullTraverser *trav,
 //  Description: The private implementation of is_in_view().
 ////////////////////////////////////////////////////////////////////
 bool CullTraverserData::
-is_in_view_impl() {
-  CPT(BoundingVolume) node_volume = node()->get_bounds();
+is_in_view_impl(Thread *current_thread) {
+  CPT(BoundingVolume) node_volume = node()->get_bounds(current_thread);
   nassertr(node_volume->is_of_type(GeometricBoundingVolume::get_class_type()), false);
   const GeometricBoundingVolume *node_gbv =
     DCAST(GeometricBoundingVolume, node_volume);
@@ -171,7 +173,7 @@ is_in_view_impl() {
     } else {
       // The node is partially, but not completely, within the viewing
       // frustum.
-      if (node()->is_final()) {
+      if (node()->is_final(current_thread)) {
         // Normally we'd keep testing child bounding volumes as we
         // continue down.  But this node has the "final" flag, so the
         // user is claiming that there is some important reason we
@@ -205,7 +207,7 @@ is_in_view_impl() {
       
     } else {
       // The node is partially within one or more clip planes.
-      if (node()->is_final()) {
+      if (node()->is_final(current_thread)) {
         // Even though the node is only partially within the clip
         // planes, stop culling against them.
         _cull_planes = CullPlanes::make_empty();

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

@@ -82,7 +82,7 @@ public:
   DrawMask _draw_mask;
 
 private:
-  bool is_in_view_impl();
+  bool is_in_view_impl(Thread *current_thread);
   int test_within_clip_planes_impl(const CullTraverser *trav,
                                    const ClipPlaneAttrib *cpa) const;
 

+ 3 - 3
panda/src/pgraph/cullableObject.I

@@ -37,10 +37,10 @@ CullableObject(CullableObject *next) :
 ////////////////////////////////////////////////////////////////////
 INLINE CullableObject::
 CullableObject(const CullTraverser *trav, const CullTraverserData &data,
-               GeomNode *geom_node, int i,
+               const GeomNode::Geoms &geoms, int i,
                CullableObject *next) :
-  _geom(geom_node->get_geom(i)),
-  _state(data._state->compose(geom_node->get_geom_state(i))),
+  _geom(geoms.get_geom(i)),
+  _state(data._state->compose(geoms.get_geom_state(i))),
   _net_transform(data.get_net_transform(trav)),
   _modelview_transform(data.get_modelview_transform(trav)),
   _internal_transform(trav->get_gsg()->get_cs_transform()->compose(_modelview_transform)),

+ 3 - 2
panda/src/pgraph/cullableObject.cxx

@@ -49,8 +49,8 @@ TypeHandle CullableObject::_type_handle;
 void CullableObject::
 munge_geom(GraphicsStateGuardianBase *gsg,
            GeomMunger *munger, const CullTraverser *traverser) {
+  Thread *current_thread = traverser->get_current_thread();
   if (_geom != (Geom *)NULL) {
-    Thread *current_thread = traverser->get_current_thread();
     _munger = munger;
 
     GeomPipelineReader geom_reader(_geom, current_thread);
@@ -145,7 +145,8 @@ munge_geom(GraphicsStateGuardianBase *gsg,
   }
   if (_next != (CullableObject *)NULL) {
     if (_next->_state != (RenderState *)NULL) {
-      _next->munge_geom(gsg, gsg->get_geom_munger(_next->_state), traverser);
+      _next->munge_geom(gsg, gsg->get_geom_munger(_next->_state, current_thread),
+                        traverser);
     } else {
       _next->munge_geom(gsg, munger, traverser);
     }

+ 1 - 2
panda/src/pgraph/cullableObject.h

@@ -21,7 +21,6 @@
 
 #include "pandabase.h"
 
-#include "geom.h"
 #include "geom.h"
 #include "geomVertexData.h"
 #include "geomMunger.h"
@@ -48,7 +47,7 @@ public:
   INLINE CullableObject(CullableObject *next = NULL);
   INLINE CullableObject(const CullTraverser *trav,
                         const CullTraverserData &data,
-                        GeomNode *geom_node, int i,
+                        const GeomNode::Geoms &geoms, int i,
                         CullableObject *next = NULL);
   INLINE CullableObject(const Geom *geom, const RenderState *state,
                         const TransformState *net_transform,

+ 181 - 34
panda/src/pgraph/geomNode.I

@@ -17,27 +17,6 @@
 ////////////////////////////////////////////////////////////////////
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomNode::GeomEntry::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE GeomNode::GeomEntry::
-GeomEntry(Geom *geom, const RenderState *state) :
-  _geom(geom),
-  _state(state)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomNode::CData::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE GeomNode::CData::
-CData() {
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomNode::get_num_geoms
 //       Access: Public
@@ -46,7 +25,7 @@ CData() {
 INLINE int GeomNode::
 get_num_geoms() const {
   CDReader cdata(_cycler);
-  return cdata->_geoms.size();
+  return cdata->get_geoms()->size();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -60,8 +39,9 @@ get_num_geoms() const {
 INLINE const Geom *GeomNode::
 get_geom(int n) const {
   CDReader cdata(_cycler);
-  nassertr(n >= 0 && n < (int)cdata->_geoms.size(), NULL);
-  return cdata->_geoms[n]._geom;
+  const GeomList &geoms = *(cdata->get_geoms());
+  nassertr(n >= 0 && n < (int)geoms.size(), NULL);
+  return geoms[n]._geom;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -98,10 +78,11 @@ get_unique_geom(int n) {
 INLINE Geom *GeomNode::
 modify_geom(int n) {
   CDWriter cdata(_cycler, true);
-  nassertr(n >= 0 && n < (int)cdata->_geoms.size(), NULL);
-  Geom *geom = cdata->_geoms[n]._geom;
+  GeomList &geoms = *(cdata->modify_geoms());
+  nassertr(n >= 0 && n < (int)geoms.size(), NULL);
+  Geom *geom = geoms[n]._geom;
   if (geom->get_ref_count() > 1) {
-    geom = cdata->_geoms[n]._geom = geom->make_copy();
+    geom = geoms[n]._geom = geom->make_copy();
   }
 
   return geom;
@@ -120,8 +101,9 @@ modify_geom(int n) {
 INLINE const RenderState *GeomNode::
 get_geom_state(int n) const {
   CDReader cdata(_cycler);
-  nassertr(n >= 0 && n < (int)cdata->_geoms.size(), NULL);
-  return cdata->_geoms[n]._state;
+  const GeomList &geoms = *(cdata->get_geoms());
+  nassertr(n >= 0 && n < (int)geoms.size(), NULL);
+  return geoms[n]._state;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -144,8 +126,9 @@ get_geom_state(int n) const {
 INLINE void GeomNode::
 set_geom_state(int n, const RenderState *state) {
   CDWriter cdata(_cycler, true);
-  nassertv(n >= 0 && n < (int)cdata->_geoms.size());
-  cdata->_geoms[n]._state = state;
+  GeomList &geoms = *(cdata->modify_geoms());
+  nassertv(n >= 0 && n < (int)geoms.size());
+  geoms[n]._state = state;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -156,9 +139,10 @@ set_geom_state(int n, const RenderState *state) {
 INLINE void GeomNode::
 remove_geom(int n) {
   CDWriter cdata(_cycler);
-  nassertv(n >= 0 && n < (int)cdata->_geoms.size());
+  GeomList &geoms = *(cdata->modify_geoms());
+  nassertv(n >= 0 && n < (int)geoms.size());
 
-  cdata->_geoms.erase(cdata->_geoms.begin() + n);
+  geoms.erase(geoms.begin() + n);
   mark_internal_bounds_stale();
 }
 
@@ -170,7 +154,7 @@ remove_geom(int n) {
 INLINE void GeomNode::
 remove_all_geoms() {
   CDWriter cdata(_cycler);
-  cdata->_geoms.clear();
+  cdata->set_geoms(new GeomList);
   mark_internal_bounds_stale();
 }
 
@@ -213,3 +197,166 @@ get_name_count(const GeomNode::NameCount &name_count, const InternalName *name)
   }
   return 0;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::get_geoms
+//       Access: Public
+//  Description: Returns an object that can be used to walk through
+//               the list of geoms of the node.  When you intend to
+//               visit multiple geoms, using this is slightly
+//               faster than calling get_geom() directly on the
+//               GeomNode, since this object avoids reopening the
+//               PipelineCycler each time.
+//
+//               This object also protects you from self-modifying
+//               loops (e.g. adding or removing geoms during
+//               traversal), since a virtual copy of the geoms is
+//               made ahead of time.  The virtual copy is fast--it is
+//               a form of copy-on-write, so the list is not actually
+//               copied unless it is modified during the traversal.
+////////////////////////////////////////////////////////////////////
+INLINE GeomNode::Geoms GeomNode::
+get_geoms(Thread *current_thread) const {
+  CDReader cdata(_cycler, current_thread);
+  return Geoms(cdata);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::GeomEntry::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GeomNode::GeomEntry::
+GeomEntry(Geom *geom, const RenderState *state) :
+  _geom(geom),
+  _state(state)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::CData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GeomNode::CData::
+CData() :
+  _geoms(new GeomNode::GeomList)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::CData::get_geoms
+//       Access: Public
+//  Description: Returns a read-only pointer to the _geoms list.
+////////////////////////////////////////////////////////////////////
+INLINE const GeomNode::GeomList *GeomNode::CData::
+get_geoms() const {
+  return _geoms;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::CData::modify_geoms
+//       Access: Public
+//  Description: Returns a modifiable, unique pointer to the _geoms
+//               list.
+////////////////////////////////////////////////////////////////////
+INLINE GeomNode::GeomList *GeomNode::CData::
+modify_geoms() {
+  if (_geoms->get_ref_count() > 1) {
+    _geoms = new GeomList(*_geoms);
+  }
+  return _geoms;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::CData::set_geoms
+//       Access: Public
+//  Description: Replaces the _geoms list with a new list.
+////////////////////////////////////////////////////////////////////
+INLINE void GeomNode::CData::
+set_geoms(GeomNode::GeomList *geoms) {
+  _geoms = geoms;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::Geoms::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GeomNode::Geoms::
+Geoms() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::Geoms::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GeomNode::Geoms::
+Geoms(const GeomNode::CData *cdata) :
+  _geoms(cdata->get_geoms())
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::Geoms::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GeomNode::Geoms::
+Geoms(const GeomNode::Geoms &copy) :
+  _geoms(copy._geoms)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::Geoms::Copy Assignment Operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void GeomNode::Geoms::
+operator = (const GeomNode::Geoms &copy) {
+  _geoms = copy._geoms;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::Geoms::get_num_geoms
+//       Access: Public
+//  Description: Returns the number of geoms of the node.
+////////////////////////////////////////////////////////////////////
+INLINE int GeomNode::Geoms::
+get_num_geoms() const {
+  nassertr(_geoms != (GeomList *)NULL, 0);
+  return _geoms->size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::Geoms::get_geom
+//       Access: Public
+//  Description: Returns the nth geom of the node.  This object should
+//               not be modified, since the same object might be
+//               shared between multiple different GeomNodes.
+////////////////////////////////////////////////////////////////////
+INLINE const Geom *GeomNode::Geoms::
+get_geom(int n) const {
+  nassertr(_geoms != (GeomList *)NULL, NULL);
+  nassertr(n >= 0 && n < (int)_geoms->size(), NULL);
+  return (*_geoms)[n]._geom;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::Geoms::get_geom_state
+//       Access: Public
+//  Description: Returns the RenderState associated with the nth geom
+//               of the node.  This is just the RenderState directly
+//               associated with the Geom; the actual state in which
+//               the Geom is rendered will also be affected by
+//               RenderStates that appear on the scene graph in nodes
+//               above this GeomNode.
+////////////////////////////////////////////////////////////////////
+INLINE const RenderState *GeomNode::Geoms::
+get_geom_state(int n) const {
+  nassertr(_geoms != (GeomList *)NULL, NULL);
+  nassertr(n >= 0 && n < (int)_geoms->size(), NULL);
+  return (*_geoms)[n]._state;
+}

+ 122 - 113
panda/src/pgraph/geomNode.cxx

@@ -35,88 +35,6 @@
 
 TypeHandle GeomNode::_type_handle;
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomNode::CData::Copy Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-GeomNode::CData::
-CData(const GeomNode::CData &copy) :
-  _geoms(copy._geoms)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomNode::CData::make_copy
-//       Access: Public, Virtual
-//  Description:
-////////////////////////////////////////////////////////////////////
-CycleData *GeomNode::CData::
-make_copy() const {
-  return new CData(*this);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomNode::CData::write_datagram
-//       Access: Public, Virtual
-//  Description: Writes the contents of this object to the datagram
-//               for shipping out to a Bam file.
-////////////////////////////////////////////////////////////////////
-void GeomNode::CData::
-write_datagram(BamWriter *manager, Datagram &dg) const {
-  int num_geoms = _geoms.size();
-  nassertv(num_geoms == (int)(PN_uint16)num_geoms);
-  dg.add_uint16(num_geoms);
-  
-  Geoms::const_iterator gi;
-  for (gi = _geoms.begin(); gi != _geoms.end(); ++gi) {
-    const GeomEntry &entry = (*gi);
-    manager->write_pointer(dg, entry._geom);
-    manager->write_pointer(dg, entry._state);
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomNode::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 GeomNode::CData::
-complete_pointers(TypedWritable **p_list, BamReader *manager) {
-  int pi = CycleData::complete_pointers(p_list, manager);
-
-  // Get the geom and state pointers.
-  Geoms::iterator gi;
-  for (gi = _geoms.begin(); gi != _geoms.end(); ++gi) {
-    GeomEntry &entry = (*gi);
-    entry._geom = DCAST(Geom, p_list[pi++]);
-    entry._state = DCAST(RenderState, p_list[pi++]);
-  }
-
-  return pi;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomNode::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 GeomNode.
-////////////////////////////////////////////////////////////////////
-void GeomNode::CData::
-fillin(DatagramIterator &scan, BamReader *manager) {
-  int num_geoms = scan.get_uint16();
-  // Read the list of geoms and states.  Push back a NULL for each one.
-  _geoms.reserve(num_geoms);
-  for (int i = 0; i < num_geoms; i++) {
-    manager->read_pointer(scan);
-    manager->read_pointer(scan);
-    _geoms.push_back(GeomEntry(NULL, NULL));
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomNode::Constructor
 //       Access: Published
@@ -194,8 +112,9 @@ apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
   Thread *current_thread = Thread::get_current_thread();
   OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
     CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
-    Geoms::iterator gi;
-    for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+    GeomList::iterator gi;
+    GeomList &geoms = *(cdata->modify_geoms());
+    for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
       GeomEntry &entry = (*gi);
       PT(Geom) new_geom = entry._geom->make_copy();
       
@@ -228,7 +147,7 @@ apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
         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
+          // 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.
@@ -357,8 +276,9 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point, bool &found_any,
   const LMatrix4f &mat = next_transform->get_mat();
 
   CDReader cdata(_cycler, current_thread);
-  Geoms::const_iterator gi;
-  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+  GeomList::const_iterator gi;
+  const GeomList &geoms = *(cdata->get_geoms());
+  for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
     const Geom *geom = (*gi)._geom;
     geom->calc_tight_bounds(min_point, max_point, found_any,
 			    geom->get_vertex_data(current_thread)->animate_vertices(current_thread),
@@ -418,7 +338,7 @@ add_geom(Geom *geom, const RenderState *state) {
   OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
     CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
 
-    cdata->_geoms.push_back(GeomEntry(geom, state));
+    cdata->modify_geoms()->push_back(GeomEntry(geom, state));
   }
   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
 
@@ -438,13 +358,13 @@ add_geoms_from(const GeomNode *other) {
     CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
     CDStageReader cdata_other(other->_cycler, pipeline_stage, current_thread);
 
-    Geoms::const_iterator gi;
-    for (gi = cdata_other->_geoms.begin(); 
-         gi != cdata_other->_geoms.end(); 
-         ++gi) {
+    GeomList::const_iterator gi;
+    const GeomList &other_geoms = *(cdata_other->get_geoms());
+    GeomList &this_geoms = *(cdata->modify_geoms());
+    for (gi = other_geoms.begin(); gi != other_geoms.end(); ++gi) {
       const GeomEntry &entry = (*gi);
       nassertv(entry._geom->check_valid());
-      cdata->_geoms.push_back(entry);
+      this_geoms.push_back(entry);
     }
   }
   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
@@ -471,8 +391,9 @@ set_geom(int n, Geom *geom) {
   nassertv(geom->check_valid());
 
   CDWriter cdata(_cycler, true);
-  nassertv(n >= 0 && n < (int)cdata->_geoms.size());
-  cdata->_geoms[n]._geom = geom;
+  GeomList &geoms = *(cdata->modify_geoms());
+  nassertv(n >= 0 && n < (int)geoms.size());
+  geoms[n]._geom = geom;
 
   mark_internal_bounds_stale();
 }
@@ -522,18 +443,19 @@ unify() {
   OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
     CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
 
-    Geoms new_geoms;
+    PT(GeomList) new_geoms = new GeomList;
 
     // 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) {
+    GeomList::const_iterator gi;
+    const GeomList &geoms = *(cdata->get_geoms());
+    for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
       const GeomEntry &entry = (*gi);
       
       bool unified = false;
-      Geoms::iterator gj;
-      for (gj = new_geoms.begin(); gj != new_geoms.end() && !unified; ++gj) {
+      GeomList::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.
@@ -547,16 +469,15 @@ unify() {
       if (!unified) {
         // Couldn't unify this Geom with anything, so just add it to the
         // output list.
-        new_geoms.push_back(entry);
+        new_geoms->push_back(entry);
       }
     }
     
     // Done!  We'll keep whatever's left in the output list.
-    cdata->_geoms.swap(new_geoms);
-    new_geoms.clear();
+    cdata->set_geoms(new_geoms);
 
     // Finally, go back through and unify the resulting geom(s).
-    for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+    for (gi = new_geoms->begin(); gi != new_geoms->end(); ++gi) {
       const GeomEntry &entry = (*gi);
       entry._geom->unify_in_place();
     }
@@ -574,8 +495,9 @@ void GeomNode::
 write_geoms(ostream &out, int indent_level) const {
   CDReader cdata(_cycler);
   write(out, indent_level);
-  Geoms::const_iterator gi;
-  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+  GeomList::const_iterator gi;
+  const GeomList &geoms = *(cdata->get_geoms());
+  for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
     const GeomEntry &entry = (*gi);
     indent(out, indent_level + 2) 
       << *entry._geom << " " << *entry._state << "\n";
@@ -592,8 +514,9 @@ void GeomNode::
 write_verbose(ostream &out, int indent_level) const {
   CDReader cdata(_cycler);
   write(out, indent_level);
-  Geoms::const_iterator gi;
-  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+  GeomList::const_iterator gi;
+  const GeomList &geoms = *(cdata->get_geoms());
+  for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
     const GeomEntry &entry = (*gi);
     indent(out, indent_level + 2) 
       << *entry._geom << " " << *entry._state << "\n";
@@ -616,8 +539,10 @@ output(ostream &out) const {
   CDReader cdata(_cycler);
 
   pset<TypeHandle> attrib_types;
-  Geoms::const_iterator gi;
-  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+
+  GeomList::const_iterator gi;
+  const GeomList &geoms = *(cdata->get_geoms());
+  for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
     const GeomEntry &entry = (*gi);
     int num_attribs = entry._state->get_num_attribs();
     for (int i = 0; i < num_attribs; i++) {
@@ -627,7 +552,7 @@ output(ostream &out) const {
   }
 
   PandaNode::output(out);
-  out << " (" << cdata->_geoms.size() << " geoms";
+  out << " (" << geoms.size() << " geoms";
 
   if (!attrib_types.empty()) {
     out << ":";
@@ -675,8 +600,10 @@ compute_internal_bounds(int pipeline_stage, Thread *current_thread) const {
   pvector<const BoundingVolume *> child_volumes;
 
   CDStageReader cdata(_cycler, pipeline_stage, current_thread);
-  Geoms::const_iterator gi;
-  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+
+  GeomList::const_iterator gi;
+  const GeomList &geoms = *(cdata->get_geoms());
+  for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
     const GeomEntry &entry = (*gi);
     child_volumes.push_back(entry._geom->get_bounds());
   }
@@ -743,3 +670,85 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   PandaNode::fillin(scan, manager);
   manager->read_cdata(scan, _cycler);
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::CData::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+GeomNode::CData::
+CData(const GeomNode::CData &copy) :
+  _geoms(copy._geoms)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::CData::make_copy
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CycleData *GeomNode::CData::
+make_copy() const {
+  return new CData(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::CData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void GeomNode::CData::
+write_datagram(BamWriter *manager, Datagram &dg) const {
+  int num_geoms = _geoms->size();
+  nassertv(num_geoms == (int)(PN_uint16)num_geoms);
+  dg.add_uint16(num_geoms);
+  
+  GeomList::const_iterator gi;
+  for (gi = _geoms->begin(); gi != _geoms->end(); ++gi) {
+    const GeomEntry &entry = (*gi);
+    manager->write_pointer(dg, entry._geom);
+    manager->write_pointer(dg, entry._state);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::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 GeomNode::CData::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = CycleData::complete_pointers(p_list, manager);
+
+  // Get the geom and state pointers.
+  GeomList::iterator gi;
+  for (gi = _geoms->begin(); gi != _geoms->end(); ++gi) {
+    GeomEntry &entry = (*gi);
+    entry._geom = DCAST(Geom, p_list[pi++]);
+    entry._state = DCAST(RenderState, p_list[pi++]);
+  }
+
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::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 GeomNode.
+////////////////////////////////////////////////////////////////////
+void GeomNode::CData::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  int num_geoms = scan.get_uint16();
+  // Read the list of geoms and states.  Push back a NULL for each one.
+  _geoms->reserve(num_geoms);
+  for (int i = 0; i < num_geoms; i++) {
+    manager->read_pointer(scan);
+    manager->read_pointer(scan);
+    _geoms->push_back(GeomEntry(NULL, NULL));
+  }
+}

+ 29 - 2
panda/src/pgraph/geomNode.h

@@ -98,7 +98,7 @@ public:
   };
 
 private:
-  typedef pvector<GeomEntry> Geoms;
+  typedef RefCountObj< pvector<GeomEntry> > GeomList;
   typedef pmap<const InternalName *, int> NameCount;
 
   INLINE void count_name(NameCount &name_count, const InternalName *name);
@@ -117,7 +117,12 @@ private:
       return GeomNode::get_class_type();
     }
 
-    Geoms _geoms;
+    INLINE const GeomList *get_geoms() const;
+    INLINE GeomList *modify_geoms();
+    INLINE void set_geoms(GeomList *geoms);
+
+  private:
+    PT(GeomList) _geoms;
   };
 
   PipelineCycler<CData> _cycler;
@@ -126,6 +131,28 @@ private:
   typedef CycleDataStageReader<CData> CDStageReader;
   typedef CycleDataStageWriter<CData> CDStageWriter;
 
+public:
+  // This class is returned from get_geoms().  It is similar to
+  // PandaNode::get_children(); use this to walk through the list of
+  // geoms faster than walking through the geoms directly from the
+  // node.
+  class EXPCL_PANDA Geoms {
+  public:
+    INLINE Geoms();
+    INLINE Geoms(const CData *cdata);
+    INLINE Geoms(const Geoms &copy);
+    INLINE void operator = (const Geoms &copy);
+
+    INLINE int get_num_geoms() const;
+    INLINE const Geom *get_geom(int n) const;
+    INLINE const RenderState *get_geom_state(int n) const;
+
+  private:
+    CPT(GeomList) _geoms;
+  };
+
+  INLINE Geoms get_geoms(Thread *current_thread = Thread::get_current_thread()) const;
+
 public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &dg);

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

@@ -131,8 +131,9 @@ transform_vertices(GeomNode *node, const LMatrix4f &mat) {
   Thread *current_thread = Thread::get_current_thread();
   OPEN_ITERATE_CURRENT_AND_UPSTREAM(node->_cycler, current_thread) {
     GeomNode::CDStageWriter cdata(node->_cycler, pipeline_stage, current_thread);
-    GeomNode::Geoms::iterator gi;
-    for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+    GeomNode::GeomList::iterator gi;
+    GeomNode::GeomList &geoms = *(cdata->modify_geoms());
+    for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
       GeomNode::GeomEntry &entry = (*gi);
       PT(Geom) new_geom = entry._geom->make_copy();
       if (transform_vertices(new_geom, mat)) {
@@ -224,8 +225,9 @@ transform_texcoords(GeomNode *node, const InternalName *from_name,
   bool any_changed = false;
 
   GeomNode::CDWriter cdata(node->_cycler);
-  GeomNode::Geoms::iterator gi;
-  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+  GeomNode::GeomList::iterator gi;
+  GeomNode::GeomList &geoms = *(cdata->modify_geoms());
+  for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
     GeomNode::GeomEntry &entry = (*gi);
     PT(Geom) new_geom = entry._geom->make_copy();
     if (transform_texcoords(new_geom, from_name, to_name, mat)) {
@@ -283,8 +285,9 @@ set_color(GeomNode *node, const Colorf &color) {
   bool any_changed = false;
 
   GeomNode::CDWriter cdata(node->_cycler);
-  GeomNode::Geoms::iterator gi;
-  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+  GeomNode::GeomList::iterator gi;
+  GeomNode::GeomList &geoms = *(cdata->modify_geoms());
+  for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
     GeomNode::GeomEntry &entry = (*gi);
     PT(Geom) new_geom = entry._geom->make_copy();
     if (set_color(new_geom, color)) {
@@ -345,8 +348,9 @@ transform_colors(GeomNode *node, const LVecBase4f &scale) {
   bool any_changed = false;
 
   GeomNode::CDWriter cdata(node->_cycler);
-  GeomNode::Geoms::iterator gi;
-  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+  GeomNode::GeomList::iterator gi;
+  GeomNode::GeomList &geoms = *(cdata->modify_geoms());
+  for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
     GeomNode::GeomEntry &entry = (*gi);
     PT(Geom) new_geom = entry._geom->make_copy();
     if (transform_colors(new_geom, scale)) {
@@ -371,8 +375,9 @@ apply_state(GeomNode *node, const RenderState *state) {
   bool any_changed = false;
 
   GeomNode::CDWriter cdata(node->_cycler);
-  GeomNode::Geoms::iterator gi;
-  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+  GeomNode::GeomList::iterator gi;
+  GeomNode::GeomList &geoms = *(cdata->modify_geoms());
+  for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
     GeomNode::GeomEntry &entry = (*gi);
     CPT(RenderState) new_state = state->compose(entry._state);
     if (entry._state != new_state) {
@@ -638,8 +643,9 @@ collect_vertex_data(GeomNode *node, int collect_bits) {
   GeomTransformer *dynamic = NULL;
 
   GeomNode::CDWriter cdata(node->_cycler);
-  GeomNode::Geoms::iterator gi;
-  for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) {
+  GeomNode::GeomList::iterator gi;
+  GeomNode::GeomList &geoms = *(cdata->modify_geoms());
+  for (gi = geoms.begin(); gi != geoms.end(); ++gi) {
     GeomNode::GeomEntry &entry = (*gi);
     PT(Geom) new_geom = entry._geom->make_copy();
     entry._geom = new_geom;

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

@@ -5391,9 +5391,9 @@ hide_bounds() {
 //               the local coordinate space of the node.
 ////////////////////////////////////////////////////////////////////
 PT(BoundingVolume) NodePath::
-get_bounds() const {
+get_bounds(Thread *current_thread) const {
   nassertr_always(!is_empty(), new BoundingSphere);
-  return node()->get_bounds()->make_copy();
+  return node()->get_bounds(current_thread)->make_copy();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -5698,7 +5698,7 @@ r_get_net_state(NodePathComponent *comp, Thread *current_thread) const {
   if (comp == (NodePathComponent *)NULL) {
     return RenderState::make_empty();
   } else {
-    CPT(RenderState) state = comp->get_node()->get_state();
+    CPT(RenderState) state = comp->get_node()->get_state(current_thread);
     int pipeline_stage = current_thread->get_pipeline_stage();
     return r_get_net_state(comp->get_next(pipeline_stage, current_thread), current_thread)->compose(state);
   }
@@ -5718,7 +5718,7 @@ r_get_partial_state(NodePathComponent *comp, int n,
   if (n == 0 || comp == (NodePathComponent *)NULL) {
     return RenderState::make_empty();
   } else {
-    CPT(RenderState) state = comp->get_node()->get_state();
+    CPT(RenderState) state = comp->get_node()->get_state(current_thread);
     int pipeline_stage = current_thread->get_pipeline_stage();
     return r_get_partial_state(comp->get_next(pipeline_stage, current_thread), n - 1, current_thread)->compose(state);
   }
@@ -5737,9 +5737,9 @@ r_get_net_transform(NodePathComponent *comp, Thread *current_thread) const {
   } else {
     int pipeline_stage = current_thread->get_pipeline_stage();
     CPT(TransformState) net_transform = r_get_net_transform(comp->get_next(pipeline_stage, current_thread), current_thread);
-    CPT(TransformState) transform = comp->get_node()->get_transform();
+    CPT(TransformState) transform = comp->get_node()->get_transform(current_thread);
 
-    CPT(RenderEffects) effects = comp->get_node()->get_effects();
+    CPT(RenderEffects) effects = comp->get_node()->get_effects(current_thread);
     if (effects->has_adjust_transform()) {
       effects->adjust_transform(net_transform, transform);
     }
@@ -5766,10 +5766,10 @@ r_get_partial_transform(NodePathComponent *comp, int n,
   if (n == 0 || comp == (NodePathComponent *)NULL) {
     return TransformState::make_identity();
   } else {
-    if (comp->get_node()->get_effects()->has_adjust_transform()) {
+    if (comp->get_node()->get_effects(current_thread)->has_adjust_transform()) {
       return NULL;
     }
-    CPT(TransformState) transform = comp->get_node()->get_transform();
+    CPT(TransformState) transform = comp->get_node()->get_transform(current_thread);
     int pipeline_stage = current_thread->get_pipeline_stage();
     CPT(TransformState) partial = r_get_partial_transform(comp->get_next(pipeline_stage, current_thread), n - 1, current_thread);
     if (partial == (const TransformState *)NULL) {
@@ -5791,7 +5791,7 @@ r_get_net_prev_transform(NodePathComponent *comp, Thread *current_thread) const
   if (comp == (NodePathComponent *)NULL) {
     return TransformState::make_identity();
   } else {
-    CPT(TransformState) transform = comp->get_node()->get_prev_transform();
+    CPT(TransformState) transform = comp->get_node()->get_prev_transform(current_thread);
     int pipeline_stage = current_thread->get_pipeline_stage();
     return r_get_net_prev_transform(comp->get_next(pipeline_stage, current_thread), current_thread)->compose(transform);
   }
@@ -5811,7 +5811,7 @@ r_get_partial_prev_transform(NodePathComponent *comp, int n, Thread *current_thr
   if (n == 0 || comp == (NodePathComponent *)NULL) {
     return TransformState::make_identity();
   } else {
-    CPT(TransformState) transform = comp->get_node()->get_prev_transform();
+    CPT(TransformState) transform = comp->get_node()->get_prev_transform(current_thread);
     int pipeline_stage = current_thread->get_pipeline_stage();
     return r_get_partial_prev_transform(comp->get_next(pipeline_stage, current_thread), n - 1, current_thread)->compose(transform);
   }

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

@@ -778,7 +778,7 @@ PUBLISHED:
   void show_bounds();
   void show_tight_bounds();
   void hide_bounds();
-  PT(BoundingVolume) get_bounds() const;
+  PT(BoundingVolume) get_bounds(Thread *current_thread = Thread::get_current_thread()) const;
   void force_recompute_bounds();
   void write_bounds(ostream &out) const;
   bool calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,

+ 13 - 12
panda/src/pgraph/pandaNode.I

@@ -24,8 +24,9 @@
 //               masks to a running draw mask, as during a traversal.
 ////////////////////////////////////////////////////////////////////
 INLINE void PandaNode::
-compose_draw_mask(DrawMask &running_draw_mask) const {
-  CDHeavyReader cdata(_cycler_heavy);
+compose_draw_mask(DrawMask &running_draw_mask,
+                  Thread *current_thread) const {
+  CDHeavyReader cdata(_cycler_heavy, current_thread);
   running_draw_mask = (running_draw_mask & ~cdata->_draw_control_mask) |
     (cdata->_draw_show_mask & cdata->_draw_control_mask);
 }
@@ -381,8 +382,8 @@ clear_state(Thread *current_thread) {
 //               applied to this node.
 ////////////////////////////////////////////////////////////////////
 INLINE const RenderEffects *PandaNode::
-get_effects() const {
-  CDHeavyReader cdata(_cycler_heavy);
+get_effects(Thread *current_thread) const {
+  CDHeavyReader cdata(_cycler_heavy, current_thread);
   return cdata->_effects;
 }
 
@@ -392,8 +393,8 @@ get_effects() const {
 //  Description: Resets this node to have no render effects.
 ////////////////////////////////////////////////////////////////////
 INLINE void PandaNode::
-clear_effects() {
-  set_effects(RenderEffects::make_empty());
+clear_effects(Thread *current_thread) {
+  set_effects(RenderEffects::make_empty(), current_thread);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -457,8 +458,8 @@ has_dirty_prev_transform() const {
 //               string.
 ////////////////////////////////////////////////////////////////////
 INLINE string PandaNode::
-get_tag(const string &key) const {
-  CDHeavyReader cdata(_cycler_heavy);
+get_tag(const string &key, Thread *current_thread) const {
+  CDHeavyReader cdata(_cycler_heavy, current_thread);
   TagData::const_iterator ti;
   ti = cdata->_tag_data.find(key);
   if (ti != cdata->_tag_data.end()) {
@@ -475,8 +476,8 @@ get_tag(const string &key) const {
 //               empty string), or false if no value has been set.
 ////////////////////////////////////////////////////////////////////
 INLINE bool PandaNode::
-has_tag(const string &key) const {
-  CDHeavyReader cdata(_cycler_heavy);
+has_tag(const string &key, Thread *current_thread) const {
+  CDHeavyReader cdata(_cycler_heavy, current_thread);
   TagData::const_iterator ti;
   ti = cdata->_tag_data.find(key);
   return (ti != cdata->_tag_data.end());
@@ -657,8 +658,8 @@ set_final(bool flag) {
 //               set_final().
 ////////////////////////////////////////////////////////////////////
 INLINE bool PandaNode::
-is_final() const {
-  CDHeavyReader cdata(_cycler_heavy);
+is_final(Thread *current_thread) const {
+  CDHeavyReader cdata(_cycler_heavy, current_thread);
   return cdata->_final_bounds;
 }
 

+ 15 - 21
panda/src/pgraph/pandaNode.cxx

@@ -392,7 +392,7 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point, bool &found_any,
                   const TransformState *transform, Thread *current_thread) const {
   CPT(TransformState) next_transform = transform->compose(get_transform());
 
-  Children cr = get_children();
+  Children cr = get_children(current_thread);
   int num_children = cr.get_num_children();
   for (int i = 0; i < num_children; i++) {
     cr.get_child(i)->calc_tight_bounds(min_point, max_point,
@@ -1001,7 +1001,7 @@ set_attrib(const RenderAttrib *attrib, int override) {
 
   // Maybe we changed a ClipPlaneAttrib.
   if (any_changed) {
-    mark_bounds_stale();
+    mark_bounds_stale(current_thread);
     state_changed();
   }
 }
@@ -1033,7 +1033,7 @@ clear_attrib(TypeHandle type) {
   // We mark the bounds stale when the state changes, in case
   // we have changed a ClipPlaneAttrib.
   if (any_changed) {
-    mark_bounds_stale();
+    mark_bounds_stale(current_thread);
     state_changed();
   }
 }
@@ -1099,7 +1099,7 @@ set_state(const RenderState *state, Thread *current_thread) {
 
   // Maybe we have changed a ClipPlaneAttrib.
   if (any_changed) {
-    mark_bounds_stale();
+    mark_bounds_stale(current_thread);
     state_changed();
   }
 }
@@ -1113,10 +1113,9 @@ set_state(const RenderState *state, Thread *current_thread) {
 //               set_attrib().
 ////////////////////////////////////////////////////////////////////
 void PandaNode::
-set_effects(const RenderEffects *effects) {
+set_effects(const RenderEffects *effects, Thread *current_thread) {
   // Apply this operation to the current stage as well as to all
   // upstream stages.
-  Thread *current_thread = Thread::get_current_thread();
   OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler_heavy, current_thread) {
     CDHeavyStageWriter cdata(_cycler_heavy, pipeline_stage, current_thread);
     cdata->_effects = effects;
@@ -1152,7 +1151,7 @@ set_transform(const TransformState *transform, Thread *current_thread) {
   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler_light);
 
   if (any_changed) {
-    mark_bounds_stale();
+    mark_bounds_stale(current_thread);
     transform_changed();
   }
 }
@@ -1254,10 +1253,9 @@ reset_all_prev_transform(Thread *current_thread) {
 //               any one key's value.
 ////////////////////////////////////////////////////////////////////
 void PandaNode::
-set_tag(const string &key, const string &value) {
+set_tag(const string &key, const string &value, Thread *current_thread) {
   // Apply this operation to the current stage as well as to all
   // upstream stages.
-  Thread *current_thread = Thread::get_current_thread();
   OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler_heavy, current_thread) {
     CDHeavyStageWriter cdata(_cycler_heavy, pipeline_stage, current_thread);
     cdata->_tag_data[key] = value;
@@ -1273,8 +1271,7 @@ set_tag(const string &key, const string &value) {
 //               has_tag() will return false for the indicated key.
 ////////////////////////////////////////////////////////////////////
 void PandaNode::
-clear_tag(const string &key) {
-  Thread *current_thread = Thread::get_current_thread();
+clear_tag(const string &key, Thread *current_thread) {
   OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler_heavy, current_thread) {
     CDHeavyStageWriter cdata(_cycler_heavy, pipeline_stage, current_thread);
     cdata->_tag_data.erase(key);
@@ -1547,7 +1544,7 @@ adjust_draw_mask(DrawMask show_mask, DrawMask hide_mask, DrawMask clear_mask) {
   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler_heavy);
 
   if (any_changed) {
-    mark_bounds_stale();
+    mark_bounds_stale(current_thread);
     draw_mask_changed();
   }
 }
@@ -1641,7 +1638,7 @@ set_into_collide_mask(CollideMask mask) {
   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler_heavy);
 
   if (any_changed) {
-    mark_bounds_stale();
+    mark_bounds_stale(current_thread);
   }
 }
 
@@ -1668,8 +1665,7 @@ get_legal_collide_mask() const {
 //               set at CollisionNodes at this level and below.
 ////////////////////////////////////////////////////////////////////
 CollideMask PandaNode::
-get_net_collide_mask() const {
-  Thread *current_thread = Thread::get_current_thread();
+get_net_collide_mask(Thread *current_thread) const {
   int pipeline_stage = current_thread->get_pipeline_stage();
   CDBoundsStageReader cdata(_cycler_bounds, pipeline_stage, current_thread);
   if (cdata->_last_update != cdata->_next_update) {
@@ -1689,8 +1685,7 @@ get_net_collide_mask() const {
 //               at this level and below.
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) PandaNode::
-get_off_clip_planes() const {
-  Thread *current_thread = Thread::get_current_thread();
+get_off_clip_planes(Thread *current_thread) const {
   int pipeline_stage = current_thread->get_pipeline_stage();
   CDBoundsStageReader cdata(_cycler_bounds, pipeline_stage, current_thread);
   if (cdata->_last_update != cdata->_next_update) {
@@ -1811,8 +1806,7 @@ set_bound(const BoundingVolume *volume) {
 //               children's bounding volumes.
 ////////////////////////////////////////////////////////////////////
 CPT(BoundingVolume) PandaNode::
-get_bounds() const {
-  Thread *current_thread = Thread::get_current_thread();
+get_bounds(Thread *current_thread) const {
   int pipeline_stage = current_thread->get_pipeline_stage();
   CDBoundsStageReader cdata(_cycler_bounds, pipeline_stage, current_thread);
   if (cdata->_last_update != cdata->_next_update) {
@@ -2943,7 +2937,7 @@ update_bounds(int pipeline_stage, PandaNode::CDBoundsStageReader &cdata) {
     child_volumes.push_back(internal_bounds);
 
     // Now expand those contents to include all of our children.
-    Children children = get_children();
+    Children children = get_children(current_thread);
     int num_children = children.get_num_children();
 
     for (int i = 0; i < num_children; ++i) {
@@ -3072,7 +3066,7 @@ update_bounds(int pipeline_stage, PandaNode::CDBoundsStageReader &cdata) {
   
 	// If we have a transform, apply it to the bounding volume we
 	// just computed.
-	CPT(TransformState) transform = get_transform();
+	CPT(TransformState) transform = get_transform(current_thread);
 	if (!transform->is_identity()) {
 	  gbv->xform(transform->get_mat());
 	}

+ 17 - 12
panda/src/pgraph/pandaNode.h

@@ -110,7 +110,8 @@ public:
   virtual int get_visible_child() const;
   virtual bool is_renderable() const;
 
-  INLINE void compose_draw_mask(DrawMask &running_draw_mask) const;
+  INLINE void compose_draw_mask(DrawMask &running_draw_mask,
+                                Thread *current_thread) const;
   INLINE bool compare_draw_mask(DrawMask running_draw_mask,
                                 DrawMask camera_mask, 
                                 Thread *current_thread) const;
@@ -169,9 +170,9 @@ PUBLISHED:
   INLINE const RenderState *get_state(Thread *current_thread = Thread::get_current_thread()) const;
   INLINE void clear_state(Thread *current_thread = Thread::get_current_thread());
 
-  void set_effects(const RenderEffects *effects);
-  INLINE const RenderEffects *get_effects() const;
-  INLINE void clear_effects();
+  void set_effects(const RenderEffects *effects, Thread *current_thread = Thread::get_current_thread());
+  INLINE const RenderEffects *get_effects(Thread *current_thread = Thread::get_current_thread()) const;
+  INLINE void clear_effects(Thread *current_thread = Thread::get_current_thread());
 
   void set_transform(const TransformState *transform, Thread *current_thread = Thread::get_current_thread());
   INLINE const TransformState *get_transform(Thread *current_thread = Thread::get_current_thread()) const;
@@ -183,10 +184,14 @@ PUBLISHED:
   INLINE bool has_dirty_prev_transform() const;
   static void reset_all_prev_transform(Thread *current_thread = Thread::get_current_thread());
 
-  void set_tag(const string &key, const string &value);
-  INLINE string get_tag(const string &key) const;
-  INLINE bool has_tag(const string &key) const;
-  void clear_tag(const string &key);
+  void set_tag(const string &key, const string &value, 
+               Thread *current_thread = Thread::get_current_thread());
+  INLINE string get_tag(const string &key, 
+                        Thread *current_thread = Thread::get_current_thread()) const;
+  INLINE bool has_tag(const string &key,
+                      Thread *current_thread = Thread::get_current_thread()) const;
+  void clear_tag(const string &key,
+                 Thread *current_thread = Thread::get_current_thread());
 
 #ifdef HAVE_PYTHON
   void set_python_tag(const string &key, PyObject *value);
@@ -216,8 +221,8 @@ PUBLISHED:
   INLINE CollideMask get_into_collide_mask() const;
   virtual CollideMask get_legal_collide_mask() const;
 
-  CollideMask get_net_collide_mask() const;
-  CPT(RenderAttrib) get_off_clip_planes() const;
+  CollideMask get_net_collide_mask(Thread *current_thread = Thread::get_current_thread()) const;
+  CPT(RenderAttrib) get_off_clip_planes(Thread *current_thread = Thread::get_current_thread()) const;
 
   virtual void output(ostream &out) const;
   virtual void write(ostream &out, int indent_level) const;
@@ -238,13 +243,13 @@ PUBLISHED:
   void set_bounds(const BoundingVolume *volume);
   void set_bound(const BoundingVolume *volume);
   INLINE void clear_bounds();
-  CPT(BoundingVolume) get_bounds() const;
+  CPT(BoundingVolume) get_bounds(Thread *current_thread = Thread::get_current_thread()) const;
   INLINE CPT(BoundingVolume) get_internal_bounds() const;
 
   void mark_bounds_stale(Thread *current_thread = Thread::get_current_thread()) const;
 
   INLINE void set_final(bool flag);
-  INLINE bool is_final() const;
+  INLINE bool is_final(Thread *current_thread = Thread::get_current_thread()) const;
 
   virtual bool is_geom_node() const;
   virtual bool is_lod_node() const;

+ 2 - 1
panda/src/recorder/mouseRecorder.cxx

@@ -131,7 +131,8 @@ write(ostream &out, int indent_level) const {
 //               calls.
 ////////////////////////////////////////////////////////////////////
 void MouseRecorder::
-do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
+do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &input, 
+                 DataNodeTransmit &output) {
   bool has_mouse = false;
   LPoint2f mouse_xy;
   LPoint2f mouse_pixel_xy;

+ 2 - 1
panda/src/recorder/mouseRecorder.h

@@ -54,7 +54,8 @@ public:
 
 protected:
   // Inherited from DataNode
-  virtual void do_transmit_data(const DataNodeTransmit &input,
+  virtual void do_transmit_data(DataGraphTraverser *trav,
+                                const DataNodeTransmit &input,
                                 DataNodeTransmit &output);
 
 private:

+ 2 - 1
panda/src/tform/buttonThrower.cxx

@@ -373,7 +373,8 @@ do_general_event(const ButtonEvent &button_event, const string &button_name) {
 //               calls.
 ////////////////////////////////////////////////////////////////////
 void ButtonThrower::
-do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
+do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &input,
+                 DataNodeTransmit &output) {
   // Clear our outgoing button events.  We'll fill it up again with
   // just those events that want to carry on.
   _button_events->clear();

+ 2 - 1
panda/src/tform/buttonThrower.h

@@ -110,7 +110,8 @@ private:
 
 protected:
   // Inherited from DataNode
-  virtual void do_transmit_data(const DataNodeTransmit &input,
+  virtual void do_transmit_data(DataGraphTraverser *trav,
+                                const DataNodeTransmit &input,
                                 DataNodeTransmit &output);
 
 private:

+ 2 - 1
panda/src/tform/driveInterface.cxx

@@ -393,7 +393,8 @@ apply(double x, double y, bool any_button) {
 //               calls.
 ////////////////////////////////////////////////////////////////////
 void DriveInterface::
-do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
+do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &input,
+                 DataNodeTransmit &output) {
   // First, update our modifier buttons.
   bool required_buttons_match;
   const ButtonEventList *button_events = check_button_events(input, required_buttons_match);

+ 2 - 1
panda/src/tform/driveInterface.h

@@ -157,7 +157,8 @@ private:
 
 protected:
   // Inherited from DataNode
-  virtual void do_transmit_data(const DataNodeTransmit &input,
+  virtual void do_transmit_data(DataGraphTraverser *trav,
+                                const DataNodeTransmit &input,
                                 DataNodeTransmit &output);
 
 private:

+ 2 - 1
panda/src/tform/mouseSubregion.cxx

@@ -69,7 +69,8 @@ MouseSubregion::
 //               calls.
 ////////////////////////////////////////////////////////////////////
 void MouseSubregion::
-do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
+do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &input,
+                 DataNodeTransmit &output) {
   bool has_mouse = false;
 
   if (input.has_data(_xy_input)) {

+ 2 - 1
panda/src/tform/mouseSubregion.h

@@ -51,7 +51,8 @@ PUBLISHED:
 
 protected:
   // Inherited from DataNode
-  virtual void do_transmit_data(const DataNodeTransmit &input,
+  virtual void do_transmit_data(DataGraphTraverser *trav,
+                                const DataNodeTransmit &input,
                                 DataNodeTransmit &output);
 
 private:

+ 8 - 4
panda/src/tform/mouseWatcher.cxx

@@ -18,6 +18,7 @@
 
 #include "mouseWatcher.h"
 #include "config_tform.h"
+#include "dataGraphTraverser.h"
 #include "mouseWatcherParameter.h"
 #include "mouseAndKeyboard.h"
 #include "mouseData.h"
@@ -107,7 +108,7 @@ remove_region(MouseWatcherRegion *region) {
     _preferred_button_down_region = (MouseWatcherRegion *)NULL;
   }
 
-  return MouseWatcherGroup::remove_region(region);
+  return MouseWatcherGroup::do_remove_region(region);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1099,7 +1100,9 @@ consider_keyboard_suppress(const MouseWatcherRegion *region) {
 //               calls.
 ////////////////////////////////////////////////////////////////////
 void MouseWatcher::
-do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
+do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
+                 DataNodeTransmit &output) {
+  Thread *current_thread = trav->get_current_thread();
   MutexHolder holder(_lock);
 
   // Initially, we do not suppress any events to objects below us in
@@ -1129,7 +1132,8 @@ do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
     if (_display_region != (DisplayRegion *)NULL) {
       // If we've got a display region, constrain the mouse to it.
       int xo, yo, width, height;
-      _display_region->get_region_pixels_i(xo, yo, width, height);
+      DisplayRegionPipelineReader dr_reader(_display_region, current_thread);
+      dr_reader.get_region_pixels_i(xo, yo, width, height);
 
       if (p[0] < xo || p[0] >= xo + width || 
           p[1] < yo || p[1] >= yo + height) {
@@ -1143,7 +1147,7 @@ do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
       } else {
         // The mouse is within the display region; rescale it.
         float left, right, bottom, top;
-        _display_region->get_dimensions(left, right, bottom, top);
+        dr_reader.get_dimensions(left, right, bottom, top);
 
         // Need to translate this into DisplayRegion [0, 1] space
         float x = (f[0] + 1.0f) / 2.0f;

+ 2 - 1
panda/src/tform/mouseWatcher.h

@@ -196,7 +196,8 @@ protected:
 
 protected:
   // Inherited from DataNode
-  virtual void do_transmit_data(const DataNodeTransmit &input,
+  virtual void do_transmit_data(DataGraphTraverser *trav,
+                                const DataNodeTransmit &input,
                                 DataNodeTransmit &output);
 
 private:

+ 35 - 24
panda/src/tform/mouseWatcherGroup.cxx

@@ -111,30 +111,7 @@ has_region(MouseWatcherRegion *region) const {
 bool MouseWatcherGroup::
 remove_region(MouseWatcherRegion *region) {
   MutexHolder holder(_lock);
-
-  // See if the region is in the vector.
-  PT(MouseWatcherRegion) pt = region;
-  Regions::iterator ri = 
-    find(_regions.begin(), _regions.end(), pt);
-  if (ri != _regions.end()) {
-    // Found it, now erase it
-#ifndef NDEBUG
-    // Also remove it from the vizzes.
-    if (_show_regions) {
-      nassertr(_vizzes.size() == _regions.size(), false);
-      size_t index = ri - _regions.begin();
-      Vizzes::iterator vi = _vizzes.begin() + index;
-      _show_regions_root.node()->remove_child(*vi);
-      _vizzes.erase(vi);
-    }
-#endif  // NDEBUG    
-
-    _regions.erase(ri);
-    return true;
-  }
-
-  // Did not find the region to erase
-  return false;
+  return do_remove_region(region);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -287,6 +264,40 @@ hide_regions() {
 }
 #endif  // NDEBUG
 
+
+////////////////////////////////////////////////////////////////////
+//     Function: MouseWatcherGroup::do_remove_region
+//       Access: Protected
+//  Description: The internal implementation of remove_region();
+//               assumes the lock is already held.
+////////////////////////////////////////////////////////////////////
+bool MouseWatcherGroup::
+do_remove_region(MouseWatcherRegion *region) {
+  // See if the region is in the vector.
+  PT(MouseWatcherRegion) pt = region;
+  Regions::iterator ri = 
+    find(_regions.begin(), _regions.end(), pt);
+  if (ri != _regions.end()) {
+    // Found it, now erase it
+#ifndef NDEBUG
+    // Also remove it from the vizzes.
+    if (_show_regions) {
+      nassertr(_vizzes.size() == _regions.size(), false);
+      size_t index = ri - _regions.begin();
+      Vizzes::iterator vi = _vizzes.begin() + index;
+      _show_regions_root.node()->remove_child(*vi);
+      _vizzes.erase(vi);
+    }
+#endif  // NDEBUG    
+
+    _regions.erase(ri);
+    return true;
+  }
+
+  // Did not find the region to erase
+  return false;
+}
+
 #ifndef NDEBUG
 ////////////////////////////////////////////////////////////////////
 //     Function: MouseWatcherGroup::update_regions

+ 3 - 0
panda/src/tform/mouseWatcherGroup.h

@@ -57,6 +57,9 @@ PUBLISHED:
   void hide_regions();
 #endif  // NDEBUG
 
+protected:
+  bool do_remove_region(MouseWatcherRegion *region);
+
 protected:
   typedef pvector< PT(MouseWatcherRegion) > Regions;
   Regions _regions;

+ 2 - 1
panda/src/tform/trackball.cxx

@@ -537,7 +537,8 @@ recompute() {
 //               calls.
 ////////////////////////////////////////////////////////////////////
 void Trackball::
-do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &output) {
+do_transmit_data(DataGraphTraverser *, const DataNodeTransmit &input,
+                 DataNodeTransmit &output) {
   // First, update our modifier buttons.
   bool required_buttons_match;
   check_button_events(input, required_buttons_match);

+ 2 - 1
panda/src/tform/trackball.h

@@ -120,7 +120,8 @@ private:
 
 protected:
   // Inherited from DataNode
-  virtual void do_transmit_data(const DataNodeTransmit &input,
+  virtual void do_transmit_data(DataGraphTraverser *trav,
+                                const DataNodeTransmit &input,
                                 DataNodeTransmit &output);
 
 private:

+ 6 - 2
panda/src/tform/transform2sg.cxx

@@ -19,6 +19,7 @@
 #include "transform2sg.h"
 #include "transformState.h"
 #include "dataNodeTransmit.h"
+#include "dataGraphTraverser.h"
 
 
 TypeHandle Transform2SG::_type_handle;
@@ -73,12 +74,15 @@ get_node() const {
 //               calls.
 ////////////////////////////////////////////////////////////////////
 void Transform2SG::
-do_transmit_data(const DataNodeTransmit &input, DataNodeTransmit &) {
+do_transmit_data(DataGraphTraverser *trav, const DataNodeTransmit &input,
+                 DataNodeTransmit &) {
+  Thread *current_thread = trav->get_current_thread();
+
   if (input.has_data(_transform_input)) {
     const TransformState *transform;
     DCAST_INTO_V(transform, input.get_data(_transform_input).get_ptr());
     if (_node != (PandaNode *)NULL) {
-      _node->set_transform(transform);
+      _node->set_transform(transform, current_thread);
     }
   }
 }

+ 2 - 1
panda/src/tform/transform2sg.h

@@ -43,7 +43,8 @@ private:
 
 protected:
   // Inherited from DataNode
-  virtual void do_transmit_data(const DataNodeTransmit &input,
+  virtual void do_transmit_data(DataGraphTraverser *trav,
+                                const DataNodeTransmit &input,
                                 DataNodeTransmit &output);
 
 private: