Browse Source

pipelining getting close; uprev bam version to 6.0

David Rose 20 years ago
parent
commit
1d2282a879
100 changed files with 2371 additions and 958 deletions
  1. 15 12
      dtool/Config.pp
  2. 2 2
      dtool/LocalSetup.pp
  3. 1 1
      dtool/Package.pp
  4. 4 5
      panda/src/collide/collisionInvSphere.cxx
  5. 1 1
      panda/src/collide/collisionInvSphere.h
  6. 2 2
      panda/src/collide/collisionLevelState.cxx
  7. 4 4
      panda/src/collide/collisionNode.I
  8. 7 7
      panda/src/collide/collisionNode.cxx
  9. 1 1
      panda/src/collide/collisionNode.h
  10. 2 1
      panda/src/collide/collisionPlane.I
  11. 4 7
      panda/src/collide/collisionPlane.cxx
  12. 1 1
      panda/src/collide/collisionPlane.h
  13. 1 0
      panda/src/collide/collisionPolygon.I
  14. 4 4
      panda/src/collide/collisionPolygon.cxx
  15. 1 1
      panda/src/collide/collisionPolygon.h
  16. 2 2
      panda/src/collide/collisionRay.I
  17. 5 8
      panda/src/collide/collisionRay.cxx
  18. 1 1
      panda/src/collide/collisionRay.h
  19. 3 2
      panda/src/collide/collisionSegment.I
  20. 5 5
      panda/src/collide/collisionSegment.cxx
  21. 1 1
      panda/src/collide/collisionSegment.h
  22. 13 0
      panda/src/collide/collisionSolid.I
  23. 28 3
      panda/src/collide/collisionSolid.cxx
  24. 9 6
      panda/src/collide/collisionSolid.h
  25. 2 2
      panda/src/collide/collisionSphere.I
  26. 5 11
      panda/src/collide/collisionSphere.cxx
  27. 1 1
      panda/src/collide/collisionSphere.h
  28. 4 4
      panda/src/collide/collisionTraverser.cxx
  29. 1 1
      panda/src/collide/collisionTube.I
  30. 5 5
      panda/src/collide/collisionTube.cxx
  31. 1 1
      panda/src/collide/collisionTube.h
  32. 1 1
      panda/src/collide/collisionVisualizer.cxx
  33. 2 2
      panda/src/dgraph/dataNode.cxx
  34. 1 1
      panda/src/dgraph/dataNode.h
  35. 14 6
      panda/src/display/config_display.cxx
  36. 80 5
      panda/src/display/displayRegion.I
  37. 164 91
      panda/src/display/displayRegion.cxx
  38. 88 29
      panda/src/display/displayRegion.h
  39. 94 44
      panda/src/display/graphicsEngine.cxx
  40. 5 3
      panda/src/display/graphicsEngine.h
  41. 1 1
      panda/src/display/graphicsOutput.cxx
  42. 0 1
      panda/src/display/graphicsStateGuardian.cxx
  43. 31 0
      panda/src/display/graphicsThreadingModel.I
  44. 11 0
      panda/src/display/graphicsThreadingModel.cxx
  45. 4 0
      panda/src/display/graphicsThreadingModel.h
  46. 5 11
      panda/src/egg2pg/eggLoader.cxx
  47. 34 10
      panda/src/express/Sources.pp
  48. 14 0
      panda/src/express/atomicAdjust.I
  49. 1 0
      panda/src/express/atomicAdjust.h
  50. 14 0
      panda/src/express/atomicAdjustDummyImpl.I
  51. 1 0
      panda/src/express/atomicAdjustDummyImpl.h
  52. 14 0
      panda/src/express/atomicAdjustNsprImpl.I
  53. 1 0
      panda/src/express/atomicAdjustNsprImpl.h
  54. 17 0
      panda/src/express/atomicAdjustPosixImpl.I
  55. 1 0
      panda/src/express/atomicAdjustPosixImpl.h
  56. 14 0
      panda/src/express/atomicAdjustWin32Impl.I
  57. 1 0
      panda/src/express/atomicAdjustWin32Impl.h
  58. 16 67
      panda/src/express/conditionVar.I
  59. 0 42
      panda/src/express/conditionVar.cxx
  60. 13 16
      panda/src/express/conditionVar.h
  61. 52 0
      panda/src/express/conditionVarDebug.I
  62. 139 0
      panda/src/express/conditionVarDebug.cxx
  63. 70 0
      panda/src/express/conditionVarDebug.h
  64. 129 0
      panda/src/express/conditionVarDirect.I
  65. 34 0
      panda/src/express/conditionVarDirect.cxx
  66. 70 0
      panda/src/express/conditionVarDirect.h
  67. 6 1
      panda/src/express/config_express.cxx
  68. 4 0
      panda/src/express/express_composite1.cxx
  69. 109 0
      panda/src/express/mutexDebug.I
  70. 217 0
      panda/src/express/mutexDebug.cxx
  71. 79 0
      panda/src/express/mutexDebug.h
  72. 106 0
      panda/src/express/mutexDirect.I
  73. 34 0
      panda/src/express/mutexDirect.cxx
  74. 66 0
      panda/src/express/mutexDirect.h
  75. 0 14
      panda/src/express/mutexDummyImpl.I
  76. 1 7
      panda/src/express/mutexDummyImpl.h
  77. 3 2
      panda/src/express/mutexImpl.h
  78. 12 0
      panda/src/express/mutexNsprImpl.I
  79. 3 0
      panda/src/express/mutexNsprImpl.h
  80. 24 0
      panda/src/express/mutexPosixImpl.I
  81. 5 0
      panda/src/express/mutexPosixImpl.h
  82. 10 0
      panda/src/express/mutexWin32Impl.I
  83. 3 0
      panda/src/express/mutexWin32Impl.h
  84. 12 72
      panda/src/express/pmutex.I
  85. 1 46
      panda/src/express/pmutex.cxx
  86. 16 32
      panda/src/express/pmutex.h
  87. 12 242
      panda/src/express/reMutex.I
  88. 0 66
      panda/src/express/reMutex.cxx
  89. 12 27
      panda/src/express/reMutex.h
  90. 110 0
      panda/src/express/reMutexDirect.I
  91. 116 0
      panda/src/express/reMutexDirect.cxx
  92. 77 0
      panda/src/express/reMutexDirect.h
  93. 1 1
      panda/src/express/referenceCount.I
  94. 6 0
      panda/src/express/selectThreadImpl.h
  95. 22 5
      panda/src/express/test_diners.cxx
  96. 4 0
      panda/src/express/thread.I
  97. 3 0
      panda/src/express/thread.cxx
  98. 12 0
      panda/src/express/thread.h
  99. 11 9
      panda/src/express/threadNsprImpl.cxx
  100. 2 2
      panda/src/express/threadNsprImpl.h

+ 15 - 12
dtool/Config.pp

@@ -320,15 +320,14 @@
 
 
 // Do you want to compile in support for pipelining?  This enables
 // Do you want to compile in support for pipelining?  This enables
 // setting and accessing multiple different copies of frame-specific
 // setting and accessing multiple different copies of frame-specific
-// data stored in nodes, etc.  At the moment, Panda cannot actually
-// take advantage of this support to do anything useful, but
-// eventually this will enable multi-stage pipelining of the render
-// process, as well as potentially remote rendering using a
-// distributed scene graph.  For now, we enable this when building
-// optimize 1 only, since turning this on does perform some additional
-// sanity checks, but doesn't do anything else useful other than
-// increase run-time overhead.
-#defer DO_PIPELINING $[<= $[OPTIMIZE], 1]
+// data stored in nodes, etc.  This is necessary, in conjunction with
+// HAVE_THREADS, to implement threaded multistage rendering in Panda.
+// However, compiling this option in does add some additional runtime
+// overhead even if it is not used.  By default, we enable pipelining
+// whenever threads are enabled, assuming that if you have threads,
+// you also want to use piplining.  We also enable it at OPTIMIZE
+// level 1, since that enables additional runtime checks.
+#defer DO_PIPELINING $[or $[<= $[OPTIMIZE], 1],$[HAVE_THREADS]]
 
 
 // Do you want to use one of the alternative malloc implementations?
 // Do you want to use one of the alternative malloc implementations?
 // This is almost always a good idea on Windows, where the standard
 // This is almost always a good idea on Windows, where the standard
@@ -578,9 +577,13 @@
 // that Panda can use.
 // that Panda can use.
 #define HAVE_THREADS
 #define HAVE_THREADS
 
 
-// Even if threading is not defined, you might want to double-check
-// that ordinary mutexes are not locked reentrantly:
-#define CHECK_REENTRANT_MUTEX
+// Whether threading is defined or not, you might want to validate the
+// thread and synchronization operations.  With threading enabled,
+// defining this will also enable deadlock detection and logging.
+// Without threading enabled, defining this will simply verify that a
+// mutex is not recursively locked.  There is, of course, additional
+// run-time overhead for these tests.
+#defer DEBUG_THREADS $[<= $[OPTIMIZE], 2]
 
 
 // Do you want to build the network interface?  What additional libraries
 // Do you want to build the network interface?  What additional libraries
 // are required?  Currently, this requires NSPR.
 // are required?  Currently, this requires NSPR.

+ 2 - 2
dtool/LocalSetup.pp

@@ -232,8 +232,8 @@ $[cdefine HAVE_CHROMIUM]
 /* Define if we want to compile the threading code.  */
 /* Define if we want to compile the threading code.  */
 $[cdefine HAVE_THREADS]
 $[cdefine HAVE_THREADS]
 
 
-/* Define to check that ordinary (non-reentrant) Mutexes are not reentrantly locked. */
-$[cdefine CHECK_REENTRANT_MUTEX]
+/* Define to enable deadlock detection, mutex recursion checks, etc. */
+$[cdefine DEBUG_THREADS]
 
 
 /* Define if we want to compile the net code.  */
 /* Define if we want to compile the net code.  */
 $[cdefine HAVE_NET]
 $[cdefine HAVE_NET]

+ 1 - 1
dtool/Package.pp

@@ -213,7 +213,7 @@
 #set HAVE_OPENCV $[HAVE_OPENCV]
 #set HAVE_OPENCV $[HAVE_OPENCV]
 
 
 #set HAVE_THREADS $[HAVE_THREADS]
 #set HAVE_THREADS $[HAVE_THREADS]
-#set CHECK_REENTRANT_MUTEX $[CHECK_REENTRANT_MUTEX]
+#set DEBUG_THREADS $[DEBUG_THREADS]
 
 
 #set NET_IPATH $[unixfilename $[NET_IPATH]]
 #set NET_IPATH $[unixfilename $[NET_IPATH]]
 #set NET_LPATH $[unixfilename $[NET_LPATH]]
 #set NET_LPATH $[unixfilename $[NET_LPATH]]

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

@@ -68,16 +68,15 @@ output(ostream &out) const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CollisionInvSphere::recompute_bound
+//     Function: CollisionInvSphere::compute_internal_bounds
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-BoundingVolume *CollisionInvSphere::
-recompute_bound(int pipeline_stage) {
-  BoundedObject::recompute_bound(pipeline_stage);
+PT(BoundingVolume) CollisionInvSphere::
+compute_internal_bounds() const {
   // An inverse sphere always has an infinite bounding volume, since
   // An inverse sphere always has an infinite bounding volume, since
   // everything outside the sphere is solid matter.
   // everything outside the sphere is solid matter.
-  return set_bound_ptr(new OmniBoundingVolume());
+  return new OmniBoundingVolume();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

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

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

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

@@ -59,7 +59,7 @@ prepare_collider(const ColliderDef &def) {
   _colliders.push_back(def);
   _colliders.push_back(def);
 
 
   CollisionSolid *collider = def._collider;
   CollisionSolid *collider = def._collider;
-  const BoundingVolume *bv = collider->get_bound();
+  CPT(BoundingVolume) bv = collider->get_bounds();
   if (!bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
   if (!bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
     _local_bounds.push_back((GeometricBoundingVolume *)NULL);
     _local_bounds.push_back((GeometricBoundingVolume *)NULL);
   } else {
   } else {
@@ -118,7 +118,7 @@ any_in_bounds() {
   }
   }
 #endif  // NDEBUG
 #endif  // NDEBUG
 
 
-  const BoundingVolume *node_bv = node()->get_bound();
+  CPT(BoundingVolume) node_bv = node()->get_bounds();
   if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
   if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
     const GeometricBoundingVolume *node_gbv;
     const GeometricBoundingVolume *node_gbv;
     DCAST_INTO_R(node_gbv, node_bv, false);
     DCAST_INTO_R(node_gbv, node_bv, false);

+ 4 - 4
panda/src/collide/collisionNode.I

@@ -81,7 +81,7 @@ get_into_collide_mask() const {
 INLINE void CollisionNode::
 INLINE void CollisionNode::
 clear_solids() {
 clear_solids() {
   _solids.clear();
   _solids.clear();
-  mark_bound_stale();
+  mark_internal_bounds_stale();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -114,7 +114,7 @@ INLINE void CollisionNode::
 set_solid(int n, CollisionSolid *solid) {
 set_solid(int n, CollisionSolid *solid) {
   nassertv(n >= 0 && n < get_num_solids());
   nassertv(n >= 0 && n < get_num_solids());
   _solids[n] = solid;
   _solids[n] = solid;
-  mark_bound_stale();
+  mark_internal_bounds_stale();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -127,7 +127,7 @@ INLINE void CollisionNode::
 remove_solid(int n) {
 remove_solid(int n) {
   nassertv(n >= 0 && n < get_num_solids());
   nassertv(n >= 0 && n < get_num_solids());
   _solids.erase(_solids.begin() + n);
   _solids.erase(_solids.begin() + n);
-  mark_bound_stale();
+  mark_internal_bounds_stale();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -140,7 +140,7 @@ remove_solid(int n) {
 INLINE int CollisionNode::
 INLINE int CollisionNode::
 add_solid(CollisionSolid *solid) {
 add_solid(CollisionSolid *solid) {
   _solids.push_back(solid);
   _solids.push_back(solid);
-  mark_bound_stale();
+  mark_internal_bounds_stale();
   return _solids.size() - 1;
   return _solids.size() - 1;
 }
 }
 
 

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

@@ -128,7 +128,7 @@ xform(const LMatrix4f &mat) {
 
 
     solid->xform(mat);
     solid->xform(mat);
   }
   }
-  mark_bound_stale();
+  mark_internal_bounds_stale();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -157,7 +157,7 @@ combine_with(PandaNode *other) {
       const PT(CollisionSolid) *solids_begin = &cother->_solids[0];
       const PT(CollisionSolid) *solids_begin = &cother->_solids[0];
       const PT(CollisionSolid) *solids_end = solids_begin + cother->_solids.size();
       const PT(CollisionSolid) *solids_end = solids_begin + cother->_solids.size();
       _solids.insert(_solids.end(), solids_begin, solids_end);
       _solids.insert(_solids.end(), solids_begin, solids_end);
-      mark_bound_stale();
+      mark_internal_bounds_stale();
       return this;
       return this;
     }
     }
 
 
@@ -348,17 +348,17 @@ get_collide_geom() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CollisionNode::recompute_internal_bound
+//     Function: CollisionNode::compute_internal_bounds
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual
 //  Description: Called when needed to recompute the node's
 //  Description: Called when needed to recompute the node's
 //               _internal_bound object.  Nodes that contain anything
 //               _internal_bound object.  Nodes that contain anything
 //               of substance should redefine this to do the right
 //               of substance should redefine this to do the right
 //               thing.
 //               thing.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-BoundingVolume *CollisionNode::
-recompute_internal_bound(int pipeline_stage) {
+PT(BoundingVolume) CollisionNode::
+compute_internal_bounds(int pipeline_stage) const {
   // First, get ourselves a fresh, empty bounding volume.
   // First, get ourselves a fresh, empty bounding volume.
-  BoundingVolume *bound = PandaNode::recompute_internal_bound(pipeline_stage);
+  PT(BoundingVolume) bound = PandaNode::compute_internal_bounds(pipeline_stage);
   nassertr(bound != (BoundingVolume *)NULL, bound);
   nassertr(bound != (BoundingVolume *)NULL, bound);
 
 
   // Now actually compute the bounding volume by putting it around all
   // Now actually compute the bounding volume by putting it around all
@@ -367,7 +367,7 @@ recompute_internal_bound(int pipeline_stage) {
 
 
   Solids::const_iterator gi;
   Solids::const_iterator gi;
   for (gi = _solids.begin(); gi != _solids.end(); ++gi) {
   for (gi = _solids.begin(); gi != _solids.end(); ++gi) {
-    child_volumes.push_back((*gi)->get_bound());
+    child_volumes.push_back((*gi)->get_bounds());
   }
   }
 
 
   const BoundingVolume **child_begin = &child_volumes[0];
   const BoundingVolume **child_begin = &child_volumes[0];

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

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

+ 2 - 1
panda/src/collide/collisionPlane.I

@@ -16,6 +16,7 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionPlane::Default Constructor
 //     Function: CollisionPlane::Default Constructor
 //       Access: Protected
 //       Access: Protected
@@ -78,7 +79,7 @@ dist_to_plane(const LPoint3f &point) const {
 INLINE void CollisionPlane::
 INLINE void CollisionPlane::
 set_plane(const Planef &plane) {
 set_plane(const Planef &plane) {
   _plane = plane;
   _plane = plane;
-  mark_bound_stale();
+  mark_internal_bounds_stale();
   mark_viz_stale();
   mark_viz_stale();
 }
 }
 
 

+ 4 - 7
panda/src/collide/collisionPlane.cxx

@@ -88,16 +88,13 @@ output(ostream &out) const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CollisionPlane::recompute_bound
+//     Function: CollisionPlane::compute_internal_bounds
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-BoundingVolume *CollisionPlane::
-recompute_bound(int pipeline_stage) {
-  BoundedObject::recompute_bound(pipeline_stage);
-  // Less than ideal: we throw away whatever we just allocated in
-  // BoundedObject.
-  return set_bound_ptr(new BoundingPlane(_plane));
+PT(BoundingVolume) CollisionPlane::
+compute_internal_bounds() const {
+  return new BoundingPlane(_plane);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

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

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

+ 1 - 0
panda/src/collide/collisionPolygon.I

@@ -16,6 +16,7 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionPolygon::Constructor
 //     Function: CollisionPolygon::Constructor
 //       Access: Public
 //       Access: Public

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

@@ -356,14 +356,14 @@ write(ostream &out, int indent_level) const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CollisionPolygon::recompute_bound
+//     Function: CollisionPolygon::compute_internal_bounds
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-BoundingVolume *CollisionPolygon::
-recompute_bound(int pipeline_stage) {
+PT(BoundingVolume) CollisionPolygon::
+compute_internal_bounds() const {
   // First, get ourselves a fresh, empty bounding volume.
   // First, get ourselves a fresh, empty bounding volume.
-  BoundingVolume *bound = BoundedObject::recompute_bound(pipeline_stage);
+  PT(BoundingVolume) bound = CollisionSolid::compute_internal_bounds();
   nassertr(bound != (BoundingVolume*)0L, bound);
   nassertr(bound != (BoundingVolume*)0L, bound);
 
 
   GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);
   GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);

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

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

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

@@ -78,7 +78,7 @@ CollisionRay(const CollisionRay &copy) :
 INLINE void CollisionRay::
 INLINE void CollisionRay::
 set_origin(const LPoint3f &origin) {
 set_origin(const LPoint3f &origin) {
   _origin = origin;
   _origin = origin;
-  mark_bound_stale();
+  mark_internal_bounds_stale();
   mark_viz_stale();
   mark_viz_stale();
 }
 }
 
 
@@ -110,7 +110,7 @@ get_origin() const {
 INLINE void CollisionRay::
 INLINE void CollisionRay::
 set_direction(const LVector3f &direction) {
 set_direction(const LVector3f &direction) {
   _direction = direction;
   _direction = direction;
-  mark_bound_stale();
+  mark_internal_bounds_stale();
   mark_viz_stale();
   mark_viz_stale();
   nassertv(_direction != LPoint3f::zero());
   nassertv(_direction != LPoint3f::zero());
 }
 }

+ 5 - 8
panda/src/collide/collisionRay.cxx

@@ -119,23 +119,20 @@ set_from_lens(LensNode *camera, const LPoint2f &point) {
     _direction = far_point - near_point;
     _direction = far_point - near_point;
   }
   }
 
 
-  mark_bound_stale();
+  mark_internal_bounds_stale();
   mark_viz_stale();
   mark_viz_stale();
 
 
   return success;
   return success;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CollisionRay::recompute_bound
+//     Function: CollisionRay::compute_internal_bounds
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-BoundingVolume *CollisionRay::
-recompute_bound(int pipeline_stage) {
-  BoundedObject::recompute_bound(pipeline_stage);
-  // Less than ideal: we throw away whatever we just allocated in
-  // BoundedObject.
-  return set_bound_ptr(new BoundingLine(_origin, _origin + _direction));
+PT(BoundingVolume) CollisionRay::
+compute_internal_bounds() const {
+  return new BoundingLine(_origin, _origin + _direction);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

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

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

+ 3 - 2
panda/src/collide/collisionSegment.I

@@ -16,6 +16,7 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionSegment::Default Constructor
 //     Function: CollisionSegment::Default Constructor
 //       Access: Public
 //       Access: Public
@@ -77,7 +78,7 @@ CollisionSegment(const CollisionSegment &copy) :
 INLINE void CollisionSegment::
 INLINE void CollisionSegment::
 set_point_a(const LPoint3f &a) {
 set_point_a(const LPoint3f &a) {
   _a = a;
   _a = a;
-  mark_bound_stale();
+  mark_internal_bounds_stale();
   mark_viz_stale();
   mark_viz_stale();
   // We don't assert here that a != b, on the assumption that you
   // We don't assert here that a != b, on the assumption that you
   // might be about to change both at once, and you'll probably start
   // might be about to change both at once, and you'll probably start
@@ -112,7 +113,7 @@ get_point_a() const {
 INLINE void CollisionSegment::
 INLINE void CollisionSegment::
 set_point_b(const LPoint3f &b) {
 set_point_b(const LPoint3f &b) {
   _b = b;
   _b = b;
-  mark_bound_stale();
+  mark_internal_bounds_stale();
   mark_viz_stale();
   mark_viz_stale();
   nassertv(_a != _b);
   nassertv(_a != _b);
 }
 }

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

@@ -116,20 +116,20 @@ set_from_lens(LensNode *camera, const LPoint2f &point) {
     success = false;
     success = false;
   }
   }
 
 
-  mark_bound_stale();
+  mark_internal_bounds_stale();
   mark_viz_stale();
   mark_viz_stale();
 
 
   return success;
   return success;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CollisionSegment::recompute_bound
+//     Function: CollisionSegment::compute_internal_bounds
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-BoundingVolume *CollisionSegment::
-recompute_bound(int pipeline_stage) {
-  BoundingVolume *bound = BoundedObject::recompute_bound(pipeline_stage);
+PT(BoundingVolume) CollisionSegment::
+compute_internal_bounds() const {
+  PT(BoundingVolume) bound = CollisionSolid::compute_internal_bounds();
 
 
   if (bound->is_of_type(GeometricBoundingVolume::get_class_type())) {
   if (bound->is_of_type(GeometricBoundingVolume::get_class_type())) {
     GeometricBoundingVolume *gbound;
     GeometricBoundingVolume *gbound;

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

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

+ 13 - 0
panda/src/collide/collisionSolid.I

@@ -135,6 +135,19 @@ get_respect_effective_normal() const {
   return (_flags & F_ignore_effective_normal) == 0;
   return (_flags & F_ignore_effective_normal) == 0;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionSolid::mark_internal_bounds_stale
+//       Access: Protected
+//  Description: Should be called by a derived class to mark the
+//               internal bounding volume stale, so that
+//               recompute_internal_bounds() will be called when the
+//               bounding volume is next requested.
+////////////////////////////////////////////////////////////////////
+INLINE void CollisionSolid::
+mark_internal_bounds_stale() {
+  _flags |= F_internal_bounds_stale;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionSolid::mark_viz_stale
 //     Function: CollisionSolid::mark_viz_stale
 //       Access: Protected
 //       Access: Protected

+ 28 - 3
panda/src/collide/collisionSolid.cxx

@@ -22,7 +22,7 @@
 #include "collisionLine.h"
 #include "collisionLine.h"
 #include "collisionRay.h"
 #include "collisionRay.h"
 #include "collisionSegment.h"
 #include "collisionSegment.h"
-
+#include "boundingSphere.h"
 #include "datagram.h"
 #include "datagram.h"
 #include "datagramIterator.h"
 #include "datagramIterator.h"
 #include "bamReader.h"
 #include "bamReader.h"
@@ -43,7 +43,7 @@ TypeHandle CollisionSolid::_type_handle;
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 CollisionSolid::
 CollisionSolid::
 CollisionSolid() {
 CollisionSolid() {
-  _flags = F_viz_geom_stale | F_tangible;
+  _flags = F_viz_geom_stale | F_tangible | F_internal_bounds_stale;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -54,6 +54,7 @@ CollisionSolid() {
 CollisionSolid::
 CollisionSolid::
 CollisionSolid(const CollisionSolid &copy) :
 CollisionSolid(const CollisionSolid &copy) :
   _effective_normal(copy._effective_normal),
   _effective_normal(copy._effective_normal),
+  _internal_bounds(copy._internal_bounds),
   _flags(copy._flags)
   _flags(copy._flags)
 {
 {
   _flags |= F_viz_geom_stale;
   _flags |= F_viz_geom_stale;
@@ -68,6 +69,20 @@ CollisionSolid::
 ~CollisionSolid() {
 ~CollisionSolid() {
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionSolid::get_bounds
+//       Access: Protected
+//  Description: Returns the solid's bounding volume.
+////////////////////////////////////////////////////////////////////
+CPT(BoundingVolume) CollisionSolid::
+get_bounds() const {
+  if (_flags & F_internal_bounds_stale) {
+    ((CollisionSolid *)this)->_internal_bounds = compute_internal_bounds();
+    ((CollisionSolid *)this)->_flags &= ~F_internal_bounds_stale;
+  }
+  return _internal_bounds;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionSolid::test_intersection
 //     Function: CollisionSolid::test_intersection
 //       Access: Public, Virtual
 //       Access: Public, Virtual
@@ -96,7 +111,7 @@ xform(const LMatrix4f &mat) {
   }
   }
 
 
   mark_viz_stale();
   mark_viz_stale();
-  mark_bound_stale();
+  mark_internal_bounds_stale();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -148,6 +163,16 @@ write(ostream &out, int indent_level) const {
   indent(out, indent_level) << (*this) << "\n";
   indent(out, indent_level) << (*this) << "\n";
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CollisionSolid::compute_internal_bounds
+//       Access: Protected, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+PT(BoundingVolume) CollisionSolid::
+compute_internal_bounds() const {
+  return new BoundingSphere;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CollisionSolid::test_intersection_from_sphere
 //     Function: CollisionSolid::test_intersection_from_sphere
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual

+ 9 - 6
panda/src/collide/collisionSolid.h

@@ -23,7 +23,6 @@
 
 
 #include "config_collide.h"
 #include "config_collide.h"
 #include "typedWritableReferenceCount.h"
 #include "typedWritableReferenceCount.h"
-#include "boundedObject.h"
 #include "luse.h"
 #include "luse.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
 #include "renderState.h"
 #include "renderState.h"
@@ -49,8 +48,7 @@ class CullTraverserData;
 //               function calls handle the subset of the N*N
 //               function calls handle the subset of the N*N
 //               intersection tests that we care about.
 //               intersection tests that we care about.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA CollisionSolid :
-  public TypedWritableReferenceCount, public BoundedObject {
+class EXPCL_PANDA CollisionSolid : public TypedWritableReferenceCount {
 public:
 public:
   CollisionSolid();
   CollisionSolid();
   CollisionSolid(const CollisionSolid &copy);
   CollisionSolid(const CollisionSolid &copy);
@@ -71,6 +69,8 @@ PUBLISHED:
   INLINE void set_respect_effective_normal(bool respect_effective_normal);
   INLINE void set_respect_effective_normal(bool respect_effective_normal);
   INLINE bool get_respect_effective_normal() const;
   INLINE bool get_respect_effective_normal() const;
 
 
+  CPT(BoundingVolume) get_bounds() const;
+  
 public:
 public:
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
   test_intersection(const CollisionEntry &entry) const;
   test_intersection(const CollisionEntry &entry) const;
@@ -86,6 +86,9 @@ PUBLISHED:
   virtual void write(ostream &out, int indent_level = 0) const;
   virtual void write(ostream &out, int indent_level = 0) const;
 
 
 protected:
 protected:
+  INLINE void mark_internal_bounds_stale();
+  virtual PT(BoundingVolume) compute_internal_bounds() const;
+
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
   test_intersection_from_sphere(const CollisionEntry &entry) const;
   test_intersection_from_sphere(const CollisionEntry &entry) const;
   virtual PT(CollisionEntry)
   virtual PT(CollisionEntry)
@@ -114,6 +117,7 @@ protected:
 
 
 private:
 private:
   LVector3f _effective_normal;
   LVector3f _effective_normal;
+  PT(BoundingVolume) _internal_bounds;
 
 
   // Be careful reordering these bits, since they are written to a bam
   // Be careful reordering these bits, since they are written to a bam
   // file.
   // file.
@@ -122,6 +126,7 @@ private:
     F_effective_normal          = 0x02,
     F_effective_normal          = 0x02,
     F_viz_geom_stale            = 0x04,
     F_viz_geom_stale            = 0x04,
     F_ignore_effective_normal   = 0x08,
     F_ignore_effective_normal   = 0x08,
+    F_internal_bounds_stale     = 0x10,
   };
   };
   int _flags;
   int _flags;
 
 
@@ -137,10 +142,8 @@ public:
   }
   }
   static void init_type() {
   static void init_type() {
     TypedWritableReferenceCount::init_type();
     TypedWritableReferenceCount::init_type();
-    BoundedObject::init_type();
     register_type(_type_handle, "CollisionSolid",
     register_type(_type_handle, "CollisionSolid",
-                  TypedWritableReferenceCount::get_class_type(),
-                  BoundedObject::get_class_type());
+                  TypedWritableReferenceCount::get_class_type());
   }
   }
   virtual TypeHandle get_type() const {
   virtual TypeHandle get_type() const {
     return get_class_type();
     return get_class_type();

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

@@ -70,7 +70,7 @@ CollisionSphere(const CollisionSphere &copy) :
 INLINE void CollisionSphere::
 INLINE void CollisionSphere::
 set_center(const LPoint3f &center) {
 set_center(const LPoint3f &center) {
   _center = center;
   _center = center;
-  mark_bound_stale();
+  mark_internal_bounds_stale();
   mark_viz_stale();
   mark_viz_stale();
 }
 }
 
 
@@ -102,7 +102,7 @@ get_center() const {
 INLINE void CollisionSphere::
 INLINE void CollisionSphere::
 set_radius(float radius) {
 set_radius(float radius) {
   _radius = radius;
   _radius = radius;
-  mark_bound_stale();
+  mark_internal_bounds_stale();
   mark_viz_stale();
   mark_viz_stale();
 }
 }
 
 

+ 5 - 11
panda/src/collide/collisionSphere.cxx

@@ -72,7 +72,7 @@ xform(const LMatrix4f &mat) {
   LVector3f radius_v = LVector3f(_radius, 0.0f, 0.0f) * mat;
   LVector3f radius_v = LVector3f(_radius, 0.0f, 0.0f) * mat;
   _radius = length(radius_v);
   _radius = length(radius_v);
   mark_viz_stale();
   mark_viz_stale();
-  mark_bound_stale();
+  mark_internal_bounds_stale();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -99,19 +99,13 @@ output(ostream &out) const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CollisionSphere::recompute_bound
+//     Function: CollisionSphere::compute_internal_bounds
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-BoundingVolume *CollisionSphere::
-recompute_bound(int pipeline_stage) {
-  BoundingVolume *bound = BoundedObject::recompute_bound(pipeline_stage);
-  nassertr(bound != (BoundingVolume*)0L, bound);
-  nassertr(!_center.is_nan() && !cnan(_radius), bound);
-  BoundingSphere sphere(_center, _radius);
-  bound->extend_by(&sphere);
-
-  return bound;
+PT(BoundingVolume) CollisionSphere::
+compute_internal_bounds() const {
+  return new BoundingSphere(_center, _radius);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

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

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

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

@@ -519,7 +519,7 @@ r_traverse(CollisionLevelState &level_state) {
   if (node->is_exact_type(CollisionNode::get_class_type())) {
   if (node->is_exact_type(CollisionNode::get_class_type())) {
     CollisionNode *cnode;
     CollisionNode *cnode;
     DCAST_INTO_V(cnode, node);
     DCAST_INTO_V(cnode, node);
-    const BoundingVolume *node_bv = cnode->get_bound();
+    CPT(BoundingVolume) node_bv = cnode->get_bounds();
     const GeometricBoundingVolume *node_gbv = NULL;
     const GeometricBoundingVolume *node_gbv = NULL;
     if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
     if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
       DCAST_INTO_V(node_gbv, node_bv);
       DCAST_INTO_V(node_gbv, node_bv);
@@ -560,7 +560,7 @@ r_traverse(CollisionLevelState &level_state) {
     
     
     GeomNode *gnode;
     GeomNode *gnode;
     DCAST_INTO_V(gnode, node);
     DCAST_INTO_V(gnode, node);
-    const BoundingVolume *node_bv = gnode->get_bound();
+    CPT(BoundingVolume) node_bv = gnode->get_bounds();
     const GeometricBoundingVolume *node_gbv = NULL;
     const GeometricBoundingVolume *node_gbv = NULL;
     if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
     if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
       DCAST_INTO_V(node_gbv, node_bv);
       DCAST_INTO_V(node_gbv, node_bv);
@@ -642,7 +642,7 @@ compare_collider_to_node(CollisionEntry &entry,
     for (int s = 0; s < num_solids; ++s) {
     for (int s = 0; s < num_solids; ++s) {
       entry._into = cnode->get_solid(s);
       entry._into = cnode->get_solid(s);
       if (entry._from != entry._into) {
       if (entry._from != entry._into) {
-        const BoundingVolume *solid_bv = entry._into->get_bound();
+        CPT(BoundingVolume) solid_bv = entry._into->get_bounds();
         const GeometricBoundingVolume *solid_gbv = NULL;
         const GeometricBoundingVolume *solid_gbv = NULL;
         if (num_solids > 1 &&
         if (num_solids > 1 &&
             solid_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
             solid_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
@@ -684,7 +684,7 @@ compare_collider_to_geom_node(CollisionEntry &entry,
       entry._into = (CollisionSolid *)NULL;
       entry._into = (CollisionSolid *)NULL;
       const Geom *geom = DCAST(Geom, gnode->get_geom(s));
       const Geom *geom = DCAST(Geom, gnode->get_geom(s));
       if (geom != (Geom *)NULL) {
       if (geom != (Geom *)NULL) {
-        const BoundingVolume *geom_bv = geom->get_bound();
+        CPT(BoundingVolume) geom_bv = geom->get_bounds();
         const GeometricBoundingVolume *geom_gbv = NULL;
         const GeometricBoundingVolume *geom_gbv = NULL;
         if (num_geoms > 1 &&
         if (num_geoms > 1 &&
             geom_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
             geom_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {

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

@@ -141,7 +141,7 @@ set_radius(float radius) {
 
 
   // We don't need to call recalc_internals(), since the radius
   // We don't need to call recalc_internals(), since the radius
   // doesn't change either of those properties.
   // doesn't change either of those properties.
-  mark_bound_stale();
+  mark_internal_bounds_stale();
   mark_viz_stale();
   mark_viz_stale();
 }
 }
 
 

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

@@ -94,13 +94,13 @@ output(ostream &out) const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: CollisionTube::recompute_bound
+//     Function: CollisionTube::compute_internal_bounds
 //       Access: Protected, Virtual
 //       Access: Protected, Virtual
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-BoundingVolume *CollisionTube::
-recompute_bound(int pipeline_stage) {
-  BoundingVolume *bound = BoundedObject::recompute_bound(pipeline_stage);
+PT(BoundingVolume) CollisionTube::
+compute_internal_bounds() const {
+  PT(BoundingVolume) bound = CollisionSolid::compute_internal_bounds();
 
 
   if (bound->is_of_type(GeometricBoundingVolume::get_class_type())) {
   if (bound->is_of_type(GeometricBoundingVolume::get_class_type())) {
     GeometricBoundingVolume *gbound;
     GeometricBoundingVolume *gbound;
@@ -458,7 +458,7 @@ recalc_internals() {
   _inv_mat.invert_from(_mat);
   _inv_mat.invert_from(_mat);
 
 
   mark_viz_stale();
   mark_viz_stale();
-  mark_bound_stale();
+  mark_internal_bounds_stale();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

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

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

+ 1 - 1
panda/src/collide/collisionVisualizer.cxx

@@ -50,7 +50,7 @@ CollisionVisualizer::
 CollisionVisualizer(const string &name) : PandaNode(name) {
 CollisionVisualizer(const string &name) : PandaNode(name) {
   // We always want to render the CollisionVisualizer node itself
   // We always want to render the CollisionVisualizer node itself
   // (even if it doesn't appear to have any geometry within it).
   // (even if it doesn't appear to have any geometry within it).
-  set_bound(OmniBoundingVolume());
+  set_internal_bounds(new OmniBoundingVolume());
   _viz_scale = 1.0f;
   _viz_scale = 1.0f;
 }
 }
 
 

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

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

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

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

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

@@ -18,6 +18,7 @@
 
 
 
 
 #include "config_display.h"
 #include "config_display.h"
+#include "displayRegion.h"
 #include "standardMunger.h"
 #include "standardMunger.h"
 #include "graphicsStateGuardian.h"
 #include "graphicsStateGuardian.h"
 #include "graphicsPipe.h"
 #include "graphicsPipe.h"
@@ -26,6 +27,7 @@
 #include "graphicsWindow.h"
 #include "graphicsWindow.h"
 #include "graphicsDevice.h"
 #include "graphicsDevice.h"
 #include "parasiteBuffer.h"
 #include "parasiteBuffer.h"
+#include "pandaSystem.h"
 
 
 ConfigureDef(config_display);
 ConfigureDef(config_display);
 NotifyCategoryDef(display, "");
 NotifyCategoryDef(display, "");
@@ -260,13 +262,19 @@ init_libdisplay() {
     return;
     return;
   }
   }
   initialized = true;
   initialized = true;
-  
-  StandardMunger::init_type();
-  GraphicsStateGuardian::init_type();
-  GraphicsPipe::init_type();
-  GraphicsOutput::init_type();
-  GraphicsWindow::init_type();
+
+  DisplayRegion::init_type();
   GraphicsBuffer::init_type();
   GraphicsBuffer::init_type();
   GraphicsDevice::init_type();
   GraphicsDevice::init_type();
+  GraphicsOutput::init_type();
+  GraphicsPipe::init_type();
+  GraphicsStateGuardian::init_type();
+  GraphicsWindow::init_type();
   ParasiteBuffer::init_type();
   ParasiteBuffer::init_type();
+  StandardMunger::init_type();
+
+#if defined(HAVE_THREADS) && defined(DO_PIPELINING)
+  PandaSystem *ps = PandaSystem::get_global_ptr();
+  ps->add_system("pipelining");
+#endif
 }
 }

+ 80 - 5
panda/src/display/displayRegion.I

@@ -25,7 +25,7 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool DisplayRegion::
 INLINE bool DisplayRegion::
 operator < (const DisplayRegion &other) const {
 operator < (const DisplayRegion &other) const {
-  return _sort < other._sort;
+  return get_sort() < other.get_sort();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -36,7 +36,8 @@ operator < (const DisplayRegion &other) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool DisplayRegion::
 INLINE bool DisplayRegion::
 is_active() const {
 is_active() const {
-  return _active;
+  CDReader cdata(_cycler);
+  return cdata->_active;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -47,7 +48,8 @@ is_active() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int DisplayRegion::
 INLINE int DisplayRegion::
 get_sort() const {
 get_sort() const {
-  return _sort;
+  CDReader cdata(_cycler);
+  return cdata->_sort;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -63,7 +65,10 @@ get_sort() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void DisplayRegion::
 INLINE void DisplayRegion::
 set_cube_map_index(int cube_map_index) {
 set_cube_map_index(int cube_map_index) {
-  _cube_map_index = cube_map_index;
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  nassertv(pipeline_stage == 0);
+  CDWriter cdata(_cycler);
+  cdata->_cube_map_index = cube_map_index;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -76,7 +81,77 @@ set_cube_map_index(int cube_map_index) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int DisplayRegion::
 INLINE int DisplayRegion::
 get_cube_map_index() const {
 get_cube_map_index() const {
-  return _cube_map_index;
+  CDReader cdata(_cycler);
+  return cdata->_cube_map_index;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegion::set_cull_result
+//       Access: Public
+//  Description: Stores the result of performing a cull operation on
+//               this DisplayRegion.  Normally, this will only be
+//               called by the GraphicsEngine; you should not call
+//               this directly.
+//
+//               The stored result will automatically be applied back
+//               to all upstream pipeline stages.
+////////////////////////////////////////////////////////////////////
+INLINE void DisplayRegion::
+set_cull_result(CullResult *cull_result, SceneSetup *scene_setup) {
+  CDCullWriter cdata(_cycler_cull, true);
+  cdata->_cull_result = cull_result;
+  cdata->_scene_setup = scene_setup;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegion::get_cull_result
+//       Access: Public
+//  Description: Returns the CullResult value that was stored on this
+//               DisplayRegion, presumably by the last successful cull
+//               operation.  This method is for the benefit of the
+//               GraphicsEngine; normally you shouldn't call this
+//               directly.
+////////////////////////////////////////////////////////////////////
+INLINE CullResult *DisplayRegion::
+get_cull_result() const {
+  CDCullReader cdata(_cycler_cull);
+  return cdata->_cull_result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegion::get_scene_setup
+//       Access: Public
+//  Description: Returns the SceneSetup value that was stored on this
+//               DisplayRegion, presumably by the last successful cull
+//               operation.  This method is for the benefit of the
+//               GraphicsEngine; normally you shouldn't call this
+//               directly.
+////////////////////////////////////////////////////////////////////
+INLINE SceneSetup *DisplayRegion::
+get_scene_setup() const {
+  CDCullReader cdata(_cycler_cull);
+  return cdata->_scene_setup;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegion::CDataCull::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE DisplayRegion::CDataCull::
+CDataCull() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegion::CDataCull::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE DisplayRegion::CDataCull::
+CDataCull(const DisplayRegion::CDataCull &copy) :
+  _cull_result(copy._cull_result),
+  _scene_setup(copy._scene_setup)
+{
 }
 }
 
 
 INLINE ostream &operator << (ostream &out, const DisplayRegion &dr) {
 INLINE ostream &operator << (ostream &out, const DisplayRegion &dr) {

+ 164 - 91
panda/src/display/displayRegion.cxx

@@ -22,9 +22,9 @@
 #include "texture.h"
 #include "texture.h"
 #include "camera.h"
 #include "camera.h"
 #include "dcast.h"
 #include "dcast.h"
-#include "mutexHolder.h"
 #include "pnmImage.h"
 #include "pnmImage.h"
 
 
+TypeHandle DisplayRegion::_type_handle;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: DisplayRegion::Constructor
 //     Function: DisplayRegion::Constructor
@@ -33,12 +33,7 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 DisplayRegion::
 DisplayRegion::
 DisplayRegion(GraphicsOutput *window) :
 DisplayRegion(GraphicsOutput *window) :
-  _l(0.), _r(1.), _b(0.), _t(1.),
-  _window(window),
-  _camera_node((Camera *)NULL),
-  _active(true),
-  _sort(0),
-  _cube_map_index(-1)
+  _window(window)
 {
 {
   _draw_buffer_type = window->get_draw_buffer_type();
   _draw_buffer_type = window->get_draw_buffer_type();
   compute_pixels();
   compute_pixels();
@@ -50,17 +45,11 @@ DisplayRegion(GraphicsOutput *window) :
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 DisplayRegion::
 DisplayRegion::
-DisplayRegion(GraphicsOutput *window, const float l,
-              const float r, const float b, const float t) :
-  _l(l), _r(r), _b(b), _t(t),
-  _window(window),
-  _camera_node((Camera *)NULL),
-  _active(true),
-  _sort(0),
-  _cube_map_index(-1)
+DisplayRegion(GraphicsOutput *window, float l, float r, float b, float t) :
+  _window(window)
 {
 {
   _draw_buffer_type = window->get_draw_buffer_type();
   _draw_buffer_type = window->get_draw_buffer_type();
-  compute_pixels();
+  set_dimensions(l, r, b, t);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -109,7 +98,8 @@ void DisplayRegion::
 cleanup() {
 cleanup() {
   set_camera(NodePath());
   set_camera(NodePath());
 
 
-  _cull_result = NULL;
+  CDCullWriter cdata(_cycler_cull, true);
+  cdata->_cull_result = NULL;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -121,11 +111,11 @@ cleanup() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void DisplayRegion::
 void DisplayRegion::
 get_dimensions(float &l, float &r, float &b, float &t) const {
 get_dimensions(float &l, float &r, float &b, float &t) const {
-  MutexHolder holder(_lock);
-  l = _l;
-  r = _r;
-  b = _b;
-  t = _t;
+  CDReader cdata(_cycler);
+  l = cdata->_l;
+  r = cdata->_r;
+  b = cdata->_b;
+  t = cdata->_t;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -137,8 +127,8 @@ get_dimensions(float &l, float &r, float &b, float &t) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 float DisplayRegion::
 float DisplayRegion::
 get_left() const {
 get_left() const {
-  MutexHolder holder(_lock);
-  return _l;
+  CDReader cdata(_cycler);
+  return cdata->_l;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -150,8 +140,8 @@ get_left() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 float DisplayRegion::
 float DisplayRegion::
 get_right() const {
 get_right() const {
-  MutexHolder holder(_lock);
-  return _r;
+  CDReader cdata(_cycler);
+  return cdata->_r;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -163,8 +153,8 @@ get_right() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 float DisplayRegion::
 float DisplayRegion::
 get_bottom() const {
 get_bottom() const {
-  MutexHolder holder(_lock);
-  return _b;
+  CDReader cdata(_cycler);
+  return cdata->_b;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -176,8 +166,8 @@ get_bottom() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 float DisplayRegion::
 float DisplayRegion::
 get_top() const {
 get_top() const {
-  MutexHolder holder(_lock);
-  return _t;
+  CDReader cdata(_cycler);
+  return cdata->_t;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -191,14 +181,17 @@ get_top() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void DisplayRegion::
 void DisplayRegion::
 set_dimensions(float l, float r, float b, float t) {
 set_dimensions(float l, float r, float b, float t) {
-  MutexHolder holder(_lock);
-  _l = l;
-  _r = r;
-  _b = b;
-  _t = t;
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  nassertv(pipeline_stage == 0);
+  CDWriter cdata(_cycler);
+
+  cdata->_l = l;
+  cdata->_r = r;
+  cdata->_b = b;
+  cdata->_t = t;
 
 
   if (_window != (GraphicsOutput *)NULL && _window->has_size()) {
   if (_window != (GraphicsOutput *)NULL && _window->has_size()) {
-    do_compute_pixels(_window->get_x_size(), _window->get_y_size());
+    do_compute_pixels(_window->get_x_size(), _window->get_y_size(), cdata);
   }
   }
 }
 }
 
 
@@ -211,7 +204,6 @@ set_dimensions(float l, float r, float b, float t) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 GraphicsOutput *DisplayRegion::
 GraphicsOutput *DisplayRegion::
 get_window() const {
 get_window() const {
-  MutexHolder holder(_lock);
   return _window;
   return _window;
 }
 }
 
 
@@ -224,7 +216,6 @@ get_window() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 GraphicsPipe *DisplayRegion::
 GraphicsPipe *DisplayRegion::
 get_pipe() const {
 get_pipe() const {
-  MutexHolder holder(_lock);
   return (_window != (GraphicsOutput *)NULL) ? _window->get_pipe() : NULL;
   return (_window != (GraphicsOutput *)NULL) ? _window->get_pipe() : NULL;
 }
 }
 
 
@@ -242,25 +233,32 @@ get_pipe() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void DisplayRegion::
 void DisplayRegion::
 set_camera(const NodePath &camera) {
 set_camera(const NodePath &camera) {
-  MutexHolder holder(_lock);
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  nassertv(pipeline_stage == 0);
+  CDWriter cdata(_cycler);
+
   Camera *camera_node = (Camera *)NULL;
   Camera *camera_node = (Camera *)NULL;
   if (!camera.is_empty()) {
   if (!camera.is_empty()) {
     DCAST_INTO_V(camera_node, camera.node());
     DCAST_INTO_V(camera_node, camera.node());
   }
   }
 
 
-  if (camera_node != _camera_node) {
-    if (_camera_node != (Camera *)NULL) {
+  if (camera_node != cdata->_camera_node) {
+    // Note that these operations on the DisplayRegion are not
+    // pipelined: they operate across all pipeline stages.  Since we
+    // have already asserted we are running in pipeline stage 0, no
+    // problem.
+    if (cdata->_camera_node != (Camera *)NULL) {
       // We need to tell the old camera we're not using him anymore.
       // We need to tell the old camera we're not using him anymore.
-      _camera_node->remove_display_region(this);
+      cdata->_camera_node->remove_display_region(this);
     }
     }
-    _camera_node = camera_node;
-    if (_camera_node != (Camera *)NULL) {
+    cdata->_camera_node = camera_node;
+    if (cdata->_camera_node != (Camera *)NULL) {
       // Now tell the new camera we are using him.
       // Now tell the new camera we are using him.
-      _camera_node->add_display_region(this);
+      cdata->_camera_node->add_display_region(this);
     }
     }
   }
   }
 
 
-  _camera = camera;
+  cdata->_camera = camera;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -272,8 +270,8 @@ set_camera(const NodePath &camera) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 NodePath DisplayRegion::
 NodePath DisplayRegion::
 get_camera() const {
 get_camera() const {
-  MutexHolder holder(_lock);
-  return _camera;
+  CDReader cdata(_cycler);
+  return cdata->_camera;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -285,9 +283,13 @@ get_camera() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void DisplayRegion::
 void DisplayRegion::
 set_active(bool active) {
 set_active(bool active) {
-  MutexHolder holder(_lock);
-  if (active != _active) {
-    _active = active;
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  nassertv(pipeline_stage == 0);
+  CDReader cdata(_cycler);
+
+  if (active != cdata->_active) {
+    CDWriter cdataw(_cycler, cdata);
+    cdataw->_active = active;
     win_display_regions_changed();
     win_display_regions_changed();
   }
   }
 }
 }
@@ -302,9 +304,13 @@ set_active(bool active) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void DisplayRegion::
 void DisplayRegion::
 set_sort(int sort) {
 set_sort(int sort) {
-  MutexHolder holder(_lock);
-  if (sort != _sort) {
-    _sort = sort;
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  nassertv(pipeline_stage == 0);
+  CDReader cdata(_cycler);
+
+  if (sort != cdata->_sort) {
+    CDWriter cdataw(_cycler, cdata);
+    cdataw->_sort = sort;
     win_display_regions_changed();
     win_display_regions_changed();
   }
   }
 }
 }
@@ -318,9 +324,13 @@ set_sort(int sort) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void DisplayRegion::
 void DisplayRegion::
 compute_pixels() {
 compute_pixels() {
-  MutexHolder holder(_lock);
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  nassertv(pipeline_stage == 0);
+
   if (_window != (GraphicsOutput *)NULL) {
   if (_window != (GraphicsOutput *)NULL) {
-    do_compute_pixels(_window->get_x_size(), _window->get_y_size());
+    CDWriter cdata(_cycler);
+    do_compute_pixels(_window->get_x_size(), _window->get_y_size(), 
+                      cdata);
   }
   }
 }
 }
 
 
@@ -333,8 +343,10 @@ compute_pixels() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void DisplayRegion::
 void DisplayRegion::
 compute_pixels(int x_size, int y_size) {
 compute_pixels(int x_size, int y_size) {
-  MutexHolder holder(_lock);
-  do_compute_pixels(x_size, y_size);
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  nassertv(pipeline_stage == 0);
+  CDWriter cdata(_cycler);
+  do_compute_pixels(x_size, y_size, cdata);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -345,11 +357,11 @@ compute_pixels(int x_size, int y_size) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void DisplayRegion::
 void DisplayRegion::
 get_pixels(int &pl, int &pr, int &pb, int &pt) const {
 get_pixels(int &pl, int &pr, int &pb, int &pt) const {
-  MutexHolder holder(_lock);
-  pl = _pl;
-  pr = _pr;
-  pb = _pb;
-  pt = _pt;
+  CDReader cdata(_cycler);
+  pl = cdata->_pl;
+  pr = cdata->_pr;
+  pb = cdata->_pb;
+  pt = cdata->_pt;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -361,11 +373,11 @@ get_pixels(int &pl, int &pr, int &pb, int &pt) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void DisplayRegion::
 void DisplayRegion::
 get_region_pixels(int &xo, int &yo, int &w, int &h) const {
 get_region_pixels(int &xo, int &yo, int &w, int &h) const {
-  MutexHolder holder(_lock);
-  xo = _pl;
-  yo = _pb;
-  w = _pr - _pl;
-  h = _pt - _pb;
+  CDReader cdata(_cycler);
+  xo = cdata->_pl;
+  yo = cdata->_pb;
+  w = cdata->_pr - cdata->_pl;
+  h = cdata->_pt - cdata->_pb;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -378,11 +390,11 @@ get_region_pixels(int &xo, int &yo, int &w, int &h) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void DisplayRegion::
 void DisplayRegion::
 get_region_pixels_i(int &xo, int &yo, int &w, int &h) const {
 get_region_pixels_i(int &xo, int &yo, int &w, int &h) const {
-  MutexHolder holder(_lock);
-  xo = _pl;
-  yo = _pti;
-  w = _pr - _pl;
-  h = _pbi - _pti;
+  CDReader cdata(_cycler);
+  xo = cdata->_pl;
+  yo = cdata->_pti;
+  w = cdata->_pr - cdata->_pl;
+  h = cdata->_pbi - cdata->_pti;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -392,8 +404,8 @@ get_region_pixels_i(int &xo, int &yo, int &w, int &h) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int DisplayRegion::
 int DisplayRegion::
 get_pixel_width() const {
 get_pixel_width() const {
-  MutexHolder holder(_lock);
-  return _pr - _pl;
+  CDReader cdata(_cycler);
+  return cdata->_pr - cdata->_pl;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -403,8 +415,8 @@ get_pixel_width() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int DisplayRegion::
 int DisplayRegion::
 get_pixel_height() const {
 get_pixel_height() const {
-  MutexHolder holder(_lock);
-  return _pt - _pb;
+  CDReader cdata(_cycler);
+  return cdata->_pt - cdata->_pb;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -414,9 +426,10 @@ get_pixel_height() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void DisplayRegion::
 void DisplayRegion::
 output(ostream &out) const {
 output(ostream &out) const {
-  MutexHolder holder(_lock);
-  out << "DisplayRegion(" << _l << " " << _r << " " << _b << " " << _t
-      << ")=pixels(" << _pl << " " << _pr << " " << _pb << " " << _pt
+  CDReader cdata(_cycler);
+  out << "DisplayRegion(" << cdata->_l << " " << cdata->_r << " "
+      << cdata->_b << " " << cdata->_t << ")=pixels(" << cdata->_pl
+      << " " << cdata->_pr << " " << cdata->_pb << " " << cdata->_pt
       << ")";
       << ")";
 }
 }
 
 
@@ -593,28 +606,88 @@ win_display_regions_changed() {
 //               assumes that we already have the lock.
 //               assumes that we already have the lock.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void DisplayRegion::
 void DisplayRegion::
-do_compute_pixels(int x_size, int y_size) {
+do_compute_pixels(int x_size, int y_size, CData *cdata) {
   if (display_cat.is_debug()) {
   if (display_cat.is_debug()) {
     display_cat.debug()
     display_cat.debug()
       << "DisplayRegion::do_compute_pixels(" << x_size << ", " << y_size << ")\n";
       << "DisplayRegion::do_compute_pixels(" << x_size << ", " << y_size << ")\n";
   }
   }
 
 
-  _pl = int((_l * x_size) + 0.5);
-  _pr = int((_r * x_size) + 0.5);
+  cdata->_pl = int((cdata->_l * x_size) + 0.5);
+  cdata->_pr = int((cdata->_r * x_size) + 0.5);
 
 
   nassertv(_window != (GraphicsOutput *)NULL);
   nassertv(_window != (GraphicsOutput *)NULL);
   if (_window->get_inverted()) {
   if (_window->get_inverted()) {
     // The window is inverted; compute the DisplayRegion accordingly.
     // The window is inverted; compute the DisplayRegion accordingly.
-    _pb = int(((1.0f - _t) * y_size) + 0.5);
-    _pt = int(((1.0f - _b) * y_size) + 0.5);
-    _pbi = int((_t * y_size) + 0.5);
-    _pti = int((_b * y_size) + 0.5);
+    cdata->_pb = int(((1.0f - cdata->_t) * y_size) + 0.5);
+    cdata->_pt = int(((1.0f - cdata->_b) * y_size) + 0.5);
+    cdata->_pbi = int((cdata->_t * y_size) + 0.5);
+    cdata->_pti = int((cdata->_b * y_size) + 0.5);
 
 
   } else {
   } else {
     // The window is normal.
     // The window is normal.
-    _pb = int((_b * y_size) + 0.5);
-    _pt = int((_t * y_size) + 0.5);
-    _pbi = int(((1.0f - _b) * y_size) + 0.5);
-    _pti = int(((1.0f - _t) * y_size) + 0.5);
+    cdata->_pb = int((cdata->_b * y_size) + 0.5);
+    cdata->_pt = int((cdata->_t * y_size) + 0.5);
+    cdata->_pbi = int(((1.0f - cdata->_b) * y_size) + 0.5);
+    cdata->_pti = int(((1.0f - cdata->_t) * y_size) + 0.5);
   }
   }
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegion::CData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DisplayRegion::CData::
+CData() :
+  _l(0.), _r(1.), _b(0.), _t(1.),
+  _camera_node((Camera *)NULL),
+  _active(true),
+  _sort(0),
+  _cube_map_index(-1)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegion::CData::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DisplayRegion::CData::
+CData(const DisplayRegion::CData &copy) :
+  _l(copy._l),
+  _r(copy._r),
+  _b(copy._b),
+  _t(copy._t),
+  _pl(copy._pl),
+  _pr(copy._pr),
+  _pb(copy._pb),
+  _pt(copy._pt),
+  _pbi(copy._pbi),
+  _pti(copy._pti),
+  _camera(copy._camera),
+  _camera_node(copy._camera_node),
+  _active(copy._active),
+  _sort(copy._sort),
+  _cube_map_index(copy._cube_map_index)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegion::CData::make_copy
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CycleData *DisplayRegion::CData::
+make_copy() const {
+  return new CData(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegion::CDataCull::make_copy
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CycleData *DisplayRegion::CDataCull::
+make_copy() const {
+  return new CDataCull(*this);
+}

+ 88 - 29
panda/src/display/displayRegion.h

@@ -15,6 +15,7 @@
 // [email protected] .
 // [email protected] .
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
+
 #ifndef DISPLAYREGION_H
 #ifndef DISPLAYREGION_H
 #define DISPLAYREGION_H
 #define DISPLAYREGION_H
 
 
@@ -24,8 +25,12 @@
 #include "referenceCount.h"
 #include "referenceCount.h"
 #include "nodePath.h"
 #include "nodePath.h"
 #include "cullResult.h"
 #include "cullResult.h"
+#include "sceneSetup.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
-#include "pmutex.h"
+#include "cycleData.h"
+#include "cycleDataReader.h"
+#include "cycleDataWriter.h"
+#include "pipelineCycler.h"
 #include "config_display.h"
 #include "config_display.h"
 
 
 #include "plist.h"
 #include "plist.h"
@@ -51,8 +56,7 @@ class EXPCL_PANDA DisplayRegion : public ReferenceCount, public DrawableRegion {
 protected:
 protected:
   DisplayRegion(GraphicsOutput *window);
   DisplayRegion(GraphicsOutput *window);
   DisplayRegion(GraphicsOutput *window,
   DisplayRegion(GraphicsOutput *window,
-                const float l, const float r,
-                const float b, const float t);
+                float l, float r, float b, float t);
 private:
 private:
   DisplayRegion(const DisplayRegion &copy);
   DisplayRegion(const DisplayRegion &copy);
   void operator = (const DisplayRegion &copy);
   void operator = (const DisplayRegion &copy);
@@ -102,40 +106,95 @@ PUBLISHED:
   bool save_screenshot(const Filename &filename);
   bool save_screenshot(const Filename &filename);
   bool get_screenshot(PNMImage &image);
   bool get_screenshot(PNMImage &image);
 
 
-private:
-  void win_display_regions_changed();
-  void do_compute_pixels(int x_size, int y_size);
-  Mutex _lock;
+public:
+  INLINE void set_cull_result(CullResult *cull_result, SceneSetup *scene_setup);
+  INLINE CullResult *get_cull_result() const;
+  INLINE SceneSetup *get_scene_setup() const;
 
 
-  float _l;
-  float _r;
-  float _b;
-  float _t;
+private:
+  class CData;
 
 
-  int _pl;
-  int _pr;
-  int _pb;
-  int _pt;
-  int _pbi;
-  int _pti;
+  void win_display_regions_changed();
+  void do_compute_pixels(int x_size, int y_size, CData *cdata);
 
 
+  // The associated window is a permanent property of the
+  // DisplayRegion.  It doesn't need to be cycled.
   GraphicsOutput *_window;
   GraphicsOutput *_window;
-  NodePath _camera;
 
 
-  // This needs to be a PT(Camera) so we prevent the Camera node from
-  // destructing while we hold its pointer.
-  PT(Camera) _camera_node;
+  // This is the data that is associated with the DisplayRegion that
+  // needs to be cycled every frame, but represents the parameters as
+  // specified by the user, and which probably will not change that
+  // often.
+  class EXPCL_PANDA CData : public CycleData {
+  public:
+    CData();
+    CData(const CData &copy);
+
+    virtual CycleData *make_copy() const;
+    virtual TypeHandle get_parent_type() const {
+      return DisplayRegion::get_class_type();
+    }
+
+    float _l;
+    float _r;
+    float _b;
+    float _t;
+    
+    int _pl;
+    int _pr;
+    int _pb;
+    int _pt;
+    int _pbi;
+    int _pti;
+    
+    NodePath _camera;
+    Camera *_camera_node;
+    
+    bool _active;
+    int _sort;
+    int _cube_map_index;
+  };
+
+  PipelineCycler<CData> _cycler;
+  typedef CycleDataReader<CData> CDReader;
+  typedef CycleDataWriter<CData> CDWriter;
+
+  // This is a special cycler created to hold the results from the
+  // cull traversal, for (a) the draw traversal, and (b) the next
+  // frame's cull traversal.  It needs to be cycled, but it gets its
+  // own cycler because it will certainly change every frame, so we
+  // don't need to lump all the heavy data above in with this
+  // lightweight cycler.
+  class EXPCL_PANDA CDataCull : public CycleData {
+  public:
+    CDataCull();
+    CDataCull(const CDataCull &copy);
+
+    virtual CycleData *make_copy() const;
+    virtual TypeHandle get_parent_type() const {
+      return DisplayRegion::get_class_type();
+    }
+
+    PT(CullResult) _cull_result;
+    PT(SceneSetup) _scene_setup;
+  };
+  PipelineCycler<CDataCull> _cycler_cull;
+  typedef CycleDataReader<CDataCull> CDCullReader;
+  typedef CycleDataWriter<CDataCull> CDCullWriter;
 
 
-  bool _active;
-  int _sort;
-  int _cube_map_index;
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    ReferenceCount::init_type();
+    register_type(_type_handle, "DisplayRegion",
+                  ReferenceCount::get_class_type());
+  }
 
 
-  // This is used to cache the culling result from last frame's
-  // drawing into this display region.  It should only be accessed or
-  // modified by the GraphicsEngine, during the cull traversal.
-  PT(CullResult) _cull_result;
+private:
+  static TypeHandle _type_handle;
 
 
-  friend class GraphicsEngine;
   friend class GraphicsOutput;
   friend class GraphicsOutput;
 };
 };
 
 

+ 94 - 44
panda/src/display/graphicsEngine.cxx

@@ -149,13 +149,14 @@ get_frame_buffer_properties() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GraphicsEngine::
 void GraphicsEngine::
 set_threading_model(const GraphicsThreadingModel &threading_model) {
 set_threading_model(const GraphicsThreadingModel &threading_model) {
-  if (!threading_model.is_single_threaded() && 
-      !Thread::is_threading_supported()) {
+#ifndef THREADED_PIPELINE
+  if (!threading_model.is_single_threaded()) {
     display_cat.warning()
     display_cat.warning()
       << "Threading model " << threading_model
       << "Threading model " << threading_model
-      << " requested but threading not supported.\n";
+      << " requested but multithreaded render pipelines not enabled in build.\n";
     return;
     return;
   }
   }
+#endif  // THREADED_PIPELINE
   MutexHolder holder(_lock);
   MutexHolder holder(_lock);
   _threading_model = threading_model;
   _threading_model = threading_model;
 }
 }
@@ -712,7 +713,7 @@ void GraphicsEngine::
 render_subframe(GraphicsOutput *win, DisplayRegion *dr,
 render_subframe(GraphicsOutput *win, DisplayRegion *dr,
                 bool cull_sorting) {
                 bool cull_sorting) {
   if (cull_sorting) {
   if (cull_sorting) {
-    cull_bin_draw(win, dr);
+    cull_to_bins(win, dr);
   } else {
   } else {
     cull_and_draw_together(win, dr);
     cull_and_draw_together(win, dr);
   }
   }
@@ -740,7 +741,7 @@ add_callback(const string &thread_name,
              GraphicsEngine::CallbackTime callback_time,
              GraphicsEngine::CallbackTime callback_time,
              GraphicsEngine::CallbackFunction *func, void *data) {
              GraphicsEngine::CallbackFunction *func, void *data) {
   MutexHolder holder(_lock);
   MutexHolder holder(_lock);
-  WindowRenderer *wr = get_window_renderer(thread_name);
+  WindowRenderer *wr = get_window_renderer(thread_name, 0);
   return wr->add_callback(callback_time, Callback(func, data));
   return wr->add_callback(callback_time, Callback(func, data));
 }
 }
 
 
@@ -757,11 +758,11 @@ add_callback(const string &thread_name,
 //               removed).
 //               removed).
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool GraphicsEngine::
 bool GraphicsEngine::
-remove_callback(const string &thread_name, 
+remove_callback(const string &thread_name,
                 GraphicsEngine::CallbackTime callback_time,
                 GraphicsEngine::CallbackTime callback_time,
                 GraphicsEngine::CallbackFunction *func, void *data) {
                 GraphicsEngine::CallbackFunction *func, void *data) {
   MutexHolder holder(_lock);
   MutexHolder holder(_lock);
-  WindowRenderer *wr = get_window_renderer(thread_name);
+  WindowRenderer *wr = get_window_renderer(thread_name, 0);
   return wr->remove_callback(callback_time, Callback(func, data));
   return wr->remove_callback(callback_time, Callback(func, data));
 }
 }
 
 
@@ -857,7 +858,7 @@ cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: GraphicsEngine::cull_bin_draw
+//     Function: GraphicsEngine::cull_to_bins
 //       Access: Private
 //       Access: Private
 //  Description: This is called in the cull thread by individual
 //  Description: This is called in the cull thread by individual
 //               RenderThread objects during the frame rendering.  It
 //               RenderThread objects during the frame rendering.  It
@@ -865,12 +866,68 @@ cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr) {
 //               drawing.
 //               drawing.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GraphicsEngine::
 void GraphicsEngine::
-cull_bin_draw(const GraphicsEngine::Windows &wlist) {
+cull_to_bins(const GraphicsEngine::Windows &wlist) {
+  Windows::const_iterator wi;
+  for (wi = wlist.begin(); wi != wlist.end(); ++wi) {
+    GraphicsOutput *win = (*wi);
+    if (win->is_active() && win->get_gsg()->is_active()) {
+      int num_display_regions = win->get_num_active_display_regions();
+      for (int i = 0; i < num_display_regions; ++i) {
+        DisplayRegion *dr = win->get_active_display_region(i);
+        if (dr != (DisplayRegion *)NULL) {
+          cull_to_bins(win, dr);
+        }
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsEngine::cull_to_bins
+//       Access: Private
+//  Description: This variant of cull_to_bins() is called
+//               by render_subframe(), as well as within the
+//               implementation of cull_to_bins(), above.
+////////////////////////////////////////////////////////////////////
+void GraphicsEngine::
+cull_to_bins(GraphicsOutput *win, DisplayRegion *dr) {
+  GraphicsStateGuardian *gsg = win->get_gsg();
+  nassertv(gsg != (GraphicsStateGuardian *)NULL);
+
+  PT(CullResult) cull_result = dr->get_cull_result();
+  if (cull_result != (CullResult *)NULL) {
+    cull_result = cull_result->make_next();
+  } else {
+    cull_result = new CullResult(gsg);
+  }
+
+  PT(SceneSetup) scene_setup = setup_scene(gsg, dr);
+  if (scene_setup != (SceneSetup *)NULL) {
+    BinCullHandler cull_handler(cull_result);
+    do_cull(&cull_handler, scene_setup, gsg);
+    
+    cull_result->finish_cull();
+    
+    // Save the results for next frame.
+    dr->set_cull_result(cull_result, scene_setup);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsEngine::draw_bins
+//       Access: Private
+//  Description: This is called in the draw thread by individual
+//               RenderThread objects during the frame rendering.  It
+//               issues the graphics commands to draw the objects that
+//               have been collected into bins by a previous call to
+//               cull_to_bins().
+////////////////////////////////////////////////////////////////////
+void GraphicsEngine::
+draw_bins(const GraphicsEngine::Windows &wlist) {
   Windows::const_iterator wi;
   Windows::const_iterator wi;
   for (wi = wlist.begin(); wi != wlist.end(); ++wi) {
   for (wi = wlist.begin(); wi != wlist.end(); ++wi) {
     GraphicsOutput *win = (*wi);
     GraphicsOutput *win = (*wi);
     if (win->is_active() && win->get_gsg()->is_active()) {
     if (win->is_active() && win->get_gsg()->is_active()) {
-      // This should be done in the draw thread, not here.
       if (win->begin_frame(GraphicsOutput::FM_render)) {
       if (win->begin_frame(GraphicsOutput::FM_render)) {
         win->clear();
         win->clear();
       
       
@@ -878,7 +935,7 @@ cull_bin_draw(const GraphicsEngine::Windows &wlist) {
         for (int i = 0; i < num_display_regions; ++i) {
         for (int i = 0; i < num_display_regions; ++i) {
           DisplayRegion *dr = win->get_active_display_region(i);
           DisplayRegion *dr = win->get_active_display_region(i);
           if (dr != (DisplayRegion *)NULL) {
           if (dr != (DisplayRegion *)NULL) {
-            cull_bin_draw(win, dr);
+            draw_bins(win, dr);
           }
           }
         }
         }
         win->end_frame(GraphicsOutput::FM_render);
         win->end_frame(GraphicsOutput::FM_render);
@@ -901,36 +958,20 @@ cull_bin_draw(const GraphicsEngine::Windows &wlist) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: GraphicsEngine::cull_bin_draw
+//     Function: GraphicsEngine::draw_bins
 //       Access: Private
 //       Access: Private
-//  Description: This variant of cull_bin_draw() is called
-//               by render_subframe(), as well as within the
-//               implementation of cull_bin_draw(), above.
+//  Description: This variant on draw_bins() is only called from
+//               draw_bins(), above.  It draws the cull result for a
+//               particular DisplayRegion.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GraphicsEngine::
 void GraphicsEngine::
-cull_bin_draw(GraphicsOutput *win, DisplayRegion *dr) {
+draw_bins(GraphicsOutput *win, DisplayRegion *dr) {
   GraphicsStateGuardian *gsg = win->get_gsg();
   GraphicsStateGuardian *gsg = win->get_gsg();
   nassertv(gsg != (GraphicsStateGuardian *)NULL);
   nassertv(gsg != (GraphicsStateGuardian *)NULL);
 
 
-  PT(CullResult) cull_result = dr->_cull_result;
-  if (cull_result != (CullResult *)NULL) {
-    cull_result = cull_result->make_next();
-  } else {
-    cull_result = new CullResult(gsg);
-  }
-
-  PT(SceneSetup) scene_setup = setup_scene(gsg, dr);
-  if (scene_setup != (SceneSetup *)NULL) {
-    BinCullHandler cull_handler(cull_result);
-    do_cull(&cull_handler, scene_setup, gsg);
-    
-    cull_result->finish_cull();
-    
-    // Save the results for next frame.
-    dr->_cull_result = cull_result;
-    
-    // Now draw.
-    // This should get deferred into the next pipeline stage.
+  PT(CullResult) cull_result = dr->get_cull_result();
+  PT(SceneSetup) scene_setup = dr->get_scene_setup();
+  if (cull_result != (CullResult *)NULL && scene_setup != (SceneSetup *)NULL) {
     do_draw(cull_result, scene_setup, win, dr);
     do_draw(cull_result, scene_setup, win, dr);
   }
   }
 }
 }
@@ -1046,7 +1087,7 @@ do_flip_frame() {
     for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
     for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
       RenderThread *thread = (*ti).second;
       RenderThread *thread = (*ti).second;
       thread->_cv_mutex.lock();
       thread->_cv_mutex.lock();
-      
+
       while (thread->_thread_state != TS_wait) {
       while (thread->_thread_state != TS_wait) {
         thread->_cv_done.wait();
         thread->_cv_done.wait();
       }
       }
@@ -1269,8 +1310,12 @@ do_add_window(GraphicsOutput *window, GraphicsStateGuardian *gsg,
   _windows_sorted = false;
   _windows_sorted = false;
   _windows.push_back(window);
   _windows.push_back(window);
   
   
-  WindowRenderer *cull = get_window_renderer(threading_model.get_cull_name());
-  WindowRenderer *draw = get_window_renderer(threading_model.get_draw_name());
+  WindowRenderer *cull = 
+    get_window_renderer(threading_model.get_cull_name(),
+                        threading_model.get_cull_stage());
+  WindowRenderer *draw = 
+    get_window_renderer(threading_model.get_draw_name(),
+                        threading_model.get_draw_stage());
   draw->add_gsg(gsg);
   draw->add_gsg(gsg);
   
   
   if (threading_model.get_cull_sorting()) {
   if (threading_model.get_cull_sorting()) {
@@ -1438,11 +1483,15 @@ get_invert_polygon_state() {
 //       Access: Private
 //       Access: Private
 //  Description: Returns the WindowRenderer with the given name.
 //  Description: Returns the WindowRenderer with the given name.
 //               Creates a new RenderThread if there is no such thread
 //               Creates a new RenderThread if there is no such thread
-//               already.  You must already be holding the lock before
-//               calling this method.
+//               already.  The pipeline_stage number specifies the
+//               pipeline stage that will be assigned to the thread
+//               (unless was previously given a higher stage).
+//
+//               You must already be holding the lock before calling
+//               this method.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 GraphicsEngine::WindowRenderer *GraphicsEngine::
 GraphicsEngine::WindowRenderer *GraphicsEngine::
-get_window_renderer(const string &name) {
+get_window_renderer(const string &name, int pipeline_stage) {
   nassertr(_lock.debug_is_locked(), NULL);
   nassertr(_lock.debug_is_locked(), NULL);
 
 
   if (name.empty()) {
   if (name.empty()) {
@@ -1455,8 +1504,8 @@ get_window_renderer(const string &name) {
   }
   }
 
 
   PT(RenderThread) thread = new RenderThread(name, this);
   PT(RenderThread) thread = new RenderThread(name, this);
-  thread->set_pipeline_stage(1);
-  _pipeline->set_min_stages(2);
+  thread->set_min_pipeline_stage(pipeline_stage);
+  _pipeline->set_min_stages(pipeline_stage + 1);
 
 
   thread->start(TP_normal, true, true);
   thread->start(TP_normal, true, true);
   _threads[name] = thread;
   _threads[name] = thread;
@@ -1592,8 +1641,9 @@ do_frame(GraphicsEngine *engine) {
 
 
   do_callbacks(CB_pre_frame);
   do_callbacks(CB_pre_frame);
 
 
-  engine->cull_bin_draw(_cull);
+  engine->cull_to_bins(_cull);
   engine->cull_and_draw_together(_cdraw);
   engine->cull_and_draw_together(_cdraw);
+  engine->draw_bins(_draw);
   engine->process_events(_window);
   engine->process_events(_window);
 
 
   // If any GSG's on the list have no more outstanding pointers, clean
   // If any GSG's on the list have no more outstanding pointers, clean

+ 5 - 3
panda/src/display/graphicsEngine.h

@@ -145,8 +145,10 @@ private:
   void cull_and_draw_together(const Windows &wlist);
   void cull_and_draw_together(const Windows &wlist);
   void cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr);
   void cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr);
 
 
-  void cull_bin_draw(const Windows &wlist);
-  void cull_bin_draw(GraphicsOutput *win, DisplayRegion *dr);
+  void cull_to_bins(const Windows &wlist);
+  void cull_to_bins(GraphicsOutput *win, DisplayRegion *dr);
+  void draw_bins(const Windows &wlist);
+  void draw_bins(GraphicsOutput *win, DisplayRegion *dr);
   void make_contexts(const Windows &wlist);
   void make_contexts(const Windows &wlist);
 
 
   void process_events(const Windows &wlist);
   void process_events(const Windows &wlist);
@@ -222,7 +224,7 @@ private:
     ThreadState _thread_state;
     ThreadState _thread_state;
   };
   };
 
 
-  WindowRenderer *get_window_renderer(const string &name);
+  WindowRenderer *get_window_renderer(const string &name, int pipeline_stage);
 
 
   Pipeline *_pipeline;
   Pipeline *_pipeline;
   Windows _windows;
   Windows _windows;

+ 1 - 1
panda/src/display/graphicsOutput.cxx

@@ -814,7 +814,7 @@ static ShowBuffersCubeMapRegions cube_map_regions[6] = {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 GraphicsOutput *GraphicsOutput::
 GraphicsOutput *GraphicsOutput::
 make_cube_map(const string &name, int size, NodePath &camera_rig,
 make_cube_map(const string &name, int size, NodePath &camera_rig,
-        DrawMask camera_mask, bool to_ram) {
+              DrawMask camera_mask, bool to_ram) {
   if (!to_ram) {
   if (!to_ram) {
     // Check the limits imposed by the GSG.  (However, if we're
     // Check the limits imposed by the GSG.  (However, if we're
     // rendering the texture to RAM only, these limits may be
     // rendering the texture to RAM only, these limits may be

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

@@ -872,7 +872,6 @@ begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
                       const GeomVertexData *data) {
                       const GeomVertexData *data) {
   _munger = munger;
   _munger = munger;
   _vertex_data = data;
   _vertex_data = data;
-
   nassertr(geom->check_valid(data), false);
   nassertr(geom->check_valid(data), false);
   return _vertex_data->has_vertex();
   return _vertex_data->has_vertex();
 }
 }

+ 31 - 0
panda/src/display/graphicsThreadingModel.I

@@ -25,7 +25,9 @@
 INLINE GraphicsThreadingModel::
 INLINE GraphicsThreadingModel::
 GraphicsThreadingModel(const GraphicsThreadingModel &copy) :
 GraphicsThreadingModel(const GraphicsThreadingModel &copy) :
   _cull_name(copy._cull_name),
   _cull_name(copy._cull_name),
+  _cull_stage(copy._cull_stage),
   _draw_name(copy._draw_name),
   _draw_name(copy._draw_name),
+  _draw_stage(copy._draw_stage),
   _cull_sorting(copy._cull_sorting)
   _cull_sorting(copy._cull_sorting)
 {
 {
 }
 }
@@ -38,7 +40,9 @@ GraphicsThreadingModel(const GraphicsThreadingModel &copy) :
 INLINE void GraphicsThreadingModel::
 INLINE void GraphicsThreadingModel::
 operator = (const GraphicsThreadingModel &copy) {
 operator = (const GraphicsThreadingModel &copy) {
   _cull_name = copy._cull_name;
   _cull_name = copy._cull_name;
+  _cull_stage = copy._cull_stage;
   _draw_name = copy._draw_name;
   _draw_name = copy._draw_name;
+  _draw_stage = copy._draw_stage;
   _cull_sorting = copy._cull_sorting;
   _cull_sorting = copy._cull_sorting;
 }
 }
 
 
@@ -53,6 +57,19 @@ get_cull_name() const {
   return _cull_name;
   return _cull_name;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsThreadingModel::get_cull_stage
+//       Access: Published
+//  Description: Returns the pipeline stage from which the cull thread
+//               should access data.  This will be 0 if the cull is
+//               run in the same thread as app, or 1 if it is its own
+//               thread.
+////////////////////////////////////////////////////////////////////
+INLINE int GraphicsThreadingModel::
+get_cull_stage() const {
+  return _cull_stage;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsThreadingModel::get_draw_name
 //     Function: GraphicsThreadingModel::get_draw_name
 //       Access: Published
 //       Access: Published
@@ -65,6 +82,20 @@ get_draw_name() const {
   return _draw_name;
   return _draw_name;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsThreadingModel::get_draw_stage
+//       Access: Published
+//  Description: Returns the pipeline stage from which the draw thread
+//               should access data.  This will be the same value as
+//               get_cull_stage() if cull and draw are run in the same
+//               thread, or one more than that value if draw should be
+//               in its own thread.
+////////////////////////////////////////////////////////////////////
+INLINE int GraphicsThreadingModel::
+get_draw_stage() const {
+  return _draw_stage;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsThreadingModel::get_cull_sorting
 //     Function: GraphicsThreadingModel::get_cull_sorting
 //       Access: Published
 //       Access: Published

+ 11 - 0
panda/src/display/graphicsThreadingModel.cxx

@@ -64,9 +64,20 @@ GraphicsThreadingModel(const string &model) {
     _cull_name = model.substr(start, slash - start);
     _cull_name = model.substr(start, slash - start);
     _draw_name = model.substr(slash + 1);
     _draw_name = model.substr(slash + 1);
   }
   }
+  if (_cull_name.empty()) {
+    _cull_stage = 0;
+  } else {
+    _cull_stage = 1;
+  }
   if (!_cull_sorting || _draw_name.empty()) {
   if (!_cull_sorting || _draw_name.empty()) {
     _draw_name = _cull_name;
     _draw_name = _cull_name;
   }
   }
+
+  if (_draw_name == _cull_name) {
+    _draw_stage = _cull_stage;
+  } else {
+    _draw_stage = _cull_stage + 1;
+  }
 }
 }
   
   
 
 

+ 4 - 0
panda/src/display/graphicsThreadingModel.h

@@ -34,7 +34,9 @@ PUBLISHED:
   
   
   string get_model() const;
   string get_model() const;
   INLINE const string &get_cull_name() const;
   INLINE const string &get_cull_name() const;
+  INLINE int get_cull_stage() const;
   INLINE const string &get_draw_name() const;
   INLINE const string &get_draw_name() const;
+  INLINE int get_draw_stage() const;
   INLINE bool get_cull_sorting() const;
   INLINE bool get_cull_sorting() const;
  
  
   INLINE bool is_single_threaded() const;
   INLINE bool is_single_threaded() const;
@@ -43,7 +45,9 @@ PUBLISHED:
 
 
 private:
 private:
   string _cull_name;
   string _cull_name;
+  int _cull_stage;
   string _draw_name;
   string _draw_name;
+  int _draw_stage;
   bool _cull_sorting;
   bool _cull_sorting;
 };
 };
 
 

+ 5 - 11
panda/src/egg2pg/eggLoader.cxx

@@ -81,7 +81,6 @@
 #include "collisionPolygon.h"
 #include "collisionPolygon.h"
 #include "parametricCurve.h"
 #include "parametricCurve.h"
 #include "nurbsCurve.h"
 #include "nurbsCurve.h"
-#include "classicNurbsCurve.h"
 #include "nurbsCurveInterface.h"
 #include "nurbsCurveInterface.h"
 #include "nurbsCurveEvaluator.h"
 #include "nurbsCurveEvaluator.h"
 #include "nurbsSurfaceEvaluator.h"
 #include "nurbsSurfaceEvaluator.h"
@@ -665,10 +664,10 @@ make_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: EggLoader::make_old_nurbs_curve
 //     Function: EggLoader::make_old_nurbs_curve
 //       Access: Private
 //       Access: Private
-//  Description: This deprecated interface creates a NurbsCurve (or a
-//               ClassicNurbsCurve) object for the EggNurbsCurve
-//               entry.  It will eventually be removed in favor of the
-//               above, which creates a RopeNode.
+//  Description: This deprecated interface creates a NurbsCurve object
+//               for the EggNurbsCurve entry.  It will eventually be
+//               removed in favor of the above, which creates a
+//               RopeNode.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void EggLoader::
 void EggLoader::
 make_old_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent,
 make_old_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent,
@@ -677,12 +676,7 @@ make_old_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent,
   assert(!parent->is_geom_node());
   assert(!parent->is_geom_node());
 
 
   PT(ParametricCurve) curve;
   PT(ParametricCurve) curve;
-
-  if (egg_load_classic_nurbs_curves) {
-    curve = new ClassicNurbsCurve;
-  } else {
-    curve = new NurbsCurve;
-  }
+  curve = new NurbsCurve;
 
 
   NurbsCurveInterface *nurbs = curve->get_nurbs_interface();
   NurbsCurveInterface *nurbs = curve->get_nurbs_interface();
   nassertv(nurbs != (NurbsCurveInterface *)NULL);
   nassertv(nurbs != (NurbsCurveInterface *)NULL);

+ 34 - 10
panda/src/express/Sources.pp

@@ -17,8 +17,11 @@
     bigEndian.h buffer.I buffer.h \
     bigEndian.h buffer.I buffer.h \
     checksumHashGenerator.I checksumHashGenerator.h circBuffer.I \
     checksumHashGenerator.I checksumHashGenerator.h circBuffer.I \
     circBuffer.h clockObject.I clockObject.h \
     circBuffer.h clockObject.I clockObject.h \
-    conditionVarDummyImpl.h conditionVarDummyImpl.I conditionVar.h \
-    conditionVar.I conditionVarImpl.h \
+    conditionVar.h conditionVar.I \
+    conditionVarDebug.h conditionVarDebug.I \
+    conditionVarDirect.h conditionVarDirect.I \
+    conditionVarDummyImpl.h conditionVarDummyImpl.I \
+    conditionVarImpl.h \
     conditionVarNsprImpl.h conditionVarNsprImpl.I \
     conditionVarNsprImpl.h conditionVarNsprImpl.I \
     conditionVarPosixImpl.h conditionVarPosixImpl.I \
     conditionVarPosixImpl.h conditionVarPosixImpl.I \
     conditionVarWin32Impl.h conditionVarWin32Impl.I \
     conditionVarWin32Impl.h conditionVarWin32Impl.I \
@@ -40,8 +43,11 @@
     memoryUsagePointerCounts.I memoryUsagePointerCounts.h \
     memoryUsagePointerCounts.I memoryUsagePointerCounts.h \
     memoryUsagePointers.I memoryUsagePointers.h \
     memoryUsagePointers.I memoryUsagePointers.h \
     multifile.I multifile.h \
     multifile.I multifile.h \
-    mutexDummyImpl.h mutexDummyImpl.I pmutex.h mutexHolder.h \
-    mutexHolder.I pmutex.I mutexImpl.h \
+    mutexDebug.h mutexDebug.I \
+    mutexDirect.h mutexDirect.I \
+    mutexDummyImpl.h mutexDummyImpl.I \
+    mutexHolder.h mutexHolder.I \
+    mutexImpl.h \
     mutexNsprImpl.h mutexNsprImpl.I \
     mutexNsprImpl.h mutexNsprImpl.I \
     mutexPosixImpl.h mutexPosixImpl.I \
     mutexPosixImpl.h mutexPosixImpl.I \
     mutexWin32Impl.h mutexWin32Impl.I \
     mutexWin32Impl.h mutexWin32Impl.I \
@@ -51,6 +57,7 @@
     ordered_vector.h ordered_vector.I ordered_vector.T \
     ordered_vector.h ordered_vector.I ordered_vector.T \
     password_hash.h \
     password_hash.h \
     patchfile.I patchfile.h \
     patchfile.I patchfile.h \
+    pmutex.h pmutex.I \
     pointerTo.I pointerTo.h \
     pointerTo.I pointerTo.h \
     pointerToArray.I pointerToArray.h \
     pointerToArray.I pointerToArray.h \
     pointerToBase.I pointerToBase.h \
     pointerToBase.I pointerToBase.h \
@@ -60,6 +67,7 @@
     ramfile.I ramfile.h \
     ramfile.I ramfile.h \
     referenceCount.I referenceCount.h \
     referenceCount.I referenceCount.h \
     reMutex.I reMutex.h \
     reMutex.I reMutex.h \
+    reMutexDirect.h reMutexDirect.I \
     reMutexHolder.I reMutexHolder.h \
     reMutexHolder.I reMutexHolder.h \
     reversedNumericData.I reversedNumericData.h \
     reversedNumericData.I reversedNumericData.h \
     selectThreadImpl.h \
     selectThreadImpl.h \
@@ -96,7 +104,10 @@
     atomicAdjustPosixImpl.cxx \
     atomicAdjustPosixImpl.cxx \
     atomicAdjustWin32Impl.cxx \
     atomicAdjustWin32Impl.cxx \
     buffer.cxx checksumHashGenerator.cxx clockObject.cxx \
     buffer.cxx checksumHashGenerator.cxx clockObject.cxx \
-    conditionVar.cxx conditionVarDummyImpl.cxx \
+    conditionVar.cxx \
+    conditionVarDebug.cxx \
+    conditionVarDirect.cxx \
+    conditionVarDummyImpl.cxx \
     conditionVarNsprImpl.cxx \
     conditionVarNsprImpl.cxx \
     conditionVarPosixImpl.cxx \
     conditionVarPosixImpl.cxx \
     conditionVarWin32Impl.cxx \
     conditionVarWin32Impl.cxx \
@@ -110,10 +121,14 @@
     mainThread.cxx \
     mainThread.cxx \
     memoryInfo.cxx memoryUsage.cxx memoryUsagePointerCounts.cxx \
     memoryInfo.cxx memoryUsage.cxx memoryUsagePointerCounts.cxx \
     memoryUsagePointers.cxx multifile.cxx \
     memoryUsagePointers.cxx multifile.cxx \
-    pmutex.cxx mutexHolder.cxx mutexDummyImpl.cxx \
+    mutexDebug.cxx \
+    mutexDirect.cxx \
+    mutexHolder.cxx \
+    mutexDummyImpl.cxx \
     mutexNsprImpl.cxx \
     mutexNsprImpl.cxx \
     mutexPosixImpl.cxx \
     mutexPosixImpl.cxx \
     mutexWin32Impl.cxx \
     mutexWin32Impl.cxx \
+    pmutex.cxx \
     namable.cxx \
     namable.cxx \
     nativeNumericData.cxx \
     nativeNumericData.cxx \
     ordered_vector.cxx \
     ordered_vector.cxx \
@@ -128,6 +143,7 @@
     ramfile.cxx \
     ramfile.cxx \
     referenceCount.cxx \
     referenceCount.cxx \
     reMutex.cxx \
     reMutex.cxx \
+    reMutexDirect.cxx \
     reMutexHolder.cxx \
     reMutexHolder.cxx \
     reversedNumericData.cxx \
     reversedNumericData.cxx \
     streamReader.cxx streamWriter.cxx \
     streamReader.cxx streamWriter.cxx \
@@ -162,8 +178,11 @@
     bigEndian.h buffer.I buffer.h checksumHashGenerator.I  \
     bigEndian.h buffer.I buffer.h checksumHashGenerator.I  \
     checksumHashGenerator.h circBuffer.I circBuffer.h clockObject.I \
     checksumHashGenerator.h circBuffer.I circBuffer.h clockObject.I \
     clockObject.h \
     clockObject.h \
-    conditionVarDummyImpl.h conditionVarDummyImpl.I conditionVar.h \
-    conditionVar.I conditionVarImpl.h \
+    conditionVar.h conditionVar.I \
+    conditionVarDebug.h conditionVarDebug.I \
+    conditionVarDirect.h conditionVarDirect.I \
+    conditionVarDummyImpl.h conditionVarDummyImpl.I \
+    conditionVarImpl.h \
     conditionVarNsprImpl.h conditionVarNsprImpl.I \
     conditionVarNsprImpl.h conditionVarNsprImpl.I \
     conditionVarPosixImpl.h conditionVarPosixImpl.I \
     conditionVarPosixImpl.h conditionVarPosixImpl.I \
     conditionVarWin32Impl.h conditionVarWin32Impl.I \
     conditionVarWin32Impl.h conditionVarWin32Impl.I \
@@ -183,8 +202,11 @@
     memoryUsage.h memoryUsagePointerCounts.I \
     memoryUsage.h memoryUsagePointerCounts.I \
     memoryUsagePointerCounts.h memoryUsagePointers.I \
     memoryUsagePointerCounts.h memoryUsagePointers.I \
     memoryUsagePointers.h multifile.I multifile.h \
     memoryUsagePointers.h multifile.I multifile.h \
-    mutexDummyImpl.h mutexDummyImpl.I pmutex.h mutexHolder.h \
-    mutexHolder.I pmutex.I mutexImpl.h \
+    mutexDebug.h mutexDebug.I \
+    mutexDirect.h mutexDirect.I \
+    mutexDummyImpl.h mutexDummyImpl.I \
+    mutexHolder.h mutexHolder.I \
+    mutexImpl.h \
     mutexNsprImpl.h mutexNsprImpl.I \
     mutexNsprImpl.h mutexNsprImpl.I \
     mutexPosixImpl.h mutexPosixImpl.I \
     mutexPosixImpl.h mutexPosixImpl.I \
     mutexWin32Impl.h mutexWin32Impl.I \
     mutexWin32Impl.h mutexWin32Impl.I \
@@ -193,6 +215,7 @@
     ordered_vector.h ordered_vector.I ordered_vector.T \
     ordered_vector.h ordered_vector.I ordered_vector.T \
     password_hash.h \
     password_hash.h \
     patchfile.I patchfile.h \
     patchfile.I patchfile.h \
+    pmutex.h pmutex.I \
     pointerTo.I pointerTo.h \
     pointerTo.I pointerTo.h \
     pointerToArray.I pointerToArray.h \
     pointerToArray.I pointerToArray.h \
     pointerToBase.I pointerToBase.h \
     pointerToBase.I pointerToBase.h \
@@ -202,6 +225,7 @@
     ramfile.I ramfile.h \
     ramfile.I ramfile.h \
     referenceCount.I referenceCount.h \
     referenceCount.I referenceCount.h \
     reMutex.I reMutex.h \
     reMutex.I reMutex.h \
+    reMutexDirect.h reMutexDirect.I \
     reMutexHolder.I reMutexHolder.h \
     reMutexHolder.I reMutexHolder.h \
     reversedNumericData.I reversedNumericData.h \
     reversedNumericData.I reversedNumericData.h \
     selectThreadImpl.h \
     selectThreadImpl.h \

+ 14 - 0
panda/src/express/atomicAdjust.I

@@ -49,3 +49,17 @@ INLINE PN_int32 AtomicAdjust::
 set(PN_int32 &var, PN_int32 new_value) {
 set(PN_int32 &var, PN_int32 new_value) {
   return AtomicAdjustImpl::set(var, new_value);
   return AtomicAdjustImpl::set(var, new_value);
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: AtomicAdjust::get
+//       Access: Public, Static
+//  Description: Atomically retrieves the snapshot value of the
+//               indicated variable.  This is the only guaranteed safe
+//               way to retrieve the value that other threads might be
+//               asynchronously setting, incrementing, or decrementing
+//               (via other AtomicAjust methods).
+////////////////////////////////////////////////////////////////////
+INLINE PN_int32 AtomicAdjust::
+get(const PN_int32 &var) {
+  return AtomicAdjustImpl::get(var);
+}

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

@@ -35,6 +35,7 @@ public:
   INLINE static PN_int32 inc(PN_int32 &var);
   INLINE static PN_int32 inc(PN_int32 &var);
   INLINE static PN_int32 dec(PN_int32 &var);
   INLINE static PN_int32 dec(PN_int32 &var);
   INLINE static PN_int32 set(PN_int32 &var, PN_int32 new_value);
   INLINE static PN_int32 set(PN_int32 &var, PN_int32 new_value);
+  INLINE static PN_int32 get(const PN_int32 &var);
 };
 };
 
 
 #include "atomicAdjust.I"
 #include "atomicAdjust.I"

+ 14 - 0
panda/src/express/atomicAdjustDummyImpl.I

@@ -51,3 +51,17 @@ set(PN_int32 &var, PN_int32 new_value) {
   var = new_value;
   var = new_value;
   return orig_value;
   return orig_value;
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: AtomicAdjustDummyImpl::get
+//       Access: Public, Static
+//  Description: Atomically retrieves the snapshot value of the
+//               indicated variable.  This is the only guaranteed safe
+//               way to retrieve the value that other threads might be
+//               asynchronously setting, incrementing, or decrementing
+//               (via other AtomicAjust methods).
+////////////////////////////////////////////////////////////////////
+INLINE PN_int32 AtomicAdjustDummyImpl::
+get(const PN_int32 &var) {
+  return var;
+}

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

@@ -38,6 +38,7 @@ public:
   INLINE static PN_int32 inc(PN_int32 &var);
   INLINE static PN_int32 inc(PN_int32 &var);
   INLINE static PN_int32 dec(PN_int32 &var);
   INLINE static PN_int32 dec(PN_int32 &var);
   INLINE static PN_int32 set(PN_int32 &var, PN_int32 new_value);
   INLINE static PN_int32 set(PN_int32 &var, PN_int32 new_value);
+  INLINE static PN_int32 get(const PN_int32 &var);
 };
 };
 
 
 #include "atomicAdjustDummyImpl.I"
 #include "atomicAdjustDummyImpl.I"

+ 14 - 0
panda/src/express/atomicAdjustNsprImpl.I

@@ -49,3 +49,17 @@ INLINE PN_int32 AtomicAdjustNsprImpl::
 set(PN_int32 &var, PN_int32 new_value) {
 set(PN_int32 &var, PN_int32 new_value) {
   return PR_AtomicSet(&var, new_value);
   return PR_AtomicSet(&var, new_value);
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: AtomicAdjustNsprImpl::get
+//       Access: Public, Static
+//  Description: Atomically retrieves the snapshot value of the
+//               indicated variable.  This is the only guaranteed safe
+//               way to retrieve the value that other threads might be
+//               asynchronously setting, incrementing, or decrementing
+//               (via other AtomicAjust methods).
+////////////////////////////////////////////////////////////////////
+INLINE PN_int32 AtomicAdjustNsprImpl::
+get(const PN_int32 &var) {
+  return var;
+}

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

@@ -38,6 +38,7 @@ public:
   INLINE static PN_int32 inc(PN_int32 &var);
   INLINE static PN_int32 inc(PN_int32 &var);
   INLINE static PN_int32 dec(PN_int32 &var);
   INLINE static PN_int32 dec(PN_int32 &var);
   INLINE static PN_int32 set(PN_int32 &var, PN_int32 new_value);
   INLINE static PN_int32 set(PN_int32 &var, PN_int32 new_value);
+  INLINE static PN_int32 get(const PN_int32 &var);
 };
 };
 
 
 #include "atomicAdjustNsprImpl.I"
 #include "atomicAdjustNsprImpl.I"

+ 17 - 0
panda/src/express/atomicAdjustPosixImpl.I

@@ -59,3 +59,20 @@ set(PN_int32 &var, PN_int32 new_value) {
   pthread_mutex_unlock(&_mutex);
   pthread_mutex_unlock(&_mutex);
   return orig_value;
   return orig_value;
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: AtomicAdjustPosixImpl::get
+//       Access: Public, Static
+//  Description: Atomically retrieves the snapshot value of the
+//               indicated variable.  This is the only guaranteed safe
+//               way to retrieve the value that other threads might be
+//               asynchronously setting, incrementing, or decrementing
+//               (via other AtomicAjust methods).
+////////////////////////////////////////////////////////////////////
+INLINE PN_int32 AtomicAdjustPosixImpl::
+get(const PN_int32 &var) {
+  pthread_mutex_lock(&_mutex);
+  PN_int32 orig_value = var;
+  pthread_mutex_unlock(&_mutex);
+  return orig_value;
+}

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

@@ -38,6 +38,7 @@ public:
   INLINE static PN_int32 inc(PN_int32 &var);
   INLINE static PN_int32 inc(PN_int32 &var);
   INLINE static PN_int32 dec(PN_int32 &var);
   INLINE static PN_int32 dec(PN_int32 &var);
   INLINE static PN_int32 set(PN_int32 &var, PN_int32 new_value);
   INLINE static PN_int32 set(PN_int32 &var, PN_int32 new_value);
+  INLINE static PN_int32 get(const PN_int32 &var);
 
 
 private:
 private:
   static pthread_mutex_t _mutex;
   static pthread_mutex_t _mutex;

+ 14 - 0
panda/src/express/atomicAdjustWin32Impl.I

@@ -49,3 +49,17 @@ INLINE PN_int32 AtomicAdjustWin32Impl::
 set(PN_int32 &var, PN_int32 new_value) {
 set(PN_int32 &var, PN_int32 new_value) {
   return InterlockedExchange((LONG *)&var, new_value);
   return InterlockedExchange((LONG *)&var, new_value);
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: AtomicAdjustWin32Impl::get
+//       Access: Public, Static
+//  Description: Atomically retrieves the snapshot value of the
+//               indicated variable.  This is the only guaranteed safe
+//               way to retrieve the value that other threads might be
+//               asynchronously setting, incrementing, or decrementing
+//               (via other AtomicAjust methods).
+////////////////////////////////////////////////////////////////////
+INLINE PN_int32 AtomicAdjustWin32Impl::
+get(const PN_int32 &var) {
+  return var;
+}

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

@@ -39,6 +39,7 @@ public:
   INLINE static PN_int32 inc(PN_int32 &var);
   INLINE static PN_int32 inc(PN_int32 &var);
   INLINE static PN_int32 dec(PN_int32 &var);
   INLINE static PN_int32 dec(PN_int32 &var);
   INLINE static PN_int32 set(PN_int32 &var, PN_int32 new_value);
   INLINE static PN_int32 set(PN_int32 &var, PN_int32 new_value);
+  INLINE static PN_int32 get(const PN_int32 &var);
 };
 };
 
 
 #include "atomicAdjustWin32Impl.I"
 #include "atomicAdjustWin32Impl.I"

+ 16 - 67
panda/src/express/conditionVar.I

@@ -29,8 +29,11 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE ConditionVar::
 INLINE ConditionVar::
 ConditionVar(Mutex &mutex) :
 ConditionVar(Mutex &mutex) :
-  _mutex(mutex),
-  _impl(mutex._impl)
+#ifdef DEBUG_THREADS
+  ConditionVarDebug(mutex)
+#else 
+  ConditionVarDirect(mutex)
+#endif  // DEBUG_THREADS
 {
 {
 }
 }
 
 
@@ -50,8 +53,11 @@ INLINE ConditionVar::
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE ConditionVar::
 INLINE ConditionVar::
 ConditionVar(const ConditionVar &copy) : 
 ConditionVar(const ConditionVar &copy) : 
-  _mutex(copy._mutex), 
-  _impl(_mutex._impl)
+#ifdef DEBUG_THREADS
+  ConditionVarDebug(copy.get_mutex())
+#else 
+  ConditionVarDirect(copy.get_mutex())
+#endif  // DEBUG_THREADS
 {
 {
   nassertv(false);
   nassertv(false);
 }
 }
@@ -73,67 +79,10 @@ operator = (const ConditionVar &copy) {
 //               variable.
 //               variable.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE Mutex &ConditionVar::
 INLINE Mutex &ConditionVar::
-get_mutex() {
-  return _mutex;
-}
-
-#ifndef CHECK_REENTRANT_MUTEX
-////////////////////////////////////////////////////////////////////
-//     Function: ConditionVar::wait
-//       Access: Public
-//  Description: Waits on the condition.  The caller must already be
-//               holding the lock associated with the condition
-//               variable before calling this function.
-//
-//               wait() will release the lock, then go to sleep until
-//               some other thread calls signal() on this condition
-//               variable.  At that time at least one thread waiting
-//               on the same ConditionVar will grab the lock again,
-//               and then return from wait().
-//
-//               It is possible that wait() will return even if no one
-//               has called signal().  It is the responsibility of the
-//               calling process to verify the condition on return
-//               from wait, and possibly loop back to wait again if
-//               necessary.
-//
-//               Note the semantics of a condition variable: the mutex
-//               must be held before wait() is called, and it will
-//               still be held when wait() returns.  However, it will
-//               be temporarily released during the wait() call
-//               itself.
-//
-//               This is defined as an inline method only when
-//               CHECK_REENTRANT_MUTEX is not defined.  If
-//               CHECK_REENTRANT_MUTEX is defined, it is an
-//               out-of-line method, just to avoid circular
-//               dependencies on #include files.
-////////////////////////////////////////////////////////////////////
-INLINE void ConditionVar::
-wait() {
-  nassertv(_mutex.debug_is_locked());
-  _impl.wait();
-}
-#endif  // CHECK_REENTRANT_MUTEX
-
-////////////////////////////////////////////////////////////////////
-//     Function: ConditionVar::signal
-//       Access: Public
-//  Description: Informs one of the other threads who are currently
-//               blocked on wait() that the relevant condition has
-//               changed.  If multiple threads are currently waiting,
-//               at least one of them will be woken up, although there
-//               is no way to predict which one.
-//
-//               The caller must be holding the mutex associated with
-//               the condition variable before making this call, which
-//               will not release the mutex.
-//
-//               If no threads are waiting, this is a no-op: the
-//               signal is lost.
-////////////////////////////////////////////////////////////////////
-INLINE void ConditionVar::
-signal() {
-  nassertv(_mutex.debug_is_locked());
-  _impl.signal();
+get_mutex() const {
+#ifdef DEBUG_THREADS
+  return (Mutex &)ConditionVarDebug::get_mutex();
+#else 
+  return (Mutex &)ConditionVarDirect::get_mutex();
+#endif  // DEBUG_THREADS
 }
 }

+ 0 - 42
panda/src/express/conditionVar.cxx

@@ -17,45 +17,3 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "conditionVar.h"
 #include "conditionVar.h"
-#include "thread.h"
-
-#ifdef CHECK_REENTRANT_MUTEX
-////////////////////////////////////////////////////////////////////
-//     Function: ConditionVar::wait
-//       Access: Public
-//  Description: Waits on the condition.  The caller must already be
-//               holding the lock associated with the condition
-//               variable before calling this function.
-//
-//               wait() will release the lock, then go to sleep until
-//               some other thread calls signal() on this condition
-//               variable.  At that time at least one thread waiting
-//               on the same ConditionVar will grab the lock again,
-//               and then return from wait().
-//
-//               It is possible that wait() will return even if no one
-//               has called signal().  It is the responsibility of the
-//               calling process to verify the condition on return
-//               from wait, and possibly loop back to wait again if
-//               necessary.
-//
-//               Note the semantics of a condition variable: the mutex
-//               must be held before wait() is called, and it will
-//               still be held when wait() returns.  However, it will
-//               be temporarily released during the wait() call
-//               itself.
-//
-//               This is defined as an inline method only when
-//               CHECK_REENTRANT_MUTEX is not defined.  If
-//               CHECK_REENTRANT_MUTEX is defined, it is an
-//               out-of-line method, just to avoid circular
-//               dependencies on #include files.
-////////////////////////////////////////////////////////////////////
-void ConditionVar::
-wait() {
-  nassertv(_mutex.debug_is_locked());
-  _impl.wait();
-
-  _mutex._locking_thread = Thread::get_current_thread();
-}
-#endif  // CHECK_REENTRANT_MUTEX

+ 13 - 16
panda/src/express/conditionVar.h

@@ -20,9 +20,8 @@
 #define CONDITIONVAR_H
 #define CONDITIONVAR_H
 
 
 #include "pandabase.h"
 #include "pandabase.h"
-#include "pmutex.h"
-#include "conditionVarImpl.h"
-#include "notify.h"
+#include "conditionVarDebug.h"
+#include "conditionVarDirect.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : ConditionVar
 //       Class : ConditionVar
@@ -35,8 +34,17 @@
 //               A condition variable is associated with a single
 //               A condition variable is associated with a single
 //               mutex, and several condition variables may share the
 //               mutex, and several condition variables may share the
 //               same mutex.
 //               same mutex.
+//
+//               This class inherits its implementation either from
+//               ConditionVarDebug or ConditionVarDirect, depending on
+//               the definition of DEBUG_THREADS.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDAEXPRESS ConditionVar {
+#ifdef DEBUG_THREADS
+class EXPCL_PANDAEXPRESS ConditionVar : public ConditionVarDebug
+#else
+class EXPCL_PANDAEXPRESS ConditionVar : public ConditionVarDirect
+#endif  // DEBUG_THREADS
+{
 public:
 public:
   INLINE ConditionVar(Mutex &mutex);
   INLINE ConditionVar(Mutex &mutex);
   INLINE ~ConditionVar();
   INLINE ~ConditionVar();
@@ -45,18 +53,7 @@ private:
   INLINE void operator = (const ConditionVar &copy);
   INLINE void operator = (const ConditionVar &copy);
 
 
 public:
 public:
-  INLINE Mutex &get_mutex();
-
-#ifdef CHECK_REENTRANT_MUTEX
-  void wait();
-#else  // CHECK_REENTRANT_MUTEX
-  INLINE void wait();
-#endif  // CHECK_REENTRANT_MUTEX
-  INLINE void signal();
-
-private:
-  Mutex &_mutex;
-  ConditionVarImpl _impl;
+  INLINE Mutex &get_mutex() const;
 };
 };
 
 
 #include "conditionVar.I"
 #include "conditionVar.I"

+ 52 - 0
panda/src/express/conditionVarDebug.I

@@ -0,0 +1,52 @@
+// Filename: conditionVarDebug.I
+// Created by:  drose (13Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarDebug::Copy Constructor
+//       Access: Private
+//  Description: Do not attempt to copy condition variables.
+////////////////////////////////////////////////////////////////////
+INLINE ConditionVarDebug::
+ConditionVarDebug(const ConditionVarDebug &copy) : 
+  _mutex(copy._mutex), 
+  _impl(_mutex._global_mutex)
+{
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarDebug::Copy Assignment Operator
+//       Access: Private
+//  Description: Do not attempt to copy condition variables.
+////////////////////////////////////////////////////////////////////
+INLINE void ConditionVarDebug::
+operator = (const ConditionVarDebug &copy) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarDebug::get_mutex
+//       Access: Public
+//  Description: Returns the mutex associated with this condition
+//               variable.
+////////////////////////////////////////////////////////////////////
+INLINE MutexDebug &ConditionVarDebug::
+get_mutex() const {
+  return _mutex;
+}

+ 139 - 0
panda/src/express/conditionVarDebug.cxx

@@ -0,0 +1,139 @@
+// Filename: conditionVarDebug.cxx
+// Created by:  drose (13Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "conditionVarDebug.h"
+#include "thread.h"
+
+#ifdef DEBUG_THREADS
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarDebug::Constructor
+//       Access: Public
+//  Description: You must pass in a Mutex to the condition variable
+//               constructor.  This mutex may be shared by other
+//               condition variables, if desired.  It is the caller's
+//               responsibility to ensure the Mutex object does not
+//               destruct during the lifetime of the condition
+//               variable.
+////////////////////////////////////////////////////////////////////
+ConditionVarDebug::
+ConditionVarDebug(MutexDebug &mutex) :
+  _mutex(mutex),
+  _impl(mutex._global_mutex)
+{
+  nassertv(!_mutex._allow_recursion);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarDebug::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+ConditionVarDebug::
+~ConditionVarDebug() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarDebug::wait
+//       Access: Public
+//  Description: Waits on the condition.  The caller must already be
+//               holding the lock associated with the condition
+//               variable before calling this function.
+//
+//               wait() will release the lock, then go to sleep until
+//               some other thread calls signal() on this condition
+//               variable.  At that time at least one thread waiting
+//               on the same ConditionVarDebug will grab the lock again,
+//               and then return from wait().
+//
+//               It is possible that wait() will return even if no one
+//               has called signal().  It is the responsibility of the
+//               calling process to verify the condition on return
+//               from wait, and possibly loop back to wait again if
+//               necessary.
+//
+//               Note the semantics of a condition variable: the mutex
+//               must be held before wait() is called, and it will
+//               still be held when wait() returns.  However, it will
+//               be temporarily released during the wait() call
+//               itself.
+////////////////////////////////////////////////////////////////////
+void ConditionVarDebug::
+wait() {
+  _mutex._global_mutex.lock();
+
+  if (!_mutex.do_debug_is_locked()) {
+    ostringstream ostr;
+    ostr << *Thread::get_current_thread() << " attempted to wait on "
+         << *this << " without holding " << _mutex;
+    nassert_raise(ostr.str());
+    _mutex._global_mutex.release();
+    return;
+  }
+  
+  _mutex.do_release();
+  _impl.wait();
+  _mutex.do_lock();
+
+  _mutex._global_mutex.release();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarDebug::signal
+//       Access: Public
+//  Description: Informs one of the other threads who are currently
+//               blocked on wait() that the relevant condition has
+//               changed.  If multiple threads are currently waiting,
+//               at least one of them will be woken up, although there
+//               is no way to predict which one.
+//
+//               The caller must be holding the mutex associated with
+//               the condition variable before making this call, which
+//               will not release the mutex.
+//
+//               If no threads are waiting, this is a no-op: the
+//               signal is lost.
+////////////////////////////////////////////////////////////////////
+void ConditionVarDebug::
+signal() {
+  _mutex._global_mutex.lock();
+  if (!_mutex.do_debug_is_locked()) {
+    ostringstream ostr;
+    ostr << *Thread::get_current_thread() << " attempted to signal "
+         << *this << " without holding " << _mutex;
+    nassert_raise(ostr.str());
+    _mutex._global_mutex.release();
+    return;
+  }
+
+  _impl.signal();
+  _mutex._global_mutex.release();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarDebug::output
+//       Access: Public, Virtual
+//  Description: This method is declared virtual in ConditionVarDebug,
+//               but non-virtual in ConditionVarDirect.
+////////////////////////////////////////////////////////////////////
+void ConditionVarDebug::
+output(ostream &out) const {
+  out << "ConditionVar " << (void *)this;
+}
+
+#endif  // DEBUG_THREADS

+ 70 - 0
panda/src/express/conditionVarDebug.h

@@ -0,0 +1,70 @@
+// Filename: conditionVarDebug.h
+// Created by:  drose (13Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONDITIONVARDEBUG_H
+#define CONDITIONVARDEBUG_H
+
+#include "pandabase.h"
+#include "pmutex.h"
+#include "conditionVarImpl.h"
+
+#ifdef DEBUG_THREADS
+
+////////////////////////////////////////////////////////////////////
+//       Class : ConditionVarDebug
+// Description : A condition variable, usually used to communicate
+//               information about changing state to a thread that is
+//               waiting for something to happen.  A condition
+//               variable can be used to "wake up" a thread when some
+//               arbitrary condition has changed.
+//
+//               A condition variable is associated with a single
+//               mutex, and several condition variables may share the
+//               same mutex.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS ConditionVarDebug {
+public:
+  ConditionVarDebug(MutexDebug &mutex);
+  virtual ~ConditionVarDebug();
+private:
+  INLINE ConditionVarDebug(const ConditionVarDebug &copy);
+  INLINE void operator = (const ConditionVarDebug &copy);
+
+public:
+  INLINE MutexDebug &get_mutex() const;
+
+  void wait();
+  void signal();
+  virtual void output(ostream &out) const;
+
+private:
+  MutexDebug &_mutex;
+  ConditionVarImpl _impl;
+};
+
+INLINE ostream &
+operator << (ostream &out, const ConditionVarDebug &cv) {
+  cv.output(out);
+  return out;
+}
+
+#include "conditionVarDebug.I"
+
+#endif  // DEBUG_THREADS
+
+#endif

+ 129 - 0
panda/src/express/conditionVarDirect.I

@@ -0,0 +1,129 @@
+// Filename: conditionVarDirect.I
+// Created by:  drose (13Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarDirect::Constructor
+//       Access: Public
+//  Description: You must pass in a Mutex to the condition variable
+//               constructor.  This mutex may be shared by other
+//               condition variables, if desired.  It is the caller's
+//               responsibility to ensure the Mutex object does not
+//               destruct during the lifetime of the condition
+//               variable.
+////////////////////////////////////////////////////////////////////
+INLINE ConditionVarDirect::
+ConditionVarDirect(MutexDirect &mutex) :
+  _mutex(mutex),
+  _impl(mutex._impl)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarDirect::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE ConditionVarDirect::
+~ConditionVarDirect() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarDirect::Copy Constructor
+//       Access: Private
+//  Description: Do not attempt to copy condition variables.
+////////////////////////////////////////////////////////////////////
+INLINE ConditionVarDirect::
+ConditionVarDirect(const ConditionVarDirect &copy) : 
+  _mutex(copy._mutex), 
+  _impl(_mutex._impl)
+{
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarDirect::Copy Assignment Operator
+//       Access: Private
+//  Description: Do not attempt to copy condition variables.
+////////////////////////////////////////////////////////////////////
+INLINE void ConditionVarDirect::
+operator = (const ConditionVarDirect &copy) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarDirect::get_mutex
+//       Access: Public
+//  Description: Returns the mutex associated with this condition
+//               variable.
+////////////////////////////////////////////////////////////////////
+INLINE MutexDirect &ConditionVarDirect::
+get_mutex() const {
+  return _mutex;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarDirect::wait
+//       Access: Public
+//  Description: Waits on the condition.  The caller must already be
+//               holding the lock associated with the condition
+//               variable before calling this function.
+//
+//               wait() will release the lock, then go to sleep until
+//               some other thread calls signal() on this condition
+//               variable.  At that time at least one thread waiting
+//               on the same ConditionVarDirect will grab the lock again,
+//               and then return from wait().
+//
+//               It is possible that wait() will return even if no one
+//               has called signal().  It is the responsibility of the
+//               calling process to verify the condition on return
+//               from wait, and possibly loop back to wait again if
+//               necessary.
+//
+//               Note the semantics of a condition variable: the mutex
+//               must be held before wait() is called, and it will
+//               still be held when wait() returns.  However, it will
+//               be temporarily released during the wait() call
+//               itself.
+////////////////////////////////////////////////////////////////////
+INLINE void ConditionVarDirect::
+wait() {
+  _impl.wait();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarDirect::signal
+//       Access: Public
+//  Description: Informs one of the other threads who are currently
+//               blocked on wait() that the relevant condition has
+//               changed.  If multiple threads are currently waiting,
+//               at least one of them will be woken up, although there
+//               is no way to predict which one.
+//
+//               The caller must be holding the mutex associated with
+//               the condition variable before making this call, which
+//               will not release the mutex.
+//
+//               If no threads are waiting, this is a no-op: the
+//               signal is lost.
+////////////////////////////////////////////////////////////////////
+INLINE void ConditionVarDirect::
+signal() {
+  _impl.signal();
+}

+ 34 - 0
panda/src/express/conditionVarDirect.cxx

@@ -0,0 +1,34 @@
+// Filename: conditionVarDirect.cxx
+// Created by:  drose (13Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "conditionVarDirect.h"
+
+#ifndef DEBUG_THREADS
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConditionVarDirect::output
+//       Access: Public
+//  Description: This method is declared virtual in ConditionVarDebug,
+//               but non-virtual in ConditionVarDirect.
+////////////////////////////////////////////////////////////////////
+void ConditionVarDirect::
+output(ostream &out) const {
+  out << "ConditionVar " << (void *)this;
+}
+
+#endif  // !DEBUG_THREADS

+ 70 - 0
panda/src/express/conditionVarDirect.h

@@ -0,0 +1,70 @@
+// Filename: conditionVarDirect.h
+// Created by:  drose (13Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONDITIONVARDIRECT_H
+#define CONDITIONVARDIRECT_H
+
+#include "pandabase.h"
+#include "mutexDirect.h"
+#include "conditionVarImpl.h"
+
+#ifndef DEBUG_THREADS
+
+////////////////////////////////////////////////////////////////////
+//       Class : ConditionVarDirect
+// Description : A condition variable, usually used to communicate
+//               information about changing state to a thread that is
+//               waiting for something to happen.  A condition
+//               variable can be used to "wake up" a thread when some
+//               arbitrary condition has changed.
+//
+//               A condition variable is associated with a single
+//               mutex, and several condition variables may share the
+//               same mutex.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS ConditionVarDirect {
+public:
+  INLINE ConditionVarDirect(MutexDirect &mutex);
+  INLINE ~ConditionVarDirect();
+private:
+  INLINE ConditionVarDirect(const ConditionVarDirect &copy);
+  INLINE void operator = (const ConditionVarDirect &copy);
+
+public:
+  INLINE MutexDirect &get_mutex() const;
+
+  INLINE void wait();
+  INLINE void signal();
+  void output(ostream &out) const;
+
+private:
+  MutexDirect &_mutex;
+  ConditionVarImpl _impl;
+};
+
+INLINE ostream &
+operator << (ostream &out, const ConditionVarDirect &cv) {
+  cv.output(out);
+  return out;
+}
+
+#include "conditionVarDirect.I"
+
+#endif  // !DEBUG_THREADS
+
+#endif

+ 6 - 1
panda/src/express/config_express.cxx

@@ -185,10 +185,15 @@ init_libexpress() {
 
 
   init_system_type_handles();
   init_system_type_handles();
 
 
-#ifdef HAVE_ZLIB
   PandaSystem *ps = PandaSystem::get_global_ptr();
   PandaSystem *ps = PandaSystem::get_global_ptr();
+
+#ifdef HAVE_ZLIB
   ps->add_system("zlib");
   ps->add_system("zlib");
 #endif
 #endif
+
+#ifdef HAVE_THREADS
+  ps->add_system("threads");
+#endif
 }
 }
 
 
 
 

+ 4 - 0
panda/src/express/express_composite1.cxx

@@ -7,6 +7,8 @@
 #include "checksumHashGenerator.cxx"
 #include "checksumHashGenerator.cxx"
 #include "clockObject.cxx"
 #include "clockObject.cxx"
 #include "conditionVar.cxx"
 #include "conditionVar.cxx"
+#include "conditionVarDebug.cxx"
+#include "conditionVarDirect.cxx"
 #include "conditionVarDummyImpl.cxx"
 #include "conditionVarDummyImpl.cxx"
 #include "conditionVarNsprImpl.cxx"
 #include "conditionVarNsprImpl.cxx"
 #include "conditionVarPosixImpl.cxx"
 #include "conditionVarPosixImpl.cxx"
@@ -29,6 +31,8 @@
 #include "memoryUsagePointerCounts.cxx"
 #include "memoryUsagePointerCounts.cxx"
 #include "memoryUsagePointers.cxx"
 #include "memoryUsagePointers.cxx"
 #include "multifile.cxx"
 #include "multifile.cxx"
+#include "mutexDebug.cxx"
+#include "mutexDirect.cxx"
 #include "mutexHolder.cxx"
 #include "mutexHolder.cxx"
 #include "mutexDummyImpl.cxx"
 #include "mutexDummyImpl.cxx"
 #include "mutexNsprImpl.cxx"
 #include "mutexNsprImpl.cxx"

+ 109 - 0
panda/src/express/mutexDebug.I

@@ -0,0 +1,109 @@
+// Filename: mutexDebug.I
+// Created by:  drose (13Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDebug::Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE MutexDebug::
+MutexDebug(bool allow_recursion) :
+  _allow_recursion(allow_recursion),
+  _locking_thread(NULL),
+  _lock_count(0),
+  _cvar(_global_mutex)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDebug::Copy Constructor
+//       Access: Private
+//  Description: Do not attempt to copy mutexes.
+////////////////////////////////////////////////////////////////////
+INLINE MutexDebug::
+MutexDebug(const MutexDebug &copy) : _cvar(_global_mutex) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDebug::Copy Assignment Operator
+//       Access: Private
+//  Description: Do not attempt to copy mutexes.
+////////////////////////////////////////////////////////////////////
+INLINE void MutexDebug::
+operator = (const MutexDebug &copy) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDebug::lock
+//       Access: Public
+//  Description: Grabs the mutex if it is available.  If it is not
+//               available, blocks until it becomes available, then
+//               grabs it.  In either case, the function does not
+//               return until the mutex is held; you should then call
+//               unlock().
+//
+//               This method is considered const so that you can lock
+//               and unlock const mutexes, mainly to allow thread-safe
+//               access to otherwise const data.
+//
+//               Also see MutexHolder.
+////////////////////////////////////////////////////////////////////
+INLINE void MutexDebug::
+lock() const {
+  _global_mutex.lock();
+  ((MutexDebug *)this)->do_lock();
+  _global_mutex.release();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDebug::release
+//       Access: Public
+//  Description: Releases the mutex.  It is an error to call this if
+//               the mutex was not already locked.
+//
+//               This method is considered const so that you can lock
+//               and unlock const mutexes, mainly to allow thread-safe
+//               access to otherwise const data.
+////////////////////////////////////////////////////////////////////
+INLINE void MutexDebug::
+release() const {
+  _global_mutex.lock();
+  ((MutexDebug *)this)->do_release();
+  _global_mutex.release();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDebug::debug_is_locked
+//       Access: Public
+//  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
+//               MutexDebug case, it always returns true, since
+//               there's not a reliable way to determine this
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool MutexDebug::
+debug_is_locked() const {
+  _global_mutex.lock();
+  bool is_locked = do_debug_is_locked();
+  _global_mutex.release();
+  return is_locked;
+}

+ 217 - 0
panda/src/express/mutexDebug.cxx

@@ -0,0 +1,217 @@
+// Filename: mutexDebug.cxx
+// Created by:  drose (13Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "mutexDebug.h"
+#include "thread.h"
+
+#ifdef DEBUG_THREADS
+
+MutexImpl MutexDebug::_global_mutex;
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDebug::Destructor
+//       Access: Protected, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+MutexDebug::
+~MutexDebug() {
+  nassertv(_locking_thread == NULL && _lock_count == 0);
+
+  // Put a distinctive, bogus lock count in upon destruction, so we'll
+  // be more likely to notice a floating pointer.
+  _lock_count = -100;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDebug::output
+//       Access: Public, Virtual
+//  Description: This method is declared virtual in MutexDebug, but
+//               non-virtual in MutexDirect.
+////////////////////////////////////////////////////////////////////
+void MutexDebug::
+output(ostream &out) const {
+  if (_allow_recursion) {
+    out << "ReMutex " << (void *)this;
+  } else {
+    out << "Mutex " << (void *)this;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDebug::do_lock
+//       Access: Private
+//  Description: The private implementation of lock() assumes that
+//               _global_mutex is held.
+////////////////////////////////////////////////////////////////////
+void MutexDebug::
+do_lock() {
+  // If this assertion is triggered, you tried to lock a
+  // recently-destructed mutex.
+  nassertv(_lock_count != -100);
+
+  Thread *this_thread = Thread::get_current_thread();
+
+  if (_locking_thread == (Thread *)NULL) {
+    // The mutex is not already locked by anyone.  Lock it.
+    _locking_thread = this_thread;
+    ++_lock_count;
+    nassertv(_lock_count == 1);
+
+  } else if (_locking_thread == this_thread) {
+    // The mutex is already locked by this thread.  Increment the lock
+    // count.
+    nassertv(_lock_count >= 0);
+    if (!_allow_recursion && _lock_count == 0) {
+      ostringstream ostr;
+      ostr << *_locking_thread << " attempted to re-lock non-reentrant "
+           << *this;
+      nassert_raise(ostr.str());
+      return;
+    }
+    ++_lock_count;
+
+  } else {
+    // The mutex is locked by some other thread.  Check for deadlock?
+    MutexDebug *next_mutex = this;
+
+    while (next_mutex != NULL) {
+      if (next_mutex->_locking_thread == this_thread) {
+        // Whoops, the thread is blocked on me!  Deadlock!
+        report_deadlock(this_thread);
+        nassert_raise("Deadlock");
+        _global_mutex.release();
+        return;
+      }
+      Thread *next_thread = next_mutex->_locking_thread;
+      if (next_thread == NULL) {
+        // Looks like this mutex isn't actually locked, which means
+        // the last thread isn't really blocked--it just hasn't woken
+        // up yet to discover that.  In any case, no deadlock.
+        break;
+      }
+
+      // The last thread is blocked on this "next thread"'s mutex, but
+      // what mutex is the next thread blocked on?
+      next_mutex = next_thread->_blocked_on_mutex;
+    }
+
+    // OK, no deadlock detected.  Carry on.
+    this_thread->_blocked_on_mutex = this;
+
+    // Go to sleep on the condition variable until it's unlocked.
+
+    if (thread_cat.is_spam()) {
+      thread_cat.spam()
+        << *this_thread << " blocking on " << *this << "\n";
+    }
+    while (_locking_thread != (Thread *)NULL) {
+      _cvar.wait();
+    }
+    if (thread_cat.is_spam()) {
+      thread_cat.spam()
+        << *this_thread << " awake\n";
+    }
+
+    this_thread->_blocked_on_mutex = NULL;
+
+    _locking_thread = this_thread;
+    ++_lock_count;
+    nassertv(_lock_count == 1);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDebug::do_release
+//       Access: Private
+//  Description: The private implementation of lock() assumes that
+//               _global_mutex is held.
+////////////////////////////////////////////////////////////////////
+void MutexDebug::
+do_release() {
+  // If this assertion is triggered, you tried to release a
+  // recently-destructed mutex.
+  nassertv(_lock_count != -100);
+
+  Thread *this_thread = Thread::get_current_thread();
+
+  if (_locking_thread != this_thread) {
+    ostringstream ostr;
+    ostr << *this_thread << " attempted to release "
+         << *this << " which it does not own";
+    nassert_raise(ostr.str());
+    _global_mutex.release();
+    return;
+  }
+
+  nassertv(_lock_count > 0);
+
+  --_lock_count;
+  if (_lock_count == 0) {
+    // That was the last lock held by this thread.  Release the lock.
+    _locking_thread = (Thread *)NULL;
+    _cvar.signal();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDebug::do_debug_is_locked
+//       Access: Private
+//  Description: The private implementation of debug_is_locked()
+//               assumes that _global_mutex is held.
+////////////////////////////////////////////////////////////////////
+bool MutexDebug::
+do_debug_is_locked() const {
+  return (_locking_thread == Thread::get_current_thread());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDebug::report_deadlock
+//       Access: Private
+//  Description: Reports a detected deadlock situation.  _global_mutex
+//               should be already held.
+////////////////////////////////////////////////////////////////////
+void MutexDebug::
+report_deadlock(Thread *this_thread) {
+  thread_cat.error()
+    << "\n\n"
+    << "****************************************************************\n"
+    << "*****                 Deadlock detected!                   *****\n"
+    << "****************************************************************\n"
+    << "\n";
+
+  thread_cat.error()
+    << *this_thread << " attempted to lock " << *this
+    << " which is held by " << *_locking_thread << "\n";
+
+  MutexDebug *next_mutex = this;
+  Thread *next_thread = next_mutex->_locking_thread;
+  next_mutex = next_thread->_blocked_on_mutex;
+  while (next_mutex != NULL) {
+    thread_cat.error()
+      << *next_thread << " is blocked waiting on "
+      << *next_mutex << " which is held by "
+      << *next_mutex->_locking_thread << "\n";
+    next_thread = next_mutex->_locking_thread;
+    next_mutex = next_thread->_blocked_on_mutex;
+  }
+
+  thread_cat.error() 
+    << "Deadlock!\n";
+}
+
+#endif  //  DEBUG_THREADS

+ 79 - 0
panda/src/express/mutexDebug.h

@@ -0,0 +1,79 @@
+// Filename: mutexDebug.h
+// Created by:  drose (13Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef MUTEXDEBUG_H
+#define MUTEXDEBUG_H
+
+#include "pandabase.h"
+#include "mutexImpl.h"
+#include "conditionVarImpl.h"
+
+class Thread;
+
+#ifdef DEBUG_THREADS
+
+////////////////////////////////////////////////////////////////////
+//       Class : MutexDebug
+// Description : This class implements a standard mutex the hard way,
+//               by doing everything by hand.  This does allow fancy
+//               things like deadlock detection, however.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS MutexDebug {
+protected:
+  INLINE MutexDebug(bool allow_recursion);
+  virtual ~MutexDebug();
+private:
+  INLINE MutexDebug(const MutexDebug &copy);
+  INLINE void operator = (const MutexDebug &copy);
+
+public:
+  INLINE void lock() const;
+  INLINE void release() const;
+  INLINE bool debug_is_locked() const;
+
+  virtual void output(ostream &out) const;
+
+private:
+  void do_lock();
+  void do_release();
+  bool do_debug_is_locked() const;
+
+  void report_deadlock(Thread *this_thread);
+
+private:
+  bool _allow_recursion;
+  Thread *_locking_thread;
+  int _lock_count;
+  ConditionVarImpl _cvar;
+
+  static MutexImpl _global_mutex;
+
+  friend class ConditionVarDebug;
+};
+
+INLINE ostream &
+operator << (ostream &out, const MutexDebug &m) {
+  m.output(out);
+  return out;
+}
+
+#include "mutexDebug.I"
+
+#endif  //  DEBUG_THREADS
+
+#endif

+ 106 - 0
panda/src/express/mutexDirect.I

@@ -0,0 +1,106 @@
+// Filename: mutexDirect.I
+// Created by:  drose (13Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDirect::Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE MutexDirect::
+MutexDirect() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDirect::Destructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE MutexDirect::
+~MutexDirect() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDirect::Copy Constructor
+//       Access: Private
+//  Description: Do not attempt to copy mutexes.
+////////////////////////////////////////////////////////////////////
+INLINE MutexDirect::
+MutexDirect(const MutexDirect &copy) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDirect::Copy Assignment Operator
+//       Access: Private
+//  Description: Do not attempt to copy mutexes.
+////////////////////////////////////////////////////////////////////
+INLINE void MutexDirect::
+operator = (const MutexDirect &copy) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDirect::lock
+//       Access: Public
+//  Description: Grabs the mutex if it is available.  If it is not
+//               available, blocks until it becomes available, then
+//               grabs it.  In either case, the function does not
+//               return until the mutex is held; you should then call
+//               unlock().
+//
+//               This method is considered const so that you can lock
+//               and unlock const mutexes, mainly to allow thread-safe
+//               access to otherwise const data.
+//
+//               Also see MutexHolder.
+////////////////////////////////////////////////////////////////////
+INLINE void MutexDirect::
+lock() const {
+  ((MutexDirect *)this)->_impl.lock();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDirect::release
+//       Access: Public
+//  Description: Releases the mutex.  It is an error to call this if
+//               the mutex was not already locked.
+//
+//               This method is considered const so that you can lock
+//               and unlock const mutexes, mainly to allow thread-safe
+//               access to otherwise const data.
+////////////////////////////////////////////////////////////////////
+INLINE void MutexDirect::
+release() const {
+  ((MutexDirect *)this)->_impl.release();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDirect::debug_is_locked
+//       Access: Public
+//  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;
+}

+ 34 - 0
panda/src/express/mutexDirect.cxx

@@ -0,0 +1,34 @@
+// Filename: mutexDirect.cxx
+// Created by:  drose (13Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "mutexDirect.h"
+
+#ifndef DEBUG_THREADS
+
+////////////////////////////////////////////////////////////////////
+//     Function: MutexDirect::output
+//       Access: Public
+//  Description: This method is declared virtual in MutexDebug, but
+//               non-virtual in MutexDirect.
+////////////////////////////////////////////////////////////////////
+void MutexDirect::
+output(ostream &out) const {
+  out << "Mutex " << (void *)this;
+}
+
+#endif  // !DEBUG_THREADS

+ 66 - 0
panda/src/express/mutexDirect.h

@@ -0,0 +1,66 @@
+// Filename: mutexDirect.h
+// Created by:  drose (13Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef MUTEXDIRECT_H
+#define MUTEXDIRECT_H
+
+#include "pandabase.h"
+#include "mutexImpl.h"
+
+class Thread;
+
+#ifndef DEBUG_THREADS
+
+////////////////////////////////////////////////////////////////////
+//       Class : MutexDirect
+// Description : This class implements a standard mutex by making
+//               direct calls to the underlying implementation layer.
+//               It doesn't perform any debugging operations.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS MutexDirect {
+protected:
+  INLINE MutexDirect();
+  INLINE ~MutexDirect();
+private:
+  INLINE MutexDirect(const MutexDirect &copy);
+  INLINE void operator = (const MutexDirect &copy);
+
+public:
+  INLINE void lock() const;
+  INLINE void release() const;
+  INLINE bool debug_is_locked() const;
+
+  void output(ostream &out) const;
+
+private:
+  MutexImpl _impl;
+
+  friend class ConditionVarDirect;
+};
+
+INLINE ostream &
+operator << (ostream &out, const MutexDirect &m) {
+  m.output(out);
+  return out;
+}
+
+#include "mutexDirect.I"
+
+#endif  // !DEBUG_THREADS
+
+#endif

+ 0 - 14
panda/src/express/mutexDummyImpl.I

@@ -24,9 +24,6 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE MutexDummyImpl::
 INLINE MutexDummyImpl::
 MutexDummyImpl() {
 MutexDummyImpl() {
-#ifdef CHECK_REENTRANT_MUTEX
-  _lock_count = 0;
-#endif
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -36,9 +33,6 @@ MutexDummyImpl() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE MutexDummyImpl::
 INLINE MutexDummyImpl::
 ~MutexDummyImpl() {
 ~MutexDummyImpl() {
-#ifdef CHECK_REENTRANT_MUTEX
-  nassertv(_lock_count == 0);
-#endif
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -48,10 +42,6 @@ INLINE MutexDummyImpl::
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void MutexDummyImpl::
 INLINE void MutexDummyImpl::
 lock() {
 lock() {
-#ifdef CHECK_REENTRANT_MUTEX
-  _lock_count++;
-  nassertv(_lock_count == 1);
-#endif
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -61,8 +51,4 @@ lock() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void MutexDummyImpl::
 INLINE void MutexDummyImpl::
 release() {
 release() {
-#ifdef CHECK_REENTRANT_MUTEX
-  _lock_count--;
-  nassertv(_lock_count == 0);
-#endif
 }
 }

+ 1 - 7
panda/src/express/mutexDummyImpl.h

@@ -30,8 +30,7 @@
 //       Class : MutexDummyImpl
 //       Class : MutexDummyImpl
 // Description : A fake mutex implementation for single-threaded
 // Description : A fake mutex implementation for single-threaded
 //               applications that don't need any synchronization
 //               applications that don't need any synchronization
-//               control.  This does nothing but assert that the same
-//               process does not try to grab the mutex twice.
+//               control.  This does nothing at all.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDAEXPRESS MutexDummyImpl {
 class EXPCL_PANDAEXPRESS MutexDummyImpl {
 public:
 public:
@@ -40,11 +39,6 @@ public:
 
 
   INLINE void lock();
   INLINE void lock();
   INLINE void release();
   INLINE void release();
-
-private:
-#ifdef CHECK_REENTRANT_MUTEX
-  int _lock_count;
-#endif
 };
 };
 
 
 #include "mutexDummyImpl.I"
 #include "mutexDummyImpl.I"

+ 3 - 2
panda/src/express/mutexImpl.h

@@ -26,7 +26,8 @@
 
 
 #include "mutexDummyImpl.h"
 #include "mutexDummyImpl.h"
 typedef MutexDummyImpl MutexImpl;
 typedef MutexDummyImpl MutexImpl;
-#undef HAVE_REMUTEXIMPL
+typedef MutexDummyImpl ReMutexImpl;
+#define HAVE_REMUTEXIMPL 1
 
 
 #elif defined(THREAD_WIN32_IMPL)
 #elif defined(THREAD_WIN32_IMPL)
 
 
@@ -46,7 +47,7 @@ typedef ReMutexPosixImpl ReMutexImpl;
 
 
 #include "mutexNsprImpl.h"
 #include "mutexNsprImpl.h"
 typedef MutexNsprImpl MutexImpl;
 typedef MutexNsprImpl MutexImpl;
-#undef HAVE_REMUTEXIMPL
+#undef HAVE_REMUTEXIMPL  // NSPR doesn't provide a reentrant mutex.
 
 
 #endif
 #endif
 
 

+ 12 - 0
panda/src/express/mutexNsprImpl.I

@@ -48,6 +48,18 @@ lock() {
   PR_Lock(_lock);
   PR_Lock(_lock);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: MutexNsprImpl::try_lock
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool MutexNsprImpl::
+try_lock() {
+  // NSPR doesn't define a try_lock function.  Too bad.  We just
+  // report that it would always block (since we don't know).
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MutexNsprImpl::release
 //     Function: MutexNsprImpl::release
 //       Access: Public
 //       Access: Public

+ 3 - 0
panda/src/express/mutexNsprImpl.h

@@ -28,6 +28,8 @@
 
 
 #include <prlock.h>
 #include <prlock.h>
 
 
+#undef MUTEX_DEFINES_TRYLOCK
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : MutexNsprImpl
 //       Class : MutexNsprImpl
 // Description : Uses NSPR to implement a mutex.
 // Description : Uses NSPR to implement a mutex.
@@ -38,6 +40,7 @@ public:
   INLINE ~MutexNsprImpl();
   INLINE ~MutexNsprImpl();
 
 
   INLINE void lock();
   INLINE void lock();
+  INLINE bool try_lock();
   INLINE void release();
   INLINE void release();
 
 
 private:
 private:

+ 24 - 0
panda/src/express/mutexPosixImpl.I

@@ -54,6 +54,18 @@ lock() {
   nassertv(result == 0);
   nassertv(result == 0);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: MutexPosixImpl::try_lock
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool MutexPosixImpl::
+try_lock() {
+  int result = pthread_mutex_trylock(&_lock);
+  nassertr(result == 0 || result == EBUSY, false);
+  return (result == 0);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MutexPosixImpl::release
 //     Function: MutexPosixImpl::release
 //       Access: Public
 //       Access: Public
@@ -102,6 +114,18 @@ lock() {
   nassertv(result == 0);
   nassertv(result == 0);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexPosixImpl::try_lock
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool ReMutexPosixImpl::
+try_lock() {
+  int result = pthread_mutex_trylock(&_lock);
+  nassertr(result == 0 || result == EBUSY, false);
+  return (result == 0);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ReMutexPosixImpl::release
 //     Function: ReMutexPosixImpl::release
 //       Access: Public
 //       Access: Public

+ 5 - 0
panda/src/express/mutexPosixImpl.h

@@ -27,6 +27,9 @@
 #include "notify.h"
 #include "notify.h"
 
 
 #include <pthread.h>
 #include <pthread.h>
+#include <errno.h>
+
+#define MUTEX_DEFINES_TRYLOCK 1
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : MutexPosixImpl
 //       Class : MutexPosixImpl
@@ -38,6 +41,7 @@ public:
   INLINE ~MutexPosixImpl();
   INLINE ~MutexPosixImpl();
 
 
   INLINE void lock();
   INLINE void lock();
+  INLINE bool try_lock();
   INLINE void release();
   INLINE void release();
 
 
 private:
 private:
@@ -55,6 +59,7 @@ public:
   INLINE ~ReMutexPosixImpl();
   INLINE ~ReMutexPosixImpl();
 
 
   INLINE void lock();
   INLINE void lock();
+  INLINE bool try_lock();
   INLINE void release();
   INLINE void release();
 
 
 private:
 private:

+ 10 - 0
panda/src/express/mutexWin32Impl.I

@@ -47,6 +47,16 @@ lock() {
   EnterCriticalSection(&_lock);
   EnterCriticalSection(&_lock);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: MutexWin32Impl::try_lock
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool MutexWin32Impl::
+try_lock() {
+  return TryEnterCriticalSection(&_lock);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MutexWin32Impl::release
 //     Function: MutexWin32Impl::release
 //       Access: Public
 //       Access: Public

+ 3 - 0
panda/src/express/mutexWin32Impl.h

@@ -28,6 +28,8 @@
 
 
 #include <windows.h>
 #include <windows.h>
 
 
+#define MUTEX_DEFINES_TRYLOCK 1
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : MutexWin32Impl
 //       Class : MutexWin32Impl
 // Description : Uses Windows native calls to implement a mutex.
 // Description : Uses Windows native calls to implement a mutex.
@@ -38,6 +40,7 @@ public:
   INLINE ~MutexWin32Impl();
   INLINE ~MutexWin32Impl();
 
 
   INLINE void lock();
   INLINE void lock();
+  INLINE bool try_lock();
   INLINE void release();
   INLINE void release();
 
 
 private:
 private:

+ 12 - 72
panda/src/express/pmutex.I

@@ -23,7 +23,12 @@
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE Mutex::
 INLINE Mutex::
-Mutex() {
+#ifdef DEBUG_THREADS
+Mutex() : MutexDebug(false)
+#else
+Mutex()
+#endif  // DEBUG_THREADS
+{
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -41,7 +46,12 @@ INLINE Mutex::
 //  Description: Do not attempt to copy mutexes.
 //  Description: Do not attempt to copy mutexes.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE Mutex::
 INLINE Mutex::
-Mutex(const Mutex &copy) {
+#ifdef DEBUG_THREADS
+Mutex(const Mutex &copy) : MutexDebug(false)
+#else
+  Mutex(const Mutex &copy)
+#endif  // DEBUG_THREADS
+{
   nassertv(false);
   nassertv(false);
 }
 }
 
 
@@ -54,73 +64,3 @@ INLINE void Mutex::
 operator = (const Mutex &copy) {
 operator = (const Mutex &copy) {
   nassertv(false);
   nassertv(false);
 }
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: Mutex::lock
-//       Access: Public
-//  Description: Grabs the mutex if it is available.  If it is not
-//               available, blocks until it becomes available, then
-//               grabs it.  In either case, the function does not
-//               return until the mutex is held; you should then call
-//               unlock().
-//
-//               This method is considered const so that you can lock
-//               and unlock const mutexes, mainly to allow thread-safe
-//               access to otherwise const data.
-//
-//               Also see MutexHolder.
-////////////////////////////////////////////////////////////////////
-INLINE void Mutex::
-lock() const {
-#ifndef CHECK_REENTRANT_MUTEX
-  // In the production case, just lock the thing immediately.  Don't
-  // bother with the out-of-line do_lock() method, since we won't be
-  // performing any checks anyway.
-  ((MutexImpl &)_impl).lock();
-
-#else 
-  // In development mode, call the out-of-line do_lock() method, which
-  // might perform an additional check or two.
-  ((Mutex *)this)->do_lock();
-#endif
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Mutex::release
-//       Access: Public
-//  Description: Releases the mutex.  It is an error to call this if
-//               the mutex was not already locked.
-//
-//               This method is considered const so that you can lock
-//               and unlock const mutexes, mainly to allow thread-safe
-//               access to otherwise const data.
-////////////////////////////////////////////////////////////////////
-INLINE void Mutex::
-release() const {
-#ifndef CHECK_REENTRANT_MUTEX
-  // In the production case, just release the thing immediately.
-  // Don't bother with the out-of-line do_release() method, since we
-  // won't be performing any checks anyway.
-  ((MutexImpl &)_impl).release();
-
-#else 
-  // In development mode, call the out-of-line do_release() method,
-  // which might perform an additional check or two.
-  ((Mutex *)this)->do_release();
-#endif
-}
-
-#ifndef CHECK_REENTRANT_MUTEX
-////////////////////////////////////////////////////////////////////
-//     Function: Mutex::debug_is_locked
-//       Access: Public
-//  Description: Returns true if the current thread has locked the
-//               Mutex, false otherwise.  This method is only
-//               meaningful if CHECK_REENTRANT_MUTEX is defined;
-//               otherwise, it always returns true.
-////////////////////////////////////////////////////////////////////
-INLINE bool Mutex::
-debug_is_locked() const {
-  return true;
-}
-#endif  // CHECK_REENTRANT_MUTEX

+ 1 - 46
panda/src/express/pmutex.cxx

@@ -19,49 +19,4 @@
 #include "pmutex.h"
 #include "pmutex.h"
 #include "thread.h"
 #include "thread.h"
 
 
-#ifdef CHECK_REENTRANT_MUTEX
-////////////////////////////////////////////////////////////////////
-//     Function: Mutex::debug_is_locked
-//       Access: Public
-//  Description: Returns true if the current thread has locked the
-//               Mutex, false otherwise.  This method is only
-//               meaningful if CHECK_REENTRANT_MUTEX is defined;
-//               otherwise, it always returns true.
-////////////////////////////////////////////////////////////////////
-bool Mutex::
-debug_is_locked() const {
-  return (_locking_thread == Thread::get_current_thread());
-}
-#endif  // CHECK_REENTRANT_MUTEX
-
-////////////////////////////////////////////////////////////////////
-//     Function: Mutex::do_lock
-//       Access: Private
-//  Description: The private implementation of lock().
-////////////////////////////////////////////////////////////////////
-void Mutex::
-do_lock() {
-#ifdef CHECK_REENTRANT_MUTEX
-  nassertv(_locking_thread != Thread::get_current_thread());
-#endif
-  _impl.lock();
-
-#ifdef CHECK_REENTRANT_MUTEX
-  _locking_thread = Thread::get_current_thread();
-#endif
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Mutex::do_release
-//       Access: Private
-//  Description: The private implementation of release().
-////////////////////////////////////////////////////////////////////
-void Mutex::
-do_release() {
-#ifdef CHECK_REENTRANT_MUTEX
-  nassertv(_locking_thread == Thread::get_current_thread());
-  _locking_thread = (Thread *)NULL;
-#endif
-
-  _impl.release();
-}
+Mutex Mutex::_notify_mutex;

+ 16 - 32
panda/src/express/pmutex.h

@@ -20,9 +20,8 @@
 #define PMUTEX_H
 #define PMUTEX_H
 
 
 #include "pandabase.h"
 #include "pandabase.h"
-#include "mutexImpl.h"
-
-class Thread;
+#include "mutexDebug.h"
+#include "mutexDirect.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : Mutex
 //       Class : Mutex
@@ -37,13 +36,18 @@ class Thread;
 //               work on all platforms; on some platforms, a thread
 //               work on all platforms; on some platforms, a thread
 //               can deadlock itself by attempting to lock the same
 //               can deadlock itself by attempting to lock the same
 //               mutex twice.  If your code requires a reentrant
 //               mutex twice.  If your code requires a reentrant
-//               mutex, use the ReMutex class instead.  When this code
-//               is compiled with CHECK_REENTRANT_MUTEX true, then it
-//               will enable assertion checks to ensure that a
-//               particular thread doesn't try to lock the same mutex
-//               twice.
+//               mutex, use the ReMutex class instead.
+//
+//               This class inherits its implementation either from
+//               MutexDebug or MutexDirect, depending on the
+//               definition of DEBUG_THREADS.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDAEXPRESS Mutex {
+#ifdef DEBUG_THREADS
+class EXPCL_PANDAEXPRESS Mutex : public MutexDebug
+#else
+class EXPCL_PANDAEXPRESS Mutex : public MutexDirect
+#endif  // DEBUG_THREADS
+{
 public:
 public:
   INLINE Mutex();
   INLINE Mutex();
   INLINE ~Mutex();
   INLINE ~Mutex();
@@ -52,29 +56,9 @@ private:
   INLINE void operator = (const Mutex &copy);
   INLINE void operator = (const Mutex &copy);
 
 
 public:
 public:
-  INLINE void lock() const;
-  INLINE void release() const;
-
-#ifdef CHECK_REENTRANT_MUTEX
-  bool debug_is_locked() const;
-#else
-  INLINE bool debug_is_locked() const;
-#endif
-
-private:
-  void do_lock();
-  void do_release();
-
-private:
-  MutexImpl _impl;
-
-#ifdef CHECK_REENTRANT_MUTEX
-  // Make sure that ordinary mutexes are not locked reentrantly
-  // (that's what a ReMutex is for).
-  Thread *_locking_thread;
-#endif
-
-  friend class ConditionVar;
+  // This is a global mutex set aside for the purpose of protecting
+  // Notify messages from being interleaved between threads.
+  static Mutex _notify_mutex;
 };
 };
 
 
 #include "pmutex.I"
 #include "pmutex.I"

+ 12 - 242
panda/src/express/reMutex.I

@@ -17,204 +17,18 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 
 
-#ifdef HAVE_REMUTEXIMPL
-// In this branch, the ReMutex class is implemented as a thin wrapper
-// around the native ReMutexImpl class.
-
-////////////////////////////////////////////////////////////////////
-//     Function: ReMutex::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE ReMutex::
-ReMutex() {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ReMutex::Destructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE ReMutex::
-~ReMutex() {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ReMutex::Copy Constructor
-//       Access: Private
-//  Description: Do not attempt to copy mutexes.
-////////////////////////////////////////////////////////////////////
-INLINE ReMutex::
-ReMutex(const ReMutex &copy) {
-  nassertv(false);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ReMutex::Copy Assignment Operator
-//       Access: Private
-//  Description: Do not attempt to copy mutexes.
-////////////////////////////////////////////////////////////////////
-INLINE void ReMutex::
-operator = (const ReMutex &copy) {
-  nassertv(false);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ReMutex::lock
-//       Access: Public
-//  Description: Grabs the mutex if it is available.  If it is not
-//               available, blocks until it becomes available, then
-//               grabs it.  In either case, the function does not
-//               return until the mutex is held; you should then call
-//               unlock().
-//
-//               This method is considered const so that you can lock
-//               and unlock const mutexes, mainly to allow thread-safe
-//               access to otherwise const data.
-//
-//               Also see MutexHolder.
-////////////////////////////////////////////////////////////////////
-INLINE void ReMutex::
-lock() const {
-  ((MutexImpl &)_impl).lock();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ReMutex::release
-//       Access: Public
-//  Description: Releases the mutex.  It is an error to call this if
-//               the mutex was not already locked.
-//
-//               This method is considered const so that you can lock
-//               and unlock const mutexes, mainly to allow thread-safe
-//               access to otherwise const data.
-////////////////////////////////////////////////////////////////////
-INLINE void ReMutex::
-release() const {
-  ((MutexImpl &)_impl).release();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ReMutex::debug_is_locked
-//       Access: Public
-//  Description: Returns true if the current thread has locked the
-//               Mutex, false otherwise.  This method is only
-//               meaningful if CHECK_REENTRANT_MUTEX is defined;
-//               otherwise, it always returns true.
-////////////////////////////////////////////////////////////////////
-INLINE bool ReMutex::
-debug_is_locked() const {
-  return true;
-}
-
-
-#elif !defined(THREAD_DUMMY_IMPL) || defined(CHECK_REENTRANT_MUTEX)
-
-// In this branch, the ReMutex object is implemented as a heavy
-// wrapper around the Mutex and ConditionVar classes, to impose
-// reentrant semantics where the underlying MutexImpl doesn't provide
-// them.
-
-////////////////////////////////////////////////////////////////////
-//     Function: ReMutex::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE ReMutex::
-ReMutex() : _cvar(_mutex) {
-  _locking_thread = NULL;
-  _lock_count = 0;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ReMutex::Destructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE ReMutex::
-~ReMutex() {
-  nassertv(_locking_thread == NULL && _lock_count == 0);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ReMutex::Copy Constructor
-//       Access: Private
-//  Description: Do not attempt to copy mutexes.
-////////////////////////////////////////////////////////////////////
-INLINE ReMutex::
-ReMutex(const ReMutex &copy) : _cvar(_mutex) {
-  nassertv(false);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ReMutex::Copy Assignment Operator
-//       Access: Private
-//  Description: Do not attempt to copy mutexes.
-////////////////////////////////////////////////////////////////////
-INLINE void ReMutex::
-operator = (const ReMutex &copy) {
-  nassertv(false);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ReMutex::lock
-//       Access: Public
-//  Description: Grabs the mutex if it is available.  If it is not
-//               available, blocks until it becomes available, then
-//               grabs it.  In either case, the function does not
-//               return until the mutex is held; you should then call
-//               unlock().
-//
-//               This method is considered const so that you can lock
-//               and unlock const mutexes, mainly to allow thread-safe
-//               access to otherwise const data.
-//
-//               Also see ReMutexHolder.
-////////////////////////////////////////////////////////////////////
-INLINE void ReMutex::
-lock() const {
-  ((ReMutex *)this)->do_lock();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ReMutex::release
-//       Access: Public
-//  Description: Releases the mutex.  It is an error to call this if
-//               the mutex was not already locked.
-//
-//               This method is considered const so that you can lock
-//               and unlock const mutexes, mainly to allow thread-safe
-//               access to otherwise const data.
-////////////////////////////////////////////////////////////////////
-INLINE void ReMutex::
-release() const {
-  ((ReMutex *)this)->do_release();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ReMutex::debug_is_locked
-//       Access: Public
-//  Description: Returns true if *this thread* already holds the lock,
-//               false if no one holds the lock or the lock is held by
-//               some other thread.
-////////////////////////////////////////////////////////////////////
-INLINE bool ReMutex::
-debug_is_locked() const {
-  MutexHolder holder(_mutex);
-  return (_locking_thread == Thread::get_current_thread());
-}
-
-#else  // !THREAD_DUMMY_IMPL || CHECK_REENTRANT_MUTEX
-
-// In this branch, the mutex is stubbed out to do nothing.
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ReMutex::Constructor
 //     Function: ReMutex::Constructor
 //       Access: Public
 //       Access: Public
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE ReMutex::
 INLINE ReMutex::
-ReMutex() {
+#ifdef DEBUG_THREADS
+ReMutex() : MutexDebug(true)
+#else
+ReMutex()
+#endif  // DEBUG_THREADS
+{
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -232,7 +46,12 @@ INLINE ReMutex::
 //  Description: Do not attempt to copy mutexes.
 //  Description: Do not attempt to copy mutexes.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE ReMutex::
 INLINE ReMutex::
-ReMutex(const ReMutex &copy) {
+#ifdef DEBUG_THREADS
+ReMutex(const ReMutex &copy) : MutexDebug(true)
+#else
+  ReMutex(const ReMutex &copy)
+#endif  // DEBUG_THREADS
+{
   nassertv(false);
   nassertv(false);
 }
 }
 
 
@@ -245,52 +64,3 @@ INLINE void ReMutex::
 operator = (const ReMutex &copy) {
 operator = (const ReMutex &copy) {
   nassertv(false);
   nassertv(false);
 }
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: ReMutex::lock
-//       Access: Public
-//  Description: Grabs the mutex if it is available.  If it is not
-//               available, blocks until it becomes available, then
-//               grabs it.  In either case, the function does not
-//               return until the mutex is held; you should then call
-//               unlock().
-//
-//               This method is considered const so that you can lock
-//               and unlock const mutexes, mainly to allow thread-safe
-//               access to otherwise const data.
-//
-//               Also see ReMutexHolder.
-////////////////////////////////////////////////////////////////////
-INLINE void ReMutex::
-lock() const {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ReMutex::release
-//       Access: Public
-//  Description: Releases the mutex.  It is an error to call this if
-//               the mutex was not already locked.
-//
-//               This method is considered const so that you can lock
-//               and unlock const mutexes, mainly to allow thread-safe
-//               access to otherwise const data.
-////////////////////////////////////////////////////////////////////
-INLINE void ReMutex::
-release() const {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ReMutex::debug_is_locked
-//       Access: Public
-//  Description: Returns true if the current thread has locked the
-//               Mutex, false otherwise.  This method is only
-//               meaningful if CHECK_REENTRANT_MUTEX is defined;
-//               otherwise, it always returns true.
-////////////////////////////////////////////////////////////////////
-INLINE bool ReMutex::
-debug_is_locked() const {
-  return true;
-}
-
-#endif  // !THREAD_DUMMY_IMPL || CHECK_REENTRANT_MUTEX
-

+ 0 - 66
panda/src/express/reMutex.cxx

@@ -17,69 +17,3 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "reMutex.h"
 #include "reMutex.h"
-
-#ifndef HAVE_REMUTEXIMPL
-
-// Most of the methods in this class are stubbed out in the
-// THREAD_DUMMY_IMPL case, especially when CHECK_REENTRANT_MUTEX is
-// not defined.  In this case, we're not performing any actual locking
-// or verification, so there's no need to do anything at all here.
-
-#if !defined(THREAD_DUMMY_IMPL) || defined(CHECK_REENTRANT_MUTEX)
-
-////////////////////////////////////////////////////////////////////
-//     Function: ReMutex::do_lock
-//       Access: Private
-//  Description: The private implementation of lock().
-////////////////////////////////////////////////////////////////////
-void ReMutex::
-do_lock() {
-  MutexHolder holder(_mutex);
-
-  if (_locking_thread == (Thread *)NULL) {
-    // The mutex is not already locked by anyone.  Lock it.
-    _locking_thread = Thread::get_current_thread();
-    ++_lock_count;
-    nassertv(_lock_count == 1);
-
-  } else if (_locking_thread == Thread::get_current_thread()) {
-    // The mutex is already locked by this thread.  Increment the lock
-    // count.
-    ++_lock_count;
-
-  } else {
-    // The mutex is locked by some other thread.  Go to sleep on the
-    // condition variable until it's unlocked.
-    while (_locking_thread != (Thread *)NULL) {
-      _cvar.wait();
-    }
-    _locking_thread = Thread::get_current_thread();
-    ++_lock_count;
-    nassertv(_lock_count == 1);
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: ReMutex::do_release
-//       Access: Private
-//  Description: The private implementation of release().
-////////////////////////////////////////////////////////////////////
-void ReMutex::
-do_release() {
-  MutexHolder holder(_mutex);
-
-  nassertv(_locking_thread == Thread::get_current_thread());
-  nassertv(_lock_count > 0);
-
-  --_lock_count;
-  if (_lock_count == 0) {
-    // That was the last lock held by this thread.  Release the lock.
-    _locking_thread = (Thread *)NULL;
-    _cvar.signal();
-  }
-}
-
-#endif  // !THREAD_DUMMY_IMPL || CHECK_REENTRANT_MUTEX
-
-#endif  // !HAVE_REMUTEXIMPL
-

+ 12 - 27
panda/src/express/reMutex.h

@@ -20,10 +20,8 @@
 #define REMUTEX_H
 #define REMUTEX_H
 
 
 #include "pandabase.h"
 #include "pandabase.h"
-#include "pmutex.h"
-#include "conditionVar.h"
-#include "mutexHolder.h"
-#include "thread.h"
+#include "mutexDebug.h"
+#include "reMutexDirect.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : ReMutex
 //       Class : ReMutex
@@ -31,36 +29,23 @@
 //               more than once by the thread that already holds it,
 //               more than once by the thread that already holds it,
 //               without deadlock.  The thread must eventually release
 //               without deadlock.  The thread must eventually release
 //               the mutex the same number of times it locked it.
 //               the mutex the same number of times it locked it.
+//
+//               This class inherits its implementation either from
+//               MutexDebug or ReMutexDirect, depending on the
+//               definition of DEBUG_THREADS.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDAEXPRESS ReMutex {
+#ifdef DEBUG_THREADS
+class EXPCL_PANDAEXPRESS ReMutex : public MutexDebug
+#else
+class EXPCL_PANDAEXPRESS ReMutex : public ReMutexDirect
+#endif  // DEBUG_THREADS
+{
 public:
 public:
   INLINE ReMutex();
   INLINE ReMutex();
   INLINE ~ReMutex();
   INLINE ~ReMutex();
 private:
 private:
   INLINE ReMutex(const ReMutex &copy);
   INLINE ReMutex(const ReMutex &copy);
   INLINE void operator = (const ReMutex &copy);
   INLINE void operator = (const ReMutex &copy);
-
-public:
-  INLINE void lock() const;
-  INLINE void release() const;
-
-  INLINE bool debug_is_locked() const;
-
-private:
-#ifdef HAVE_REMUTEXIMPL
-  // If the native Mutex implementation provides a reentrant flavor,
-  // just use that.
-  ReMutexImpl _impl;
-
-#elif !defined(THREAD_DUMMY_IMPL) || defined(CHECK_REENTRANT_MUTEX)
-  void do_lock();
-  void do_release();
-
-  Mutex _mutex;
-  ConditionVar _cvar;
-  Thread *_locking_thread;
-  int _lock_count;
-#endif  // !THREAD_DUMMY_IMPL || CHECK_REENTRANT_MUTEX
 };
 };
 
 
 #include "reMutex.I"
 #include "reMutex.I"

+ 110 - 0
panda/src/express/reMutexDirect.I

@@ -0,0 +1,110 @@
+// Filename: reMutexDirect.I
+// Created by:  drose (13Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexDirect::Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE ReMutexDirect::
+ReMutexDirect() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexDirect::Destructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE ReMutexDirect::
+~ReMutexDirect() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexDirect::Copy Constructor
+//       Access: Private
+//  Description: Do not attempt to copy reMutexes.
+////////////////////////////////////////////////////////////////////
+INLINE ReMutexDirect::
+ReMutexDirect(const ReMutexDirect &copy) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexDirect::Copy Assignment Operator
+//       Access: Private
+//  Description: Do not attempt to copy reMutexes.
+////////////////////////////////////////////////////////////////////
+INLINE void ReMutexDirect::
+operator = (const ReMutexDirect &copy) {
+  nassertv(false);
+}
+
+#ifdef HAVE_REMUTEXIMPL
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexDirect::lock
+//       Access: Public
+//  Description: Grabs the reMutex if it is available.  If it is not
+//               available, blocks until it becomes available, then
+//               grabs it.  In either case, the function does not
+//               return until the reMutex is held; you should then call
+//               unlock().
+//
+//               This method is considered const so that you can lock
+//               and unlock const reMutexes, mainly to allow thread-safe
+//               access to otherwise const data.
+//
+//               Also see ReMutexHolder.
+////////////////////////////////////////////////////////////////////
+INLINE void ReMutexDirect::
+lock() const {
+  ((ReMutexDirect *)this)->_impl.lock();
+}
+#endif  // HAVE_REMUTEXIMPL
+
+#ifdef HAVE_REMUTEXIMPL
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexDirect::release
+//       Access: Public
+//  Description: Releases the reMutex.  It is an error to call this if
+//               the reMutex was not already locked.
+//
+//               This method is considered const so that you can lock
+//               and unlock const reMutexes, mainly to allow thread-safe
+//               access to otherwise const data.
+////////////////////////////////////////////////////////////////////
+INLINE void ReMutexDirect::
+release() const {
+  ((ReMutexDirect *)this)->_impl.release();
+}
+#endif  // HAVE_REMUTEXIMPL
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexDirect::debug_is_locked
+//       Access: Public
+//  Description: Returns true if the current thread has locked the
+//               ReMutex, false otherwise.  This method is only intended
+//               for use in debugging, hence the method name; in the
+//               ReMutexDirect case, it always returns true, since
+//               there's not a reliable way to determine this
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool ReMutexDirect::
+debug_is_locked() const {
+  return true;
+}

+ 116 - 0
panda/src/express/reMutexDirect.cxx

@@ -0,0 +1,116 @@
+// Filename: reMutexDirect.cxx
+// Created by:  drose (13Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "reMutexDirect.h"
+
+#ifndef HAVE_REMUTEXIMPL
+MutexImpl ReMutexDirect::_global_lock;
+#endif  // !HAVE_REMUTEXIMPL
+
+#ifndef HAVE_REMUTEXIMPL
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexDirect::lock
+//       Access: Public
+//  Description: Grabs the reMutex if it is available.  If it is not
+//               available, blocks until it becomes available, then
+//               grabs it.  In either case, the function does not
+//               return until the reMutex is held; you should then call
+//               unlock().
+//
+//               This method is considered const so that you can lock
+//               and unlock const reMutexes, mainly to allow thread-safe
+//               access to otherwise const data.
+//
+//               Also see ReMutexHolder.
+////////////////////////////////////////////////////////////////////
+void ReMutexDirect::
+lock() const {
+  _global_mutex.lock();
+
+  if (_locking_thread == (Thread *)NULL) {
+    // The mutex is not already locked by anyone.  Lock it.
+    _locking_thread = Thread::get_current_thread();
+    ++_lock_count;
+    nassertd(_lock_count == 1) {
+    }
+
+  } else if (_locking_thread == Thread::get_current_thread()) {
+    // The mutex is already locked by this thread.  Increment the lock
+    // count.
+    ++_lock_count;
+    nassertd(_lock_count > 0) {
+    }
+
+  } else {
+    // The mutex is locked by some other thread.  Go to sleep on the
+    // condition variable until it's unlocked.
+    while (_locking_thread != (Thread *)NULL) {
+      _cvar.wait();
+    }
+
+    _locking_thread = Thread::get_current_thread();
+    ++_lock_count;
+    nassertd(_lock_count == 1) {
+    }
+  }
+  _global_mutex.release();
+}
+#endif  // !HAVE_REMUTEXIMPL
+
+#ifndef HAVE_REMUTEXIMPL
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexDirect::release
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void ReMutexDirect::
+release() {
+  _global_mutex.lock();
+
+  if (_locking_thread != Thread::get_current_thread()) {
+    ostringstream ostr;
+    ostr << *_locking_thread << " attempted to release "
+         << *this << " which it does not own";
+    nassert_raise(ostr.str());
+    _global_mutex.release();
+    return;
+  }
+
+  nassertd(_lock_count > 0) {
+  }
+
+  --_lock_count;
+  if (_lock_count == 0) {
+    // That was the last lock held by this thread.  Release the lock.
+    _locking_thread = (Thread *)NULL;
+    _cvar.signal();
+  }
+  _global_mutex.release();
+}
+#endif  // !HAVE_REMUTEXIMPL
+
+////////////////////////////////////////////////////////////////////
+//     Function: ReMutexDirect::output
+//       Access: Public
+//  Description: This method is declared virtual in MutexDebug, but
+//               non-virtual in ReMutexDirect.
+////////////////////////////////////////////////////////////////////
+void ReMutexDirect::
+output(ostream &out) const {
+  out << "ReMutex " << (void *)this;
+}

+ 77 - 0
panda/src/express/reMutexDirect.h

@@ -0,0 +1,77 @@
+// Filename: reMutexDirect.h
+// Created by:  drose (13Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef REMUTEXDIRECT_H
+#define REMUTEXDIRECT_H
+
+#include "pandabase.h"
+#include "mutexImpl.h"
+#include "conditionVarImpl.h"
+
+class Thread;
+
+////////////////////////////////////////////////////////////////////
+//       Class : ReMutexDirect
+// Description : This class implements a standard reMutex by making
+//               direct calls to the underlying implementation layer.
+//               It doesn't perform any debugging operations.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS ReMutexDirect {
+protected:
+  INLINE ReMutexDirect();
+  INLINE ~ReMutexDirect();
+private:
+  INLINE ReMutexDirect(const ReMutexDirect &copy);
+  INLINE void operator = (const ReMutexDirect &copy);
+
+public:
+#ifdef HAVE_REMUTEXIMPL
+  INLINE void lock() const;
+  INLINE void release() const;
+#else
+  void lock() const;
+  void release() const;
+#endif
+
+  INLINE bool debug_is_locked() const;
+
+  void output(ostream &out) const;
+
+private:
+#ifdef HAVE_REMUTEXIMPL
+  ReMutexImpl _impl;
+
+#else
+  // If we don't have a reentrant mutex, we have to hand-roll one.
+  Thread *_locking_thread;
+  int _lock_count;
+  ConditionVarImpl _cvar;
+
+  static MutexImpl _global_lock;
+#endif  // HAVE_REMUTEXIMPL
+};
+
+INLINE ostream &
+operator << (ostream &out, const ReMutexDirect &m) {
+  m.output(out);
+  return out;
+}
+
+#include "reMutexDirect.I"
+
+#endif

+ 1 - 1
panda/src/express/referenceCount.I

@@ -165,7 +165,7 @@ get_ref_count() const {
 #ifndef NDEBUG
 #ifndef NDEBUG
   test_ref_count_integrity();
   test_ref_count_integrity();
 #endif
 #endif
-  return _ref_count;
+  return AtomicAdjust::get(_ref_count);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 6 - 0
panda/src/express/selectThreadImpl.h

@@ -55,5 +55,11 @@
 
 
 #endif
 #endif
 
 
+// Let's also factor out some of the other configuration variables.
+#if defined(DO_PIPELINING) && defined(HAVE_THREADS)
+#define THREADED_PIPELINE 1
+#else
+#undef THREADED_PIPELINE
+#endif
 
 
 #endif
 #endif

+ 22 - 5
panda/src/express/test_diners.cxx

@@ -33,9 +33,7 @@
 static int last_rand = 0;
 static int last_rand = 0;
 #endif /* __WIN32__ */
 #endif /* __WIN32__ */
 
 
-Mutex print_mutex;
-
-#define PRINTMSG(x) { MutexHolder l(print_mutex); x; }
+#define PRINTMSG(x) { MutexHolder l(Mutex::_notify_mutex); x; }
 
 
 Mutex rand_mutex;
 Mutex rand_mutex;
 
 
@@ -53,7 +51,16 @@ static double random_f(double max)
 // afford luxuries like 2 chopsticks per person.
 // afford luxuries like 2 chopsticks per person.
 #define N_DINERS 5
 #define N_DINERS 5
 
 
-Mutex chopsticks[N_DINERS];
+class ChopstickMutex : public Mutex {
+public:
+  void output(ostream &out) const {
+    out << "chopstick " << _n;
+  }
+  int _n;
+};
+
+
+ChopstickMutex chopsticks[N_DINERS];
 
 
 // At most n philosophers are allowed into the room, others would have to
 // At most n philosophers are allowed into the room, others would have to
 // wait at the door.  This restriction demonstrates the use of condition
 // wait at the door.  This restriction demonstrates the use of condition
@@ -81,17 +88,20 @@ private:
     int r = l+1;
     int r = l+1;
     if (r == N_DINERS)
     if (r == N_DINERS)
       r = 0;
       r = 0;
+    /*
     if (l & 1) {
     if (l & 1) {
       int t = l;
       int t = l;
       l = r;
       l = r;
       r = t;
       r = t;
     }
     }
+    */
     PRINTMSG(cerr << "Philosopher #" << _id << " has entered the room."
     PRINTMSG(cerr << "Philosopher #" << _id << " has entered the room."
              << endl);
              << endl);
     int count = (int)random_f(10.0) + 1;
     int count = (int)random_f(10.0) + 1;
     while (--count) {
     while (--count) {
-      chopsticks[l].lock();
       chopsticks[r].lock();
       chopsticks[r].lock();
+      Thread::sleep(1);
+      chopsticks[l].lock();
       PRINTMSG(cerr << "Philosopher #" << _id
       PRINTMSG(cerr << "Philosopher #" << _id
                << " is eating spaghetti now." << endl);
                << " is eating spaghetti now." << endl);
       Thread::sleep(random_f(3.0));
       Thread::sleep(random_f(3.0));
@@ -115,12 +125,19 @@ public:
   philosopher(const int id) : Thread("philosopher", "a") {
   philosopher(const int id) : Thread("philosopher", "a") {
     _id = id;
     _id = id;
   }
   }
+
+  virtual void output(ostream &out) const {
+    out << "philosopher " << _id;
+  }
 };
 };
 
 
 int main(int, char**)
 int main(int, char**)
 {
 {
   int i;
   int i;
   room_mutex.lock();
   room_mutex.lock();
+  for (i=0; i<N_DINERS; ++i) {
+    chopsticks[i]._n = i;
+  }
   for (i=0; i<N_DINERS; ++i) {
   for (i=0; i<N_DINERS; ++i) {
     phils[i] = new philosopher(i);
     phils[i] = new philosopher(i);
     phils[i]->start(TP_normal, false, false);
     phils[i]->start(TP_normal, false, false);

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

@@ -44,6 +44,10 @@ Thread(const string &name, const string &sync_name) :
   _started = false;
   _started = false;
   _pstats_index = -1;
   _pstats_index = -1;
   _pipeline_stage = 0;
   _pipeline_stage = 0;
+
+#ifdef DEBUG_THREADS
+  _blocked_on_mutex = NULL;
+#endif
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 3 - 0
panda/src/express/thread.cxx

@@ -31,6 +31,9 @@ TypeHandle Thread::_type_handle;
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 Thread::
 Thread::
 ~Thread() {
 ~Thread() {
+#ifdef DEBUG_THREADS
+  nassertv(_blocked_on_mutex == NULL);
+#endif
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

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

@@ -26,6 +26,10 @@
 #include "threadImpl.h"
 #include "threadImpl.h"
 #include "notify.h"
 #include "notify.h"
 
 
+class Mutex;
+class ReMutex;
+class MutexDebug;
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : Thread
 //       Class : Thread
 // Description : A thread; that is, a lightweight process.  This is an
 // Description : A thread; that is, a lightweight process.  This is an
@@ -87,12 +91,18 @@ private:
 
 
 protected:
 protected:
   bool _started;
   bool _started;
+
+private:
   string _name;
   string _name;
   string _sync_name;
   string _sync_name;
   ThreadImpl _impl;
   ThreadImpl _impl;
   int _pstats_index;
   int _pstats_index;
   int _pipeline_stage;
   int _pipeline_stage;
 
 
+#ifdef DEBUG_THREADS
+  MutexDebug *_blocked_on_mutex;
+#endif
+
 private:
 private:
   static Thread *_main_thread;
   static Thread *_main_thread;
   static Thread *_external_thread;
   static Thread *_external_thread;
@@ -114,6 +124,8 @@ public:
 private:
 private:
   static TypeHandle _type_handle;
   static TypeHandle _type_handle;
 
 
+  friend class MutexDebug;
+
   friend class ThreadDummyImpl;
   friend class ThreadDummyImpl;
   friend class ThreadWin32Impl;
   friend class ThreadWin32Impl;
   friend class ThreadPosixImpl;
   friend class ThreadPosixImpl;

+ 11 - 9
panda/src/express/threadNsprImpl.cxx

@@ -23,7 +23,6 @@
 
 
 #include "pointerTo.h"
 #include "pointerTo.h"
 #include "config_express.h"
 #include "config_express.h"
-#include "mutexHolder.h"
 
 
 PRUintn ThreadNsprImpl::_pt_ptr_index = 0;
 PRUintn ThreadNsprImpl::_pt_ptr_index = 0;
 bool ThreadNsprImpl::_got_pt_ptr_index = false;
 bool ThreadNsprImpl::_got_pt_ptr_index = false;
@@ -89,11 +88,15 @@ ThreadNsprImpl::
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool ThreadNsprImpl::
 bool ThreadNsprImpl::
 start(ThreadPriority priority, bool global, bool joinable) {
 start(ThreadPriority priority, bool global, bool joinable) {
-  MutexHolder holder(_mutex);
+  _mutex.lock();
   if (thread_cat.is_debug()) {
   if (thread_cat.is_debug()) {
     thread_cat.debug() << "Starting thread " << _parent_obj->get_name() << "\n";
     thread_cat.debug() << "Starting thread " << _parent_obj->get_name() << "\n";
   }
   }
-  nassertr(_thread == (PRThread *)NULL, false);
+  nassertd(_thread == (PRThread *)NULL) {
+    _mutex.release();
+    return false;
+  }
+
   _joinable = joinable;
   _joinable = joinable;
 
 
   if (!_got_pt_ptr_index) {
   if (!_got_pt_ptr_index) {
@@ -107,10 +110,6 @@ start(ThreadPriority priority, bool global, bool joinable) {
     nspr_pri = PR_PRIORITY_LOW;
     nspr_pri = PR_PRIORITY_LOW;
     break;
     break;
 
 
-  case TP_normal:
-    nspr_pri = PR_PRIORITY_NORMAL;
-    break;
-
   case TP_high:
   case TP_high:
     nspr_pri = PR_PRIORITY_HIGH;
     nspr_pri = PR_PRIORITY_HIGH;
     break;
     break;
@@ -119,8 +118,8 @@ start(ThreadPriority priority, bool global, bool joinable) {
     nspr_pri = PR_PRIORITY_URGENT;
     nspr_pri = PR_PRIORITY_URGENT;
     break;
     break;
 
 
+  case TP_normal:
   default:
   default:
-    nassertr(false, false);
     nspr_pri = PR_PRIORITY_NORMAL;
     nspr_pri = PR_PRIORITY_NORMAL;
   }
   }
 
 
@@ -140,10 +139,12 @@ start(ThreadPriority priority, bool global, bool joinable) {
     // reference count we incremented above, and return false to
     // reference count we incremented above, and return false to
     // indicate failure.
     // indicate failure.
     unref_delete(_parent_obj);
     unref_delete(_parent_obj);
+    _mutex.release();
     return false;
     return false;
   }
   }
 
 
   // Thread was successfully started.
   // Thread was successfully started.
+  _mutex.release();
   return true;
   return true;
 }
 }
 
 
@@ -172,11 +173,12 @@ interrupt() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void ThreadNsprImpl::
 void ThreadNsprImpl::
 join() {
 join() {
-  MutexHolder holder(_mutex);
+  _mutex.lock();
   if (_joinable && _thread != (PRThread *)NULL) {
   if (_joinable && _thread != (PRThread *)NULL) {
     PR_JoinThread(_thread);
     PR_JoinThread(_thread);
     _thread = (PRThread *)NULL;
     _thread = (PRThread *)NULL;
   }
   }
+  _mutex.release();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 2 - 2
panda/src/express/threadNsprImpl.h

@@ -26,7 +26,7 @@
 
 
 #include "notify.h"
 #include "notify.h"
 #include "threadPriority.h"
 #include "threadPriority.h"
-#include "pmutex.h"
+#include "mutexNsprImpl.h"
 
 
 #include <prthread.h>
 #include <prthread.h>
 #include <prinit.h>
 #include <prinit.h>
@@ -57,7 +57,7 @@ private:
   static void root_func(void *data);
   static void root_func(void *data);
   static void init_pt_ptr_index();
   static void init_pt_ptr_index();
 
 
-  Mutex _mutex;
+  MutexNsprImpl _mutex;
   Thread *_parent_obj;
   Thread *_parent_obj;
   PRThread *_thread;
   PRThread *_thread;
   bool _joinable;
   bool _joinable;

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