Browse Source

more threading improvements

David Rose 17 years ago
parent
commit
fab502970f
70 changed files with 1117 additions and 492 deletions
  1. 2 1
      dtool/Config.pp
  2. 3 2
      panda/src/chan/bindAnimRequest.I
  3. 2 1
      panda/src/chan/bindAnimRequest.h
  4. 2 1
      panda/src/chan/partBundle.cxx
  5. 5 4
      panda/src/collide/collisionNode.cxx
  6. 3 1
      panda/src/collide/collisionNode.h
  7. 2 0
      panda/src/collide/config_collide.cxx
  8. 6 5
      panda/src/display/config_display.cxx
  9. 171 167
      panda/src/display/graphicsEngine.cxx
  10. 2 1
      panda/src/display/graphicsStateGuardian.cxx
  11. 15 1
      panda/src/downloader/bioStreamBuf.cxx
  12. 1 0
      panda/src/event/asyncTaskManager.cxx
  13. 1 1
      panda/src/event/eventQueue.cxx
  14. 7 4
      panda/src/gobj/geom.cxx
  15. 72 69
      panda/src/gobj/geomPrimitive.cxx
  16. 3 2
      panda/src/gobj/textureReloadRequest.I
  17. 2 1
      panda/src/gobj/textureReloadRequest.h
  18. 5 4
      panda/src/parametrics/ropeNode.cxx
  19. 3 1
      panda/src/parametrics/ropeNode.h
  20. 5 4
      panda/src/parametrics/sheetNode.cxx
  21. 3 1
      panda/src/parametrics/sheetNode.h
  22. 19 10
      panda/src/pgraph/geomNode.cxx
  23. 5 2
      panda/src/pgraph/geomNode.h
  24. 14 1
      panda/src/pgraph/loader.cxx
  25. 2 0
      panda/src/pgraph/loader.h
  26. 5 4
      panda/src/pgraph/lodNode.cxx
  27. 3 1
      panda/src/pgraph/lodNode.h
  28. 3 2
      panda/src/pgraph/modelLoadRequest.I
  29. 2 1
      panda/src/pgraph/modelLoadRequest.h
  30. 18 4
      panda/src/pgraph/pandaNode.I
  31. 88 32
      panda/src/pgraph/pandaNode.cxx
  32. 8 3
      panda/src/pgraph/pandaNode.h
  33. 5 4
      panda/src/pgraph/planeNode.cxx
  34. 3 1
      panda/src/pgraph/planeNode.h
  35. 5 4
      panda/src/pgraph/portalNode.cxx
  36. 3 1
      panda/src/pgraph/portalNode.h
  37. 3 1
      panda/src/pgui/pgButton.I
  38. 12 0
      panda/src/pgui/pgButton.cxx
  39. 36 5
      panda/src/pgui/pgEntry.I
  40. 18 0
      panda/src/pgui/pgEntry.cxx
  41. 39 0
      panda/src/pgui/pgItem.I
  42. 47 10
      panda/src/pgui/pgItem.cxx
  43. 12 2
      panda/src/pgui/pgItem.h
  44. 13 0
      panda/src/pgui/pgScrollFrame.I
  45. 13 0
      panda/src/pgui/pgScrollFrame.cxx
  46. 25 0
      panda/src/pgui/pgSliderBar.I
  47. 105 73
      panda/src/pgui/pgSliderBar.cxx
  48. 6 0
      panda/src/pgui/pgVirtualFrame.I
  49. 5 0
      panda/src/pgui/pgVirtualFrame.cxx
  50. 7 0
      panda/src/pgui/pgWaitBar.I
  51. 4 0
      panda/src/pgui/pgWaitBar.cxx
  52. 1 1
      panda/src/pipeline/conditionVarPosixImpl.cxx
  53. 14 2
      panda/src/pipeline/conditionVarSimpleImpl.cxx
  54. 69 5
      panda/src/pipeline/mutexDebug.cxx
  55. 14 2
      panda/src/pipeline/mutexDebug.h
  56. 57 0
      panda/src/pipeline/mutexDirect.I
  57. 5 0
      panda/src/pipeline/mutexDirect.h
  58. 7 1
      panda/src/pipeline/mutexSimpleImpl.cxx
  59. 42 0
      panda/src/pipeline/reMutexDirect.I
  60. 5 0
      panda/src/pipeline/reMutexDirect.h
  61. 4 5
      panda/src/pipeline/thread.cxx
  62. 15 8
      panda/src/pipeline/threadSimpleManager.cxx
  63. 2 2
      panda/src/pipeline/threadSimpleManager.h
  64. 8 8
      panda/src/putil/copyOnWriteObject.I
  65. 2 2
      panda/src/putil/copyOnWriteObject.cxx
  66. 13 4
      panda/src/putil/copyOnWriteObject.h
  67. 2 2
      panda/src/putil/copyOnWritePointer.cxx
  68. 0 9
      panda/src/putil/copyOnWritePointer.h
  69. 9 7
      panda/src/text/textNode.cxx
  70. 5 2
      panda/src/text/textNode.h

+ 2 - 1
dtool/Config.pp

@@ -714,7 +714,8 @@
 // This gives you control over when the context switch happens, and
 // This gives you control over when the context switch happens, and
 // may make mutexes unnecessary, if you are somewhat careful in your
 // may make mutexes unnecessary, if you are somewhat careful in your
 // code design.  Disabling mutexes saves a tiny bit of runtime and
 // code design.  Disabling mutexes saves a tiny bit of runtime and
-// memory overhead.
+// memory overhead.  NOT RECOMMENDED!  Many internal Panda functions
+// aren't quite secure enough to enable this mode for now.
 #define SIMPLE_THREADS_NO_MUTEX
 #define SIMPLE_THREADS_NO_MUTEX
 
 
 // Whether threading is defined or not, you might want to validate the
 // Whether threading is defined or not, you might want to validate the

+ 3 - 2
panda/src/chan/bindAnimRequest.I

@@ -19,11 +19,12 @@
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE BindAnimRequest::
 INLINE BindAnimRequest::
-BindAnimRequest(const Filename &filename, const LoaderOptions &options,
+BindAnimRequest(const string &name,
+                const Filename &filename, const LoaderOptions &options,
                 Loader *loader,
                 Loader *loader,
                 AnimControl *control, int hierarchy_match_flags,
                 AnimControl *control, int hierarchy_match_flags,
                 const PartSubset &subset) :
                 const PartSubset &subset) :
-  ModelLoadRequest(filename, options, loader),
+  ModelLoadRequest(name, filename, options, loader),
   _control(control),
   _control(control),
   _hierarchy_match_flags(hierarchy_match_flags),
   _hierarchy_match_flags(hierarchy_match_flags),
   _subset(subset)
   _subset(subset)

+ 2 - 1
panda/src/chan/bindAnimRequest.h

@@ -33,7 +33,8 @@ public:
   ALLOC_DELETED_CHAIN(BindAnimRequest);
   ALLOC_DELETED_CHAIN(BindAnimRequest);
 
 
 PUBLISHED:
 PUBLISHED:
-  INLINE BindAnimRequest(const Filename &filename, 
+  INLINE BindAnimRequest(const string &name,
+                         const Filename &filename, 
                          const LoaderOptions &options,
                          const LoaderOptions &options,
                          Loader *loader,
                          Loader *loader,
                          AnimControl *control,
                          AnimControl *control,

+ 2 - 1
panda/src/chan/partBundle.cxx

@@ -363,7 +363,8 @@ load_bind_anim(Loader *loader, const Filename &filename,
   }
   }
 
 
   PT(BindAnimRequest) request = 
   PT(BindAnimRequest) request = 
-    new BindAnimRequest(filename, anim_options, loader, control, 
+    new BindAnimRequest(string("bind:") + filename.get_basename(),
+                        filename, anim_options, loader, control, 
                         hierarchy_match_flags, subset);
                         hierarchy_match_flags, subset);
   loader->load_async(request);
   loader->load_async(request);
 
 

+ 5 - 4
panda/src/collide/collisionNode.cxx

@@ -307,7 +307,9 @@ set_from_collide_mask(CollideMask mask) {
 //               thing.
 //               thing.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void CollisionNode::
 void CollisionNode::
-compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
+compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
+                        int &internal_vertices,
+                        int pipeline_stage,
                         Thread *current_thread) const {
                         Thread *current_thread) const {
   pvector<CPT(BoundingVolume) > child_volumes_ref;
   pvector<CPT(BoundingVolume) > child_volumes_ref;
   pvector<const BoundingVolume *> child_volumes;
   pvector<const BoundingVolume *> child_volumes;
@@ -345,9 +347,8 @@ compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage,
     ((BoundingVolume *)gbv)->around(child_begin, child_end);
     ((BoundingVolume *)gbv)->around(child_begin, child_end);
   }
   }
   
   
-  bdata->_internal_bounds = gbv;
-  bdata->_internal_vertices = 0;
-  bdata->_internal_bounds_stale = false;
+  internal_bounds = gbv;
+  internal_vertices = 0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

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

@@ -71,7 +71,9 @@ PUBLISHED:
   INLINE static CollideMask get_default_collide_mask();
   INLINE static CollideMask get_default_collide_mask();
 
 
 protected:
 protected:
-  virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
+  virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
+                                       int &internal_vertices,
+                                       int pipeline_stage,
                                        Thread *current_thread) const;
                                        Thread *current_thread) const;
 
 
 private:
 private:

+ 2 - 0
panda/src/collide/config_collide.cxx

@@ -26,6 +26,7 @@
 #include "collisionInvSphere.h"
 #include "collisionInvSphere.h"
 #include "collisionLine.h"
 #include "collisionLine.h"
 #include "collisionLevelStateBase.h"
 #include "collisionLevelStateBase.h"
+#include "collisionGeom.h"
 #include "collisionNode.h"
 #include "collisionNode.h"
 #include "collisionParabola.h"
 #include "collisionParabola.h"
 #include "collisionPlane.h"
 #include "collisionPlane.h"
@@ -129,6 +130,7 @@ init_libcollide() {
   CollisionInvSphere::init_type();
   CollisionInvSphere::init_type();
   CollisionLine::init_type();
   CollisionLine::init_type();
   CollisionLevelStateBase::init_type();
   CollisionLevelStateBase::init_type();
+  CollisionGeom::init_type();
   CollisionNode::init_type();
   CollisionNode::init_type();
   CollisionParabola::init_type();
   CollisionParabola::init_type();
   CollisionPlane::init_type();
   CollisionPlane::init_type();

+ 6 - 5
panda/src/display/config_display.cxx

@@ -206,13 +206,14 @@ ConfigVariableBool alpha_scale_via_texture
           "color-scale-via-lighting."));
           "color-scale-via-lighting."));
 
 
 ConfigVariableBool allow_incomplete_render
 ConfigVariableBool allow_incomplete_render
-("allow-incomplete-render", false,
+("allow-incomplete-render", true,
  PRC_DESC("When this is true, the frame may be rendered even if some of the "
  PRC_DESC("When this is true, the frame may be rendered even if some of the "
-          "geometry in the scene has been paged out.  The nonresident "
-          "geometry will be rendered as soon as it can be paged back in, "
+          "geometry in the scene has been paged out, or if the textures are "
+          "unavailable.  The nonresident geometry and textures will be "
+          "rendered as soon as they can be read from disk, "
           "which may be several frames in the future.  When this is false, "
           "which may be several frames in the future.  When this is false, "
-          "geometry is always paged in when needed, holding up the frame "
-          "render if necessary."));
+          "geometry is always paged in immediately when needed, holding up "
+          "the frame render if necessary."));
 
 
 ConfigVariableInt win_size
 ConfigVariableInt win_size
 ("win-size", "640 480",
 ("win-size", "640 480",

+ 171 - 167
panda/src/display/graphicsEngine.cxx

@@ -610,191 +610,195 @@ render_frame() {
       << "render_frame() - frame " << global_clock->get_frame_count() << "\n";
       << "render_frame() - frame " << global_clock->get_frame_count() << "\n";
   }
   }
 
 
-  ReMutexHolder holder(_lock, current_thread);
-
-  if (!_windows_sorted) {
-    do_resort_windows();
-  }
-
-  if (sync_flip && _flip_state != FS_flip) {
-    do_flip_frame(current_thread);
-  }
+  {
+    ReMutexHolder holder(_lock, current_thread);
 
 
-  // Are any of the windows ready to be deleted?
-  Windows new_windows;
-  new_windows.reserve(_windows.size());
-  Windows::iterator wi;
-  for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
-    GraphicsOutput *win = (*wi);
-    if (win->get_delete_flag()) {
-      do_remove_window(win, current_thread);
-      
-    } else {
-      new_windows.push_back(win);
-      
-      // Let's calculate each scene's bounding volume here in App,
-      // before we cycle the pipeline.  The cull traversal will
-      // calculate it anyway, but if we calculate it in App first
-      // before it gets calculated in the Cull thread, it will be more
-      // likely to stick for subsequent frames, so we won't have to
-      // recompute it each frame.
-      int num_drs = win->get_num_active_display_regions();
-      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(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(current_thread);
-            }
-            if (!scene.is_empty()) {
-              scene.get_bounds(current_thread);
+    if (!_windows_sorted) {
+      do_resort_windows();
+    }
+    
+    if (sync_flip && _flip_state != FS_flip) {
+      do_flip_frame(current_thread);
+    }
+    
+    // Are any of the windows ready to be deleted?
+    Windows new_windows;
+    new_windows.reserve(_windows.size());
+    Windows::iterator wi;
+    for (wi = _windows.begin(); wi != _windows.end(); ++wi) {
+      GraphicsOutput *win = (*wi);
+      if (win->get_delete_flag()) {
+        do_remove_window(win, current_thread);
+        
+      } else {
+        new_windows.push_back(win);
+        
+        // Let's calculate each scene's bounding volume here in App,
+        // before we cycle the pipeline.  The cull traversal will
+        // calculate it anyway, but if we calculate it in App first
+        // before it gets calculated in the Cull thread, it will be more
+        // likely to stick for subsequent frames, so we won't have to
+        // recompute it each frame.
+        int num_drs = win->get_num_active_display_regions();
+        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(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(current_thread);
+              }
+              if (!scene.is_empty()) {
+                scene.get_bounds(current_thread);
+              }
             }
             }
           }
           }
         }
         }
       }
       }
     }
     }
-  }
-  _windows.swap(new_windows);
-
-  // Now it's time to do any drawing from the main frame--after all of
-  // the App code has executed, but before we begin the next frame.
-  _app.do_frame(this, current_thread);
-  
-  // Grab each thread's mutex again after all windows have flipped,
-  // and wait for the thread to finish.
-  {
-    PStatTimer timer(_wait_pcollector, current_thread);
-    Threads::const_iterator ti;
-    for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
-      RenderThread *thread = (*ti).second;
-      thread->_cv_mutex.lock();
-      
-      while (thread->_thread_state != TS_wait) {
-        thread->_cv_done.wait();
+    _windows.swap(new_windows);
+    
+    // Now it's time to do any drawing from the main frame--after all of
+    // the App code has executed, but before we begin the next frame.
+    _app.do_frame(this, current_thread);
+    
+    // Grab each thread's mutex again after all windows have flipped,
+    // and wait for the thread to finish.
+    {
+      PStatTimer timer(_wait_pcollector, current_thread);
+      Threads::const_iterator ti;
+      for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
+        RenderThread *thread = (*ti).second;
+        thread->_cv_mutex.lock();
+        
+        while (thread->_thread_state != TS_wait) {
+          thread->_cv_done.wait();
+        }
       }
       }
     }
     }
-  }
-
+    
 #if defined(THREADED_PIPELINE) && defined(DO_PSTATS)
 #if defined(THREADED_PIPELINE) && defined(DO_PSTATS)
-  _cyclers_pcollector.set_level(_pipeline->get_num_cyclers());
-  _dirty_cyclers_pcollector.set_level(_pipeline->get_num_dirty_cyclers());
-
+    _cyclers_pcollector.set_level(_pipeline->get_num_cyclers());
+    _dirty_cyclers_pcollector.set_level(_pipeline->get_num_dirty_cyclers());
+    
 #ifdef DEBUG_THREADS
 #ifdef DEBUG_THREADS
-  if (PStatClient::is_connected()) {
-    _pipeline->iterate_all_cycler_types(pstats_count_cycler_type, this);
-    _pipeline->iterate_dirty_cycler_types(pstats_count_dirty_cycler_type, this);
-  }
+    if (PStatClient::is_connected()) {
+      _pipeline->iterate_all_cycler_types(pstats_count_cycler_type, this);
+      _pipeline->iterate_dirty_cycler_types(pstats_count_dirty_cycler_type, this);
+    }
 #endif  // DEBUG_THREADS
 #endif  // DEBUG_THREADS
-
+    
 #endif  // THREADED_PIPELINE && DO_PSTATS
 #endif  // THREADED_PIPELINE && DO_PSTATS
-
-  GeomCacheManager::flush_level();
-  CullTraverser::flush_level();
-  RenderState::flush_level();
-  TransformState::flush_level();
-  CullableObject::flush_level();
-
-  // Now cycle the pipeline and officially begin the next frame.
+    
+    GeomCacheManager::flush_level();
+    CullTraverser::flush_level();
+    RenderState::flush_level();
+    TransformState::flush_level();
+    CullableObject::flush_level();
+    
+    // Now cycle the pipeline and officially begin the next frame.
 #ifdef THREADED_PIPELINE
 #ifdef THREADED_PIPELINE
-  {
-    PStatTimer timer(_cycle_pcollector, current_thread);
-    _pipeline->cycle();
-  }
+    {
+      PStatTimer timer(_cycle_pcollector, current_thread);
+      _pipeline->cycle();
+    }
 #endif  // THREADED_PIPELINE
 #endif  // THREADED_PIPELINE
-
-  global_clock->tick(current_thread);
-  if (global_clock->check_errors(current_thread)) {
-    throw_event("clock_error");
-  }
-
+    
+    global_clock->tick(current_thread);
+    if (global_clock->check_errors(current_thread)) {
+      throw_event("clock_error");
+    }
+    
 #ifdef DO_PSTATS
 #ifdef DO_PSTATS
-  PStatClient::main_tick();
-
-  // Reset our pcollectors that track data across the frame.
-  CullTraverser::_nodes_pcollector.clear_level();
-  CullTraverser::_geom_nodes_pcollector.clear_level();
-  CullTraverser::_geoms_pcollector.clear_level();
-  GeomCacheManager::_geom_cache_active_pcollector.clear_level();
-  GeomCacheManager::_geom_cache_record_pcollector.clear_level();
-  GeomCacheManager::_geom_cache_erase_pcollector.clear_level();
-  GeomCacheManager::_geom_cache_evict_pcollector.clear_level();
-  
-  GraphicsStateGuardian::init_frame_pstats();
-
-  _transform_states_pcollector.set_level(TransformState::get_num_states());
-  _render_states_pcollector.set_level(RenderState::get_num_states());
-  if (pstats_unused_states) {
-    _transform_states_unused_pcollector.set_level(TransformState::get_num_unused_states());
-    _render_states_unused_pcollector.set_level(RenderState::get_num_unused_states());
-  }
-
-  _sw_sprites_pcollector.clear_level();
-
-  _cnode_volume_pcollector.clear_level();
-  _gnode_volume_pcollector.clear_level();
-  _geom_volume_pcollector.clear_level();
-  _node_volume_pcollector.clear_level();
-  _volume_pcollector.clear_level();
-  _test_pcollector.clear_level();
-  _volume_polygon_pcollector.clear_level();
-  _test_polygon_pcollector.clear_level();
-  _volume_plane_pcollector.clear_level();
-  _test_plane_pcollector.clear_level();
-  _volume_sphere_pcollector.clear_level();
-  _test_sphere_pcollector.clear_level();
-  _volume_tube_pcollector.clear_level();
-  _test_tube_pcollector.clear_level();
-  _volume_inv_sphere_pcollector.clear_level();
-  _test_inv_sphere_pcollector.clear_level();
-  _volume_geom_pcollector.clear_level();
-  _test_geom_pcollector.clear_level();
-  _occlusion_untested_pcollector.clear_level();
-  _occlusion_passed_pcollector.clear_level();
-  _occlusion_failed_pcollector.clear_level();
-  _occlusion_tests_pcollector.clear_level();
-
-  if (PStatClient::is_connected()) {
-    size_t small_buf = GeomVertexArrayData::get_small_lru()->get_total_size();
-    size_t independent = GeomVertexArrayData::get_independent_lru()->get_total_size();
-    size_t resident = VertexDataPage::get_global_lru(VertexDataPage::RC_resident)->get_total_size();
-    size_t compressed = VertexDataPage::get_global_lru(VertexDataPage::RC_compressed)->get_total_size();
-    size_t pending = VertexDataPage::get_pending_lru()->get_total_size();
-
-    VertexDataSaveFile *save_file = VertexDataPage::get_save_file();
-    size_t total_disk = save_file->get_total_file_size();
-    size_t used_disk = save_file->get_used_file_size();
-
-    _vertex_data_small_pcollector.set_level(small_buf);
-    _vertex_data_independent_pcollector.set_level(independent);
-    _vertex_data_pending_pcollector.set_level(pending);
-    _vertex_data_resident_pcollector.set_level(resident);
-    _vertex_data_compressed_pcollector.set_level(compressed);
-    _vertex_data_unused_disk_pcollector.set_level(total_disk - used_disk);
-    _vertex_data_used_disk_pcollector.set_level(used_disk);
-  }
-
+    PStatClient::main_tick();
+    
+    // Reset our pcollectors that track data across the frame.
+    CullTraverser::_nodes_pcollector.clear_level();
+    CullTraverser::_geom_nodes_pcollector.clear_level();
+    CullTraverser::_geoms_pcollector.clear_level();
+    GeomCacheManager::_geom_cache_active_pcollector.clear_level();
+    GeomCacheManager::_geom_cache_record_pcollector.clear_level();
+    GeomCacheManager::_geom_cache_erase_pcollector.clear_level();
+    GeomCacheManager::_geom_cache_evict_pcollector.clear_level();
+    
+    GraphicsStateGuardian::init_frame_pstats();
+    
+    _transform_states_pcollector.set_level(TransformState::get_num_states());
+    _render_states_pcollector.set_level(RenderState::get_num_states());
+    if (pstats_unused_states) {
+      _transform_states_unused_pcollector.set_level(TransformState::get_num_unused_states());
+      _render_states_unused_pcollector.set_level(RenderState::get_num_unused_states());
+    }
+    
+    _sw_sprites_pcollector.clear_level();
+    
+    _cnode_volume_pcollector.clear_level();
+    _gnode_volume_pcollector.clear_level();
+    _geom_volume_pcollector.clear_level();
+    _node_volume_pcollector.clear_level();
+    _volume_pcollector.clear_level();
+    _test_pcollector.clear_level();
+    _volume_polygon_pcollector.clear_level();
+    _test_polygon_pcollector.clear_level();
+    _volume_plane_pcollector.clear_level();
+    _test_plane_pcollector.clear_level();
+    _volume_sphere_pcollector.clear_level();
+    _test_sphere_pcollector.clear_level();
+    _volume_tube_pcollector.clear_level();
+    _test_tube_pcollector.clear_level();
+    _volume_inv_sphere_pcollector.clear_level();
+    _test_inv_sphere_pcollector.clear_level();
+    _volume_geom_pcollector.clear_level();
+    _test_geom_pcollector.clear_level();
+    _occlusion_untested_pcollector.clear_level();
+    _occlusion_passed_pcollector.clear_level();
+    _occlusion_failed_pcollector.clear_level();
+    _occlusion_tests_pcollector.clear_level();
+    
+    if (PStatClient::is_connected()) {
+      size_t small_buf = GeomVertexArrayData::get_small_lru()->get_total_size();
+      size_t independent = GeomVertexArrayData::get_independent_lru()->get_total_size();
+      size_t resident = VertexDataPage::get_global_lru(VertexDataPage::RC_resident)->get_total_size();
+      size_t compressed = VertexDataPage::get_global_lru(VertexDataPage::RC_compressed)->get_total_size();
+      size_t pending = VertexDataPage::get_pending_lru()->get_total_size();
+      
+      VertexDataSaveFile *save_file = VertexDataPage::get_save_file();
+      size_t total_disk = save_file->get_total_file_size();
+      size_t used_disk = save_file->get_used_file_size();
+      
+      _vertex_data_small_pcollector.set_level(small_buf);
+      _vertex_data_independent_pcollector.set_level(independent);
+      _vertex_data_pending_pcollector.set_level(pending);
+      _vertex_data_resident_pcollector.set_level(resident);
+      _vertex_data_compressed_pcollector.set_level(compressed);
+      _vertex_data_unused_disk_pcollector.set_level(total_disk - used_disk);
+      _vertex_data_used_disk_pcollector.set_level(used_disk);
+    }
+    
 #endif  // DO_PSTATS
 #endif  // DO_PSTATS
-
-  GeomVertexArrayData::lru_epoch();
-  
-  // Now signal all of our threads to begin their next frame.
-  Threads::const_iterator ti;
-  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
-    RenderThread *thread = (*ti).second;
-    if (thread->_thread_state == TS_wait) {
-      thread->_thread_state = TS_do_frame;
-      thread->_cv_start.signal();
+    
+    GeomVertexArrayData::lru_epoch();
+    
+    // Now signal all of our threads to begin their next frame.
+    Threads::const_iterator ti;
+    for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
+      RenderThread *thread = (*ti).second;
+      if (thread->_thread_state == TS_wait) {
+        thread->_thread_state = TS_do_frame;
+        thread->_cv_start.signal();
+      }
+      thread->_cv_mutex.release();
     }
     }
-    thread->_cv_mutex.release();
+    
+    // Some threads may still be drawing, so indicate that we have to
+    // wait for those threads before we can flip.
+    _flip_state = _auto_flip ? FS_flip : FS_draw;
   }
   }
 
 
-  // Some threads may still be drawing, so indicate that we have to
-  // wait for those threads before we can flip.
-  _flip_state = _auto_flip ? FS_flip : FS_draw;
-
+  // Now the lock is released.
+  
   if (yield_timeslice) {
   if (yield_timeslice) {
     // Nap for a moment to yield the timeslice, to be polite to other
     // Nap for a moment to yield the timeslice, to be polite to other
     // running applications.
     // running applications.
@@ -804,7 +808,7 @@ render_frame() {
     PStatTimer timer(_yield_pcollector, current_thread);
     PStatTimer timer(_yield_pcollector, current_thread);
     Thread::consider_yield();
     Thread::consider_yield();
   }
   }
-
+  
   // Anything that happens outside of GraphicsEngine::render_frame()
   // Anything that happens outside of GraphicsEngine::render_frame()
   // is deemed to be App.
   // is deemed to be App.
   _app_pcollector.start();
   _app_pcollector.start();

+ 2 - 1
panda/src/display/graphicsStateGuardian.cxx

@@ -2240,7 +2240,8 @@ async_reload_texture(TextureContext *tc) {
   nassertv(_loader != (Loader *)NULL);
   nassertv(_loader != (Loader *)NULL);
 
 
   PT(AsyncTask) request = 
   PT(AsyncTask) request = 
-    new TextureReloadRequest(_prepared_objects, tc->get_texture(),
+    new TextureReloadRequest(string("reload:") + tc->get_texture()->get_name(),
+                             _prepared_objects, tc->get_texture(),
                              _supports_compressed_texture);
                              _supports_compressed_texture);
   _loader->load_async(request);
   _loader->load_async(request);
 }
 }

+ 15 - 1
panda/src/downloader/bioStreamBuf.cxx

@@ -144,6 +144,7 @@ sync() {
 
 
   size_t num_wrote = write_chars(pbase(), n);
   size_t num_wrote = write_chars(pbase(), n);
   pbump(-(int)n);
   pbump(-(int)n);
+
   if (num_wrote != n) {
   if (num_wrote != n) {
     return EOF;
     return EOF;
   }
   }
@@ -213,6 +214,12 @@ underflow() {
         }
         }
         gbump(num_bytes);
         gbump(num_bytes);
         return EOF;
         return EOF;
+
+      } 
+        
+      if (downloader_cat.is_spam()) {
+        downloader_cat.spam()
+          << "read " << read_count << " bytes from " << _source << "\n";
       }
       }
 
 
       // Slide what we did read to the top of the buffer.
       // Slide what we did read to the top of the buffer.
@@ -241,7 +248,7 @@ write_chars(const char *start, size_t length) {
     int write_count = BIO_write(*_source, start, length);
     int write_count = BIO_write(*_source, start, length);
     if (downloader_cat.is_spam()) {
     if (downloader_cat.is_spam()) {
       downloader_cat.spam()
       downloader_cat.spam()
-        << "wrote " << write_count << " bytes.\n";
+        << "wrote " << write_count << " bytes to " << _source << "\n";
     }
     }
     thread_consider_yield();
     thread_consider_yield();
     while (write_count != (int)(length - wrote_so_far)) {
     while (write_count != (int)(length - wrote_so_far)) {
@@ -272,10 +279,17 @@ write_chars(const char *start, size_t length) {
             downloader_cat.spam()
             downloader_cat.spam()
               << "waiting to write to BIO.\n";
               << "waiting to write to BIO.\n";
           }
           }
+#if defined(HAVE_THREADS) && defined(SIMPLE_THREADS)
+          // In SIMPLE_THREADS mode, instead of blocking, simply yield
+          // the thread.
+          thread_yield();
+#else
+          // In any other threading mode, we actually want to block.
           fd_set wset;
           fd_set wset;
           FD_ZERO(&wset);
           FD_ZERO(&wset);
           FD_SET(fd, &wset);
           FD_SET(fd, &wset);
           select(fd + 1, NULL, &wset, NULL, NULL);
           select(fd + 1, NULL, &wset, NULL, NULL);
+#endif  // SIMPLE_THREADS
         }        
         }        
         
         
       } else {
       } else {

+ 1 - 0
panda/src/event/asyncTaskManager.cxx

@@ -37,6 +37,7 @@ TypeHandle AsyncTaskManager::_type_handle;
 AsyncTaskManager::
 AsyncTaskManager::
 AsyncTaskManager(const string &name) :
 AsyncTaskManager(const string &name) :
   Namable(name),
   Namable(name),
+  _lock("AsyncTaskManager::_lock"),
   _num_tasks(0),
   _num_tasks(0),
   _clock(ClockObject::get_global_clock()),
   _clock(ClockObject::get_global_clock()),
   _frame_cvar(_lock)
   _frame_cvar(_lock)

+ 1 - 1
panda/src/event/eventQueue.cxx

@@ -25,7 +25,7 @@ EventQueue *EventQueue::_global_event_queue = NULL;
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 EventQueue::
 EventQueue::
-EventQueue() : _lock("EventQueue") {
+EventQueue() : _lock("EventQueue::_lock") {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

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

@@ -1421,15 +1421,18 @@ combine_primitives(GeomPrimitive *a_prim, const GeomPrimitive *b_prim,
     b_prim2 = b_prim_copy;
     b_prim2 = b_prim_copy;
   }
   }
 
 
-  PT(GeomVertexArrayDataHandle) a_handle = a_prim->modify_vertices()->modify_handle(current_thread);
-  CPT(GeomVertexArrayDataHandle) b_handle = b_prim2->get_vertices()->get_handle(current_thread);
+  PT(GeomVertexArrayData) a_vertices = a_prim->modify_vertices();
+  CPT(GeomVertexArrayData) b_vertices = b_prim2->get_vertices();
 
 
   if (a_prim->requires_unused_vertices()) {
   if (a_prim->requires_unused_vertices()) {
-    GeomVertexReader index(b_handle->get_object(), 0);
+    GeomVertexReader index(b_vertices, 0);
     int b_vertex = index.get_data1i();
     int b_vertex = index.get_data1i();
-    a_prim->append_unused_vertices(a_handle->get_object(), b_vertex);
+    a_prim->append_unused_vertices(a_vertices, b_vertex);
   }
   }
 
 
+  PT(GeomVertexArrayDataHandle) a_handle = a_vertices->modify_handle(current_thread);
+  CPT(GeomVertexArrayDataHandle) b_handle = b_vertices->get_handle(current_thread);
+
   size_t orig_a_vertices = a_handle->get_num_rows();
   size_t orig_a_vertices = a_handle->get_num_rows();
 
 
   a_handle->copy_subdata_from(a_handle->get_data_size_bytes(), 0,
   a_handle->copy_subdata_from(a_handle->get_data_size_bytes(), 0,

+ 72 - 69
panda/src/gobj/geomPrimitive.cxx

@@ -1385,11 +1385,11 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
   } else {
   } else {
     // Indexed case.
     // Indexed case.
     GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0, current_thread);
     GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0, current_thread);
-    int num_vertices = get_num_vertices();
 
 
     if (got_mat) {
     if (got_mat) {
-      for (int i = 0; i < num_vertices; ++i) {
-        reader.set_row(index.get_data1i());
+      while (!index.is_at_end()) {
+        int ii = index.get_data1i();
+        reader.set_row(ii);
         LPoint3f vertex = mat.xform_point(reader.get_data3f());
         LPoint3f vertex = mat.xform_point(reader.get_data3f());
         
         
         if (found_any) {
         if (found_any) {
@@ -1406,7 +1406,7 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
         }
         }
       }
       }
     } else {
     } else {
-      for (int i = 0; i < num_vertices; ++i) {
+      while (!index.is_at_end()) {
         int ii = index.get_data1i();
         int ii = index.get_data1i();
         reader.set_row(ii);
         reader.set_row(ii);
         const LVecBase3f &vertex = reader.get_data3f();
         const LVecBase3f &vertex = reader.get_data3f();
@@ -1523,76 +1523,79 @@ recompute_minmax(GeomPrimitive::CData *cdata) {
     cdata->_mins.clear();
     cdata->_mins.clear();
     cdata->_maxs.clear();
     cdata->_maxs.clear();
 
 
-  } else if (get_num_vertices() == 0) {
-    // Or if we don't have any vertices, the minmax is also trivial.
-    cdata->_min_vertex = 0;
-    cdata->_max_vertex = 0;
-    cdata->_mins.clear();
-    cdata->_maxs.clear();
-
-  } else if (get_num_vertices_per_primitive() == 0) {
-    // This is a complex primitive type like a triangle strip; compute
-    // the minmax of each primitive (as well as the overall minmax).
-    GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0);
-
-    cdata->_mins = make_index_data();
-    cdata->_maxs = make_index_data();
-
-    GeomVertexWriter mins(cdata->_mins.get_write_pointer(), 0);
-    GeomVertexWriter maxs(cdata->_maxs.get_write_pointer(), 0);
-
-    int pi = 0;
-
-    unsigned int vertex = index.get_data1i();
-    cdata->_min_vertex = vertex;
-    cdata->_max_vertex = vertex;
-    unsigned int min_prim = vertex;
-    unsigned int max_prim = vertex;
-
-    int num_vertices = get_num_vertices();
-    for (int vi = 1; vi < num_vertices; ++vi) {
-      nassertv(!index.is_at_end());
+  } else {
+    int num_vertices = cdata->_vertices.get_read_pointer()->get_num_rows();
+
+    if (num_vertices == 0) {
+      // Or if we don't have any vertices, the minmax is also trivial.
+      cdata->_min_vertex = 0;
+      cdata->_max_vertex = 0;
+      cdata->_mins.clear();
+      cdata->_maxs.clear();
+      
+    } else if (get_num_vertices_per_primitive() == 0) {
+      // This is a complex primitive type like a triangle strip; compute
+      // the minmax of each primitive (as well as the overall minmax).
+      GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0);
+      
+      cdata->_mins = make_index_data();
+      cdata->_maxs = make_index_data();
+      
+      GeomVertexWriter mins(cdata->_mins.get_write_pointer(), 0);
+      GeomVertexWriter maxs(cdata->_maxs.get_write_pointer(), 0);
+      
+      int pi = 0;
+      
       unsigned int vertex = index.get_data1i();
       unsigned int vertex = index.get_data1i();
-      cdata->_min_vertex = min(cdata->_min_vertex, vertex);
-      cdata->_max_vertex = max(cdata->_max_vertex, vertex);
-
-      if (vi == cdata->_ends[pi]) {
-        mins.add_data1i(min_prim);
-        maxs.add_data1i(max_prim);
-        min_prim = vertex;
-        max_prim = vertex;
-        ++pi;
-
-      } else {
-        min_prim = min(min_prim, vertex);
-        max_prim = max(max_prim, vertex);
+      cdata->_min_vertex = vertex;
+      cdata->_max_vertex = vertex;
+      unsigned int min_prim = vertex;
+      unsigned int max_prim = vertex;
+      
+      for (int vi = 1; vi < num_vertices; ++vi) {
+        nassertv(!index.is_at_end());
+        unsigned int vertex = index.get_data1i();
+        cdata->_min_vertex = min(cdata->_min_vertex, vertex);
+        cdata->_max_vertex = max(cdata->_max_vertex, vertex);
+
+        nassertv(pi < cdata->_ends.size());
+        if (vi == cdata->_ends[pi]) {
+          mins.add_data1i(min_prim);
+          maxs.add_data1i(max_prim);
+          min_prim = vertex;
+          max_prim = vertex;
+          ++pi;
+          
+        } else {
+          min_prim = min(min_prim, vertex);
+          max_prim = max(max_prim, vertex);
+        }
       }
       }
-    }
-    mins.add_data1i(min_prim);
-    maxs.add_data1i(max_prim);
-    nassertv(mins.get_array_data()->get_num_rows() == (int)cdata->_ends.size());
-
-  } else {
-    // This is a simple primitive type like a triangle; just compute
-    // the overall minmax.
-    GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0);
-
-    cdata->_mins.clear();
-    cdata->_maxs.clear();
-
-    unsigned int vertex = index.get_data1i();
-    cdata->_min_vertex = vertex;
-    cdata->_max_vertex = vertex;
-
-    int num_vertices = get_num_vertices();
-    for (int vi = 1; vi < num_vertices; ++vi) {
-      nassertv(!index.is_at_end());
+      mins.add_data1i(min_prim);
+      maxs.add_data1i(max_prim);
+      nassertv(mins.get_array_data()->get_num_rows() == (int)cdata->_ends.size());
+      
+    } else {
+      // This is a simple primitive type like a triangle; just compute
+      // the overall minmax.
+      GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0);
+      
+      cdata->_mins.clear();
+      cdata->_maxs.clear();
+      
       unsigned int vertex = index.get_data1i();
       unsigned int vertex = index.get_data1i();
-      cdata->_min_vertex = min(cdata->_min_vertex, vertex);
-      cdata->_max_vertex = max(cdata->_max_vertex, vertex);
+      cdata->_min_vertex = vertex;
+      cdata->_max_vertex = vertex;
+      
+      for (int vi = 1; vi < num_vertices; ++vi) {
+        nassertv(!index.is_at_end());
+        unsigned int vertex = index.get_data1i();
+        cdata->_min_vertex = min(cdata->_min_vertex, vertex);
+        cdata->_max_vertex = max(cdata->_max_vertex, vertex);
+      }
     }
     }
   }
   }
-
+    
   cdata->_got_minmax = true;
   cdata->_got_minmax = true;
 }
 }
 
 

+ 3 - 2
panda/src/gobj/textureReloadRequest.I

@@ -20,9 +20,10 @@
 //               via load_async(), to begin an asynchronous load.
 //               via load_async(), to begin an asynchronous load.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE TextureReloadRequest::
 INLINE TextureReloadRequest::
-TextureReloadRequest(PreparedGraphicsObjects *pgo, Texture *texture, 
+TextureReloadRequest(const string &name,
+                     PreparedGraphicsObjects *pgo, Texture *texture, 
                      bool allow_compressed) :
                      bool allow_compressed) :
-  AsyncTask(texture->get_name()),
+  AsyncTask(name),
   _pgo(pgo),
   _pgo(pgo),
   _texture(texture),
   _texture(texture),
   _allow_compressed(allow_compressed),
   _allow_compressed(allow_compressed),

+ 2 - 1
panda/src/gobj/textureReloadRequest.h

@@ -36,7 +36,8 @@ public:
   ALLOC_DELETED_CHAIN(TextureReloadRequest);
   ALLOC_DELETED_CHAIN(TextureReloadRequest);
 
 
 PUBLISHED:
 PUBLISHED:
-  INLINE TextureReloadRequest(PreparedGraphicsObjects *pgo, Texture *texture,
+  INLINE TextureReloadRequest(const string &name,
+                              PreparedGraphicsObjects *pgo, Texture *texture,
                               bool allow_compressed);
                               bool allow_compressed);
   
   
   INLINE PreparedGraphicsObjects *get_prepared_graphics_objects() const;
   INLINE PreparedGraphicsObjects *get_prepared_graphics_objects() const;

+ 5 - 4
panda/src/parametrics/ropeNode.cxx

@@ -238,15 +238,16 @@ reset_bound(const NodePath &rel_to) {
 //               thing.
 //               thing.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void RopeNode::
 void RopeNode::
-compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
+compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
+                        int &internal_vertices,
+                        int pipeline_stage,
                         Thread *current_thread) const {
                         Thread *current_thread) const {
   PT(BoundingVolume) bounds = 
   PT(BoundingVolume) bounds = 
     do_recompute_bounds(NodePath((PandaNode *)this), pipeline_stage, 
     do_recompute_bounds(NodePath((PandaNode *)this), pipeline_stage, 
                         current_thread);
                         current_thread);
 
 
-  bdata->_internal_bounds = bounds;
-  bdata->_internal_vertices = 0;  // TODO--estimate this better.
-  bdata->_internal_bounds_stale = false;
+  internal_bounds = bounds;
+  internal_vertices = 0;  // TODO--estimate this better.
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

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

@@ -142,7 +142,9 @@ PUBLISHED:
   void reset_bound(const NodePath &rel_to);
   void reset_bound(const NodePath &rel_to);
 
 
 protected:
 protected:
-  virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
+  virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
+                                       int &internal_vertices,
+                                       int pipeline_stage,
                                        Thread *current_thread) const;
                                        Thread *current_thread) const;
 
 
 private:
 private:

+ 5 - 4
panda/src/parametrics/sheetNode.cxx

@@ -220,15 +220,16 @@ reset_bound(const NodePath &rel_to) {
 //               thing.
 //               thing.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void SheetNode::
 void SheetNode::
-compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
+compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
+                        int &internal_vertices,
+                        int pipeline_stage,
                         Thread *current_thread) const {
                         Thread *current_thread) const {
   PT(BoundingVolume) bounds = 
   PT(BoundingVolume) bounds = 
     do_recompute_bounds(NodePath((PandaNode *)this), pipeline_stage, 
     do_recompute_bounds(NodePath((PandaNode *)this), pipeline_stage, 
                         current_thread);
                         current_thread);
 
 
-  bdata->_internal_bounds = bounds;
-  bdata->_internal_vertices = 0;  // TODO--estimate this better.
-  bdata->_internal_bounds_stale = false;
+  internal_bounds = bounds;
+  internal_vertices = 0;  // TODO--estimate this better.
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

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

@@ -63,7 +63,9 @@ PUBLISHED:
   void reset_bound(const NodePath &rel_to);
   void reset_bound(const NodePath &rel_to);
 
 
 protected:
 protected:
-  virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
+  virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
+                                       int &internal_vertices,
+                                       int pipeline_stage,
                                        Thread *current_thread) const;
                                        Thread *current_thread) const;
 
 
 private:
 private:

+ 19 - 10
panda/src/pgraph/geomNode.cxx

@@ -366,10 +366,13 @@ safe_to_combine() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomNode::
 void GeomNode::
 r_prepare_scene(const RenderState *state,
 r_prepare_scene(const RenderState *state,
-                PreparedGraphicsObjects *prepared_objects) {
-  int num_geoms = get_num_geoms();
-  for (int i = 0; i < num_geoms; i++) {
-    CPT(RenderState) geom_state = state->compose(get_geom_state(i));
+                PreparedGraphicsObjects *prepared_objects,
+                Thread *current_thread) {
+  CDReader cdata(_cycler, current_thread);
+  GeomList::const_iterator gi;
+  CPT(GeomList) geoms = cdata->get_geoms();
+  for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
+    CPT(RenderState) geom_state = state->compose((*gi)._state);
     const RenderAttrib *attrib = 
     const RenderAttrib *attrib = 
       geom_state->get_attrib(TextureAttrib::get_class_type());
       geom_state->get_attrib(TextureAttrib::get_class_type());
     if (attrib != (const RenderAttrib *)NULL) {
     if (attrib != (const RenderAttrib *)NULL) {
@@ -382,7 +385,7 @@ r_prepare_scene(const RenderState *state,
     }
     }
   }
   }
   
   
-  PandaNode::r_prepare_scene(state, prepared_objects);
+  PandaNode::r_prepare_scene(state, prepared_objects, current_thread);
 }
 }
 
 
 
 
@@ -842,24 +845,31 @@ do_premunge(GraphicsStateGuardianBase *gsg,
 //               something internally.
 //               something internally.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GeomNode::
 void GeomNode::
-compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
+compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
+                        int &internal_vertices,
+                        int pipeline_stage,
                         Thread *current_thread) const {
                         Thread *current_thread) const {
   int num_vertices = 0;
   int num_vertices = 0;
 
 
   CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
   CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
 
 
   pvector<const BoundingVolume *> child_volumes;
   pvector<const BoundingVolume *> child_volumes;
+  pvector<CPT(BoundingVolume) > child_volumes_ref;
   bool all_box = true;
   bool all_box = true;
 
 
   GeomList::const_iterator gi;
   GeomList::const_iterator gi;
   CPT(GeomList) geoms = cdata->get_geoms();
   CPT(GeomList) geoms = cdata->get_geoms();
+  child_volumes.reserve(geoms->size());
+  child_volumes_ref.reserve(geoms->size());
+
   for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
   for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
     const GeomEntry &entry = (*gi);
     const GeomEntry &entry = (*gi);
     CPT(Geom) geom = entry._geom.get_read_pointer();
     CPT(Geom) geom = entry._geom.get_read_pointer();
-    const BoundingVolume *volume = geom->get_bounds();
+    CPT(BoundingVolume) volume = geom->get_bounds();
 
 
     if (!volume->is_empty()) {
     if (!volume->is_empty()) {
       child_volumes.push_back(volume);
       child_volumes.push_back(volume);
+      child_volumes_ref.push_back(volume);
       if (!volume->is_exact_type(BoundingBox::get_class_type())) {
       if (!volume->is_exact_type(BoundingBox::get_class_type())) {
         all_box = false;
         all_box = false;
       }
       }
@@ -885,9 +895,8 @@ compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage,
     ((BoundingVolume *)gbv)->around(child_begin, child_end);
     ((BoundingVolume *)gbv)->around(child_begin, child_end);
   }
   }
   
   
-  bdata->_internal_bounds = gbv;
-  bdata->_internal_vertices = num_vertices;
-  bdata->_internal_bounds_stale = false;
+  internal_bounds = gbv;
+  internal_vertices = num_vertices;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

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

@@ -60,7 +60,8 @@ public:
   virtual bool safe_to_combine() const;
   virtual bool safe_to_combine() const;
 
 
   virtual void r_prepare_scene(const RenderState *state,
   virtual void r_prepare_scene(const RenderState *state,
-                               PreparedGraphicsObjects *prepared_objects);
+                               PreparedGraphicsObjects *prepared_objects,
+                               Thread *current_thread);
 
 
 PUBLISHED:
 PUBLISHED:
   INLINE void set_preserved(bool value);
   INLINE void set_preserved(bool value);
@@ -97,7 +98,9 @@ public:
                    GeomTransformer &transformer);
                    GeomTransformer &transformer);
 
 
 protected:
 protected:
-  virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
+  virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
+                                       int &internal_vertices,
+                                       int pipeline_stage,
                                        Thread *current_thread) const;
                                        Thread *current_thread) const;
 
 
 public:
 public:

+ 14 - 1
panda/src/pgraph/loader.cxx

@@ -17,6 +17,7 @@
 #include "loaderFileTypeRegistry.h"
 #include "loaderFileTypeRegistry.h"
 #include "config_pgraph.h"
 #include "config_pgraph.h"
 #include "modelPool.h"
 #include "modelPool.h"
+#include "modelLoadRequest.h"
 #include "config_express.h"
 #include "config_express.h"
 #include "config_util.h"
 #include "config_util.h"
 #include "virtualFileSystem.h"
 #include "virtualFileSystem.h"
@@ -63,6 +64,18 @@ Loader(const string &name) :
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: Loader::make_async_request
+//       Access: Published
+//  Description: Returns a new AsyncTask object suitable for adding to
+//               load_async() to start an asynchronous model load.
+////////////////////////////////////////////////////////////////////
+PT(AsyncTask) Loader::
+make_async_request(const Filename &filename, const LoaderOptions &options) {
+  return new ModelLoadRequest(string("model:")+filename.get_basename(),
+                              filename, options, this);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Loader::load_bam_stream
 //     Function: Loader::load_bam_stream
 //       Access: Published
 //       Access: Published
@@ -367,6 +380,6 @@ void Loader::
 make_global_ptr() {
 make_global_ptr() {
   nassertv(_global_ptr == (Loader *)NULL);
   nassertv(_global_ptr == (Loader *)NULL);
 
 
-  _global_ptr = new Loader("taskMgr");
+  _global_ptr = new Loader("loader");
 }
 }
 
 

+ 2 - 0
panda/src/pgraph/loader.h

@@ -86,6 +86,8 @@ PUBLISHED:
   BLOCKING INLINE PT(PandaNode) load_sync(const Filename &filename, 
   BLOCKING INLINE PT(PandaNode) load_sync(const Filename &filename, 
                                           const LoaderOptions &options = LoaderOptions()) const;
                                           const LoaderOptions &options = LoaderOptions()) const;
 
 
+  PT(AsyncTask) make_async_request(const Filename &filename, 
+                                   const LoaderOptions &options = LoaderOptions());
   INLINE void load_async(AsyncTask *request);
   INLINE void load_async(AsyncTask *request);
 
 
   BLOCKING PT(PandaNode) load_bam_stream(istream &in);
   BLOCKING PT(PandaNode) load_bam_stream(istream &in);

+ 5 - 4
panda/src/pgraph/lodNode.cxx

@@ -476,7 +476,9 @@ show_switches_cull_callback(CullTraverser *trav, CullTraverserData &data) {
 //               something internally.
 //               something internally.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void LODNode::
 void LODNode::
-compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
+compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
+                        int &internal_vertices,
+                        int pipeline_stage,
                         Thread *current_thread) const {
                         Thread *current_thread) const {
   // First, get ourselves a fresh, empty bounding volume.
   // First, get ourselves a fresh, empty bounding volume.
   PT(BoundingVolume) bound = new BoundingSphere;
   PT(BoundingVolume) bound = new BoundingSphere;
@@ -508,9 +510,8 @@ compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage,
     bound->around(child_begin, child_end);
     bound->around(child_begin, child_end);
   }
   }
 
 
-  bdata->_internal_bounds = bound;
-  bdata->_internal_vertices = 0;
-  bdata->_internal_bounds_stale = false;
+  internal_bounds = bound;
+  internal_vertices = 0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 3 - 1
panda/src/pgraph/lodNode.h

@@ -83,7 +83,9 @@ protected:
   int compute_child(CullTraverser *trav, CullTraverserData &data);
   int compute_child(CullTraverser *trav, CullTraverserData &data);
 
 
   bool show_switches_cull_callback(CullTraverser *trav, CullTraverserData &data);
   bool show_switches_cull_callback(CullTraverser *trav, CullTraverserData &data);
-  virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
+  virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
+                                       int &internal_vertices,
+                                       int pipeline_stage,
                                        Thread *current_thread) const;
                                        Thread *current_thread) const;
 
 
   INLINE void consider_verify_lods(CullTraverser *trav, CullTraverserData &data);
   INLINE void consider_verify_lods(CullTraverser *trav, CullTraverserData &data);

+ 3 - 2
panda/src/pgraph/modelLoadRequest.I

@@ -20,9 +20,10 @@
 //               via load_async(), to begin an asynchronous load.
 //               via load_async(), to begin an asynchronous load.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE ModelLoadRequest::
 INLINE ModelLoadRequest::
-ModelLoadRequest(const Filename &filename, const LoaderOptions &options,
+ModelLoadRequest(const string &name, 
+                 const Filename &filename, const LoaderOptions &options,
                  Loader *loader) :
                  Loader *loader) :
-  AsyncTask(filename.get_basename()),
+  AsyncTask(name),
   _filename(filename),
   _filename(filename),
   _options(options),
   _options(options),
   _loader(loader),
   _loader(loader),

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

@@ -36,7 +36,8 @@ public:
   ALLOC_DELETED_CHAIN(ModelLoadRequest);
   ALLOC_DELETED_CHAIN(ModelLoadRequest);
 
 
 PUBLISHED:
 PUBLISHED:
-  INLINE ModelLoadRequest(const Filename &filename, 
+  INLINE ModelLoadRequest(const string &name,
+                          const Filename &filename, 
                           const LoaderOptions &options,
                           const LoaderOptions &options,
                           Loader *loader);
                           Loader *loader);
   
   

+ 18 - 4
panda/src/pgraph/pandaNode.I

@@ -708,7 +708,7 @@ INLINE void PandaNode::
 mark_internal_bounds_stale(int pipeline_stage, Thread *current_thread) {
 mark_internal_bounds_stale(int pipeline_stage, Thread *current_thread) {
   {
   {
     CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
     CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
-    cdata->_internal_bounds_stale = true;
+    ++cdata->_internal_bounds_mark;
   }
   }
   mark_bounds_stale(pipeline_stage, current_thread);
   mark_bounds_stale(pipeline_stage, current_thread);
 }
 }
@@ -942,9 +942,9 @@ get_parent() const {
 INLINE PandaNode::BoundsData::
 INLINE PandaNode::BoundsData::
 BoundsData() :
 BoundsData() :
   _internal_bounds(NULL),
   _internal_bounds(NULL),
-  _internal_vertices(0),
-  _internal_bounds_stale(true)
+  _internal_vertices(0)
 {
 {
+  ++_internal_bounds_mark;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -956,10 +956,24 @@ INLINE PandaNode::BoundsData::
 BoundsData(const PandaNode::BoundsData &copy) :
 BoundsData(const PandaNode::BoundsData &copy) :
   _internal_bounds(copy._internal_bounds),
   _internal_bounds(copy._internal_bounds),
   _internal_vertices(copy._internal_vertices),
   _internal_vertices(copy._internal_vertices),
-  _internal_bounds_stale(copy._internal_bounds_stale)
+  _internal_bounds_mark(copy._internal_bounds_mark),
+  _internal_bounds_computed(copy._internal_bounds_computed)
 {
 {
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::BoundsData::copy_bounds
+//       Access: Protected
+//  Description: Copies just the BoundsData part of the structure.
+////////////////////////////////////////////////////////////////////
+INLINE void PandaNode::BoundsData::
+copy_bounds(const PandaNode::BoundsData &copy) {
+  _internal_bounds = copy._internal_bounds;
+  _internal_vertices = copy._internal_vertices;
+  _internal_bounds_mark = copy._internal_bounds_mark;
+  _internal_bounds_computed = copy._internal_bounds_computed;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::CData::set_fancy_bit
 //     Function: PandaNode::CData::set_fancy_bit
 //       Access: Public
 //       Access: Public

+ 88 - 32
panda/src/pgraph/pandaNode.cxx

@@ -162,7 +162,9 @@ PandaNode(const PandaNode &copy) :
     cdata->_into_collide_mask = copy_cdata->_into_collide_mask;
     cdata->_into_collide_mask = copy_cdata->_into_collide_mask;
     cdata->_user_bounds = copy_cdata->_user_bounds;
     cdata->_user_bounds = copy_cdata->_user_bounds;
     cdata->_internal_bounds = NULL;
     cdata->_internal_bounds = NULL;
-    cdata->_internal_bounds_stale = true;
+    cdata->_internal_bounds_computed = UpdateSeq::initial();
+    cdata->_internal_bounds_mark = UpdateSeq::initial();
+    ++cdata->_internal_bounds_mark;
     cdata->_final_bounds = copy_cdata->_final_bounds;
     cdata->_final_bounds = copy_cdata->_final_bounds;
     cdata->_fancy_bits = copy_cdata->_fancy_bits;
     cdata->_fancy_bits = copy_cdata->_fancy_bits;
     
     
@@ -1772,7 +1774,7 @@ copy_all_properties(PandaNode *other) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PandaNode::
 void PandaNode::
 replace_node(PandaNode *other) {
 replace_node(PandaNode *other) {
-  nassertv(Thread::get_current_pipeline_stage() == 0);
+  //  nassertv(Thread::get_current_pipeline_stage() == 0);
 
 
   if (other == this) {
   if (other == this) {
     // Trivial.
     // Trivial.
@@ -2123,8 +2125,9 @@ get_off_clip_planes(Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PandaNode::
 void PandaNode::
 prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *net_state) {
 prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *net_state) {
+  Thread *current_thread = Thread::get_current_thread();
   PreparedGraphicsObjects *prepared_objects = gsg->get_prepared_objects();
   PreparedGraphicsObjects *prepared_objects = gsg->get_prepared_objects();
-  r_prepare_scene(net_state, prepared_objects);
+  r_prepare_scene(net_state, prepared_objects, current_thread);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2421,18 +2424,42 @@ as_light() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CPT(BoundingVolume) PandaNode::
 CPT(BoundingVolume) PandaNode::
 get_internal_bounds(int pipeline_stage, Thread *current_thread) const {
 get_internal_bounds(int pipeline_stage, Thread *current_thread) const {
-  CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
-  if (cdata->_user_bounds != (BoundingVolume *)NULL) {
-    return cdata->_user_bounds;
-  }
+  while (true) {
+    UpdateSeq mark;
+    {
+      CDStageReader cdata(_cycler, pipeline_stage, current_thread);
+      if (cdata->_user_bounds != (BoundingVolume *)NULL) {
+        return cdata->_user_bounds;
+      }
+      
+      if (cdata->_internal_bounds_mark == cdata->_internal_bounds_computed) {
+        return cdata->_internal_bounds;
+      }
 
 
-  if (cdata->_internal_bounds_stale) {
-    CDStageWriter cdataw(((PandaNode *)this)->_cycler, pipeline_stage, cdata);
-    compute_internal_bounds(cdataw, pipeline_stage, current_thread);
-    nassertr(!cdataw->_internal_bounds.is_null(), NULL);
-    return cdataw->_internal_bounds;
+      mark = cdata->_internal_bounds_mark;
+    }
+
+    // First, call compute_internal_bounds without acquiring the lock.
+    // This avoids a deadlock condition.
+    CPT(BoundingVolume) internal_bounds;
+    int internal_vertices;
+    compute_internal_bounds(internal_bounds, internal_vertices,
+                            pipeline_stage, current_thread);
+    nassertr(!internal_bounds.is_null(), NULL);
+    
+    // Now, acquire the lock, and apply the above-computed bounds.
+    CDStageWriter cdataw(((PandaNode *)this)->_cycler, pipeline_stage);
+    if (cdataw->_internal_bounds_mark == mark) {
+      cdataw->_internal_bounds_computed = mark;
+      cdataw->_internal_bounds = internal_bounds;
+      cdataw->_internal_vertices = internal_vertices;
+      return cdataw->_internal_bounds;
+    }
+
+    // Dang, someone in another thread incremented
+    // _internal_bounds_mark while we weren't holding the lock.  That
+    // means we need to go back and do it again.
   }
   }
-  return cdata->_internal_bounds;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2447,14 +2474,38 @@ get_internal_bounds(int pipeline_stage, Thread *current_thread) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int PandaNode::
 int PandaNode::
 get_internal_vertices(int pipeline_stage, Thread *current_thread) const {
 get_internal_vertices(int pipeline_stage, Thread *current_thread) const {
-  CDLockedStageReader cdata(_cycler, pipeline_stage, current_thread);
-  if (cdata->_internal_bounds_stale) {
-    CDStageWriter cdataw(((PandaNode *)this)->_cycler, pipeline_stage, cdata);
-    compute_internal_bounds(cdataw, pipeline_stage, current_thread);
-    nassertr(!cdataw->_internal_bounds.is_null(), 0);
-    return cdataw->_internal_vertices;
+  while (true) {
+    UpdateSeq mark;
+    {
+      CDStageReader cdata(_cycler, pipeline_stage, current_thread);
+      if (cdata->_internal_bounds_mark == cdata->_internal_bounds_computed) {
+        return cdata->_internal_vertices;
+      }
+
+      mark = cdata->_internal_bounds_mark;
+    }
+
+    // First, call compute_internal_bounds without acquiring the lock.
+    // This avoids a deadlock condition.
+    CPT(BoundingVolume) internal_bounds;
+    int internal_vertices;
+    compute_internal_bounds(internal_bounds, internal_vertices,
+                            pipeline_stage, current_thread);
+    nassertr(!internal_bounds.is_null(), NULL);
+    
+    // Now, acquire the lock, and apply the above-computed bounds.
+    CDStageWriter cdataw(((PandaNode *)this)->_cycler, pipeline_stage);
+    if (cdataw->_internal_bounds_mark == mark) {
+      cdataw->_internal_bounds_computed = mark;
+      cdataw->_internal_bounds = internal_bounds;
+      cdataw->_internal_vertices = internal_vertices;
+      return cdataw->_internal_vertices;
+    }
+
+    // Dang, someone in another thread incremented
+    // _internal_bounds_mark while we weren't holding the lock.  That
+    // means we need to go back and do it again.
   }
   }
-  return cdata->_internal_vertices;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2472,7 +2523,7 @@ set_internal_bounds(const BoundingVolume *volume) {
   OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
   OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
     CDStageWriter cdataw(_cycler, pipeline_stage, current_thread);
     CDStageWriter cdataw(_cycler, pipeline_stage, current_thread);
     cdataw->_internal_bounds = volume;
     cdataw->_internal_bounds = volume;
-    cdataw->_internal_bounds_stale = false;
+    cdataw->_internal_bounds_computed = cdataw->_internal_bounds_mark;
   }
   }
   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
   CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
   mark_bounds_stale(current_thread);
   mark_bounds_stale(current_thread);
@@ -2539,11 +2590,12 @@ force_bounds_stale(int pipeline_stage, Thread *current_thread) {
 //               something internally.
 //               something internally.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PandaNode::
 void PandaNode::
-compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
+compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
+                        int &internal_vertices,
+                        int pipeline_stage,
                         Thread *current_thread) const {
                         Thread *current_thread) const {
-  bdata->_internal_bounds = new BoundingSphere;
-  bdata->_internal_vertices = 0;
-  bdata->_internal_bounds_stale = false;
+  internal_bounds = new BoundingSphere;
+  internal_vertices = 0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -2693,12 +2745,15 @@ r_copy_children(const PandaNode *from, PandaNode::InstanceMap &inst_map,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PandaNode::
 void PandaNode::
 r_prepare_scene(const RenderState *state,
 r_prepare_scene(const RenderState *state,
-                PreparedGraphicsObjects *prepared_objects) {
-  int num_children = get_num_children();
-  for (int i = 0; i < num_children; i++) {
-    PandaNode *child = get_child(i);
+                PreparedGraphicsObjects *prepared_objects,
+                Thread *current_thread) {
+  Children children = get_children(current_thread);
+  // We must call get_num_children() each time through the loop, in
+  // case we're running SIMPLE_THREADS and we get interrupted.
+  for (int i = 0; i < children.get_num_children(); i++) {
+    PandaNode *child = children.get_child(i);
     CPT(RenderState) child_state = state->compose(child->get_state());
     CPT(RenderState) child_state = state->compose(child->get_state());
-    child->r_prepare_scene(child_state, prepared_objects);
+    child->r_prepare_scene(child_state, prepared_objects, current_thread);
   }
   }
 }
 }
 
 
@@ -3546,6 +3601,8 @@ update_bounds(int pipeline_stage, PandaNode::CDLockedStageReader &cdata) {
     // Also get the list of the node's children.
     // Also get the list of the node's children.
     Children children(cdata);
     Children children(cdata);
 
 
+    int num_vertices = cdata->_internal_vertices;
+
     // Now that we've got all the data we need from the node, we can
     // Now that we've got all the data we need from the node, we can
     // release the lock.
     // release the lock.
     _cycler.release_read_stage(pipeline_stage, cdata.take_pointer());
     _cycler.release_read_stage(pipeline_stage, cdata.take_pointer());
@@ -3579,8 +3636,6 @@ update_bounds(int pipeline_stage, PandaNode::CDLockedStageReader &cdata) {
       }
       }
     }
     }
 
 
-    int num_vertices = cdata->_internal_vertices;
-
     // Now expand those contents to include all of our children.
     // Now expand those contents to include all of our children.
 
 
     for (int i = 0; i < num_children; ++i) {
     for (int i = 0; i < num_children; ++i) {
@@ -3754,6 +3809,7 @@ update_bounds(int pipeline_stage, PandaNode::CDLockedStageReader &cdata) {
             << "} " << *this << "::update_bounds();\n";
             << "} " << *this << "::update_bounds();\n";
         }
         }
 
 
+        nassertr(cdataw->_last_update == cdataw->_next_update, cdataw)
         return cdataw;
         return cdataw;
       }
       }
       
       

+ 8 - 3
panda/src/pgraph/pandaNode.h

@@ -301,7 +301,9 @@ protected:
   void force_bounds_stale(int pipeline_stage, Thread *current_thread);
   void force_bounds_stale(int pipeline_stage, Thread *current_thread);
   INLINE void mark_internal_bounds_stale(int pipeline_stage, Thread *current_thread);
   INLINE void mark_internal_bounds_stale(int pipeline_stage, Thread *current_thread);
 
 
-  virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
+  virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
+                                       int &internal_vertices,
+                                       int pipeline_stage,
                                        Thread *current_thread) const;
                                        Thread *current_thread) const;
   virtual void parents_changed();
   virtual void parents_changed();
   virtual void children_changed();
   virtual void children_changed();
@@ -319,7 +321,8 @@ protected:
 
 
 public:
 public:
   virtual void r_prepare_scene(const RenderState *state,
   virtual void r_prepare_scene(const RenderState *state,
-                               PreparedGraphicsObjects *prepared_objects);
+                               PreparedGraphicsObjects *prepared_objects,
+                               Thread *current_thread);
 
 
 protected:
 protected:
   // This is a base class of CData, defined below.  It contains just
   // This is a base class of CData, defined below.  It contains just
@@ -329,6 +332,7 @@ protected:
   protected:
   protected:
     INLINE BoundsData();
     INLINE BoundsData();
     INLINE BoundsData(const BoundsData &copy);
     INLINE BoundsData(const BoundsData &copy);
+    INLINE void copy_bounds(const BoundsData &copy);
 
 
   public:
   public:
     // This is the "internal" bounding volume, which is normally
     // This is the "internal" bounding volume, which is normally
@@ -337,7 +341,8 @@ protected:
     // overriding compute_internal_bounds().
     // overriding compute_internal_bounds().
     CPT(BoundingVolume) _internal_bounds;
     CPT(BoundingVolume) _internal_bounds;
     int _internal_vertices;
     int _internal_vertices;
-    bool _internal_bounds_stale;
+    UpdateSeq _internal_bounds_mark;     // incremented on mark_stale
+    UpdateSeq _internal_bounds_computed; // set to above when computing
   };
   };
 
 
 private:
 private:

+ 5 - 4
panda/src/pgraph/planeNode.cxx

@@ -201,12 +201,13 @@ is_renderable() const {
 //               something internally.
 //               something internally.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PlaneNode::
 void PlaneNode::
-compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
+compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
+                        int &internal_vertices,
+                        int pipeline_stage,
                         Thread *current_thread) const {
                         Thread *current_thread) const {
   CDStageReader cdata(_cycler, pipeline_stage, current_thread);
   CDStageReader cdata(_cycler, pipeline_stage, current_thread);
-  bdata->_internal_bounds = new BoundingPlane(cdata->_plane);
-  bdata->_internal_vertices = 0;
-  bdata->_internal_bounds_stale = false;
+  internal_bounds = new BoundingPlane(cdata->_plane);
+  internal_vertices = 0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 3 - 1
panda/src/pgraph/planeNode.h

@@ -72,7 +72,9 @@ public:
   INLINE static UpdateSeq get_sort_seq();
   INLINE static UpdateSeq get_sort_seq();
 
 
 protected:
 protected:
-  virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
+  virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
+                                       int &internal_vertices,
+                                       int pipeline_stage,
                                        Thread *current_thread) const;
                                        Thread *current_thread) const;
   PT(Geom) get_viz(CullTraverser *trav, CullTraverserData &data);
   PT(Geom) get_viz(CullTraverser *trav, CullTraverserData &data);
   
   

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

@@ -379,7 +379,9 @@ draw() const {
 //               thing.
 //               thing.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PortalNode::
 void PortalNode::
-compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
+compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
+                        int &internal_vertices,
+                        int pipeline_stage,
                         Thread *current_thread) const {
                         Thread *current_thread) const {
   // First, get ourselves a fresh, empty bounding volume.
   // First, get ourselves a fresh, empty bounding volume.
   PT(BoundingVolume) bound = new BoundingSphere;
   PT(BoundingVolume) bound = new BoundingSphere;
@@ -394,9 +396,8 @@ compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage,
   // Now actually compute the bounding volume by putting it around all
   // Now actually compute the bounding volume by putting it around all
   gbv->around(vertices_begin, vertices_end);
   gbv->around(vertices_begin, vertices_end);
 
 
-  bdata->_internal_bounds = bound;
-  bdata->_internal_vertices = 0;
-  bdata->_internal_bounds_stale = false;
+  internal_bounds = bound;
+  internal_vertices = 0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 3 - 1
panda/src/pgraph/portalNode.h

@@ -93,7 +93,9 @@ PUBLISHED:
   //  void draw () const;
   //  void draw () const;
 
 
 protected:
 protected:
-  virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
+  virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
+                                       int &internal_vertices,
+                                       int pipeline_stage,
                                        Thread *current_thread) const;
                                        Thread *current_thread) const;
 
 
 private:
 private:

+ 3 - 1
panda/src/pgui/pgButton.I

@@ -93,7 +93,8 @@ get_click_prefix() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE string PGButton::
 INLINE string PGButton::
 get_click_event(const ButtonHandle &button) const {
 get_click_event(const ButtonHandle &button) const {
-  return "click-" + button.get_name() + "-" + get_id();
+  ReMutexHolder holder(_lock);
+  return get_click_prefix() + button.get_name() + "-" + get_id();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -104,5 +105,6 @@ get_click_event(const ButtonHandle &button) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PGButton::
 INLINE bool PGButton::
 is_button_down() {
 is_button_down() {
+  ReMutexHolder holder(_lock);
   return _button_down;
   return _button_down;
 }
 }

+ 12 - 0
panda/src/pgui/pgButton.cxx

@@ -69,6 +69,7 @@ PGButton(const PGButton &copy) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PandaNode *PGButton::
 PandaNode *PGButton::
 make_copy() const {
 make_copy() const {
+  ReMutexHolder holder(_lock);
   return new PGButton(*this);
   return new PGButton(*this);
 }
 }
 
 
@@ -80,6 +81,7 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGButton::
 void PGButton::
 enter_region(const MouseWatcherParameter &param) {
 enter_region(const MouseWatcherParameter &param) {
+  ReMutexHolder holder(_lock);
   if (get_active()) {
   if (get_active()) {
     set_state(_button_down ? S_depressed : S_rollover);
     set_state(_button_down ? S_depressed : S_rollover);
   }
   }
@@ -94,6 +96,7 @@ enter_region(const MouseWatcherParameter &param) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGButton::
 void PGButton::
 exit_region(const MouseWatcherParameter &param) {
 exit_region(const MouseWatcherParameter &param) {
+  ReMutexHolder holder(_lock);
   if (get_active()) {
   if (get_active()) {
     set_state(S_ready);
     set_state(S_ready);
   }
   }
@@ -109,6 +112,7 @@ exit_region(const MouseWatcherParameter &param) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGButton::
 void PGButton::
 press(const MouseWatcherParameter &param, bool background) {
 press(const MouseWatcherParameter &param, bool background) {
+  ReMutexHolder holder(_lock);
   if (has_click_button(param.get_button())) {
   if (has_click_button(param.get_button())) {
     if (get_active()) {
     if (get_active()) {
       _button_down = true;
       _button_down = true;
@@ -127,6 +131,7 @@ press(const MouseWatcherParameter &param, bool background) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGButton::
 void PGButton::
 release(const MouseWatcherParameter &param, bool background) {
 release(const MouseWatcherParameter &param, bool background) {
+  ReMutexHolder holder(_lock);
   if (has_click_button(param.get_button())) {
   if (has_click_button(param.get_button())) {
     _button_down = false;
     _button_down = false;
     if (get_active()) {
     if (get_active()) {
@@ -149,6 +154,7 @@ release(const MouseWatcherParameter &param, bool background) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGButton::
 void PGButton::
 click(const MouseWatcherParameter &param) {
 click(const MouseWatcherParameter &param) {
+  ReMutexHolder holder(_lock);
   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
   string event = get_click_event(param.get_button());
   string event = get_click_event(param.get_button());
   play_sound(event);
   play_sound(event);
@@ -170,6 +176,7 @@ click(const MouseWatcherParameter &param) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGButton::
 void PGButton::
 setup(const string &label, float bevel) {
 setup(const string &label, float bevel) {
+  ReMutexHolder holder(_lock);
   clear_state_def(S_ready);
   clear_state_def(S_ready);
   clear_state_def(S_depressed);
   clear_state_def(S_depressed);
   clear_state_def(S_rollover);
   clear_state_def(S_rollover);
@@ -226,6 +233,7 @@ setup(const string &label, float bevel) {
 void PGButton::
 void PGButton::
 setup(const NodePath &ready, const NodePath &depressed, 
 setup(const NodePath &ready, const NodePath &depressed, 
       const NodePath &rollover, const NodePath &inactive) {
       const NodePath &rollover, const NodePath &inactive) {
+  ReMutexHolder holder(_lock);
   clear_state_def(S_ready);
   clear_state_def(S_ready);
   clear_state_def(S_depressed);
   clear_state_def(S_depressed);
   clear_state_def(S_rollover);
   clear_state_def(S_rollover);
@@ -246,6 +254,7 @@ setup(const NodePath &ready, const NodePath &depressed,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGButton:: 
 void PGButton:: 
 set_active(bool active) {
 set_active(bool active) {
+  ReMutexHolder holder(_lock);
   if (active != get_active()) {
   if (active != get_active()) {
     PGItem::set_active(active);
     PGItem::set_active(active);
     set_state(active ? S_ready : S_inactive);
     set_state(active ? S_ready : S_inactive);
@@ -262,6 +271,7 @@ set_active(bool active) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PGButton::
 bool PGButton::
 add_click_button(const ButtonHandle &button) {
 add_click_button(const ButtonHandle &button) {
+  ReMutexHolder holder(_lock);
   return _click_buttons.insert(button).second;
   return _click_buttons.insert(button).second;
 }
 }
 
 
@@ -276,6 +286,7 @@ add_click_button(const ButtonHandle &button) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PGButton::
 bool PGButton::
 remove_click_button(const ButtonHandle &button) {
 remove_click_button(const ButtonHandle &button) {
+  ReMutexHolder holder(_lock);
   return (_click_buttons.erase(button) != 0);
   return (_click_buttons.erase(button) != 0);
 }
 }
 
 
@@ -288,5 +299,6 @@ remove_click_button(const ButtonHandle &button) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PGButton::
 bool PGButton::
 has_click_button(const ButtonHandle &button) {
 has_click_button(const ButtonHandle &button) {
+  ReMutexHolder holder(_lock);
   return (_click_buttons.count(button) != 0);
   return (_click_buttons.count(button) != 0);
 }
 }

+ 36 - 5
panda/src/pgui/pgEntry.I

@@ -27,6 +27,7 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PGEntry:: 
 INLINE bool PGEntry:: 
 set_text(const string &text) {
 set_text(const string &text) {
+  ReMutexHolder holder(_lock);
   TextNode *text_node = get_text_def(S_focus);
   TextNode *text_node = get_text_def(S_focus);
   nassertr(text_node != (TextNode *)NULL, false);
   nassertr(text_node != (TextNode *)NULL, false);
   return set_wtext(text_node->decode_text(text));
   return set_wtext(text_node->decode_text(text));
@@ -44,6 +45,7 @@ set_text(const string &text) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE string PGEntry:: 
 INLINE string PGEntry:: 
 get_plain_text() const {
 get_plain_text() const {
+  ReMutexHolder holder(_lock);
   TextNode *text_node = get_text_def(S_focus);
   TextNode *text_node = get_text_def(S_focus);
   nassertr(text_node != (TextNode *)NULL, string());
   nassertr(text_node != (TextNode *)NULL, string());
   return text_node->encode_wtext(get_plain_wtext());
   return text_node->encode_wtext(get_plain_wtext());
@@ -59,6 +61,7 @@ get_plain_text() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE string PGEntry:: 
 INLINE string PGEntry:: 
 get_text() const {
 get_text() const {
+  ReMutexHolder holder(_lock);
   TextNode *text_node = get_text_def(S_focus);
   TextNode *text_node = get_text_def(S_focus);
   nassertr(text_node != (TextNode *)NULL, string());
   nassertr(text_node != (TextNode *)NULL, string());
   return text_node->encode_wtext(get_wtext());
   return text_node->encode_wtext(get_wtext());
@@ -79,6 +82,7 @@ get_text() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int PGEntry::
 INLINE int PGEntry::
 get_num_characters() const {
 get_num_characters() const {
+  ReMutexHolder holder(_lock);
   return _text.get_num_characters();
   return _text.get_num_characters();
 }
 }
 
 
@@ -91,6 +95,7 @@ get_num_characters() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE wchar_t PGEntry::
 INLINE wchar_t PGEntry::
 get_character(int n) const {
 get_character(int n) const {
+  ReMutexHolder holder(_lock);
   return _text.get_character(n);
   return _text.get_character(n);
 }
 }
 
 
@@ -104,6 +109,7 @@ get_character(int n) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const TextGraphic *PGEntry::
 INLINE const TextGraphic *PGEntry::
 get_graphic(int n) const {
 get_graphic(int n) const {
+  ReMutexHolder holder(_lock);
   return _text.get_graphic(n);
   return _text.get_graphic(n);
 }
 }
 
 
@@ -116,6 +122,7 @@ get_graphic(int n) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const TextProperties &PGEntry::
 INLINE const TextProperties &PGEntry::
 get_properties(int n) const {
 get_properties(int n) const {
+  ReMutexHolder holder(_lock);
   return _text.get_properties(n);
   return _text.get_properties(n);
 }
 }
 
 
@@ -129,6 +136,7 @@ get_properties(int n) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGEntry:: 
 INLINE void PGEntry:: 
 set_cursor_position(int position) {
 set_cursor_position(int position) {
+  ReMutexHolder holder(_lock);
   if (_cursor_position != position) {
   if (_cursor_position != position) {
     _cursor_position = position;
     _cursor_position = position;
     _cursor_stale = true;
     _cursor_stale = true;
@@ -143,6 +151,7 @@ set_cursor_position(int position) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int PGEntry:: 
 INLINE int PGEntry:: 
 get_cursor_position() const {
 get_cursor_position() const {
+  ReMutexHolder holder(_lock);
   return _cursor_position;
   return _cursor_position;
 }
 }
 
 
@@ -158,6 +167,7 @@ get_cursor_position() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGEntry:: 
 INLINE void PGEntry:: 
 set_max_chars(int max_chars) {
 set_max_chars(int max_chars) {
+  ReMutexHolder holder(_lock);
   _max_chars = max_chars;
   _max_chars = max_chars;
 }
 }
 
 
@@ -170,6 +180,7 @@ set_max_chars(int max_chars) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int PGEntry:: 
 INLINE int PGEntry:: 
 get_max_chars() const {
 get_max_chars() const {
+  ReMutexHolder holder(_lock);
   return _max_chars;
   return _max_chars;
 }
 }
 
 
@@ -190,6 +201,7 @@ get_max_chars() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGEntry:: 
 INLINE void PGEntry:: 
 set_max_width(float max_width) {
 set_max_width(float max_width) {
+  ReMutexHolder holder(_lock);
   _max_width = max_width;
   _max_width = max_width;
   _text_geom_stale = true;
   _text_geom_stale = true;
 }
 }
@@ -203,6 +215,7 @@ set_max_width(float max_width) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE float PGEntry:: 
 INLINE float PGEntry:: 
 get_max_width() const {
 get_max_width() const {
+  ReMutexHolder holder(_lock);
   return _max_width;
   return _max_width;
 }
 }
 
 
@@ -215,6 +228,7 @@ get_max_width() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGEntry:: 
 INLINE void PGEntry:: 
 set_num_lines(int num_lines) {
 set_num_lines(int num_lines) {
+  ReMutexHolder holder(_lock);
   nassertv(num_lines >= 1);
   nassertv(num_lines >= 1);
   _num_lines = num_lines;
   _num_lines = num_lines;
   _text_geom_stale = true;
   _text_geom_stale = true;
@@ -228,6 +242,7 @@ set_num_lines(int num_lines) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int PGEntry:: 
 INLINE int PGEntry:: 
 get_num_lines() const {
 get_num_lines() const {
+  ReMutexHolder holder(_lock);
   return _num_lines;
   return _num_lines;
 }
 }
 
 
@@ -242,6 +257,7 @@ get_num_lines() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGEntry:: 
 INLINE void PGEntry:: 
 set_blink_rate(float blink_rate) {
 set_blink_rate(float blink_rate) {
+  ReMutexHolder holder(_lock);
   _blink_rate = blink_rate;
   _blink_rate = blink_rate;
 }
 }
 
 
@@ -253,6 +269,7 @@ set_blink_rate(float blink_rate) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE float PGEntry:: 
 INLINE float PGEntry:: 
 get_blink_rate() const {
 get_blink_rate() const {
+  ReMutexHolder holder(_lock);
   return _blink_rate;
   return _blink_rate;
 }
 }
 
 
@@ -265,6 +282,7 @@ get_blink_rate() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const NodePath &PGEntry:: 
 INLINE const NodePath &PGEntry:: 
 get_cursor_def() {
 get_cursor_def() {
+  ReMutexHolder holder(_lock);
   return _cursor_def;
   return _cursor_def;
 }
 }
 
 
@@ -276,6 +294,7 @@ get_cursor_def() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGEntry:: 
 INLINE void PGEntry:: 
 clear_cursor_def() {
 clear_cursor_def() {
+  ReMutexHolder holder(_lock);
   _cursor_def.remove_node();
   _cursor_def.remove_node();
   _cursor_def = _cursor_scale.attach_new_node("cursor");
   _cursor_def = _cursor_scale.attach_new_node("cursor");
 }
 }
@@ -289,6 +308,7 @@ clear_cursor_def() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGEntry:: 
 INLINE void PGEntry:: 
 set_cursor_keys_active(bool flag) {
 set_cursor_keys_active(bool flag) {
+  ReMutexHolder holder(_lock);
   _cursor_keys_active = flag;
   _cursor_keys_active = flag;
 }
 }
 
 
@@ -301,6 +321,7 @@ set_cursor_keys_active(bool flag) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PGEntry:: 
 INLINE bool PGEntry:: 
 get_cursor_keys_active() const {
 get_cursor_keys_active() const {
+  ReMutexHolder holder(_lock);
   return _cursor_keys_active;
   return _cursor_keys_active;
 }
 }
 
 
@@ -320,6 +341,7 @@ get_cursor_keys_active() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGEntry:: 
 INLINE void PGEntry:: 
 set_obscure_mode(bool flag) {
 set_obscure_mode(bool flag) {
+  ReMutexHolder holder(_lock);
   if (_obscure_mode != flag) {
   if (_obscure_mode != flag) {
     _obscure_mode = flag;
     _obscure_mode = flag;
     _text_geom_stale = true;
     _text_geom_stale = true;
@@ -334,6 +356,7 @@ set_obscure_mode(bool flag) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PGEntry:: 
 INLINE bool PGEntry:: 
 get_obscure_mode() const {
 get_obscure_mode() const {
+  ReMutexHolder holder(_lock);
   return _obscure_mode;
   return _obscure_mode;
 }
 }
 
 
@@ -356,6 +379,7 @@ get_obscure_mode() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGEntry:: 
 INLINE void PGEntry:: 
 set_candidate_active(const string &candidate_active) {
 set_candidate_active(const string &candidate_active) {
+  ReMutexHolder holder(_lock);
   _candidate_active = candidate_active;
   _candidate_active = candidate_active;
 }
 }
 
 
@@ -366,6 +390,7 @@ set_candidate_active(const string &candidate_active) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const string &PGEntry:: 
 INLINE const string &PGEntry:: 
 get_candidate_active() const {
 get_candidate_active() const {
+  ReMutexHolder holder(_lock);
   return _candidate_active;
   return _candidate_active;
 }
 }
 
 
@@ -388,6 +413,7 @@ get_candidate_active() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGEntry:: 
 INLINE void PGEntry:: 
 set_candidate_inactive(const string &candidate_inactive) {
 set_candidate_inactive(const string &candidate_inactive) {
+  ReMutexHolder holder(_lock);
   _candidate_inactive = candidate_inactive;
   _candidate_inactive = candidate_inactive;
 }
 }
 
 
@@ -398,6 +424,7 @@ set_candidate_inactive(const string &candidate_inactive) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const string &PGEntry:: 
 INLINE const string &PGEntry:: 
 get_candidate_inactive() const {
 get_candidate_inactive() const {
+  ReMutexHolder holder(_lock);
   return _candidate_inactive;
   return _candidate_inactive;
 }
 }
 
 
@@ -469,7 +496,7 @@ get_erase_prefix() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE string PGEntry::
 INLINE string PGEntry::
 get_accept_event(const ButtonHandle &button) const {
 get_accept_event(const ButtonHandle &button) const {
-  return "accept-" + button.get_name() + "-" + get_id();
+  return get_accept_prefix() + button.get_name() + "-" + get_id();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -480,7 +507,7 @@ get_accept_event(const ButtonHandle &button) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE string PGEntry::
 INLINE string PGEntry::
 get_accept_failed_event(const ButtonHandle &button) const {
 get_accept_failed_event(const ButtonHandle &button) const {
-  return "acceptfailed-" + button.get_name() + "-" + get_id();
+  return get_accept_failed_prefix() + button.get_name() + "-" + get_id();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -493,7 +520,7 @@ get_accept_failed_event(const ButtonHandle &button) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE string PGEntry::
 INLINE string PGEntry::
 get_overflow_event() const {
 get_overflow_event() const {
-  return "overflow-" + get_id();
+  return get_overflow_prefix() + get_id();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -504,7 +531,7 @@ get_overflow_event() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE string PGEntry::
 INLINE string PGEntry::
 get_type_event() const {
 get_type_event() const {
-  return "type-" + get_id();
+  return get_type_prefix() + get_id();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -515,7 +542,7 @@ get_type_event() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE string PGEntry::
 INLINE string PGEntry::
 get_erase_event() const {
 get_erase_event() const {
-  return "erase-" + get_id();
+  return get_erase_prefix() + get_id();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -530,6 +557,7 @@ get_erase_event() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PGEntry:: 
 INLINE bool PGEntry:: 
 set_wtext(const wstring &wtext) {
 set_wtext(const wstring &wtext) {
+  ReMutexHolder holder(_lock);
   bool ret = _text.set_wtext(wtext);
   bool ret = _text.set_wtext(wtext);
   if (_obscure_mode) {
   if (_obscure_mode) {
     ret = _obscure_text.set_wtext(wstring(_text.get_num_characters(), '*'));
     ret = _obscure_text.set_wtext(wstring(_text.get_num_characters(), '*'));
@@ -547,6 +575,7 @@ set_wtext(const wstring &wtext) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE wstring PGEntry:: 
 INLINE wstring PGEntry:: 
 get_plain_wtext() const {
 get_plain_wtext() const {
+  ReMutexHolder holder(_lock);
   return _text.get_plain_wtext();
   return _text.get_plain_wtext();
 }
 }
 
 
@@ -558,6 +587,7 @@ get_plain_wtext() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE wstring PGEntry:: 
 INLINE wstring PGEntry:: 
 get_wtext() const {
 get_wtext() const {
+  ReMutexHolder holder(_lock);
   return _text.get_wtext();
   return _text.get_wtext();
 }
 }
 
 
@@ -569,5 +599,6 @@ get_wtext() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGEntry:: 
 INLINE void PGEntry:: 
 set_accept_enabled(bool enabled) {
 set_accept_enabled(bool enabled) {
+  ReMutexHolder holder(_lock);
   _accept_enabled = enabled;
   _accept_enabled = enabled;
 }
 }

+ 18 - 0
panda/src/pgui/pgEntry.cxx

@@ -124,6 +124,7 @@ PGEntry(const PGEntry &copy) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PandaNode *PGEntry::
 PandaNode *PGEntry::
 make_copy() const {
 make_copy() const {
+  ReMutexHolder holder(_lock);
   return new PGEntry(*this);
   return new PGEntry(*this);
 }
 }
 
 
@@ -136,6 +137,7 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGEntry::
 void PGEntry::
 xform(const LMatrix4f &mat) {
 xform(const LMatrix4f &mat) {
+  ReMutexHolder holder(_lock);
   PGItem::xform(mat);
   PGItem::xform(mat);
   _text_render_root.set_mat(_text_render_root.get_mat() * mat);
   _text_render_root.set_mat(_text_render_root.get_mat() * mat);
 }
 }
@@ -167,6 +169,7 @@ xform(const LMatrix4f &mat) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PGEntry::
 bool PGEntry::
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
+  ReMutexHolder holder(_lock);
   PGItem::cull_callback(trav, data);
   PGItem::cull_callback(trav, data);
   update_text();
   update_text();
   update_cursor();
   update_cursor();
@@ -188,6 +191,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGEntry::
 void PGEntry::
 press(const MouseWatcherParameter &param, bool background) {
 press(const MouseWatcherParameter &param, bool background) {
+  ReMutexHolder holder(_lock);
   if (get_active()) {
   if (get_active()) {
     if (param.has_button()) {
     if (param.has_button()) {
       // Make sure _text is initialized properly.
       // Make sure _text is initialized properly.
@@ -296,6 +300,7 @@ press(const MouseWatcherParameter &param, bool background) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGEntry::
 void PGEntry::
 keystroke(const MouseWatcherParameter &param, bool background) {
 keystroke(const MouseWatcherParameter &param, bool background) {
+  ReMutexHolder holder(_lock);
   if (get_active()) {
   if (get_active()) {
     if (param.has_keycode()) {
     if (param.has_keycode()) {
       // Make sure _text is initialized properly.
       // Make sure _text is initialized properly.
@@ -402,6 +407,7 @@ keystroke(const MouseWatcherParameter &param, bool background) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGEntry::
 void PGEntry::
 candidate(const MouseWatcherParameter &param, bool background) {
 candidate(const MouseWatcherParameter &param, bool background) {
+  ReMutexHolder holder(_lock);
   if (get_active()) {
   if (get_active()) {
     if (param.has_candidate()) {
     if (param.has_candidate()) {
       // Save the candidate string so it can be displayed.
       // Save the candidate string so it can be displayed.
@@ -426,6 +432,7 @@ candidate(const MouseWatcherParameter &param, bool background) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGEntry::
 void PGEntry::
 accept(const MouseWatcherParameter &param) {
 accept(const MouseWatcherParameter &param) {
+  ReMutexHolder holder(_lock);
   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
   string event = get_accept_event(param.get_button());
   string event = get_accept_event(param.get_button());
   play_sound(event);
   play_sound(event);
@@ -441,6 +448,7 @@ accept(const MouseWatcherParameter &param) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGEntry::
 void PGEntry::
 accept_failed(const MouseWatcherParameter &param) {
 accept_failed(const MouseWatcherParameter &param) {
+  ReMutexHolder holder(_lock);
   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
   string event = get_accept_failed_event(param.get_button());
   string event = get_accept_failed_event(param.get_button());
   play_sound(event);
   play_sound(event);
@@ -458,6 +466,7 @@ accept_failed(const MouseWatcherParameter &param) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGEntry::
 void PGEntry::
 overflow(const MouseWatcherParameter &param) {
 overflow(const MouseWatcherParameter &param) {
+  ReMutexHolder holder(_lock);
   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
   string event = get_overflow_event();
   string event = get_overflow_event();
   play_sound(event);
   play_sound(event);
@@ -472,6 +481,7 @@ overflow(const MouseWatcherParameter &param) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGEntry::
 void PGEntry::
 type(const MouseWatcherParameter &param) {
 type(const MouseWatcherParameter &param) {
+  ReMutexHolder holder(_lock);
   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
   string event = get_type_event();
   string event = get_type_event();
   play_sound(event);
   play_sound(event);
@@ -486,6 +496,7 @@ type(const MouseWatcherParameter &param) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGEntry::
 void PGEntry::
 erase(const MouseWatcherParameter &param) {
 erase(const MouseWatcherParameter &param) {
+  ReMutexHolder holder(_lock);
   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
   PGMouseWatcherParameter *ep = new PGMouseWatcherParameter(param);
   string event = get_erase_event();
   string event = get_erase_event();
   play_sound(event);
   play_sound(event);
@@ -503,6 +514,7 @@ erase(const MouseWatcherParameter &param) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGEntry::
 void PGEntry::
 setup(float width, int num_lines) {
 setup(float width, int num_lines) {
+  ReMutexHolder holder(_lock);
   setup_minimal(width, num_lines);
   setup_minimal(width, num_lines);
 
 
   TextNode *text_node = get_text_def(S_focus);
   TextNode *text_node = get_text_def(S_focus);
@@ -570,6 +582,7 @@ setup(float width, int num_lines) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGEntry::
 void PGEntry::
 setup_minimal(float width, int num_lines) {
 setup_minimal(float width, int num_lines) {
+  ReMutexHolder holder(_lock);
   set_text(string());
   set_text(string());
   _cursor_position = 0;
   _cursor_position = 0;
   set_max_chars(0);
   set_max_chars(0);
@@ -608,6 +621,7 @@ setup_minimal(float width, int num_lines) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGEntry::
 void PGEntry::
 set_text_def(int state, TextNode *node) {
 set_text_def(int state, TextNode *node) {
+  ReMutexHolder holder(_lock);
   nassertv(state >= 0 && state < 1000);  // Sanity check.
   nassertv(state >= 0 && state < 1000);  // Sanity check.
   if (node == (TextNode *)NULL && state >= (int)_text_defs.size()) {
   if (node == (TextNode *)NULL && state >= (int)_text_defs.size()) {
     // If we're setting it to NULL, we don't need to slot a new one.
     // If we're setting it to NULL, we don't need to slot a new one.
@@ -627,6 +641,7 @@ set_text_def(int state, TextNode *node) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 TextNode *PGEntry:: 
 TextNode *PGEntry:: 
 get_text_def(int state) const {
 get_text_def(int state) const {
+  ReMutexHolder holder(_lock);
   if (state < 0 || state >= (int)_text_defs.size()) {
   if (state < 0 || state >= (int)_text_defs.size()) {
     // If we don't have a definition, use the global one.
     // If we don't have a definition, use the global one.
     return get_text_node();
     return get_text_node();
@@ -646,6 +661,7 @@ get_text_def(int state) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGEntry:: 
 void PGEntry:: 
 set_active(bool active) {
 set_active(bool active) {
+  ReMutexHolder holder(_lock);
   PGItem::set_active(active);
   PGItem::set_active(active);
   update_state();
   update_state();
 }
 }
@@ -658,6 +674,7 @@ set_active(bool active) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGEntry:: 
 void PGEntry:: 
 set_focus(bool focus) {
 set_focus(bool focus) {
+  ReMutexHolder holder(_lock);
   PGItem::set_focus(focus);
   PGItem::set_focus(focus);
   _blink_start = ClockObject::get_global_clock()->get_frame_time();
   _blink_start = ClockObject::get_global_clock()->get_frame_time();
   update_state();
   update_state();
@@ -673,6 +690,7 @@ set_focus(bool focus) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PGEntry:: 
 bool PGEntry:: 
 is_wtext() const {
 is_wtext() const {
+  ReMutexHolder holder(_lock);
   for (int i = 0; i < _text.get_num_characters(); ++i) {
   for (int i = 0; i < _text.get_num_characters(); ++i) {
     wchar_t ch = _text.get_character(i);
     wchar_t ch = _text.get_character(i);
     if ((ch & ~0x7f) != 0) {
     if ((ch & ~0x7f) != 0) {

+ 39 - 0
panda/src/pgui/pgItem.I

@@ -13,6 +13,17 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PGItem::set_name
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void PGItem::
+set_name(const string &name) {
+  Namable::set_name(name);
+  _lock.set_name(name);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PGItem::get_region
 //     Function: PGItem::get_region
 //       Access: Public
 //       Access: Public
@@ -26,6 +37,7 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PGMouseWatcherRegion *PGItem:: 
 INLINE PGMouseWatcherRegion *PGItem:: 
 get_region() const {
 get_region() const {
+  ReMutexHolder holder(_lock);
   return _region;
   return _region;
 }
 }
 
 
@@ -40,6 +52,7 @@ get_region() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGItem:: 
 INLINE void PGItem:: 
 set_notify(PGItemNotify *notify) {
 set_notify(PGItemNotify *notify) {
+  ReMutexHolder holder(_lock);
   if (_notify != (PGItemNotify *)NULL) {
   if (_notify != (PGItemNotify *)NULL) {
     _notify->remove_item(this);
     _notify->remove_item(this);
   }
   }
@@ -57,6 +70,7 @@ set_notify(PGItemNotify *notify) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PGItem:: 
 INLINE bool PGItem:: 
 has_notify() const {
 has_notify() const {
+  ReMutexHolder holder(_lock);
   return (_notify != (PGItemNotify *)NULL);
   return (_notify != (PGItemNotify *)NULL);
 }
 }
 
 
@@ -69,6 +83,7 @@ has_notify() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PGItemNotify *PGItem:: 
 INLINE PGItemNotify *PGItem:: 
 get_notify() const {
 get_notify() const {
+  ReMutexHolder holder(_lock);
   return _notify;
   return _notify;
 }
 }
 
 
@@ -97,6 +112,7 @@ set_frame(float left, float right, float bottom, float top) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGItem::
 INLINE void PGItem::
 set_frame(const LVecBase4f &frame) {
 set_frame(const LVecBase4f &frame) {
+  ReMutexHolder holder(_lock);
   if (!_has_frame || _frame != frame) {
   if (!_has_frame || _frame != frame) {
     _has_frame = true;
     _has_frame = true;
     _frame = frame;
     _frame = frame;
@@ -113,6 +129,7 @@ set_frame(const LVecBase4f &frame) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const LVecBase4f &PGItem::
 INLINE const LVecBase4f &PGItem::
 get_frame() const {
 get_frame() const {
+  ReMutexHolder holder(_lock);
   nassertr(has_frame(), _frame);
   nassertr(has_frame(), _frame);
   return _frame;
   return _frame;
 }
 }
@@ -125,6 +142,7 @@ get_frame() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PGItem::
 INLINE bool PGItem::
 has_frame() const {
 has_frame() const {
+  ReMutexHolder holder(_lock);
   return _has_frame;
   return _has_frame;
 }
 }
 
 
@@ -137,6 +155,7 @@ has_frame() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGItem::
 INLINE void PGItem::
 clear_frame() {
 clear_frame() {
+  ReMutexHolder holder(_lock);
   if (_has_frame) {
   if (_has_frame) {
     _has_frame = false;
     _has_frame = false;
     frame_changed();
     frame_changed();
@@ -154,6 +173,7 @@ clear_frame() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGItem::
 INLINE void PGItem::
 set_state(int state) {
 set_state(int state) {
+  ReMutexHolder holder(_lock);
   _state = state;
   _state = state;
 }
 }
 
 
@@ -165,6 +185,7 @@ set_state(int state) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int PGItem::
 INLINE int PGItem::
 get_state() const {
 get_state() const {
+  ReMutexHolder holder(_lock);
   return _state;
   return _state;
 }
 }
 
 
@@ -176,6 +197,7 @@ get_state() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PGItem::
 INLINE bool PGItem::
 get_active() const {
 get_active() const {
+  ReMutexHolder holder(_lock);
   return (_flags & F_active) != 0;
   return (_flags & F_active) != 0;
 }
 }
 
 
@@ -187,6 +209,7 @@ get_active() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PGItem::
 INLINE bool PGItem::
 get_focus() const {
 get_focus() const {
+  ReMutexHolder holder(_lock);
   return (_flags & F_focus) != 0;
   return (_flags & F_focus) != 0;
 }
 }
 
 
@@ -198,6 +221,7 @@ get_focus() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PGItem::
 INLINE bool PGItem::
 get_background_focus() const {
 get_background_focus() const {
+  ReMutexHolder holder(_lock);
   return (_flags & F_background_focus) != 0;
   return (_flags & F_background_focus) != 0;
 }
 }
 
 
@@ -210,6 +234,7 @@ get_background_focus() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGItem::
 INLINE void PGItem::
 set_suppress_flags(int suppress_flags) {
 set_suppress_flags(int suppress_flags) {
+  ReMutexHolder holder(_lock);
   _region->set_suppress_flags(suppress_flags);
   _region->set_suppress_flags(suppress_flags);
 }
 }
 
 
@@ -222,6 +247,7 @@ set_suppress_flags(int suppress_flags) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int PGItem::
 INLINE int PGItem::
 get_suppress_flags() const {
 get_suppress_flags() const {
+  ReMutexHolder holder(_lock);
   return _region->get_suppress_flags();
   return _region->get_suppress_flags();
 }
 }
 
 
@@ -235,6 +261,7 @@ get_suppress_flags() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const string &PGItem::
 INLINE const string &PGItem::
 get_id() const {
 get_id() const {
+  ReMutexHolder holder(_lock);
   return _region->get_name();
   return _region->get_name();
 }
 }
 
 
@@ -253,6 +280,7 @@ get_id() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGItem::
 INLINE void PGItem::
 set_id(const string &id) {
 set_id(const string &id) {
+  ReMutexHolder holder(_lock);
   _region->set_name(id);
   _region->set_name(id);
 }
 }
 
 
@@ -395,6 +423,7 @@ get_keystroke_prefix() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE string PGItem::
 INLINE string PGItem::
 get_enter_event() const {
 get_enter_event() const {
+  ReMutexHolder holder(_lock);
   return get_enter_prefix() + get_id();
   return get_enter_prefix() + get_id();
 }
 }
 
 
@@ -407,6 +436,7 @@ get_enter_event() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE string PGItem::
 INLINE string PGItem::
 get_exit_event() const {
 get_exit_event() const {
+  ReMutexHolder holder(_lock);
   return get_exit_prefix() + get_id();
   return get_exit_prefix() + get_id();
 }
 }
 
 
@@ -421,6 +451,7 @@ get_exit_event() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE string PGItem::
 INLINE string PGItem::
 get_within_event() const {
 get_within_event() const {
+  ReMutexHolder holder(_lock);
   return get_within_prefix() + get_id();
   return get_within_prefix() + get_id();
 }
 }
 
 
@@ -436,6 +467,7 @@ get_within_event() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE string PGItem::
 INLINE string PGItem::
 get_without_event() const {
 get_without_event() const {
+  ReMutexHolder holder(_lock);
   return get_without_prefix() + get_id();
   return get_without_prefix() + get_id();
 }
 }
 
 
@@ -447,6 +479,7 @@ get_without_event() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE string PGItem::
 INLINE string PGItem::
 get_focus_in_event() const {
 get_focus_in_event() const {
+  ReMutexHolder holder(_lock);
   return get_focus_in_prefix() + get_id();
   return get_focus_in_prefix() + get_id();
 }
 }
 
 
@@ -458,6 +491,7 @@ get_focus_in_event() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE string PGItem::
 INLINE string PGItem::
 get_focus_out_event() const {
 get_focus_out_event() const {
+  ReMutexHolder holder(_lock);
   return get_focus_out_prefix() + get_id();
   return get_focus_out_prefix() + get_id();
 }
 }
 
 
@@ -471,6 +505,7 @@ get_focus_out_event() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE string PGItem::
 INLINE string PGItem::
 get_press_event(const ButtonHandle &button) const {
 get_press_event(const ButtonHandle &button) const {
+  ReMutexHolder holder(_lock);
   return get_press_prefix() + button.get_name() + "-" + get_id();
   return get_press_prefix() + button.get_name() + "-" + get_id();
 }
 }
 
 
@@ -484,6 +519,7 @@ get_press_event(const ButtonHandle &button) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE string PGItem::
 INLINE string PGItem::
 get_repeat_event(const ButtonHandle &button) const {
 get_repeat_event(const ButtonHandle &button) const {
+  ReMutexHolder holder(_lock);
   return get_repeat_prefix() + button.get_name() + "-" + get_id();
   return get_repeat_prefix() + button.get_name() + "-" + get_id();
 }
 }
 
 
@@ -497,6 +533,7 @@ get_repeat_event(const ButtonHandle &button) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE string PGItem::
 INLINE string PGItem::
 get_release_event(const ButtonHandle &button) const {
 get_release_event(const ButtonHandle &button) const {
+  ReMutexHolder holder(_lock);
   return get_release_prefix() + button.get_name() + "-" + get_id();
   return get_release_prefix() + button.get_name() + "-" + get_id();
 }
 }
 
 
@@ -508,6 +545,7 @@ get_release_event(const ButtonHandle &button) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE string PGItem::
 INLINE string PGItem::
 get_keystroke_event() const {
 get_keystroke_event() const {
+  ReMutexHolder holder(_lock);
   return get_keystroke_prefix() + get_id();
   return get_keystroke_prefix() + get_id();
 }
 }
 
 
@@ -543,6 +581,7 @@ get_focus_item() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE LMatrix4f PGItem::
 INLINE LMatrix4f PGItem::
 get_frame_inv_xform() const {
 get_frame_inv_xform() const {
+  ReMutexHolder holder(_lock);
   return _frame_inv_xform;
   return _frame_inv_xform;
 }
 }
 
 

+ 47 - 10
panda/src/pgui/pgItem.cxx

@@ -58,7 +58,8 @@ is_right(const LVector2f &v1, const LVector2f &v2) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PGItem::
 PGItem::
 PGItem(const string &name) : 
 PGItem(const string &name) : 
-  PandaNode(name)
+  PandaNode(name),
+  _lock(name)
 {
 {
   set_cull_callback();
   set_cull_callback();
 
 
@@ -142,6 +143,7 @@ PGItem(const PGItem &copy) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PandaNode *PGItem::
 PandaNode *PGItem::
 make_copy() const {
 make_copy() const {
+  ReMutexHolder holder(_lock);
   return new PGItem(*this);
   return new PGItem(*this);
 }
 }
 
 
@@ -154,6 +156,7 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 transform_changed() {
 transform_changed() {
+  ReMutexHolder holder(_lock);
   PandaNode::transform_changed();
   PandaNode::transform_changed();
   if (has_notify()) {
   if (has_notify()) {
     get_notify()->item_transform_changed(this);
     get_notify()->item_transform_changed(this);
@@ -169,6 +172,7 @@ transform_changed() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 draw_mask_changed() {
 draw_mask_changed() {
+  ReMutexHolder holder(_lock);
   PandaNode::draw_mask_changed();
   PandaNode::draw_mask_changed();
   if (has_notify()) {
   if (has_notify()) {
     get_notify()->item_draw_mask_changed(this);
     get_notify()->item_draw_mask_changed(this);
@@ -202,6 +206,7 @@ draw_mask_changed() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PGItem::
 bool PGItem::
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
+  ReMutexHolder holder(_lock);
   bool this_node_hidden = data.is_this_node_hidden(trav);
   bool this_node_hidden = data.is_this_node_hidden(trav);
   if (!this_node_hidden && has_frame() && get_active()) {
   if (!this_node_hidden && has_frame() && get_active()) {
     // The item has a frame, so we want to generate a region for it
     // The item has a frame, so we want to generate a region for it
@@ -296,8 +301,11 @@ is_renderable() const {
 //               thing.
 //               thing.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
-compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
+compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
+                        int &internal_vertices,
+                        int pipeline_stage,
                         Thread *current_thread) const {
                         Thread *current_thread) const {
+  ReMutexHolder holder(_lock, current_thread);
   int num_vertices = 0;
   int num_vertices = 0;
 
 
   // First, get ourselves a fresh, empty bounding volume.
   // First, get ourselves a fresh, empty bounding volume.
@@ -330,9 +338,8 @@ compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage,
 
 
   bound->around(child_begin, child_end);
   bound->around(child_begin, child_end);
 
 
-  bdata->_internal_bounds = bound;
-  bdata->_internal_vertices = num_vertices;
-  bdata->_internal_bounds_stale = false;
+  internal_bounds = bound;
+  internal_vertices = num_vertices;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -345,18 +352,20 @@ compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 r_prepare_scene(const RenderState *state,
 r_prepare_scene(const RenderState *state,
-                PreparedGraphicsObjects *prepared_objects) {
+                PreparedGraphicsObjects *prepared_objects,
+                Thread *current_thread) {
+  ReMutexHolder holder(_lock);
   StateDefs::iterator di;
   StateDefs::iterator di;
   for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
   for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
     NodePath &root = (*di)._root;
     NodePath &root = (*di)._root;
     if (!root.is_empty()) {
     if (!root.is_empty()) {
       PandaNode *child = root.node();
       PandaNode *child = root.node();
       CPT(RenderState) child_state = state->compose(child->get_state());
       CPT(RenderState) child_state = state->compose(child->get_state());
-      child->r_prepare_scene(child_state, prepared_objects);
+      child->r_prepare_scene(child_state, prepared_objects, current_thread);
     }
     }
   }
   }
   
   
-  PandaNode::r_prepare_scene(state, prepared_objects);
+  PandaNode::r_prepare_scene(state, prepared_objects, current_thread);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -368,6 +377,7 @@ r_prepare_scene(const RenderState *state,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 xform(const LMatrix4f &mat) {
 xform(const LMatrix4f &mat) {
+  ReMutexHolder holder(_lock);
   // Transform the frame.
   // Transform the frame.
   LPoint3f ll(_frame[0], 0.0f, _frame[2]);
   LPoint3f ll(_frame[0], 0.0f, _frame[2]);
   LPoint3f ur(_frame[1], 0.0f, _frame[3]);
   LPoint3f ur(_frame[1], 0.0f, _frame[3]);
@@ -407,6 +417,7 @@ bool PGItem::
 activate_region(const LMatrix4f &transform, int sort,
 activate_region(const LMatrix4f &transform, int sort,
                 const ClipPlaneAttrib *cpa,
                 const ClipPlaneAttrib *cpa,
                 const ScissorAttrib *sa) {
                 const ScissorAttrib *sa) {
+  ReMutexHolder holder(_lock);
   // Transform all four vertices, and get the new bounding box.  This
   // Transform all four vertices, and get the new bounding box.  This
   // way the region works (mostly) even if has been rotated.
   // way the region works (mostly) even if has been rotated.
   LPoint3f ll(_frame[0], 0.0f, _frame[2]);
   LPoint3f ll(_frame[0], 0.0f, _frame[2]);
@@ -503,6 +514,7 @@ activate_region(const LMatrix4f &transform, int sort,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 enter_region(const MouseWatcherParameter &param) {
 enter_region(const MouseWatcherParameter &param) {
+  ReMutexHolder holder(_lock);
   if (pgui_cat.is_debug()) {
   if (pgui_cat.is_debug()) {
     pgui_cat.debug()
     pgui_cat.debug()
       << *this << "::enter_region(" << param << ")\n";
       << *this << "::enter_region(" << param << ")\n";
@@ -529,6 +541,7 @@ enter_region(const MouseWatcherParameter &param) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 exit_region(const MouseWatcherParameter &param) {
 exit_region(const MouseWatcherParameter &param) {
+  ReMutexHolder holder(_lock);
   if (pgui_cat.is_debug()) {
   if (pgui_cat.is_debug()) {
     pgui_cat.debug()
     pgui_cat.debug()
       << *this << "::exit_region(" << param << ")\n";
       << *this << "::exit_region(" << param << ")\n";
@@ -558,6 +571,7 @@ exit_region(const MouseWatcherParameter &param) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 within_region(const MouseWatcherParameter &param) {
 within_region(const MouseWatcherParameter &param) {
+  ReMutexHolder holder(_lock);
   if (pgui_cat.is_debug()) {
   if (pgui_cat.is_debug()) {
     pgui_cat.debug()
     pgui_cat.debug()
       << *this << "::within_region(" << param << ")\n";
       << *this << "::within_region(" << param << ")\n";
@@ -582,6 +596,7 @@ within_region(const MouseWatcherParameter &param) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 without_region(const MouseWatcherParameter &param) {
 without_region(const MouseWatcherParameter &param) {
+  ReMutexHolder holder(_lock);
   if (pgui_cat.is_debug()) {
   if (pgui_cat.is_debug()) {
     pgui_cat.debug()
     pgui_cat.debug()
       << *this << "::without_region(" << param << ")\n";
       << *this << "::without_region(" << param << ")\n";
@@ -605,6 +620,7 @@ without_region(const MouseWatcherParameter &param) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 focus_in() {
 focus_in() {
+  ReMutexHolder holder(_lock);
   if (pgui_cat.is_debug()) {
   if (pgui_cat.is_debug()) {
     pgui_cat.debug()
     pgui_cat.debug()
       << *this << "::focus_in()\n";
       << *this << "::focus_in()\n";
@@ -627,6 +643,7 @@ focus_in() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 focus_out() {
 focus_out() {
+  ReMutexHolder holder(_lock);
   if (pgui_cat.is_debug()) {
   if (pgui_cat.is_debug()) {
     pgui_cat.debug()
     pgui_cat.debug()
       << *this << "::focus_out()\n";
       << *this << "::focus_out()\n";
@@ -650,6 +667,7 @@ focus_out() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 press(const MouseWatcherParameter &param, bool background) {
 press(const MouseWatcherParameter &param, bool background) {
+  ReMutexHolder holder(_lock);
   if (pgui_cat.is_debug()) {
   if (pgui_cat.is_debug()) {
     pgui_cat.debug()
     pgui_cat.debug()
       << *this << "::press(" << param << ", " << background << ")\n";
       << *this << "::press(" << param << ", " << background << ")\n";
@@ -681,6 +699,7 @@ press(const MouseWatcherParameter &param, bool background) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 release(const MouseWatcherParameter &param, bool background) {
 release(const MouseWatcherParameter &param, bool background) {
+  ReMutexHolder holder(_lock);
   if (pgui_cat.is_debug()) {
   if (pgui_cat.is_debug()) {
     pgui_cat.debug()
     pgui_cat.debug()
       << *this << "::release(" << param << ", " << background << ")\n";
       << *this << "::release(" << param << ", " << background << ")\n";
@@ -706,6 +725,7 @@ release(const MouseWatcherParameter &param, bool background) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 keystroke(const MouseWatcherParameter &param, bool background) {
 keystroke(const MouseWatcherParameter &param, bool background) {
+  ReMutexHolder holder(_lock);
   if (pgui_cat.is_debug()) {
   if (pgui_cat.is_debug()) {
     pgui_cat.debug()
     pgui_cat.debug()
       << *this << "::keystroke(" << param << ", " << background << ")\n";
       << *this << "::keystroke(" << param << ", " << background << ")\n";
@@ -731,6 +751,7 @@ keystroke(const MouseWatcherParameter &param, bool background) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 candidate(const MouseWatcherParameter &param, bool background) {
 candidate(const MouseWatcherParameter &param, bool background) {
+  ReMutexHolder holder(_lock);
   if (pgui_cat.is_debug()) {
   if (pgui_cat.is_debug()) {
     pgui_cat.debug()
     pgui_cat.debug()
       << *this << "::candidate(" << param << ", " << background << ")\n";
       << *this << "::candidate(" << param << ", " << background << ")\n";
@@ -752,6 +773,7 @@ candidate(const MouseWatcherParameter &param, bool background) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 move(const MouseWatcherParameter &param) {
 move(const MouseWatcherParameter &param) {
+  ReMutexHolder holder(_lock);
   if (pgui_cat.is_debug()) {
   if (pgui_cat.is_debug()) {
     pgui_cat.debug()
     pgui_cat.debug()
       << *this << "::move(" << param << ")\n";
       << *this << "::move(" << param << ")\n";
@@ -841,6 +863,7 @@ background_candidate(const MouseWatcherParameter &param) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 set_active(bool active) {
 set_active(bool active) {
+  ReMutexHolder holder(_lock);
   if (active) {
   if (active) {
     _flags |= F_active;
     _flags |= F_active;
   } else {
   } else {
@@ -867,6 +890,7 @@ set_active(bool active) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 set_focus(bool focus) {
 set_focus(bool focus) {
+  ReMutexHolder holder(_lock);
   if (focus) {
   if (focus) {
     if (!get_active()) {
     if (!get_active()) {
       // Cannot set focus on an inactive item.
       // Cannot set focus on an inactive item.
@@ -911,6 +935,7 @@ set_focus(bool focus) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 set_background_focus(bool focus) {
 set_background_focus(bool focus) {
+  ReMutexHolder holder(_lock);
   if (focus != get_background_focus()) {
   if (focus != get_background_focus()) {
     if (focus) {
     if (focus) {
       // Activate background focus.
       // Activate background focus.
@@ -940,6 +965,7 @@ set_background_focus(bool focus) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int PGItem::
 int PGItem::
 get_num_state_defs() const {
 get_num_state_defs() const {
+  ReMutexHolder holder(_lock);
   return _state_defs.size();
   return _state_defs.size();
 }
 }
 
 
@@ -952,6 +978,7 @@ get_num_state_defs() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PGItem::
 bool PGItem::
 has_state_def(int state) const {
 has_state_def(int state) const {
+  ReMutexHolder holder(_lock);
   if (state < 0 || state >= (int)_state_defs.size()) {
   if (state < 0 || state >= (int)_state_defs.size()) {
     return false;
     return false;
   }
   }
@@ -967,6 +994,7 @@ has_state_def(int state) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 clear_state_def(int state) {
 clear_state_def(int state) {
+  ReMutexHolder holder(_lock);
   if (state < 0 || state >= (int)_state_defs.size()) {
   if (state < 0 || state >= (int)_state_defs.size()) {
     return;
     return;
   }
   }
@@ -988,6 +1016,7 @@ clear_state_def(int state) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 NodePath &PGItem::
 NodePath &PGItem::
 get_state_def(int state) {
 get_state_def(int state) {
+  ReMutexHolder holder(_lock);
   nassertr(state >= 0 && state < 1000, get_state_def(0));  // Sanity check.
   nassertr(state >= 0 && state < 1000, get_state_def(0));  // Sanity check.
   slot_state_def(state);
   slot_state_def(state);
 
 
@@ -1012,6 +1041,7 @@ get_state_def(int state) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 NodePath PGItem::
 NodePath PGItem::
 instance_to_state_def(int state, const NodePath &path) {
 instance_to_state_def(int state, const NodePath &path) {
+  ReMutexHolder holder(_lock);
   if (path.is_empty()) {
   if (path.is_empty()) {
     // If the source is empty, quietly do nothing.
     // If the source is empty, quietly do nothing.
     return NodePath();
     return NodePath();
@@ -1030,6 +1060,7 @@ instance_to_state_def(int state, const NodePath &path) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PGFrameStyle PGItem::
 PGFrameStyle PGItem::
 get_frame_style(int state) {
 get_frame_style(int state) {
+  ReMutexHolder holder(_lock);
   if (state < 0 || state >= (int)_state_defs.size()) {
   if (state < 0 || state >= (int)_state_defs.size()) {
     return PGFrameStyle();
     return PGFrameStyle();
   }
   }
@@ -1044,14 +1075,15 @@ get_frame_style(int state) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 set_frame_style(int state, const PGFrameStyle &style) {
 set_frame_style(int state, const PGFrameStyle &style) {
+  ReMutexHolder holder(_lock);
   // Get the state def node, mainly to ensure that this state is
   // Get the state def node, mainly to ensure that this state is
   // slotted and listed as having been defined.
   // slotted and listed as having been defined.
   NodePath &root = get_state_def(state);
   NodePath &root = get_state_def(state);
   nassertv(!root.is_empty());
   nassertv(!root.is_empty());
-
+  
   _state_defs[state]._frame_style = style;
   _state_defs[state]._frame_style = style;
   _state_defs[state]._frame_stale = true;
   _state_defs[state]._frame_stale = true;
-
+    
   mark_internal_bounds_stale();
   mark_internal_bounds_stale();
 }
 }
 
 
@@ -1064,6 +1096,7 @@ set_frame_style(int state, const PGFrameStyle &style) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 set_sound(const string &event, AudioSound *sound) {
 set_sound(const string &event, AudioSound *sound) {
+  ReMutexHolder holder(_lock);
   _sounds[event] = sound;
   _sounds[event] = sound;
 }
 }
 
 
@@ -1075,6 +1108,7 @@ set_sound(const string &event, AudioSound *sound) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGItem::
 void PGItem::
 clear_sound(const string &event) {
 clear_sound(const string &event) {
+  ReMutexHolder holder(_lock);
   _sounds.erase(event);
   _sounds.erase(event);
 }
 }
 
 
@@ -1086,6 +1120,7 @@ clear_sound(const string &event) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 AudioSound *PGItem::
 AudioSound *PGItem::
 get_sound(const string &event) const {
 get_sound(const string &event) const {
+  ReMutexHolder holder(_lock);
   Sounds::const_iterator si = _sounds.find(event);
   Sounds::const_iterator si = _sounds.find(event);
   if (si != _sounds.end()) {
   if (si != _sounds.end()) {
     return (*si).second;
     return (*si).second;
@@ -1101,6 +1136,7 @@ get_sound(const string &event) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PGItem::
 bool PGItem::
 has_sound(const string &event) const {
 has_sound(const string &event) const {
+  ReMutexHolder holder(_lock);
   return (_sounds.count(event) != 0);
   return (_sounds.count(event) != 0);
 }
 }
 #endif  // HAVE_AUDIO
 #endif  // HAVE_AUDIO
@@ -1134,6 +1170,7 @@ get_text_node() {
 void PGItem::
 void PGItem::
 play_sound(const string &event) {
 play_sound(const string &event) {
 #ifdef HAVE_AUDIO
 #ifdef HAVE_AUDIO
+  ReMutexHolder holder(_lock);
   Sounds::const_iterator si = _sounds.find(event);
   Sounds::const_iterator si = _sounds.find(event);
   if (si != _sounds.end()) {
   if (si != _sounds.end()) {
     AudioSound *sound = (*si).second;
     AudioSound *sound = (*si).second;

+ 12 - 2
panda/src/pgui/pgItem.h

@@ -29,6 +29,8 @@
 #include "textNode.h"
 #include "textNode.h"
 #include "plane.h"
 #include "plane.h"
 #include "pmap.h"
 #include "pmap.h"
+#include "reMutex.h"
+#include "reMutexHolder.h"
 
 
 class PGTop;
 class PGTop;
 class MouseWatcherParameter;
 class MouseWatcherParameter;
@@ -55,6 +57,8 @@ PUBLISHED:
   PGItem(const string &name);
   PGItem(const string &name);
   virtual ~PGItem();
   virtual ~PGItem();
 
 
+  INLINE void set_name(const string &name);
+
 protected:
 protected:
   PGItem(const PGItem &copy);
   PGItem(const PGItem &copy);
 
 
@@ -65,11 +69,14 @@ protected:
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
   virtual bool is_renderable() const;
   virtual bool is_renderable() const;
 
 
-  virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
+  virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
+                                       int &internal_vertices,
+                                       int pipeline_stage,
                                        Thread *current_thread) const;
                                        Thread *current_thread) const;
 
 
   virtual void r_prepare_scene(const RenderState *state,
   virtual void r_prepare_scene(const RenderState *state,
-                               PreparedGraphicsObjects *prepared_objects);
+                               PreparedGraphicsObjects *prepared_objects,
+                               Thread *current_thread);
 
 
 public:
 public:
   virtual void xform(const LMatrix4f &mat);
   virtual void xform(const LMatrix4f &mat);
@@ -191,6 +198,9 @@ private:
 
 
   bool clip_frame(pvector<LPoint2f> &source_points, const Planef &plane) const;
   bool clip_frame(pvector<LPoint2f> &source_points, const Planef &plane) const;
 
 
+protected:
+  ReMutex _lock;
+
 private:
 private:
   PGItemNotify *_notify;
   PGItemNotify *_notify;
 
 

+ 13 - 0
panda/src/pgui/pgScrollFrame.I

@@ -34,6 +34,7 @@ set_virtual_frame(float left, float right, float bottom, float top) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGScrollFrame::
 INLINE void PGScrollFrame::
 set_virtual_frame(const LVecBase4f &frame) {
 set_virtual_frame(const LVecBase4f &frame) {
+  ReMutexHolder holder(_lock);
   _has_virtual_frame = true;
   _has_virtual_frame = true;
   _virtual_frame = frame;
   _virtual_frame = frame;
 
 
@@ -52,6 +53,7 @@ set_virtual_frame(const LVecBase4f &frame) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const LVecBase4f &PGScrollFrame::
 INLINE const LVecBase4f &PGScrollFrame::
 get_virtual_frame() const {
 get_virtual_frame() const {
+  ReMutexHolder holder(_lock);
   return _has_virtual_frame ? _virtual_frame : get_clip_frame();
   return _has_virtual_frame ? _virtual_frame : get_clip_frame();
 }
 }
 
 
@@ -64,6 +66,7 @@ get_virtual_frame() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PGScrollFrame::
 INLINE bool PGScrollFrame::
 has_virtual_frame() const {
 has_virtual_frame() const {
+  ReMutexHolder holder(_lock);
   return _has_virtual_frame;
   return _has_virtual_frame;
 }
 }
 
 
@@ -77,6 +80,7 @@ has_virtual_frame() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGScrollFrame::
 INLINE void PGScrollFrame::
 clear_virtual_frame() {
 clear_virtual_frame() {
+  ReMutexHolder holder(_lock);
   _has_virtual_frame = false;
   _has_virtual_frame = false;
 }
 }
 
 
@@ -92,6 +96,7 @@ clear_virtual_frame() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGScrollFrame:: 
 INLINE void PGScrollFrame:: 
 set_manage_pieces(bool manage_pieces) {
 set_manage_pieces(bool manage_pieces) {
+  ReMutexHolder holder(_lock);
   _manage_pieces = manage_pieces;
   _manage_pieces = manage_pieces;
   _needs_remanage = true;
   _needs_remanage = true;
   _needs_recompute_clip = true;
   _needs_recompute_clip = true;
@@ -105,6 +110,7 @@ set_manage_pieces(bool manage_pieces) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PGScrollFrame:: 
 INLINE bool PGScrollFrame:: 
 get_manage_pieces() const {
 get_manage_pieces() const {
+  ReMutexHolder holder(_lock);
   return _manage_pieces;
   return _manage_pieces;
 }
 }
 
 
@@ -122,6 +128,7 @@ get_manage_pieces() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGScrollFrame:: 
 INLINE void PGScrollFrame:: 
 set_auto_hide(bool auto_hide) {
 set_auto_hide(bool auto_hide) {
+  ReMutexHolder holder(_lock);
   _auto_hide = auto_hide;
   _auto_hide = auto_hide;
   if (_auto_hide) {
   if (_auto_hide) {
     set_manage_pieces(true);
     set_manage_pieces(true);
@@ -137,6 +144,7 @@ set_auto_hide(bool auto_hide) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PGScrollFrame:: 
 INLINE bool PGScrollFrame:: 
 get_auto_hide() const {
 get_auto_hide() const {
+  ReMutexHolder holder(_lock);
   return _auto_hide;
   return _auto_hide;
 }
 }
 
 
@@ -150,6 +158,7 @@ get_auto_hide() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGScrollFrame::
 INLINE void PGScrollFrame::
 set_horizontal_slider(PGSliderBar *horizontal_slider) {
 set_horizontal_slider(PGSliderBar *horizontal_slider) {
+  ReMutexHolder holder(_lock);
   if (_horizontal_slider != (PGSliderBar *)NULL) {
   if (_horizontal_slider != (PGSliderBar *)NULL) {
     _horizontal_slider->set_notify(NULL);
     _horizontal_slider->set_notify(NULL);
   }
   }
@@ -181,6 +190,7 @@ clear_horizontal_slider() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PGSliderBar *PGScrollFrame::
 INLINE PGSliderBar *PGScrollFrame::
 get_horizontal_slider() const {
 get_horizontal_slider() const {
+  ReMutexHolder holder(_lock);
   return _horizontal_slider;
   return _horizontal_slider;
 }
 }
 
 
@@ -194,6 +204,7 @@ get_horizontal_slider() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGScrollFrame::
 INLINE void PGScrollFrame::
 set_vertical_slider(PGSliderBar *vertical_slider) {
 set_vertical_slider(PGSliderBar *vertical_slider) {
+  ReMutexHolder holder(_lock);
   if (_vertical_slider != (PGSliderBar *)NULL) {
   if (_vertical_slider != (PGSliderBar *)NULL) {
     _vertical_slider->set_notify(NULL);
     _vertical_slider->set_notify(NULL);
   }
   }
@@ -225,6 +236,7 @@ clear_vertical_slider() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PGSliderBar *PGScrollFrame::
 INLINE PGSliderBar *PGScrollFrame::
 get_vertical_slider() const {
 get_vertical_slider() const {
+  ReMutexHolder holder(_lock);
   return _vertical_slider;
   return _vertical_slider;
 }
 }
 
 
@@ -236,6 +248,7 @@ get_vertical_slider() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGScrollFrame::
 INLINE void PGScrollFrame::
 recompute() {
 recompute() {
+  ReMutexHolder holder(_lock);
   recompute_clip();
   recompute_clip();
   recompute_canvas();
   recompute_canvas();
 }
 }

+ 13 - 0
panda/src/pgui/pgScrollFrame.cxx

@@ -76,6 +76,7 @@ PGScrollFrame(const PGScrollFrame &copy) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PandaNode *PGScrollFrame::
 PandaNode *PGScrollFrame::
 make_copy() const {
 make_copy() const {
+  ReMutexHolder holder(_lock);
   return new PGScrollFrame(*this);
   return new PGScrollFrame(*this);
 }
 }
 
 
@@ -106,6 +107,7 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PGScrollFrame::
 bool PGScrollFrame::
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
+  ReMutexHolder holder(_lock);
   if (_manage_pieces && _needs_remanage) {
   if (_manage_pieces && _needs_remanage) {
     remanage();
     remanage();
   }
   }
@@ -127,6 +129,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGScrollFrame::
 void PGScrollFrame::
 xform(const LMatrix4f &mat) {
 xform(const LMatrix4f &mat) {
+  ReMutexHolder holder(_lock);
   PGVirtualFrame::xform(mat);
   PGVirtualFrame::xform(mat);
 
 
   _needs_remanage = true;
   _needs_remanage = true;
@@ -143,6 +146,7 @@ void PGScrollFrame::
 setup(float width, float height,
 setup(float width, float height,
       float left, float right, float bottom, float top,
       float left, float right, float bottom, float top,
       float slider_width, float bevel) {
       float slider_width, float bevel) {
+  ReMutexHolder holder(_lock);
   set_state(0);
   set_state(0);
   clear_state_def(0);
   clear_state_def(0);
 
 
@@ -196,6 +200,7 @@ setup(float width, float height,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGScrollFrame::
 void PGScrollFrame::
 remanage() {
 remanage() {
+  ReMutexHolder holder(_lock);
   _needs_remanage = false;
   _needs_remanage = false;
 
 
   const LVecBase4f &frame = get_frame();
   const LVecBase4f &frame = get_frame();
@@ -312,6 +317,7 @@ remanage() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGScrollFrame::
 void PGScrollFrame::
 frame_changed() {
 frame_changed() {
+  ReMutexHolder holder(_lock);
   PGVirtualFrame::frame_changed();
   PGVirtualFrame::frame_changed();
   _needs_remanage = true;
   _needs_remanage = true;
   _needs_recompute_clip = true;
   _needs_recompute_clip = true;
@@ -325,6 +331,7 @@ frame_changed() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGScrollFrame::
 void PGScrollFrame::
 item_transform_changed(PGItem *) {
 item_transform_changed(PGItem *) {
+  ReMutexHolder holder(_lock);
   _needs_recompute_clip = true;
   _needs_recompute_clip = true;
 }
 }
 
 
@@ -336,6 +343,7 @@ item_transform_changed(PGItem *) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGScrollFrame::
 void PGScrollFrame::
 item_frame_changed(PGItem *) {
 item_frame_changed(PGItem *) {
+  ReMutexHolder holder(_lock);
   _needs_recompute_clip = true;
   _needs_recompute_clip = true;
 }
 }
 
 
@@ -347,6 +355,7 @@ item_frame_changed(PGItem *) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGScrollFrame::
 void PGScrollFrame::
 item_draw_mask_changed(PGItem *) {
 item_draw_mask_changed(PGItem *) {
+  ReMutexHolder holder(_lock);
   _needs_remanage = true;
   _needs_remanage = true;
   _needs_recompute_clip = true;
   _needs_recompute_clip = true;
 }
 }
@@ -359,6 +368,7 @@ item_draw_mask_changed(PGItem *) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGScrollFrame::
 void PGScrollFrame::
 slider_bar_adjust(PGSliderBar *) {
 slider_bar_adjust(PGSliderBar *) {
+  ReMutexHolder holder(_lock);
   _needs_recompute_canvas = true;
   _needs_recompute_canvas = true;
 }
 }
 
 
@@ -370,6 +380,7 @@ slider_bar_adjust(PGSliderBar *) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGScrollFrame::
 void PGScrollFrame::
 recompute_clip() {
 recompute_clip() {
+  ReMutexHolder holder(_lock);
   _needs_recompute_clip = false;
   _needs_recompute_clip = false;
   _needs_recompute_canvas = true;
   _needs_recompute_canvas = true;
 
 
@@ -397,6 +408,7 @@ recompute_clip() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGScrollFrame::
 void PGScrollFrame::
 recompute_canvas() {
 recompute_canvas() {
+  ReMutexHolder holder(_lock);
   _needs_recompute_canvas = false;
   _needs_recompute_canvas = false;
 
 
   const LVecBase4f &clip = get_clip_frame();
   const LVecBase4f &clip = get_clip_frame();
@@ -423,6 +435,7 @@ float PGScrollFrame::
 interpolate_canvas(float clip_min, float clip_max, 
 interpolate_canvas(float clip_min, float clip_max, 
                    float canvas_min, float canvas_max,
                    float canvas_min, float canvas_max,
                    PGSliderBar *slider_bar) {
                    PGSliderBar *slider_bar) {
+  ReMutexHolder holder(_lock);
   float t = 0.0f;
   float t = 0.0f;
   if (slider_bar != (PGSliderBar *)NULL) {
   if (slider_bar != (PGSliderBar *)NULL) {
     t = slider_bar->get_ratio();
     t = slider_bar->get_ratio();

+ 25 - 0
panda/src/pgui/pgSliderBar.I

@@ -60,6 +60,7 @@ get_notify() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGSliderBar:: 
 INLINE void PGSliderBar:: 
 set_axis(const LVector3f &axis) {
 set_axis(const LVector3f &axis) {
+  ReMutexHolder holder(_lock);
   _axis = axis;
   _axis = axis;
   _needs_remanage = true;
   _needs_remanage = true;
   _needs_recompute = true;
   _needs_recompute = true;
@@ -73,6 +74,7 @@ set_axis(const LVector3f &axis) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const LVector3f &PGSliderBar:: 
 INLINE const LVector3f &PGSliderBar:: 
 get_axis() const {
 get_axis() const {
+  ReMutexHolder holder(_lock);
   return _axis;
   return _axis;
 }
 }
 
 
@@ -83,6 +85,7 @@ get_axis() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGSliderBar:: 
 INLINE void PGSliderBar:: 
 set_range(float min_value, float max_value) {
 set_range(float min_value, float max_value) {
+  ReMutexHolder holder(_lock);
   nassertv(min_value != max_value);
   nassertv(min_value != max_value);
   _min_value = min_value;
   _min_value = min_value;
   _max_value = max_value;
   _max_value = max_value;
@@ -101,6 +104,7 @@ set_range(float min_value, float max_value) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE float PGSliderBar:: 
 INLINE float PGSliderBar:: 
 get_min_value() const {
 get_min_value() const {
+  ReMutexHolder holder(_lock);
   return _min_value;
   return _min_value;
 }
 }
 
 
@@ -112,6 +116,7 @@ get_min_value() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE float PGSliderBar:: 
 INLINE float PGSliderBar:: 
 get_max_value() const {
 get_max_value() const {
+  ReMutexHolder holder(_lock);
   return _max_value;
   return _max_value;
 }
 }
 
 
@@ -123,6 +128,7 @@ get_max_value() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGSliderBar:: 
 INLINE void PGSliderBar:: 
 set_scroll_size(float value) {
 set_scroll_size(float value) {
+  ReMutexHolder holder(_lock);
   _scroll_value = value;
   _scroll_value = value;
   _needs_recompute = true;
   _needs_recompute = true;
 }
 }
@@ -134,6 +140,7 @@ set_scroll_size(float value) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE float PGSliderBar:: 
 INLINE float PGSliderBar:: 
 get_scroll_size() const {
 get_scroll_size() const {
+  ReMutexHolder holder(_lock);
   return _scroll_value;
   return _scroll_value;
 }
 }
 
 
@@ -148,6 +155,7 @@ get_scroll_size() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGSliderBar:: 
 INLINE void PGSliderBar:: 
 set_page_size(float value) {
 set_page_size(float value) {
+  ReMutexHolder holder(_lock);
   _page_value = value;
   _page_value = value;
   _needs_recompute = true;
   _needs_recompute = true;
 }
 }
@@ -159,6 +167,7 @@ set_page_size(float value) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE float PGSliderBar:: 
 INLINE float PGSliderBar:: 
 get_page_size() const {
 get_page_size() const {
+  ReMutexHolder holder(_lock);
   return _page_value;
   return _page_value;
 }
 }
 
 
@@ -171,6 +180,7 @@ get_page_size() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGSliderBar:: 
 INLINE void PGSliderBar:: 
 set_value(float value) {
 set_value(float value) {
+  ReMutexHolder holder(_lock);
   set_ratio((value - _min_value) / (_max_value - _min_value));
   set_ratio((value - _min_value) / (_max_value - _min_value));
 }
 }
 
 
@@ -181,6 +191,7 @@ set_value(float value) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE float PGSliderBar:: 
 INLINE float PGSliderBar:: 
 get_value() const {
 get_value() const {
+  ReMutexHolder holder(_lock);
   return get_ratio() * (_max_value - _min_value) + _min_value;
   return get_ratio() * (_max_value - _min_value) + _min_value;
 }
 }
 
 
@@ -192,6 +203,7 @@ get_value() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGSliderBar:: 
 INLINE void PGSliderBar:: 
 set_ratio(float ratio) {
 set_ratio(float ratio) {
+  ReMutexHolder holder(_lock);
   if (!is_button_down()) {
   if (!is_button_down()) {
     internal_set_ratio(ratio);
     internal_set_ratio(ratio);
   }
   }
@@ -205,6 +217,7 @@ set_ratio(float ratio) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE float PGSliderBar:: 
 INLINE float PGSliderBar:: 
 get_ratio() const {
 get_ratio() const {
+  ReMutexHolder holder(_lock);
   return _ratio;
   return _ratio;
 }
 }
 
 
@@ -218,6 +231,7 @@ get_ratio() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PGSliderBar:: 
 INLINE bool PGSliderBar:: 
 is_button_down() const {
 is_button_down() const {
+  ReMutexHolder holder(_lock);
   return _dragging || _mouse_button_page || 
   return _dragging || _mouse_button_page || 
     (_scroll_button_held != (PGItem *)NULL);
     (_scroll_button_held != (PGItem *)NULL);
 }
 }
@@ -232,6 +246,7 @@ is_button_down() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGSliderBar:: 
 INLINE void PGSliderBar:: 
 set_resize_thumb(bool resize_thumb) {
 set_resize_thumb(bool resize_thumb) {
+  ReMutexHolder holder(_lock);
   _resize_thumb = resize_thumb;
   _resize_thumb = resize_thumb;
   _needs_recompute = true;
   _needs_recompute = true;
 }
 }
@@ -244,6 +259,7 @@ set_resize_thumb(bool resize_thumb) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PGSliderBar:: 
 INLINE bool PGSliderBar:: 
 get_resize_thumb() const {
 get_resize_thumb() const {
+  ReMutexHolder holder(_lock);
   return _resize_thumb;
   return _resize_thumb;
 }
 }
 
 
@@ -258,6 +274,7 @@ get_resize_thumb() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGSliderBar:: 
 INLINE void PGSliderBar:: 
 set_manage_pieces(bool manage_pieces) {
 set_manage_pieces(bool manage_pieces) {
+  ReMutexHolder holder(_lock);
   _manage_pieces = manage_pieces;
   _manage_pieces = manage_pieces;
   _needs_remanage = true;
   _needs_remanage = true;
   _needs_recompute = true;
   _needs_recompute = true;
@@ -271,6 +288,7 @@ set_manage_pieces(bool manage_pieces) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PGSliderBar:: 
 INLINE bool PGSliderBar:: 
 get_manage_pieces() const {
 get_manage_pieces() const {
+  ReMutexHolder holder(_lock);
   return _manage_pieces;
   return _manage_pieces;
 }
 }
 
 
@@ -288,6 +306,7 @@ get_manage_pieces() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGSliderBar::
 INLINE void PGSliderBar::
 set_thumb_button(PGButton *thumb_button) {
 set_thumb_button(PGButton *thumb_button) {
+  ReMutexHolder holder(_lock);
   if (_thumb_button != (PGButton *)NULL) {
   if (_thumb_button != (PGButton *)NULL) {
     _thumb_button->set_notify(NULL);
     _thumb_button->set_notify(NULL);
   }
   }
@@ -319,6 +338,7 @@ clear_thumb_button() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PGButton *PGSliderBar::
 INLINE PGButton *PGSliderBar::
 get_thumb_button() const {
 get_thumb_button() const {
+  ReMutexHolder holder(_lock);
   return _thumb_button;
   return _thumb_button;
 }
 }
 
 
@@ -336,6 +356,7 @@ get_thumb_button() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGSliderBar::
 INLINE void PGSliderBar::
 set_left_button(PGButton *left_button) {
 set_left_button(PGButton *left_button) {
+  ReMutexHolder holder(_lock);
   if (_left_button != (PGButton *)NULL) {
   if (_left_button != (PGButton *)NULL) {
     _left_button->set_notify(NULL);
     _left_button->set_notify(NULL);
   }
   }
@@ -368,6 +389,7 @@ clear_left_button() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PGButton *PGSliderBar::
 INLINE PGButton *PGSliderBar::
 get_left_button() const {
 get_left_button() const {
+  ReMutexHolder holder(_lock);
   return _left_button;
   return _left_button;
 }
 }
 
 
@@ -385,6 +407,7 @@ get_left_button() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGSliderBar::
 INLINE void PGSliderBar::
 set_right_button(PGButton *right_button) {
 set_right_button(PGButton *right_button) {
+  ReMutexHolder holder(_lock);
   if (_right_button != (PGButton *)NULL) {
   if (_right_button != (PGButton *)NULL) {
     _right_button->set_notify(NULL);
     _right_button->set_notify(NULL);
   }
   }
@@ -417,6 +440,7 @@ clear_right_button() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PGButton *PGSliderBar::
 INLINE PGButton *PGSliderBar::
 get_right_button() const {
 get_right_button() const {
+  ReMutexHolder holder(_lock);
   return _right_button;
   return _right_button;
 }
 }
 
 
@@ -441,6 +465,7 @@ get_adjust_prefix() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE string PGSliderBar::
 INLINE string PGSliderBar::
 get_adjust_event() const {
 get_adjust_event() const {
+  ReMutexHolder holder(_lock);
   return get_adjust_prefix() + get_id();
   return get_adjust_prefix() + get_id();
 }
 }
 
 

+ 105 - 73
panda/src/pgui/pgSliderBar.cxx

@@ -98,6 +98,7 @@ PGSliderBar(const PGSliderBar &copy) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PandaNode *PGSliderBar::
 PandaNode *PGSliderBar::
 make_copy() const {
 make_copy() const {
+  ReMutexHolder holder(_lock);
   return new PGSliderBar(*this);
   return new PGSliderBar(*this);
 }
 }
 
 
@@ -110,6 +111,7 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGSliderBar::
 void PGSliderBar::
 press(const MouseWatcherParameter &param, bool background) {
 press(const MouseWatcherParameter &param, bool background) {
+  ReMutexHolder holder(_lock);
   if (param.has_mouse()) {
   if (param.has_mouse()) {
     _mouse_pos = param.get_mouse();
     _mouse_pos = param.get_mouse();
   }
   }
@@ -138,6 +140,7 @@ press(const MouseWatcherParameter &param, bool background) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGSliderBar::
 void PGSliderBar::
 release(const MouseWatcherParameter &param, bool background) {
 release(const MouseWatcherParameter &param, bool background) {
+  ReMutexHolder holder(_lock);
   if (MouseButton::is_mouse_button(param.get_button())) {
   if (MouseButton::is_mouse_button(param.get_button())) {
     _mouse_button_page = false;
     _mouse_button_page = false;
   }
   }
@@ -155,6 +158,7 @@ release(const MouseWatcherParameter &param, bool background) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGSliderBar::
 void PGSliderBar::
 move(const MouseWatcherParameter &param) {
 move(const MouseWatcherParameter &param) {
+  ReMutexHolder holder(_lock);
   _mouse_pos = param.get_mouse();
   _mouse_pos = param.get_mouse();
   if (_dragging) {
   if (_dragging) {
     // We only get here if we the user originally clicked on the
     // We only get here if we the user originally clicked on the
@@ -193,6 +197,7 @@ move(const MouseWatcherParameter &param) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PGSliderBar::
 bool PGSliderBar::
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
+  ReMutexHolder holder(_lock);
   if (_manage_pieces && _needs_remanage) {
   if (_manage_pieces && _needs_remanage) {
     remanage();
     remanage();
   }
   }
@@ -226,6 +231,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGSliderBar::
 void PGSliderBar::
 xform(const LMatrix4f &mat) {
 xform(const LMatrix4f &mat) {
+  ReMutexHolder holder(_lock);
   PGItem::xform(mat);
   PGItem::xform(mat);
   _axis = _axis * mat;
   _axis = _axis * mat;
 
 
@@ -248,6 +254,7 @@ xform(const LMatrix4f &mat) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGSliderBar::
 void PGSliderBar::
 adjust() {
 adjust() {
+  ReMutexHolder holder(_lock);
   string event = get_adjust_event();
   string event = get_adjust_event();
   play_sound(event);
   play_sound(event);
   throw_event(event);
   throw_event(event);
@@ -273,6 +280,7 @@ adjust() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGSliderBar::
 void PGSliderBar::
 setup_scroll_bar(bool vertical, float length, float width, float bevel) {
 setup_scroll_bar(bool vertical, float length, float width, float bevel) {
+  ReMutexHolder holder(_lock);
   set_state(0);
   set_state(0);
   clear_state_def(0);
   clear_state_def(0);
 
 
@@ -346,6 +354,7 @@ setup_scroll_bar(bool vertical, float length, float width, float bevel) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGSliderBar::
 void PGSliderBar::
 setup_slider(bool vertical, float length, float width, float bevel) {
 setup_slider(bool vertical, float length, float width, float bevel) {
+  ReMutexHolder holder(_lock);
   set_state(0);
   set_state(0);
   clear_state_def(0);
   clear_state_def(0);
 
 
@@ -401,6 +410,7 @@ setup_slider(bool vertical, float length, float width, float bevel) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGSliderBar::
 void PGSliderBar::
 set_active(bool active) {
 set_active(bool active) {
+  ReMutexHolder holder(_lock);
   PGItem::set_active(active);
   PGItem::set_active(active);
 
 
   // This also implicitly sets the managed pieces.
   // This also implicitly sets the managed pieces.
@@ -424,6 +434,7 @@ set_active(bool active) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGSliderBar::
 void PGSliderBar::
 remanage() {
 remanage() {
+  ReMutexHolder holder(_lock);
   _needs_remanage = false;
   _needs_remanage = false;
 
 
   const LVecBase4f &frame = get_frame();
   const LVecBase4f &frame = get_frame();
@@ -475,6 +486,7 @@ remanage() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGSliderBar::
 void PGSliderBar::
 recompute() {
 recompute() {
+  ReMutexHolder holder(_lock);
   _needs_recompute = false;
   _needs_recompute = false;
 
 
   if (_min_value != _max_value) {
   if (_min_value != _max_value) {
@@ -486,88 +498,101 @@ recompute() {
     _page_ratio = 0.0f;
     _page_ratio = 0.0f;
   }
   }
 
 
-  LVecBase4f frame = get_frame();
-  reduce_region(frame, _left_button);
-  reduce_region(frame, _right_button);
-
-  if (fabs(_axis[0]) > fabs(_axis[1] + _axis[2])) {
-    // The slider is X-dominant.
-    
-    _min_x = frame[0];
-    _max_x = frame[1];
-  
-    float trough_width = _max_x - _min_x;
-    
-    if (_thumb_button != (PGButton *)NULL) {
-      const LVecBase4f &thumb_frame = _thumb_button->get_frame();
-
-      if (_resize_thumb) {
-        // If we're allowed to adjust the thumb's size, we don't need to
-        // find out how wide it is.
-        _thumb_width = trough_width * min(1.0f, _page_ratio);
-        _thumb_button->set_frame(-_thumb_width / 2.0f, _thumb_width / 2.0f,
-                                 thumb_frame[2], thumb_frame[3]);
-      } else {
-        // If we're not adjusting the thumb's size, we do need to know
-        // its current width.
-        _thumb_width = thumb_frame[1] - thumb_frame[0];
-      }
-    }
-
-    _range_x = trough_width - _thumb_width;
-
-    const LVecBase4f &thumb_frame = _thumb_button->get_frame();
-    if (_axis[0] >= 0.0f) {
-      // The slider runs forwards, left to right.
-      _thumb_start = (_min_x - thumb_frame[0]) * _axis;
-    } else {
-      // The slider runs backwards: right to left.
-      _thumb_start = (thumb_frame[1] - _max_x) * _axis;
-    }
-    _thumb_start += LVector3f::rfu(0.0f, 0.0f, (frame[2] + frame[3]) / 2.0f);
+  if (!has_frame()) {
+    _min_x = 0.0f;
+    _max_x = 0.0f;
+    _thumb_width = 0.0f;
+    _range_x = 0.0f;
+    _thumb_start.set(0.0f, 0.0f, 0.0f);
 
 
   } else {
   } else {
-    // The slider is Y-dominant.  We call it X in the variable names,
-    // but it's really Y (or even Z).
-    
-    _min_x = frame[2];
-    _max_x = frame[3];
-  
-    float trough_width = _max_x - _min_x;
+    LVecBase4f frame = get_frame();
+    reduce_region(frame, _left_button);
+    reduce_region(frame, _right_button);
     
     
-    if (_thumb_button == (PGButton *)NULL) {
-      _thumb_width = 0.0f;
-      _range_x = 0.0f;
-      _thumb_start.set(0.0f, 0.0f, 0.0f);
-
-    } else {
-      const LVecBase4f &thumb_frame = _thumb_button->get_frame();
-
-      if (_resize_thumb) {
-        // If we're allowed to adjust the thumb's size, we don't need to
-        // find out how wide it is.
-        _thumb_width = trough_width * min(1.0f, _page_ratio);
-        _thumb_button->set_frame(thumb_frame[0], thumb_frame[1],
-                                 -_thumb_width / 2.0f, _thumb_width / 2.0f);
+    if (fabs(_axis[0]) > fabs(_axis[1] + _axis[2])) {
+      // The slider is X-dominant.
+      
+      _min_x = frame[0];
+      _max_x = frame[1];
+      
+      float trough_width = _max_x - _min_x;
+      
+      if (_thumb_button == (PGButton *)NULL) {
+        _thumb_width = 0.0f;
+        _range_x = 0.0f;
+        _thumb_start.set(0.0f, 0.0f, 0.0f);
+        
       } else {
       } else {
-        // If we're not adjusting the thumb's size, we do need to know
-        // its current width.
-        _thumb_width = thumb_frame[3] - thumb_frame[2];
+        const LVecBase4f &thumb_frame = _thumb_button->get_frame();
+        
+        if (_resize_thumb) {
+          // If we're allowed to adjust the thumb's size, we don't need to
+          // find out how wide it is.
+          _thumb_width = trough_width * min(1.0f, _page_ratio);
+          _thumb_button->set_frame(-_thumb_width / 2.0f, _thumb_width / 2.0f,
+                                   thumb_frame[2], thumb_frame[3]);
+        } else {
+          // If we're not adjusting the thumb's size, we do need to know
+          // its current width.
+          _thumb_width = thumb_frame[1] - thumb_frame[0];
+        }
+        
+        _range_x = trough_width - _thumb_width;
+        
+        if (_axis[0] >= 0.0f) {
+          // The slider runs forwards, left to right.
+          _thumb_start = (_min_x - thumb_frame[0]) * _axis;
+        } else {
+          // The slider runs backwards: right to left.
+          _thumb_start = (thumb_frame[1] - _max_x) * _axis;
+        }
+        _thumb_start += LVector3f::rfu(0.0f, 0.0f, (frame[2] + frame[3]) / 2.0f);
       }
       }
-
-      _range_x = trough_width - _thumb_width;
-
-      if (_axis[1] >= 0.0f && _axis[2] >= 0.0f) {
-        // The slider runs forwards, bottom to top.
-        _thumb_start = (_min_x - thumb_frame[2]) * _axis;
+      
+    } else {
+      // The slider is Y-dominant.  We call it X in the variable names,
+      // but it's really Y (or even Z).
+      
+      _min_x = frame[2];
+      _max_x = frame[3];
+      
+      float trough_width = _max_x - _min_x;
+      
+      if (_thumb_button == (PGButton *)NULL) {
+        _thumb_width = 0.0f;
+        _range_x = 0.0f;
+        _thumb_start.set(0.0f, 0.0f, 0.0f);
+        
       } else {
       } else {
-        // The slider runs backwards: top to bottom.
-        _thumb_start = (thumb_frame[3] - _max_x) * _axis;
+        const LVecBase4f &thumb_frame = _thumb_button->get_frame();
+        
+        if (_resize_thumb) {
+          // If we're allowed to adjust the thumb's size, we don't need to
+          // find out how wide it is.
+          _thumb_width = trough_width * min(1.0f, _page_ratio);
+          _thumb_button->set_frame(thumb_frame[0], thumb_frame[1],
+                                   -_thumb_width / 2.0f, _thumb_width / 2.0f);
+        } else {
+          // If we're not adjusting the thumb's size, we do need to know
+          // its current width.
+          _thumb_width = thumb_frame[3] - thumb_frame[2];
+        }
+        
+        _range_x = trough_width - _thumb_width;
+        
+        if (_axis[1] >= 0.0f && _axis[2] >= 0.0f) {
+          // The slider runs forwards, bottom to top.
+          _thumb_start = (_min_x - thumb_frame[2]) * _axis;
+        } else {
+          // The slider runs backwards: top to bottom.
+          _thumb_start = (thumb_frame[3] - _max_x) * _axis;
+        }
+        _thumb_start += LVector3f::rfu((frame[0] + frame[1]) / 2.0f, 0.0f, 0.0f);
       }
       }
-      _thumb_start += LVector3f::rfu((frame[0] + frame[1]) / 2.0f, 0.0f, 0.0f);
     }
     }
   }
   }
-    
+
   reposition();
   reposition();
 }
 }
 
 
@@ -578,6 +603,7 @@ recompute() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGSliderBar::
 void PGSliderBar::
 frame_changed() {
 frame_changed() {
+  ReMutexHolder holder(_lock);
   PGItem::frame_changed();
   PGItem::frame_changed();
   _needs_remanage = true;
   _needs_remanage = true;
   _needs_recompute = true;
   _needs_recompute = true;
@@ -591,6 +617,7 @@ frame_changed() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGSliderBar::
 void PGSliderBar::
 item_transform_changed(PGItem *) {
 item_transform_changed(PGItem *) {
+  ReMutexHolder holder(_lock);
   _needs_recompute = true;
   _needs_recompute = true;
 }
 }
 
 
@@ -602,6 +629,7 @@ item_transform_changed(PGItem *) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGSliderBar::
 void PGSliderBar::
 item_frame_changed(PGItem *) {
 item_frame_changed(PGItem *) {
+  ReMutexHolder holder(_lock);
   _needs_recompute = true;
   _needs_recompute = true;
 }
 }
 
 
@@ -613,6 +641,7 @@ item_frame_changed(PGItem *) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGSliderBar::
 void PGSliderBar::
 item_draw_mask_changed(PGItem *) {
 item_draw_mask_changed(PGItem *) {
+  ReMutexHolder holder(_lock);
   _needs_recompute = true;
   _needs_recompute = true;
 }
 }
 
 
@@ -624,6 +653,7 @@ item_draw_mask_changed(PGItem *) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGSliderBar::
 void PGSliderBar::
 item_press(PGItem *item, const MouseWatcherParameter &param) {
 item_press(PGItem *item, const MouseWatcherParameter &param) {
+  ReMutexHolder holder(_lock);
   if (param.has_mouse()) {
   if (param.has_mouse()) {
     _mouse_pos = param.get_mouse();
     _mouse_pos = param.get_mouse();
   }
   }
@@ -648,6 +678,7 @@ item_press(PGItem *item, const MouseWatcherParameter &param) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGSliderBar::
 void PGSliderBar::
 item_release(PGItem *item, const MouseWatcherParameter &) {
 item_release(PGItem *item, const MouseWatcherParameter &) {
+  ReMutexHolder holder(_lock);
   if (item == _scroll_button_held) {
   if (item == _scroll_button_held) {
     _scroll_button_held = NULL;
     _scroll_button_held = NULL;
 
 
@@ -667,6 +698,7 @@ item_release(PGItem *item, const MouseWatcherParameter &) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGSliderBar::
 void PGSliderBar::
 item_move(PGItem *item, const MouseWatcherParameter &param) {
 item_move(PGItem *item, const MouseWatcherParameter &param) {
+  ReMutexHolder holder(_lock);
   _mouse_pos = param.get_mouse();
   _mouse_pos = param.get_mouse();
   if (item == _thumb_button) {
   if (item == _thumb_button) {
     if (_dragging) {
     if (_dragging) {

+ 6 - 0
panda/src/pgui/pgVirtualFrame.I

@@ -37,6 +37,7 @@ set_clip_frame(float left, float right, float bottom, float top) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const LVecBase4f &PGVirtualFrame::
 INLINE const LVecBase4f &PGVirtualFrame::
 get_clip_frame() const {
 get_clip_frame() const {
+  ReMutexHolder holder(_lock);
   return _has_clip_frame ? _clip_frame : get_frame();
   return _has_clip_frame ? _clip_frame : get_frame();
 }
 }
 
 
@@ -49,6 +50,7 @@ get_clip_frame() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool PGVirtualFrame::
 INLINE bool PGVirtualFrame::
 has_clip_frame() const {
 has_clip_frame() const {
+  ReMutexHolder holder(_lock);
   return _has_clip_frame;
   return _has_clip_frame;
 }
 }
 
 
@@ -61,6 +63,7 @@ has_clip_frame() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGVirtualFrame::
 INLINE void PGVirtualFrame::
 set_canvas_transform(const TransformState *transform) {
 set_canvas_transform(const TransformState *transform) {
+  ReMutexHolder holder(_lock);
   _canvas_node->set_transform(transform);
   _canvas_node->set_transform(transform);
 }
 }
 
 
@@ -73,6 +76,7 @@ set_canvas_transform(const TransformState *transform) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE const TransformState *PGVirtualFrame::
 INLINE const TransformState *PGVirtualFrame::
 get_canvas_transform() const {
 get_canvas_transform() const {
+  ReMutexHolder holder(_lock);
   return _canvas_node->get_transform();
   return _canvas_node->get_transform();
 }
 }
 
 
@@ -84,6 +88,7 @@ get_canvas_transform() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PandaNode *PGVirtualFrame::
 INLINE PandaNode *PGVirtualFrame::
 get_canvas_node() const {
 get_canvas_node() const {
+  ReMutexHolder holder(_lock);
   return _canvas_node;
   return _canvas_node;
 }
 }
 
 
@@ -94,5 +99,6 @@ get_canvas_node() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PandaNode *PGVirtualFrame::
 INLINE PandaNode *PGVirtualFrame::
 get_canvas_parent() const {
 get_canvas_parent() const {
+  ReMutexHolder holder(_lock);
   return _canvas_parent;
   return _canvas_parent;
 }
 }

+ 5 - 0
panda/src/pgui/pgVirtualFrame.cxx

@@ -72,6 +72,7 @@ PGVirtualFrame(const PGVirtualFrame &copy) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PandaNode *PGVirtualFrame::
 PandaNode *PGVirtualFrame::
 make_copy() const {
 make_copy() const {
+  ReMutexHolder holder(_lock);
   return new PGVirtualFrame(*this);
   return new PGVirtualFrame(*this);
 }
 }
 
 
@@ -93,6 +94,7 @@ make_copy() const {
 void PGVirtualFrame::
 void PGVirtualFrame::
 r_copy_children(const PandaNode *from, PandaNode::InstanceMap &inst_map,
 r_copy_children(const PandaNode *from, PandaNode::InstanceMap &inst_map,
                 Thread *current_thread) {
                 Thread *current_thread) {
+  ReMutexHolder holder(_lock);
   PandaNode::r_copy_children(from, inst_map, current_thread);
   PandaNode::r_copy_children(from, inst_map, current_thread);
 
 
   // Reassign the canvas_node to point to the new copy, if it's there.
   // Reassign the canvas_node to point to the new copy, if it's there.
@@ -129,6 +131,7 @@ r_copy_children(const PandaNode *from, PandaNode::InstanceMap &inst_map,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGVirtualFrame::
 void PGVirtualFrame::
 setup(float width, float height) {
 setup(float width, float height) {
+  ReMutexHolder holder(_lock);
   set_state(0);
   set_state(0);
   clear_state_def(0);
   clear_state_def(0);
 
 
@@ -159,6 +162,7 @@ setup(float width, float height) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGVirtualFrame::
 void PGVirtualFrame::
 set_clip_frame(const LVecBase4f &frame) {
 set_clip_frame(const LVecBase4f &frame) {
+  ReMutexHolder holder(_lock);
   if (!_has_clip_frame || _clip_frame != frame) {
   if (!_has_clip_frame || _clip_frame != frame) {
     _has_clip_frame = true;
     _has_clip_frame = true;
     _clip_frame = frame;
     _clip_frame = frame;
@@ -182,6 +186,7 @@ set_clip_frame(const LVecBase4f &frame) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGVirtualFrame::
 void PGVirtualFrame::
 clear_clip_frame() {
 clear_clip_frame() {
+  ReMutexHolder holder(_lock);
   if (_has_clip_frame) {
   if (_has_clip_frame) {
     _has_clip_frame = false;
     _has_clip_frame = false;
     
     

+ 7 - 0
panda/src/pgui/pgWaitBar.I

@@ -20,6 +20,7 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGWaitBar:: 
 INLINE void PGWaitBar:: 
 set_range(float range) {
 set_range(float range) {
+  ReMutexHolder holder(_lock);
   _range = range;
   _range = range;
   _bar_state = -1;
   _bar_state = -1;
 }
 }
@@ -31,6 +32,7 @@ set_range(float range) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE float PGWaitBar:: 
 INLINE float PGWaitBar:: 
 get_range() const {
 get_range() const {
+  ReMutexHolder holder(_lock);
   return _range;
   return _range;
 }
 }
 
 
@@ -42,6 +44,7 @@ get_range() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGWaitBar:: 
 INLINE void PGWaitBar:: 
 set_value(float value) {
 set_value(float value) {
+  ReMutexHolder holder(_lock);
   _value = value;
   _value = value;
   _bar_state = -1;
   _bar_state = -1;
 }
 }
@@ -53,6 +56,7 @@ set_value(float value) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE float PGWaitBar:: 
 INLINE float PGWaitBar:: 
 get_value() const {
 get_value() const {
+  ReMutexHolder holder(_lock);
   return _value;
   return _value;
 }
 }
 
 
@@ -63,6 +67,7 @@ get_value() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE float PGWaitBar:: 
 INLINE float PGWaitBar:: 
 get_percent() const {
 get_percent() const {
+  ReMutexHolder holder(_lock);
   return (_value / _range) * 100.0f;
   return (_value / _range) * 100.0f;
 }
 }
 
 
@@ -74,6 +79,7 @@ get_percent() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PGWaitBar:: 
 INLINE void PGWaitBar:: 
 set_bar_style(const PGFrameStyle &style) {
 set_bar_style(const PGFrameStyle &style) {
+  ReMutexHolder holder(_lock);
   _bar_style = style;
   _bar_style = style;
   _bar_state = -1;
   _bar_state = -1;
 }
 }
@@ -86,5 +92,6 @@ set_bar_style(const PGFrameStyle &style) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE PGFrameStyle PGWaitBar:: 
 INLINE PGFrameStyle PGWaitBar:: 
 get_bar_style() const {
 get_bar_style() const {
+  ReMutexHolder holder(_lock);
   return _bar_style;
   return _bar_style;
 }
 }

+ 4 - 0
panda/src/pgui/pgWaitBar.cxx

@@ -67,6 +67,7 @@ PGWaitBar(const PGWaitBar &copy) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PandaNode *PGWaitBar::
 PandaNode *PGWaitBar::
 make_copy() const {
 make_copy() const {
+  ReMutexHolder holder(_lock);
   return new PGWaitBar(*this);
   return new PGWaitBar(*this);
 }
 }
 
 
@@ -97,6 +98,7 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool PGWaitBar::
 bool PGWaitBar::
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
+  ReMutexHolder holder(_lock);
   update();
   update();
   return PGItem::cull_callback(trav, data);
   return PGItem::cull_callback(trav, data);
 }
 }
@@ -109,6 +111,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGWaitBar::
 void PGWaitBar::
 setup(float width, float height, float range) {
 setup(float width, float height, float range) {
+  ReMutexHolder holder(_lock);
   set_state(0);
   set_state(0);
   clear_state_def(0);
   clear_state_def(0);
 
 
@@ -136,6 +139,7 @@ setup(float width, float height, float range) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PGWaitBar:: 
 void PGWaitBar:: 
 update() {
 update() {
+  ReMutexHolder holder(_lock);
   int state = get_state();
   int state = get_state();
 
 
   // If the bar was last drawn in this state and is still current, we
   // If the bar was last drawn in this state and is still current, we

+ 1 - 1
panda/src/pipeline/conditionVarPosixImpl.cxx

@@ -41,7 +41,7 @@ wait(double timeout) {
   ts.tv_nsec += (int)((timeout - seconds) * 1000000.0);
   ts.tv_nsec += (int)((timeout - seconds) * 1000000.0);
 
 
   int result = pthread_cond_timedwait(&_cvar, &_mutex._lock, &ts);
   int result = pthread_cond_timedwait(&_cvar, &_mutex._lock, &ts);
-  nassertv(result == 0);
+  nassertv(result == 0 || errno == ETIMEDOUT);
 }
 }
 
 
 #endif  // HAVE_POSIX_THREADS
 #endif  // HAVE_POSIX_THREADS

+ 14 - 2
panda/src/pipeline/conditionVarSimpleImpl.cxx

@@ -65,7 +65,13 @@ wait(double timeout) {
 void ConditionVarSimpleImpl::
 void ConditionVarSimpleImpl::
 do_signal() {
 do_signal() {
   ThreadSimpleManager *manager = ThreadSimpleManager::get_global_ptr();
   ThreadSimpleManager *manager = ThreadSimpleManager::get_global_ptr();
-  manager->unblock_one(this);
+  if (manager->unblock_one(this)) {
+    // There had been a thread waiting on this condition variable.
+    // Switch contexts immediately, to make fairness more likely.
+    ThreadSimpleImpl *thread = manager->get_current_thread();
+    manager->enqueue_ready(thread);
+    manager->next_context();
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -76,7 +82,13 @@ do_signal() {
 void ConditionVarSimpleImpl::
 void ConditionVarSimpleImpl::
 do_signal_all() {
 do_signal_all() {
   ThreadSimpleManager *manager = ThreadSimpleManager::get_global_ptr();
   ThreadSimpleManager *manager = ThreadSimpleManager::get_global_ptr();
-  manager->unblock_all(this);
+  if (manager->unblock_all(this)) {
+    // There had been a thread waiting on this condition variable.
+    // Switch contexts immediately, to make fairness more likely.
+    ThreadSimpleImpl *thread = manager->get_current_thread();
+    manager->enqueue_ready(thread);
+    manager->next_context();
+  }
 }
 }
 
 
 #endif  // THREAD_SIMPLE_IMPL
 #endif  // THREAD_SIMPLE_IMPL

+ 69 - 5
panda/src/pipeline/mutexDebug.cxx

@@ -27,7 +27,7 @@ MutexTrueImpl *MutexDebug::_global_lock;
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 MutexDebug::
 MutexDebug::
 MutexDebug(const string &name, bool allow_recursion) :
 MutexDebug(const string &name, bool allow_recursion) :
-  _name(name),
+  Namable(name),
   _allow_recursion(allow_recursion),
   _allow_recursion(allow_recursion),
   _locking_thread(NULL),
   _locking_thread(NULL),
   _lock_count(0),
   _lock_count(0),
@@ -58,9 +58,9 @@ MutexDebug::
 void MutexDebug::
 void MutexDebug::
 output(ostream &out) const {
 output(ostream &out) const {
   if (_allow_recursion) {
   if (_allow_recursion) {
-    out << "ReMutex " << _name << " " << (void *)this;
+    out << "ReMutex " << get_name() << " " << (void *)this;
   } else {
   } else {
-    out << "Mutex " << _name << " " << (void *)this;
+    out << "Mutex " << get_name() << " " << (void *)this;
   }
   }
 }
 }
 
 
@@ -90,7 +90,7 @@ do_lock() {
     nassertv(_lock_count > 0);
     nassertv(_lock_count > 0);
     if (!_allow_recursion) {
     if (!_allow_recursion) {
       ostringstream ostr;
       ostringstream ostr;
-      ostr << *_locking_thread << " attempted to double-lock non-reentrant "
+      ostr << *this_thread << " attempted to double-lock non-reentrant "
            << *this;
            << *this;
       nassert_raise(ostr.str());
       nassert_raise(ostr.str());
     }
     }
@@ -99,6 +99,26 @@ do_lock() {
   } else {
   } else {
     // The mutex is locked by some other thread.
     // The mutex is locked by some other thread.
 
 
+#ifdef PHONY_MUTEX
+    // In this case, we don't really have mutexes anyway.
+    MissedThreads::iterator mi = _missed_threads.insert(MissedThreads::value_type(this_thread, 0)).first;
+    if ((*mi).second == 0) {
+      thread_cat.info()
+        << *this_thread << " not stopped by " << *this << " (held by "
+        << *_locking_thread << ")\n";
+    } else {
+      if (!_allow_recursion) {
+        ostringstream ostr;
+        ostr << *this_thread << " attempted to double-lock non-reentrant "
+             << *this;
+        nassert_raise(ostr.str());
+      }
+    }
+    ++((*mi).second);
+
+#else // PHONY_MUTEX
+    // This is the real case.  We have mutexes, so enforce it.
+
     // Check for deadlock.
     // Check for deadlock.
     MutexDebug *next_mutex = this;
     MutexDebug *next_mutex = this;
     while (next_mutex != NULL) {
     while (next_mutex != NULL) {
@@ -133,6 +153,7 @@ do_lock() {
         << *this_thread << " blocking on " << *this << " (held by "
         << *this_thread << " blocking on " << *this << " (held by "
         << *_locking_thread << ")\n";
         << *_locking_thread << ")\n";
     }
     }
+
     while (_locking_thread != (Thread *)NULL) {
     while (_locking_thread != (Thread *)NULL) {
       _cvar_impl.wait();
       _cvar_impl.wait();
     }
     }
@@ -147,6 +168,7 @@ do_lock() {
     _locking_thread = this_thread;
     _locking_thread = this_thread;
     ++_lock_count;
     ++_lock_count;
     nassertv(_lock_count == 1);
     nassertv(_lock_count == 1);
+#endif  // PHONY_MUTEX
   }
   }
 }
 }
 
 
@@ -165,10 +187,27 @@ do_release() {
   Thread *this_thread = Thread::get_current_thread();
   Thread *this_thread = Thread::get_current_thread();
 
 
   if (_locking_thread != this_thread) {
   if (_locking_thread != this_thread) {
+#ifdef PHONY_MUTEX
+    // No real mutexes.  This just means we blew past a mutex without
+    // locking it, above.
+
+    MissedThreads::iterator mi = _missed_threads.find(this_thread);
+    nassertv(mi != _missed_threads.end());
+    nassertv((*mi).second > 0);
+    --((*mi).second);
+
+    if ((*mi).second == 0) {
+      _missed_threads.erase(mi);
+    }
+
+#else  // PHONY_MUTEX
+    // In the real-mutex case, this is an error condition.
     ostringstream ostr;
     ostringstream ostr;
     ostr << *this_thread << " attempted to release "
     ostr << *this_thread << " attempted to release "
          << *this << " which it does not own";
          << *this << " which it does not own";
     nassert_raise(ostr.str());
     nassert_raise(ostr.str());
+#endif  // PHONY_MUTEX
+
     _global_lock->release();
     _global_lock->release();
     return;
     return;
   }
   }
@@ -179,7 +218,19 @@ do_release() {
   if (_lock_count == 0) {
   if (_lock_count == 0) {
     // That was the last lock held by this thread.  Release the lock.
     // That was the last lock held by this thread.  Release the lock.
     _locking_thread = (Thread *)NULL;
     _locking_thread = (Thread *)NULL;
+
+#ifdef PHONY_MUTEX
+    if (!_missed_threads.empty()) {
+      // Promote some other thread to be the honorary lock holder.
+      MissedThreads::iterator mi = _missed_threads.begin();
+      _locking_thread = (*mi).first;
+      _lock_count = (*mi).second;
+      _missed_threads.erase(mi);
+      nassertv(_lock_count > 0);
+    }
+#else
     _cvar_impl.signal();
     _cvar_impl.signal();
+#endif
   }
   }
 }
 }
 
 
@@ -191,7 +242,20 @@ do_release() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool MutexDebug::
 bool MutexDebug::
 do_debug_is_locked() const {
 do_debug_is_locked() const {
-  return (_locking_thread == Thread::get_current_thread());
+  Thread *this_thread = Thread::get_current_thread();
+  if (_locking_thread == this_thread) {
+    return true;
+  }
+
+#ifdef PHONY_MUTEX
+  MissedThreads::const_iterator mi = _missed_threads.find(this_thread);
+  if (mi != _missed_threads.end()) {
+    nassertr((*mi).second > 0, false);
+    return true;
+  }
+#endif
+
+  return false;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 14 - 2
panda/src/pipeline/mutexDebug.h

@@ -19,16 +19,25 @@
 #include "mutexTrueImpl.h"
 #include "mutexTrueImpl.h"
 #include "conditionVarImpl.h"
 #include "conditionVarImpl.h"
 #include "thread.h"
 #include "thread.h"
+#include "namable.h"
 
 
 #ifdef DEBUG_THREADS
 #ifdef DEBUG_THREADS
 
 
+#if defined(SIMPLE_THREADS) && defined(SIMPLE_THREADS_NO_MUTEX)
+// In this mode, we don't actually lock and unlock a mutex.  We just
+// wave at them as they go by.  This actually involves a bit more
+// work, here in the debug mode, than a real mutex, because we have to
+// track all the threads that failed to lock the mutex.
+#define PHONY_MUTEX
+#endif
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : MutexDebug
 //       Class : MutexDebug
 // Description : This class implements a standard mutex the hard way,
 // Description : This class implements a standard mutex the hard way,
 //               by doing everything by hand.  This does allow fancy
 //               by doing everything by hand.  This does allow fancy
 //               things like deadlock detection, however.
 //               things like deadlock detection, however.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA_PIPELINE MutexDebug {
+class EXPCL_PANDA_PIPELINE MutexDebug : public Namable {
 protected:
 protected:
   MutexDebug(const string &name, bool allow_recursion);
   MutexDebug(const string &name, bool allow_recursion);
   virtual ~MutexDebug();
   virtual ~MutexDebug();
@@ -57,10 +66,13 @@ private:
 private:
 private:
   INLINE static MutexTrueImpl *get_global_lock();
   INLINE static MutexTrueImpl *get_global_lock();
 
 
-  string _name;
   bool _allow_recursion;
   bool _allow_recursion;
   Thread *_locking_thread;
   Thread *_locking_thread;
   int _lock_count;
   int _lock_count;
+#ifdef PHONY_MUTEX
+  typedef pmap<Thread *, int> MissedThreads;
+  MissedThreads _missed_threads;
+#endif
 
 
   ConditionVarImpl _cvar_impl;
   ConditionVarImpl _cvar_impl;
 
 

+ 57 - 0
panda/src/pipeline/mutexDirect.I

@@ -102,3 +102,60 @@ INLINE bool MutexDirect::
 debug_is_locked() const {
 debug_is_locked() const {
   return true;
   return true;
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDirect::debug_is_locked
+//       Access: Published
+//  Description: Returns true if the current thread has locked the
+//               Mutex, false otherwise.  This method is only intended
+//               for use in debugging, hence the method name; in the
+//               MutexDirect case, it always returns true, since
+//               there's not a reliable way to determine this
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool MutexDirect::
+debug_is_locked() const {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDirect::set_name
+//       Access: Public
+//  Description: The mutex name is only defined when compiling in
+//               DEBUG_THREADS mode.
+////////////////////////////////////////////////////////////////////
+INLINE void MutexDirect::
+set_name(const string &) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDirect::clear_name
+//       Access: Public
+//  Description: The mutex name is only defined when compiling in
+//               DEBUG_THREADS mode.
+////////////////////////////////////////////////////////////////////
+INLINE void MutexDirect::
+clear_name() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDirect::has_name
+//       Access: Public
+//  Description: The mutex name is only defined when compiling in
+//               DEBUG_THREADS mode.
+////////////////////////////////////////////////////////////////////
+INLINE bool MutexDirect::
+has_name() const {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDirect::get_name
+//       Access: Public
+//  Description: The mutex name is only defined when compiling in
+//               DEBUG_THREADS mode.
+////////////////////////////////////////////////////////////////////
+INLINE string MutexDirect::
+get_name() const {
+  return string();
+}

+ 5 - 0
panda/src/pipeline/mutexDirect.h

@@ -42,6 +42,11 @@ PUBLISHED:
   INLINE void release() const;
   INLINE void release() const;
   INLINE bool debug_is_locked() const;
   INLINE bool debug_is_locked() const;
 
 
+  INLINE void set_name(const string &name);
+  INLINE void clear_name();
+  INLINE bool has_name() const;
+  INLINE string get_name() const;
+
   void output(ostream &out) const;
   void output(ostream &out) const;
 
 
 private:
 private:

+ 7 - 1
panda/src/pipeline/mutexSimpleImpl.cxx

@@ -46,7 +46,13 @@ do_lock() {
 void MutexSimpleImpl::
 void MutexSimpleImpl::
 do_release() {
 do_release() {
   ThreadSimpleManager *manager = ThreadSimpleManager::get_global_ptr();
   ThreadSimpleManager *manager = ThreadSimpleManager::get_global_ptr();
-  manager->unblock_one(this);
+  if (manager->unblock_one(this)) {
+    // There had been a thread waiting on this mutex.  Switch contexts
+    // immediately, to make fairness more likely.
+    ThreadSimpleImpl *thread = manager->get_current_thread();
+    manager->enqueue_ready(thread);
+    manager->next_context();
+  }
 }
 }
 
 
 #endif  // THREAD_SIMPLE_IMPL
 #endif  // THREAD_SIMPLE_IMPL

+ 42 - 0
panda/src/pipeline/reMutexDirect.I

@@ -165,6 +165,48 @@ debug_is_locked() const {
   return true;
   return true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexDirect::set_name
+//       Access: Public
+//  Description: The mutex name is only defined when compiling in
+//               DEBUG_THREADS mode.
+////////////////////////////////////////////////////////////////////
+INLINE void ReMutexDirect::
+set_name(const string &) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexDirect::clear_name
+//       Access: Public
+//  Description: The mutex name is only defined when compiling in
+//               DEBUG_THREADS mode.
+////////////////////////////////////////////////////////////////////
+INLINE void ReMutexDirect::
+clear_name() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexDirect::has_name
+//       Access: Public
+//  Description: The mutex name is only defined when compiling in
+//               DEBUG_THREADS mode.
+////////////////////////////////////////////////////////////////////
+INLINE bool ReMutexDirect::
+has_name() const {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexDirect::get_name
+//       Access: Public
+//  Description: The mutex name is only defined when compiling in
+//               DEBUG_THREADS mode.
+////////////////////////////////////////////////////////////////////
+INLINE string ReMutexDirect::
+get_name() const {
+  return string();
+}
+
 #ifndef HAVE_REMUTEXIMPL
 #ifndef HAVE_REMUTEXIMPL
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ReMutexDirect::do_lock
 //     Function: ReMutexDirect::do_lock

+ 5 - 0
panda/src/pipeline/reMutexDirect.h

@@ -43,6 +43,11 @@ PUBLISHED:
 
 
   INLINE bool debug_is_locked() const;
   INLINE bool debug_is_locked() const;
 
 
+  INLINE void set_name(const string &name);
+  INLINE void clear_name();
+  INLINE bool has_name() const;
+  INLINE string get_name() const;
+
   void output(ostream &out) const;
   void output(ostream &out) const;
 
 
 private:
 private:

+ 4 - 5
panda/src/pipeline/thread.cxx

@@ -245,11 +245,10 @@ call_python_func(PyObject *function, PyObject *args) {
 
 
   } else {
   } else {
 #ifdef SIMPLE_THREADS
 #ifdef SIMPLE_THREADS
-    // We can't use the PyGILState interface, which assumes we are using
-    // true OS-level threading (and we might be just using
-    // SIMPLE_THREADS).  PyGILState enforces policies like only one
-    // thread state per OS-level thread, which is not true in the case
-    // of SIMPLE_THREADS.
+    // We can't use the PyGILState interface, which assumes we are
+    // using true OS-level threading.  PyGILState enforces policies
+    // like only one thread state per OS-level thread, which is not
+    // true in the case of SIMPLE_THREADS.
     
     
     PyThreadState *orig_thread_state = PyThreadState_Get();
     PyThreadState *orig_thread_state = PyThreadState_Get();
     PyInterpreterState *istate = orig_thread_state->interp;
     PyInterpreterState *istate = orig_thread_state->interp;

+ 15 - 8
panda/src/pipeline/threadSimpleManager.cxx

@@ -106,16 +106,17 @@ enqueue_block(ThreadSimpleImpl *thread, BlockerSimple *blocker) {
 //     Function: ThreadSimpleManager::unblock_one
 //     Function: ThreadSimpleManager::unblock_one
 //       Access: Public
 //       Access: Public
 //  Description: Unblocks one thread waiting on the indicated blocker,
 //  Description: Unblocks one thread waiting on the indicated blocker,
-//               if any.
+//               if any.  Returns true if anything was unblocked,
+//               false otherwise.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void ThreadSimpleManager::
+bool ThreadSimpleManager::
 unblock_one(BlockerSimple *blocker) {
 unblock_one(BlockerSimple *blocker) {
   Blocked::iterator bi = _blocked.find(blocker);
   Blocked::iterator bi = _blocked.find(blocker);
   if (bi != _blocked.end()) {
   if (bi != _blocked.end()) {
-    nassertv(blocker->_flags & BlockerSimple::F_has_waiters);
+    nassertr(blocker->_flags & BlockerSimple::F_has_waiters, false);
 
 
     FifoThreads &threads = (*bi).second;
     FifoThreads &threads = (*bi).second;
-    nassertv(!threads.empty());
+    nassertr(!threads.empty(), false);
     ThreadSimpleImpl *thread = threads.front();
     ThreadSimpleImpl *thread = threads.front();
     threads.pop_front();
     threads.pop_front();
     _ready.push_back(thread);
     _ready.push_back(thread);
@@ -123,23 +124,27 @@ unblock_one(BlockerSimple *blocker) {
       blocker->_flags &= ~BlockerSimple::F_has_waiters;
       blocker->_flags &= ~BlockerSimple::F_has_waiters;
       _blocked.erase(bi);
       _blocked.erase(bi);
     }
     }
+    return true;
   }
   }
+
+  return false;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ThreadSimpleManager::unblock_all
 //     Function: ThreadSimpleManager::unblock_all
 //       Access: Public
 //       Access: Public
 //  Description: Unblocks all threads waiting on the indicated
 //  Description: Unblocks all threads waiting on the indicated
-//               blocker.
+//               blocker.  Returns true if anything was unblocked,
+//               false otherwise.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void ThreadSimpleManager::
+bool ThreadSimpleManager::
 unblock_all(BlockerSimple *blocker) {
 unblock_all(BlockerSimple *blocker) {
   Blocked::iterator bi = _blocked.find(blocker);
   Blocked::iterator bi = _blocked.find(blocker);
   if (bi != _blocked.end()) {
   if (bi != _blocked.end()) {
-    nassertv(blocker->_flags & BlockerSimple::F_has_waiters);
+    nassertr(blocker->_flags & BlockerSimple::F_has_waiters, false);
 
 
     FifoThreads &threads = (*bi).second;
     FifoThreads &threads = (*bi).second;
-    nassertv(!threads.empty());
+    nassertr(!threads.empty(), false);
     while (!threads.empty()) {
     while (!threads.empty()) {
       ThreadSimpleImpl *thread = threads.front();
       ThreadSimpleImpl *thread = threads.front();
       threads.pop_front();
       threads.pop_front();
@@ -147,7 +152,9 @@ unblock_all(BlockerSimple *blocker) {
     }
     }
     blocker->_flags &= ~BlockerSimple::F_has_waiters;
     blocker->_flags &= ~BlockerSimple::F_has_waiters;
     _blocked.erase(bi);
     _blocked.erase(bi);
+    return true;
   }
   }
+  return false;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 2 - 2
panda/src/pipeline/threadSimpleManager.h

@@ -60,8 +60,8 @@ public:
   void enqueue_ready(ThreadSimpleImpl *thread);
   void enqueue_ready(ThreadSimpleImpl *thread);
   void enqueue_sleep(ThreadSimpleImpl *thread, double seconds);
   void enqueue_sleep(ThreadSimpleImpl *thread, double seconds);
   void enqueue_block(ThreadSimpleImpl *thread, BlockerSimple *blocker);
   void enqueue_block(ThreadSimpleImpl *thread, BlockerSimple *blocker);
-  void unblock_one(BlockerSimple *blocker);
-  void unblock_all(BlockerSimple *blocker);
+  bool unblock_one(BlockerSimple *blocker);
+  bool unblock_all(BlockerSimple *blocker);
   void enqueue_finished(ThreadSimpleImpl *thread);
   void enqueue_finished(ThreadSimpleImpl *thread);
   void preempt(ThreadSimpleImpl *thread);
   void preempt(ThreadSimpleImpl *thread);
   void next_context();
   void next_context();

+ 8 - 8
panda/src/putil/copyOnWriteObject.I

@@ -26,17 +26,17 @@ TypeHandle CopyOnWriteObj1<Base, Param1>::_type_handle;
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE CopyOnWriteObject::
 INLINE CopyOnWriteObject::
 CopyOnWriteObject() 
 CopyOnWriteObject() 
-#ifdef HAVE_THREADS
+#ifdef COW_THREADED
   : _lock_cvar(_lock_mutex) 
   : _lock_cvar(_lock_mutex) 
 #endif
 #endif
 {
 {
 #ifdef DO_MEMORY_USAGE
 #ifdef DO_MEMORY_USAGE
   MemoryUsage::update_type(this, this);
   MemoryUsage::update_type(this, this);
 #endif
 #endif
-#ifdef HAVE_THREADS
+#ifdef COW_THREADED
   _lock_status = LS_unlocked;
   _lock_status = LS_unlocked;
   _locking_thread = NULL;
   _locking_thread = NULL;
-#endif  // HAVE_THREADS
+#endif  // COW_THREADED
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -47,17 +47,17 @@ CopyOnWriteObject()
 INLINE CopyOnWriteObject::
 INLINE CopyOnWriteObject::
 CopyOnWriteObject(const CopyOnWriteObject &copy) :
 CopyOnWriteObject(const CopyOnWriteObject &copy) :
   CachedTypedWritableReferenceCount(copy)
   CachedTypedWritableReferenceCount(copy)
-#ifdef HAVE_THREADS
+#ifdef COW_THREADED
   , _lock_cvar(_lock_mutex)
   , _lock_cvar(_lock_mutex)
 #endif
 #endif
 {
 {
 #ifdef DO_MEMORY_USAGE
 #ifdef DO_MEMORY_USAGE
   MemoryUsage::update_type(this, this);
   MemoryUsage::update_type(this, this);
 #endif
 #endif
-#ifdef HAVE_THREADS
+#ifdef COW_THREADED
   _lock_status = LS_unlocked;
   _lock_status = LS_unlocked;
   _locking_thread = NULL;
   _locking_thread = NULL;
-#endif  // HAVE_THREADS
+#endif  // COW_THREADED
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -70,7 +70,7 @@ operator = (const CopyOnWriteObject &copy) {
   CachedTypedWritableReferenceCount::operator = (copy);
   CachedTypedWritableReferenceCount::operator = (copy);
 }
 }
 
 
-#ifdef HAVE_THREADS
+#ifdef COW_THREADED
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CopyOnWriteObject::cache_ref
 //     Function: CopyOnWriteObject::cache_ref
 //       Access: Published
 //       Access: Published
@@ -81,7 +81,7 @@ cache_ref() const {
   MutexHolder holder(_lock_mutex);
   MutexHolder holder(_lock_mutex);
   CachedTypedWritableReferenceCount::cache_ref();
   CachedTypedWritableReferenceCount::cache_ref();
 }
 }
-#endif  // HAVE_THREADS
+#endif  // COW_THREADED
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CopyOnWriteObj::Constructor
 //     Function: CopyOnWriteObj::Constructor

+ 2 - 2
panda/src/putil/copyOnWriteObject.cxx

@@ -18,7 +18,7 @@
 
 
 TypeHandle CopyOnWriteObject::_type_handle;
 TypeHandle CopyOnWriteObject::_type_handle;
 
 
-#ifdef HAVE_THREADS
+#ifdef COW_THREADED
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CopyOnWriteObject::unref
 //     Function: CopyOnWriteObject::unref
 //       Access: Public
 //       Access: Public
@@ -40,4 +40,4 @@ unref() const {
   }
   }
   return is_zero;
   return is_zero;
 }
 }
-#endif  // HAVE_THREADS
+#endif  // COW_THREADED

+ 13 - 4
panda/src/putil/copyOnWriteObject.h

@@ -22,6 +22,15 @@
 #include "conditionVar.h"
 #include "conditionVar.h"
 #include "mutexHolder.h"
 #include "mutexHolder.h"
 
 
+// Should we implement full thread protection for CopyOnWritePointer?
+// If we can be assured that no other thread will interrupt while a
+// write pointer is held, we don't need thread protection.
+#if defined(HAVE_THREADS) && !(defined(SIMPLE_THREADS) && defined(SIMPLE_THREADS_NO_MUTEX))
+  #define COW_THREADED 1
+#else
+  #undef COW_THREADED
+#endif
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : CopyOnWriteObject
 //       Class : CopyOnWriteObject
 // Description : This base class provides basic reference counting,
 // Description : This base class provides basic reference counting,
@@ -35,16 +44,16 @@ public:
   INLINE void operator = (const CopyOnWriteObject &copy);
   INLINE void operator = (const CopyOnWriteObject &copy);
 
 
 PUBLISHED:
 PUBLISHED:
-#ifdef HAVE_THREADS
+#ifdef COW_THREADED
   bool unref() const;
   bool unref() const;
   INLINE void cache_ref() const;
   INLINE void cache_ref() const;
-#endif  // HAVE_THREADS
+#endif  // COW_THREADED
 
 
 protected:
 protected:
   virtual PT(CopyOnWriteObject) make_cow_copy()=0;
   virtual PT(CopyOnWriteObject) make_cow_copy()=0;
 
 
 private:
 private:
-#ifdef HAVE_THREADS
+#ifdef COW_THREADED
   enum LockStatus {
   enum LockStatus {
     LS_unlocked,
     LS_unlocked,
     LS_locked_read,
     LS_locked_read,
@@ -54,7 +63,7 @@ private:
   ConditionVar _lock_cvar;
   ConditionVar _lock_cvar;
   LockStatus _lock_status;
   LockStatus _lock_status;
   Thread *_locking_thread;
   Thread *_locking_thread;
-#endif  // HAVE_THREADS
+#endif  // COW_THREADED
 
 
 public:
 public:
   virtual TypeHandle get_type() const {
   virtual TypeHandle get_type() const {

+ 2 - 2
panda/src/putil/copyOnWritePointer.cxx

@@ -51,7 +51,7 @@ get_read_pointer() const {
   }
   }
 
 
   _object->_lock_status = CopyOnWriteObject::LS_locked_read;
   _object->_lock_status = CopyOnWriteObject::LS_locked_read;
-  _object->_locking_thread = Thread::get_current_thread();
+  _object->_locking_thread = current_thread;
   return _object;
   return _object;
 }
 }
 #endif  // COW_THREADED
 #endif  // COW_THREADED
@@ -129,7 +129,7 @@ get_write_pointer() {
     // have saved himself a reference.
     // have saved himself a reference.
   }
   }
   _object->_lock_status = CopyOnWriteObject::LS_locked_write;
   _object->_lock_status = CopyOnWriteObject::LS_locked_write;
-  _object->_locking_thread = Thread::get_current_thread();
+  _object->_locking_thread = current_thread;
 
 
   return _object;
   return _object;
 }
 }

+ 0 - 9
panda/src/putil/copyOnWritePointer.h

@@ -21,15 +21,6 @@
 #include "pointerTo.h"
 #include "pointerTo.h"
 #include "dcast.h"
 #include "dcast.h"
 
 
-// Should we implement full thread protection for CopyOnWritePointer?
-// If we can be assured that no other thread will interrupt while a
-// write pointer is held, we don't need thread protection.
-#if defined(HAVE_THREADS) && !(defined(SIMPLE_THREADS) && defined(SIMPLE_THREADS_NO_MUTEX))
-  #define COW_THREADED 1
-#else
-  #undef COW_THREADED
-#endif
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : CopyOnWritePointer
 //       Class : CopyOnWritePointer
 // Description : This safely stores the primary, owned pointer to a
 // Description : This safely stores the primary, owned pointer to a

+ 9 - 7
panda/src/text/textNode.cxx

@@ -620,7 +620,9 @@ is_renderable() const {
 //               thing.
 //               thing.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void TextNode::
 void TextNode::
-compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage, 
+compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
+                        int &internal_vertices,
+                        int pipeline_stage,
                         Thread *current_thread) const {
                         Thread *current_thread) const {
   // First, get ourselves a fresh, empty bounding volume.
   // First, get ourselves a fresh, empty bounding volume.
   PT(BoundingVolume) bound = new BoundingSphere;
   PT(BoundingVolume) bound = new BoundingSphere;
@@ -644,9 +646,8 @@ compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage,
 
 
   gbv->around(vertices, vertices + 8);
   gbv->around(vertices, vertices + 8);
 
 
-  bdata->_internal_bounds = bound;
-  bdata->_internal_vertices = 0;  // TODO: estimate this better.
-  bdata->_internal_bounds_stale = false;
+  internal_bounds = bound;
+  internal_vertices = 0;  // TODO: estimate this better.
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -659,16 +660,17 @@ compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void TextNode::
 void TextNode::
 r_prepare_scene(const RenderState *state,
 r_prepare_scene(const RenderState *state,
-                PreparedGraphicsObjects *prepared_objects) {
+                PreparedGraphicsObjects *prepared_objects,
+                Thread *current_thread) {
   check_rebuild();
   check_rebuild();
 
 
   PandaNode *child = _internal_geom;
   PandaNode *child = _internal_geom;
   if (child != (PandaNode *)NULL) {
   if (child != (PandaNode *)NULL) {
     CPT(RenderState) child_state = state->compose(child->get_state());
     CPT(RenderState) child_state = state->compose(child->get_state());
-    child->r_prepare_scene(child_state, prepared_objects);
+    child->r_prepare_scene(child_state, prepared_objects, current_thread);
   }
   }
   
   
-  PandaNode::r_prepare_scene(state, prepared_objects);
+  PandaNode::r_prepare_scene(state, prepared_objects, current_thread);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 5 - 2
panda/src/text/textNode.h

@@ -239,11 +239,14 @@ public:
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
   virtual bool is_renderable() const;
   virtual bool is_renderable() const;
 
 
-  virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
+  virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
+                                       int &internal_vertices,
+                                       int pipeline_stage,
                                        Thread *current_thread) const;
                                        Thread *current_thread) const;
 
 
   virtual void r_prepare_scene(const RenderState *state,
   virtual void r_prepare_scene(const RenderState *state,
-                               PreparedGraphicsObjects *prepared_objects);
+                               PreparedGraphicsObjects *prepared_objects,
+                               Thread *current_thread);
 
 
 private:
 private:
   INLINE void invalidate_no_measure();
   INLINE void invalidate_no_measure();