Browse Source

move Pipeline* and Cycle* from putil, in order to pipeline the ClockObject class

David Rose 20 years ago
parent
commit
851eb4cd8b
44 changed files with 4998 additions and 52 deletions
  1. 41 2
      panda/src/express/Sources.pp
  2. 55 13
      panda/src/express/clockObject.I
  3. 47 30
      panda/src/express/clockObject.cxx
  4. 37 3
      panda/src/express/clockObject.h
  5. 2 0
      panda/src/express/config_express.cxx
  6. 0 1
      panda/src/express/config_express.h
  7. 27 0
      panda/src/express/cycleData.I
  8. 109 0
      panda/src/express/cycleData.cxx
  9. 81 0
      panda/src/express/cycleData.h
  10. 214 0
      panda/src/express/cycleDataReader.I
  11. 19 0
      panda/src/express/cycleDataReader.cxx
  12. 67 0
      panda/src/express/cycleDataReader.h
  13. 219 0
      panda/src/express/cycleDataStageReader.I
  14. 19 0
      panda/src/express/cycleDataStageReader.cxx
  15. 61 0
      panda/src/express/cycleDataStageReader.h
  16. 239 0
      panda/src/express/cycleDataStageWriter.I
  17. 19 0
      panda/src/express/cycleDataStageWriter.cxx
  18. 68 0
      panda/src/express/cycleDataStageWriter.h
  19. 309 0
      panda/src/express/cycleDataWriter.I
  20. 19 0
      panda/src/express/cycleDataWriter.cxx
  21. 71 0
      panda/src/express/cycleDataWriter.h
  22. 63 0
      panda/src/express/cyclerHolder.I
  23. 19 0
      panda/src/express/cyclerHolder.cxx
  24. 48 0
      panda/src/express/cyclerHolder.h
  25. 8 0
      panda/src/express/express_composite1.cxx
  26. 5 3
      panda/src/express/express_composite2.cxx
  27. 84 0
      panda/src/express/pipeline.I
  28. 377 0
      panda/src/express/pipeline.cxx
  29. 101 0
      panda/src/express/pipeline.h
  30. 287 0
      panda/src/express/pipelineCycler.I
  31. 19 0
      panda/src/express/pipelineCycler.cxx
  32. 171 0
      panda/src/express/pipelineCycler.h
  33. 52 0
      panda/src/express/pipelineCyclerBase.h
  34. 420 0
      panda/src/express/pipelineCyclerDummyImpl.I
  35. 19 0
      panda/src/express/pipelineCyclerDummyImpl.cxx
  36. 94 0
      panda/src/express/pipelineCyclerDummyImpl.h
  37. 83 0
      panda/src/express/pipelineCyclerLinks.I
  38. 55 0
      panda/src/express/pipelineCyclerLinks.h
  39. 396 0
      panda/src/express/pipelineCyclerTrivialImpl.I
  40. 19 0
      panda/src/express/pipelineCyclerTrivialImpl.cxx
  41. 100 0
      panda/src/express/pipelineCyclerTrivialImpl.h
  42. 381 0
      panda/src/express/pipelineCyclerTrueImpl.I
  43. 350 0
      panda/src/express/pipelineCyclerTrueImpl.cxx
  44. 124 0
      panda/src/express/pipelineCyclerTrueImpl.h

+ 41 - 2
panda/src/express/Sources.pp

@@ -26,6 +26,12 @@
     conditionVarPosixImpl.h conditionVarPosixImpl.I \
     conditionVarWin32Impl.h conditionVarWin32Impl.I \
     config_express.h \
+    cycleData.h cycleData.I \
+    cycleDataReader.h cycleDataReader.I \
+    cycleDataStageReader.h cycleDataStageReader.I \
+    cycleDataStageWriter.h cycleDataStageWriter.I \
+    cycleDataWriter.h cycleDataWriter.I \
+    cyclerHolder.h cyclerHolder.I \
     datagram.I datagram.h datagramGenerator.I \
     datagramGenerator.h \
     datagramIterator.I datagramIterator.h datagramSink.I datagramSink.h \
@@ -52,6 +58,13 @@
     ordered_vector.h ordered_vector.I ordered_vector.T \
     password_hash.h \
     patchfile.I patchfile.h \
+    pipeline.h pipeline.I \
+    pipelineCycler.h pipelineCycler.I \
+    pipelineCyclerLinks.h pipelineCyclerLinks.I \
+    pipelineCyclerBase.h  \
+    pipelineCyclerDummyImpl.h pipelineCyclerDummyImpl.I \
+    pipelineCyclerTrivialImpl.h pipelineCyclerTrivialImpl.I \
+    pipelineCyclerTrueImpl.h pipelineCyclerTrueImpl.I \
     pmutex.h pmutex.I \
     pointerTo.I pointerTo.h \
     pointerToArray.I pointerToArray.h \
@@ -105,7 +118,14 @@
     conditionVarNsprImpl.cxx \
     conditionVarPosixImpl.cxx \
     conditionVarWin32Impl.cxx \
-    config_express.cxx datagram.cxx datagramGenerator.cxx \
+    config_express.cxx \
+    cycleData.cxx \
+    cycleDataReader.cxx \
+    cycleDataStageReader.cxx \
+    cycleDataStageWriter.cxx \
+    cycleDataWriter.cxx \
+    cyclerHolder.cxx \
+    datagram.cxx datagramGenerator.cxx \
     datagramIterator.cxx \
     datagramSink.cxx dcast.cxx \
     encryptStreamBuf.cxx encryptStream.cxx \
@@ -118,6 +138,11 @@
     mutexDebug.cxx \
     mutexDirect.cxx \
     mutexHolder.cxx \
+    pipeline.cxx \
+    pipelineCycler.cxx \
+    pipelineCyclerDummyImpl.cxx \
+    pipelineCyclerTrivialImpl.cxx \
+    pipelineCyclerTrueImpl.cxx \
     pmutex.cxx \
     namable.cxx \
     nativeNumericData.cxx \
@@ -176,7 +201,14 @@
     conditionVarNsprImpl.h conditionVarNsprImpl.I \
     conditionVarPosixImpl.h conditionVarPosixImpl.I \
     conditionVarWin32Impl.h conditionVarWin32Impl.I \
-    config_express.h datagram.I datagram.h \
+    config_express.h \
+    cycleData.h cycleData.I \
+    cycleDataReader.h cycleDataReader.I \
+    cycleDataStageReader.h cycleDataStageReader.I \
+    cycleDataStageWriter.h cycleDataStageWriter.I \
+    cycleDataWriter.h cycleDataWriter.I \
+    cyclerHolder.h cyclerHolder.I \
+    datagram.I datagram.h \
     datagramGenerator.I datagramGenerator.h \
     datagramIterator.I datagramIterator.h \
     datagramSink.I datagramSink.h dcast.T dcast.h \
@@ -200,6 +232,13 @@
     ordered_vector.h ordered_vector.I ordered_vector.T \
     password_hash.h \
     patchfile.I patchfile.h \
+    pipeline.h pipeline.I \
+    pipelineCycler.h pipelineCycler.I \
+    pipelineCyclerLinks.h pipelineCyclerLinks.I \
+    pipelineCyclerBase.h  \
+    pipelineCyclerDummyImpl.h pipelineCyclerDummyImpl.I \
+    pipelineCyclerTrivialImpl.h pipelineCyclerTrivialImpl.I \
+    pipelineCyclerTrueImpl.h pipelineCyclerTrueImpl.I \
     pmutex.h pmutex.I \
     pointerTo.I pointerTo.h \
     pointerToArray.I pointerToArray.h \

+ 55 - 13
panda/src/express/clockObject.I

@@ -28,13 +28,35 @@ INLINE ClockObject::
 ////////////////////////////////////////////////////////////////////
 //     Function: ClockObject::set_mode
 //       Access: Published
-//  Description: Changes the mode of the clock, e.g. from normal
-//               (real-time) to non-real-time.  In non-real-time mode,
-//               tick() will add the value of dt to the value returned
-//               by get_frame_time(), regardless of how much time has
-//               actually passed.  In normal, real-time mode, tick()
-//               will set the value returned by get_frame_time() to
-//               the current real time.
+//  Description: Changes the mode of the clock.  Normally, the clock
+//               is in mode M_normal. In this mode, each call to
+//               tick() will set the value returned by
+//               get_frame_time() to the current real time; thus, the
+//               clock simply reports time advancing.
+//
+//               Other possible modes:
+//
+//               M_non_real_time - the clock ignores real time
+//               completely; at each call to tick(), it pretends that
+//               exactly dt seconds have elapsed since the last call
+//               to tick().  You may set the value of dt with
+//               set_dt().
+//
+//               M_forced - the clock forces the application to run at
+//               the rate specified by set_dt().  If the application
+//               would run faster than this rate, the clock will slow
+//               down the application; if the application would run
+//               slower than this rate, the clock slows down time so
+//               that the application believes it is running at the
+//               given rate.
+//
+//               M_degrade - the clock runs at real time, but the
+//               application is slowed down by a set factor of its
+//               frame rate, specified by set_degrade_factor().
+//
+//               M_slave - the clock does not advance, but relies on
+//               the user to call set_frame_time() and/or
+//               set_frame_count() each frame.
 ////////////////////////////////////////////////////////////////////
 INLINE void ClockObject::
 set_mode(ClockObject::Mode mode) {
@@ -66,7 +88,8 @@ get_mode() const {
 ////////////////////////////////////////////////////////////////////
 INLINE double ClockObject::
 get_frame_time() const {
-  return _reported_frame_time;
+  CDReader cdata(_cycler);
+  return cdata->_reported_frame_time;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -130,7 +153,8 @@ reset() {
 ////////////////////////////////////////////////////////////////////
 INLINE int ClockObject::
 get_frame_count() const {
-  return _frame_count;
+  CDReader cdata(_cycler);
+  return cdata->_frame_count;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -156,10 +180,11 @@ get_net_frame_rate() const {
 ////////////////////////////////////////////////////////////////////
 INLINE double ClockObject::
 get_dt() const {
+  CDReader cdata(_cycler);
   if (_max_dt > 0.0) {
-    return min(_max_dt, _dt);
+    return min(_max_dt, cdata->_dt);
   }
-  return _dt;
+  return cdata->_dt;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -172,7 +197,9 @@ get_dt() const {
 ////////////////////////////////////////////////////////////////////
 INLINE void ClockObject::
 set_dt(double dt) {
-  _dt = dt;
+  nassertv(Thread::get_current_pipeline_stage() == 0);
+  CDWriter cdata(_cycler);
+  cdata->_dt = dt;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -282,10 +309,11 @@ get_average_frame_rate_interval() const {
 ////////////////////////////////////////////////////////////////////
 INLINE double ClockObject::
 get_average_frame_rate() const {
+  CDStageReader cdata(_cycler, 0);
   if (_ticks.size() <= 1) {
     return 0.0;
   } else {
-    return _ticks.size() / (_reported_frame_time - _ticks.front());
+    return _ticks.size() / (cdata->_reported_frame_time - _ticks.front());
   }
 }
 
@@ -301,6 +329,7 @@ get_average_frame_rate() const {
 ////////////////////////////////////////////////////////////////////
 INLINE bool ClockObject::
 check_errors() {
+  CDReader cdata(_cycler);  // Just to hold a mutex.
   int orig_error_count = _error_count;
   _error_count = _true_clock->get_error_count();
   return (_error_count != orig_error_count);
@@ -321,6 +350,19 @@ get_global_clock() {
   return _global_clock;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: ClockObject::CData::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE ClockObject::CData::
+CData(const ClockObject::CData &copy) : 
+  _frame_count(copy._frame_count), 
+  _reported_frame_time(copy._reported_frame_time),
+  _dt(copy._dt)
+{
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: TimeVal::contructor
 //       Access: Published

+ 47 - 30
panda/src/express/clockObject.cxx

@@ -20,16 +20,10 @@
 #include "clockObject.h"
 #include "config_express.h"
 #include "configVariableEnum.h"
-
-#if defined(WIN32)
-  #define WINDOWS_LEAN_AND_MEAN
-  #include <wtypes.h>  // For select()
-  #undef WINDOWS_LEAN_AND_MEAN  
-#else
-  #include <sys/time.h>  // For select()
-#endif
+#include "thread.h"
 
 ClockObject *ClockObject::_global_clock = (ClockObject *)NULL;
+TypeHandle ClockObject::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
 //     Function: ClockObject::Constructor
@@ -47,10 +41,7 @@ ClockObject() {
 
   _start_short_time = _true_clock->get_short_time();
   _start_long_time = _true_clock->get_long_time();
-  _frame_count = 0;
   _actual_frame_time = 0.0;
-  _reported_frame_time = 0.0;
-  _dt = 1.0 / clock_frame_rate;
   _max_dt = max_dt;
   _degrade_factor = clock_degrade_factor;
   _average_frame_rate_interval = average_frame_rate_interval;
@@ -91,6 +82,7 @@ set_real_time(double time) {
 ////////////////////////////////////////////////////////////////////
 void ClockObject::
 set_frame_time(double time) {
+  nassertv(Thread::get_current_pipeline_stage() == 0);
 #ifdef NOTIFY_DEBUG
   if (this == _global_clock && _mode != M_slave) {
     express_cat.warning()
@@ -98,8 +90,9 @@ set_frame_time(double time) {
       << " seconds.\n";
   }
 #endif  // NOTIFY_DEBUG
+  CDWriter cdata(_cycler);
   _actual_frame_time = time;
-  _reported_frame_time = time;
+  cdata->_reported_frame_time = time;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -111,6 +104,7 @@ set_frame_time(double time) {
 ////////////////////////////////////////////////////////////////////
 void ClockObject::
 set_frame_count(int frame_count) {
+  nassertv(Thread::get_current_pipeline_stage() == 0);
 #ifdef NOTIFY_DEBUG
   if (this == _global_clock && _mode != M_slave) {
     express_cat.warning()
@@ -118,7 +112,8 @@ set_frame_count(int frame_count) {
       << frame_count - get_frame_count() << " frames.\n";
   }
 #endif  // NOTIFY_DEBUG
-  _frame_count = frame_count;
+  CDWriter cdata(_cycler);
+  cdata->_frame_count = frame_count;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -133,7 +128,9 @@ set_frame_count(int frame_count) {
 ////////////////////////////////////////////////////////////////////
 void ClockObject::
 tick() {
-  double old_reported_time = _reported_frame_time;
+  nassertv(Thread::get_current_pipeline_stage() == 0);
+  CDWriter cdata(_cycler);
+  double old_reported_time = cdata->_reported_frame_time;
 
   if (_mode != M_slave) {
     double old_time = _actual_frame_time;
@@ -142,39 +139,39 @@ tick() {
     switch (_mode) {
     case M_normal:
       // Time runs as it will; we simply report time elapsing.
-      _dt = _actual_frame_time - old_time;
-      _reported_frame_time = _actual_frame_time;
+      cdata->_dt = _actual_frame_time - old_time;
+      cdata->_reported_frame_time = _actual_frame_time;
       break;
       
     case M_non_real_time:
       // Ignore real time.  We always report the same interval having
       // elapsed each frame.
-      _reported_frame_time += _dt;
+      cdata->_reported_frame_time += cdata->_dt;
       break;
       
     case M_forced:
       // If we are running faster than the desired interval, slow down.
       // If we are running slower than the desired interval, ignore that
       // and pretend we're running at the specified rate.
-      wait_until(old_time + _dt);
-      _reported_frame_time += _dt;
+      wait_until(old_time + cdata->_dt);
+      cdata->_reported_frame_time += cdata->_dt;
       break;
       
     case M_degrade:
       // Each frame, wait a certain fraction of the previous frame's
       // time to degrade performance uniformly.
-      _dt = (_actual_frame_time - old_time) * _degrade_factor;
+      cdata->_dt = (_actual_frame_time - old_time) * _degrade_factor;
       
       if (_degrade_factor < 1.0) {
         // If the degrade_factor is less than one, we want to simulate a
         // higher frame rate by incrementing the clock more slowly.
-        _reported_frame_time += _dt;
+        cdata->_reported_frame_time += cdata->_dt;
         
       } else {
         // Otherwise, we simulate a lower frame rate by waiting until
         // the appropriate time has elapsed.
-        wait_until(old_time + _dt);
-        _reported_frame_time = _actual_frame_time;
+        wait_until(old_time + cdata->_dt);
+        cdata->_reported_frame_time = _actual_frame_time;
       }
       
       break;
@@ -184,13 +181,13 @@ tick() {
       break;
     }
 
-    _frame_count++;
+    cdata->_frame_count++;
   }
 
   if (_average_frame_rate_interval > 0.0) {
     _ticks.push_back(old_reported_time);
     while (!_ticks.empty() && 
-           _reported_frame_time - _ticks.front() > _average_frame_rate_interval) {
+           cdata->_reported_frame_time - _ticks.front() > _average_frame_rate_interval) {
       _ticks.pop_front();
     }
   }
@@ -213,7 +210,8 @@ tick() {
 void ClockObject::
 sync_frame_time() {
   if (_mode == M_normal) {
-    _reported_frame_time = get_real_time();
+    CDWriter cdata(_cycler);
+    cdata->_reported_frame_time = get_real_time();
   }
 }
 
@@ -229,10 +227,7 @@ wait_until(double want_time) {
   double wait_interval = (want_time - _actual_frame_time) - sleep_precision;
     
   if (wait_interval > 0.0) {
-    struct timeval tv;
-    tv.tv_sec = (int)(wait_interval);
-    tv.tv_usec = (int)((wait_interval - tv.tv_sec) * 1000000.0);
-    select(0, NULL, NULL, NULL, &tv);
+    Thread::sleep(wait_interval);
   }
   
   // Now busy-wait until the actual time elapses.
@@ -262,6 +257,28 @@ make_global_clock() {
   _global_clock->set_mode(clock_mode);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: ClockObject::CData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+ClockObject::CData::
+CData() {
+  _frame_count = 0;
+  _reported_frame_time = 0.0;
+  _dt = 1.0 / clock_frame_rate;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ClockObject::CData::make_copy
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CycleData *ClockObject::CData::
+make_copy() const {
+  return new CData(*this);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ClockObject::Mode ostream operator
 //  Description:

+ 37 - 3
panda/src/express/clockObject.h

@@ -23,6 +23,11 @@
 
 #include "trueClock.h"
 #include "pdeque.h"
+#include "cycleData.h"
+#include "cycleDataReader.h"
+#include "cycleDataWriter.h"
+#include "cycleDataStageReader.h"
+#include "pipelineCycler.h"
 
 class EXPCL_PANDAEXPRESS TimeVal {
 PUBLISHED:
@@ -113,10 +118,7 @@ private:
   Mode _mode;
   double _start_short_time;
   double _start_long_time;
-  int _frame_count;
   double _actual_frame_time;
-  double _reported_frame_time;
-  double _dt;
   double _max_dt;
   double _degrade_factor;
   int _error_count;
@@ -127,7 +129,39 @@ private:
   typedef pdeque<double> Ticks;
   Ticks _ticks;
 
+  // This is the data that needs to be cycled each frame.
+  class EXPCL_PANDAEXPRESS CData : public CycleData {
+  public:
+    CData();
+    INLINE CData(const CData &copy);
+
+    virtual CycleData *make_copy() const;
+    virtual TypeHandle get_parent_type() const {
+      return ClockObject::get_class_type();
+    }
+
+    int _frame_count;
+    double _reported_frame_time;
+    double _dt;
+  };
+
+  PipelineCycler<CData> _cycler;
+  typedef CycleDataReader<CData> CDReader;
+  typedef CycleDataWriter<CData> CDWriter;
+  typedef CycleDataStageReader<CData> CDStageReader;
+
   static ClockObject *_global_clock;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    register_type(_type_handle, "ClockObject");
+  }
+
+private:
+  static TypeHandle _type_handle;
 };
 
 EXPCL_PANDAEXPRESS ostream &

+ 2 - 0
panda/src/express/config_express.cxx

@@ -18,6 +18,7 @@
 
 
 #include "config_express.h"
+#include "clockObject.h"
 #include "datagram.h"
 #include "mainThread.h"
 #include "externalThread.h"
@@ -168,6 +169,7 @@ init_libexpress() {
   }
   initialized = true;
 
+  ClockObject::init_type();
   Datagram::init_type();
   MainThread::init_type();
   ExternalThread::init_type();

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

@@ -21,7 +21,6 @@
 
 #include "pandabase.h"
 #include "notifyCategoryProxy.h"
-#include "clockObject.h"
 #include "dconfig.h"
 
 // We include these files to force them to be instrumented by

+ 27 - 0
panda/src/express/cycleData.I

@@ -0,0 +1,27 @@
+// Filename: cycleData.I
+// Created by:  drose (21Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: CycleData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE CycleData::
+CycleData() {
+}

+ 109 - 0
panda/src/express/cycleData.cxx

@@ -0,0 +1,109 @@
+// Filename: cycleData.cxx
+// Created by:  drose (21Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "cycleData.h"
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleData::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CycleData::
+~CycleData() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void CycleData::
+write_datagram(BamWriter *, Datagram &) const {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void CycleData::
+write_datagram(BamWriter *, Datagram &, void *) const {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleData::complete_pointers
+//       Access: Public, Virtual
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int CycleData::
+complete_pointers(TypedWritable **, BamReader *) {
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleData::fillin
+//       Access: Public, Virtual
+//  Description: This internal function is intended to be called by
+//               each class's make_from_bam() method to read in all of
+//               the relevant data from the BamFile for the new
+//               object.
+////////////////////////////////////////////////////////////////////
+void CycleData::
+fillin(DatagramIterator &, BamReader *) {
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleData::fillin
+//       Access: Public, Virtual
+//  Description: This internal function is intended to be called by
+//               each class's make_from_bam() method to read in all of
+//               the relevant data from the BamFile for the new
+//               object.
+////////////////////////////////////////////////////////////////////
+void CycleData::
+fillin(DatagramIterator &, BamReader *, void *) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleData::get_parent_type
+//       Access: Public, Virtual
+//  Description: Returns the type of the container that owns the
+//               CycleData.  This is useful mainly for debugging.
+////////////////////////////////////////////////////////////////////
+TypeHandle CycleData::
+get_parent_type() const {
+  return TypeHandle::none();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleData::output
+//       Access: Public, Virtual
+//  Description: Formats the contents of the CycleData in some
+//               meaningful way for humans.  This is useful mainly for
+//               debugging.
+////////////////////////////////////////////////////////////////////
+void CycleData::
+output(ostream &out) const {
+  out << get_parent_type() << "::CData";
+}

+ 81 - 0
panda/src/express/cycleData.h

@@ -0,0 +1,81 @@
+// Filename: cycleData.h
+// Created by:  drose (21Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 CYCLEDATA_H
+#define CYCLEDATA_H
+
+#include "pandabase.h"
+#include "typeHandle.h"
+#include "referenceCount.h"
+
+class BamWriter;
+class BamReader;
+class TypedWritable;
+class Datagram;
+class DatagramIterator;
+
+////////////////////////////////////////////////////////////////////
+//       Class : CycleData
+// Description : A single page of data maintained by a PipelineCycler.
+//               Normally you should inherit from this class to define
+//               the data structures that are important to protect
+//               between stages of a pipeline.  See PipelineCycler.
+////////////////////////////////////////////////////////////////////
+#ifdef DO_PIPELINING
+
+// If we are compiling in pipelining support, we maintain a pointer to
+// a CycleData object in each containing class, instead of the object
+// itself.  Thus, it should be a ReferenceCount object.
+class EXPCL_PANDAEXPRESS CycleData : public ReferenceCount 
+
+#else  // !DO_PIPELINING
+
+// If we are *not* compiling in pipelining support, the CycleData
+// object is stored directly within its containing classes, and hence
+// should not be a ReferenceCount object.
+class EXPCL_PANDAEXPRESS CycleData
+
+#endif  // DO_PIPELINING
+{
+public:
+  INLINE CycleData();
+  virtual ~CycleData();
+
+  virtual CycleData *make_copy() const=0;
+
+  virtual void write_datagram(BamWriter *, Datagram &) const;
+  virtual void write_datagram(BamWriter *, Datagram &, void *extra_data) const;
+  virtual int complete_pointers(TypedWritable **p_list, BamReader *manager);
+  virtual void fillin(DatagramIterator &scan, BamReader *manager);
+  virtual void fillin(DatagramIterator &scan, BamReader *manager,
+                      void *extra_data);
+
+  virtual TypeHandle get_parent_type() const;
+  virtual void output(ostream &out) const;
+};
+
+INLINE ostream &
+operator << (ostream &out, const CycleData &cd) {
+  cd.output(out);
+  return out;
+}
+
+#include "cycleData.I"
+
+#endif
+

+ 214 - 0
panda/src/express/cycleDataReader.I

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

+ 19 - 0
panda/src/express/cycleDataReader.cxx

@@ -0,0 +1,19 @@
+// Filename: cycleDataReader.cxx
+// Created by:  drose (21Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "cycleDataReader.h"

+ 67 - 0
panda/src/express/cycleDataReader.h

@@ -0,0 +1,67 @@
+// Filename: cycleDataReader.h
+// Created by:  drose (21Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 CYCLEDATAREADER_H
+#define CYCLEDATAREADER_H
+
+#include "pandabase.h"
+
+#include "cycleData.h"
+#include "pipelineCycler.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : CycleDataReader
+// Description : This template class calls PipelineCycler::read() in
+//               the constructor and PipelineCycler::release_read() in
+//               the destructor.  In the interim, it provides a
+//               transparent read-only access to the CycleData.
+//
+//               It exists as a syntactic convenience to access the
+//               data in the CycleData.  It also allows the whole
+//               system to compile down to nothing if
+//               SUPPORT_PIPELINING is not defined.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+class CycleDataReader {
+public:
+  INLINE CycleDataReader(const PipelineCycler<CycleDataType> &cycler);
+  INLINE CycleDataReader(const CycleDataReader<CycleDataType> &copy);
+  INLINE void operator = (const CycleDataReader<CycleDataType> &copy);
+
+  INLINE ~CycleDataReader();
+
+  INLINE const CycleDataType *operator -> () const;
+  INLINE operator const CycleDataType * () const;
+
+  INLINE const CycleDataType *take_pointer();
+
+private:
+#ifdef DO_PIPELINING
+  // This is the data stored for a real pipelining implementation.
+  const PipelineCycler<CycleDataType> *_cycler;
+  const CycleDataType *_pointer;
+  CycleDataType *_write_pointer;
+#else  // !DO_PIPELINING
+  // This is all we need for the trivial, do-nothing implementation.
+  const CycleDataType *_pointer;
+#endif  // DO_PIPELINING
+};
+
+#include "cycleDataReader.I"
+
+#endif

+ 219 - 0
panda/src/express/cycleDataStageReader.I

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

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

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

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

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

+ 239 - 0
panda/src/express/cycleDataStageWriter.I

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

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

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

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

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

+ 309 - 0
panda/src/express/cycleDataWriter.I

@@ -0,0 +1,309 @@
+// Filename: cycleDataWriter.I
+// Created by:  drose (21Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifdef DO_PIPELINING
+// This is the implementation for full support of pipelining (as well
+// as the sanity-check only implementation).
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::Constructor (full)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataWriter<CycleDataType>::
+CycleDataWriter(PipelineCycler<CycleDataType> &cycler) :
+  _cycler(&cycler)
+{
+  _pointer = _cycler->write();
+  nassertv(_pointer != (CycleDataType *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::Constructor (full)
+//       Access: Public
+//  Description: This two-parameter constructor, with a bool parameter
+//               for the second parameter, automatically propagates
+//               the CycleData pointer upstream from the current
+//               stage, either stopping at the first pointer
+//               encountered that's different, going or all the way to
+//               stage 0, according to force_to_0.  See
+//               PipelineCycler::write_upstream().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataWriter<CycleDataType>::
+CycleDataWriter(PipelineCycler<CycleDataType> &cycler, bool force_to_0) :
+  _cycler(&cycler)
+{
+  _pointer = _cycler->write_upstream(force_to_0);
+  nassertv(_pointer != (CycleDataType *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::Copy Constructor (full)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataWriter<CycleDataType>::
+CycleDataWriter(const CycleDataWriter<CycleDataType> &copy) :
+  _cycler(copy._cycler),
+  _pointer(copy._pointer)
+{
+  nassertv(_pointer != (CycleDataType *)NULL);
+  _cycler->increment_write(_pointer);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::Copy Assigment (full)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE void CycleDataWriter<CycleDataType>::
+operator = (const CycleDataWriter<CycleDataType> &copy) {
+  nassertv(_pointer == (CycleDataType *)NULL);
+
+  _cycler = copy._cycler;
+  _pointer = copy._pointer;
+
+  nassertv(_pointer != (CycleDataType *)NULL);
+  _cycler->increment_write(_pointer);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::Constructor (full)
+//       Access: Public
+//  Description: This flavor of the constructor elevates the pointer
+//               from the CycleDataReader from a read to a write
+//               pointer (and invalidates the reader).
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataWriter<CycleDataType>::
+CycleDataWriter(PipelineCycler<CycleDataType> &cycler,
+                CycleDataReader<CycleDataType> &take_from) :
+  _cycler(&cycler)
+{
+  _pointer = _cycler->elevate_read(take_from.take_pointer());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::Constructor (full)
+//       Access: Public
+//  Description: This flavor of the constructor elevates the pointer
+//               from the CycleDataReader from a read to a write
+//               pointer (and invalidates the reader).  It also
+//               propagates the pointer back upstream; see
+//               PipelineCycler::write_upstream().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataWriter<CycleDataType>::
+CycleDataWriter(PipelineCycler<CycleDataType> &cycler,
+                CycleDataReader<CycleDataType> &take_from,
+		bool force_to_0) :
+  _cycler(&cycler)
+{
+  _pointer = _cycler->elevate_read_upstream(take_from.take_pointer(), force_to_0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::Destructor (full)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataWriter<CycleDataType>::
+~CycleDataWriter() {
+  if (_pointer != (CycleDataType *)NULL) {
+    _cycler->release_write(_pointer);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::operator -> (full)
+//       Access: Public
+//  Description: This provides an indirect member access to the actual
+//               CycleData data.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *CycleDataWriter<CycleDataType>::
+operator -> () {
+  nassertr(_pointer != (CycleDataType *)NULL, _cycler->cheat());
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::operator -> (full)
+//       Access: Public
+//  Description: This provides an indirect member access to the actual
+//               CycleData data.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *CycleDataWriter<CycleDataType>::
+operator -> () const {
+  nassertr(_pointer != (CycleDataType *)NULL, _cycler->cheat());
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::Typecast pointer (full)
+//       Access: Public
+//  Description: This allows the CycleDataWriter to be passed to any
+//               function that expects a CycleDataType pointer.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataWriter<CycleDataType>::
+operator CycleDataType * () {
+  nassertr(_pointer != (CycleDataType *)NULL, _cycler->cheat());
+  return _pointer;
+}
+
+#else  // !DO_PIPELINING
+// This is the trivial, do-nothing implementation.
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::Constructor (trivial)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataWriter<CycleDataType>::
+CycleDataWriter(PipelineCycler<CycleDataType> &cycler) {
+  _pointer = cycler.write();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::Constructor (full)
+//       Access: Public
+//  Description: This two-parameter constructor, with a bool parameter
+//               for the second parameter, automatically propagates
+//               the CycleData pointer upstream from the current
+//               stage, either stopping at the first pointer
+//               encountered that's different, going or all the way to
+//               stage 0, according to force_to_0.  See
+//               PipelineCycler::write_upstream().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataWriter<CycleDataType>::
+CycleDataWriter(PipelineCycler<CycleDataType> &cycler, bool) {
+  _pointer = cycler.write();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::Copy Constructor (trivial)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataWriter<CycleDataType>::
+CycleDataWriter(const CycleDataWriter<CycleDataType> &copy) :
+  _pointer(copy._pointer)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::Copy Assigment (trivial)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE void CycleDataWriter<CycleDataType>::
+operator = (const CycleDataWriter<CycleDataType> &copy) {
+  _pointer = copy._pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::Constructor (trivial)
+//       Access: Public
+//  Description: This flavor of the constructor elevates the pointer
+//               from the CycleDataReader from a read to a write
+//               pointer (and invalidates the reader).
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataWriter<CycleDataType>::
+CycleDataWriter(PipelineCycler<CycleDataType> &,
+                CycleDataReader<CycleDataType> &take_from) :
+  _pointer((CycleDataType *)take_from.take_pointer())
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::Constructor (trivial)
+//       Access: Public
+//  Description: This flavor of the constructor elevates the pointer
+//               from the CycleDataReader from a read to a write
+//               pointer (and invalidates the reader).  It also
+//               propagates the pointer back upstream; see
+//               PipelineCycler::write_upstream().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataWriter<CycleDataType>::
+CycleDataWriter(PipelineCycler<CycleDataType> &,
+                CycleDataReader<CycleDataType> &take_from,
+		bool force_to_0) :
+  _pointer((CycleDataType *)take_from.take_pointer())
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::Destructor (trivial)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataWriter<CycleDataType>::
+~CycleDataWriter() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::operator -> (trivial)
+//       Access: Public
+//  Description: This provides an indirect member access to the actual
+//               CycleData data.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *CycleDataWriter<CycleDataType>::
+operator -> () {
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::operator -> (trivial)
+//       Access: Public
+//  Description: This provides an indirect member access to the actual
+//               CycleData data.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *CycleDataWriter<CycleDataType>::
+operator -> () const {
+  return _pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataWriter::Typecast pointer (trivial)
+//       Access: Public
+//  Description: This allows the CycleDataWriter to be passed to any
+//               function that expects a CycleDataType pointer.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataWriter<CycleDataType>::
+operator CycleDataType * () {
+  return _pointer;
+}
+
+#endif  // DO_PIPELINING

+ 19 - 0
panda/src/express/cycleDataWriter.cxx

@@ -0,0 +1,19 @@
+// Filename: cycleDataWriter.cxx
+// Created by:  drose (21Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "cycleDataWriter.h"

+ 71 - 0
panda/src/express/cycleDataWriter.h

@@ -0,0 +1,71 @@
+// Filename: cycleDataWriter.h
+// Created by:  drose (21Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 CYCLEDATAWRITER_H
+#define CYCLEDATAWRITER_H
+
+#include "pandabase.h"
+
+#include "cycleData.h"
+#include "pipelineCycler.h"
+#include "cycleDataReader.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : CycleDataWriter
+// Description : This template class calls PipelineCycler::write() in
+//               the constructor and PipelineCycler::release_write() in
+//               the destructor.  In the interim, it provides a
+//               transparent read-write access to the CycleData.
+//
+//               It exists as a syntactic convenience to access the
+//               data in the CycleData.  It also allows the whole
+//               system to compile down to nothing if
+//               SUPPORT_PIPELINING is not defined.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+class CycleDataWriter {
+public:
+  INLINE CycleDataWriter(PipelineCycler<CycleDataType> &cycler);
+  INLINE CycleDataWriter(PipelineCycler<CycleDataType> &cycler, bool force_to_0);
+  INLINE CycleDataWriter(const CycleDataWriter<CycleDataType> &copy);
+  INLINE void operator = (const CycleDataWriter<CycleDataType> &copy);
+
+  INLINE CycleDataWriter(PipelineCycler<CycleDataType> &cycler, CycleDataReader<CycleDataType> &take_from);
+  INLINE CycleDataWriter(PipelineCycler<CycleDataType> &cycler, CycleDataReader<CycleDataType> &take_from, bool force_to_0);
+
+  INLINE ~CycleDataWriter();
+
+  INLINE CycleDataType *operator -> ();
+  INLINE const CycleDataType *operator -> () const;
+
+  INLINE operator CycleDataType * ();
+
+private:
+#ifdef DO_PIPELINING
+  // This is the data stored for a real pipelining implementation.
+  PipelineCycler<CycleDataType> *_cycler;
+  CycleDataType *_pointer;
+#else  // !DO_PIPELINING
+  // This is all we need for the trivial, do-nothing implementation.
+  CycleDataType *_pointer;
+#endif  // DO_PIPELINING
+};
+
+#include "cycleDataWriter.I"
+
+#endif

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

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

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

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

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

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

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

@@ -14,6 +14,12 @@
 #include "conditionVarPosixImpl.cxx"
 #include "conditionVarWin32Impl.cxx"
 #include "config_express.cxx"
+#include "cycleData.cxx"
+#include "cycleDataReader.cxx"
+#include "cycleDataStageReader.cxx"
+#include "cycleDataStageWriter.cxx"
+#include "cycleDataWriter.cxx"
+#include "cyclerHolder.cxx"
 #include "datagram.cxx"
 #include "datagramGenerator.cxx"
 #include "datagramIterator.cxx"
@@ -37,3 +43,5 @@
 #include "namable.cxx"
 #include "nativeNumericData.cxx"
 #include "ordered_vector.cxx"
+#include "patchfile.cxx"
+#include "password_hash.cxx"

+ 5 - 3
panda/src/express/express_composite2.cxx

@@ -1,5 +1,8 @@
-#include "patchfile.cxx"
-#include "password_hash.cxx"
+#include "pipeline.cxx"
+#include "pipelineCycler.cxx"
+#include "pipelineCyclerDummyImpl.cxx"
+#include "pipelineCyclerTrivialImpl.cxx"
+#include "pipelineCyclerTrueImpl.cxx"
 #include "pmutex.cxx"
 #include "pointerTo.cxx"
 #include "pointerToArray.cxx"
@@ -42,4 +45,3 @@
 #include "windowsRegistry.cxx"
 #include "zStream.cxx"
 #include "zStreamBuf.cxx"
-

+ 84 - 0
panda/src/express/pipeline.I

@@ -0,0 +1,84 @@
+// Filename: pipeline.I
+// Created by:  drose (21Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: Pipeline::get_render_pipeline
+//       Access: Public, Static
+//  Description: Returns a pointer to the global render pipeline.
+////////////////////////////////////////////////////////////////////
+INLINE Pipeline *Pipeline::
+get_render_pipeline() {
+  if (_render_pipeline == (Pipeline *)NULL) {
+    make_render_pipeline();
+  }
+  return _render_pipeline;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Pipeline::set_min_stages
+//       Access: Public
+//  Description: Ensures that at least the indicated number of stages
+//               are in the pipeline.
+////////////////////////////////////////////////////////////////////
+INLINE void Pipeline::
+set_min_stages(int min_stages) {
+  set_num_stages(max(min_stages, get_num_stages()));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Pipeline::get_num_stages
+//       Access: Public
+//  Description: Returns the number of stages required for the
+//               pipeline.
+////////////////////////////////////////////////////////////////////
+INLINE int Pipeline::
+get_num_stages() const {
+  return _num_stages;
+}
+
+#ifdef THREADED_PIPELINE
+////////////////////////////////////////////////////////////////////
+//     Function: Pipeline::get_num_cyclers
+//       Access: Public
+//  Description: Returns the number of PipelineCyclers in the universe
+//               that reference this Pipeline object.
+////////////////////////////////////////////////////////////////////
+INLINE int Pipeline::
+get_num_cyclers() const {
+  ReMutexHolder holder(_lock);
+  return _num_cyclers;
+}
+#endif  // THREADED_PIPELINE
+
+#ifdef THREADED_PIPELINE
+////////////////////////////////////////////////////////////////////
+//     Function: Pipeline::get_num_dirty_cyclers
+//       Access: Public
+//  Description: Returns the number of PipelineCyclers in the universe
+//               that reference this Pipeline object and are currently
+//               marked "dirty"; that is, there is a difference in
+//               pointer value between some of their stages.
+////////////////////////////////////////////////////////////////////
+INLINE int Pipeline::
+get_num_dirty_cyclers() const {
+  ReMutexHolder holder(_lock);
+  return _dirty_cyclers.size();
+}
+#endif  // THREADED_PIPELINE
+

+ 377 - 0
panda/src/express/pipeline.cxx

@@ -0,0 +1,377 @@
+// Filename: pipeline.cxx
+// Created by:  drose (21Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "pipeline.h"
+#include "pipelineCyclerTrueImpl.h"
+#include "reMutexHolder.h"
+#include "configVariableInt.h"
+#include "config_express.h"
+
+Pipeline *Pipeline::_render_pipeline = (Pipeline *)NULL;
+
+////////////////////////////////////////////////////////////////////
+//     Function: Pipeline::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+Pipeline::
+Pipeline(const string &name, int num_stages) :
+  Namable(name)
+{
+#ifdef THREADED_PIPELINE
+  // Set up the linked list of cyclers to be a circular list that
+  // begins with this object.
+  _prev = this;
+  _next = this;
+
+  _num_cyclers = 0;
+  _cycling = false;
+
+#endif  // THREADED_PIPELINE
+
+  set_num_stages(num_stages);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Pipeline::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+Pipeline::
+~Pipeline() {
+#ifdef THREADED_PIPELINE
+  nassertv(_num_cyclers == 0);
+  nassertv(_prev == this && _next == this);
+  _prev = NULL;
+  _next = NULL;
+  nassertv(!_cycling);
+#endif  // THREADED_PIPELINE
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Pipeline::cycle
+//       Access: Public
+//  Description: Flows all the pipeline data down to the next stage.
+////////////////////////////////////////////////////////////////////
+void Pipeline::
+cycle() {
+#ifdef THREADED_PIPELINE
+  pvector< PT(CycleData) > saved_cdatas;
+  saved_cdatas.reserve(_dirty_cyclers.size());
+  {
+    ReMutexHolder holder(_lock);
+    if (_num_stages == 1) {
+      // No need to cycle if there's only one stage.
+      nassertv(_dirty_cyclers.empty());
+      return;
+    }
+    
+    nassertv(!_cycling);
+    _cycling = true;
+    
+    Cyclers next_dirty_cyclers;
+
+    Cyclers::iterator ci;
+    switch (_num_stages) {
+    case 2:
+      for (ci = _dirty_cyclers.begin(); ci != _dirty_cyclers.end(); ++ci) {
+        PipelineCyclerTrueImpl *cycler = (*ci);
+        ReMutexHolder holder2(cycler->_lock);
+        
+        // We save the result of cycle(), so that we can defer the
+        // side-effects that might occur when CycleDatas destruct, at
+        // least until the end of this loop.
+        saved_cdatas.push_back(cycler->cycle_2());
+        
+        if (cycler->_dirty) {
+          // The cycler is still dirty after cycling.  Preserve it in the
+          // set for next time.
+          bool inserted = next_dirty_cyclers.insert(cycler).second;
+          nassertv(inserted);
+        } else {
+#ifdef DEBUG_THREADS
+          inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
+#endif
+        }
+      }
+      break;
+
+    case 3:
+      for (ci = _dirty_cyclers.begin(); ci != _dirty_cyclers.end(); ++ci) {
+        PipelineCyclerTrueImpl *cycler = (*ci);
+        ReMutexHolder holder2(cycler->_lock);
+        
+        saved_cdatas.push_back(cycler->cycle_3());
+        if (cycler->_dirty) {
+          bool inserted = next_dirty_cyclers.insert(cycler).second;
+          nassertv(inserted);
+        } else {
+#ifdef DEBUG_THREADS
+          inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
+#endif
+        }
+      }
+      break;
+
+    default:
+      for (ci = _dirty_cyclers.begin(); ci != _dirty_cyclers.end(); ++ci) {
+        PipelineCyclerTrueImpl *cycler = (*ci);
+        ReMutexHolder holder2(cycler->_lock);
+        
+        saved_cdatas.push_back(cycler->cycle());
+        if (cycler->_dirty) {
+          bool inserted = next_dirty_cyclers.insert(cycler).second;
+          nassertv(inserted);
+        } else {
+#ifdef DEBUG_THREADS
+          inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
+#endif
+        }
+      }
+      break;
+    }
+      
+    // Finally, we're ready for the next frame.
+    _dirty_cyclers.swap(next_dirty_cyclers);
+    _cycling = false;
+  }
+
+  // And now it's safe to let the CycleData pointers in saved_cdatas
+  // destruct, which may cause cascading deletes, and which will in
+  // turn case PipelineCyclers to remove themselves from (or add
+  // themselves to) the _dirty_cyclers list.
+
+#endif  // THREADED_PIPELINE
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Pipeline::set_num_stages
+//       Access: Public
+//  Description: Specifies the number of stages required for the
+//               pipeline.
+////////////////////////////////////////////////////////////////////
+void Pipeline::
+set_num_stages(int num_stages) {
+  nassertv(num_stages >= 1);
+#ifdef THREADED_PIPELINE
+  ReMutexHolder holder(_lock);
+  if (num_stages != _num_stages) {
+
+    // We need to lock every PipelineCycler object attached to this
+    // pipeline before we can adjust the number of stages.
+    PipelineCyclerLinks *links;
+    for (links = this->_next; links != this; links = links->_next) {
+      PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
+      cycler->_lock.lock();
+    }
+
+    _num_stages = num_stages;
+
+    for (links = this->_next; links != this; links = links->_next) {
+      PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
+      cycler->set_num_stages(num_stages);
+    }
+
+    // Now release them all.
+    int count = 0;
+    for (links = this->_next; links != this; links = links->_next) {
+      PipelineCyclerTrueImpl *cycler = (PipelineCyclerTrueImpl *)links;
+      cycler->_lock.release();
+      ++count;
+    }
+    nassertv(count == _num_cyclers);
+  }
+
+#else  // THREADED_PIPELINE
+  if (num_stages != 1) {
+    express_cat.warning()
+      << "Requested " << num_stages
+      << " pipeline stages but multithreaded render pipelines not enabled in build.\n";
+  }
+  _num_stages = 1;
+#endif  // THREADED_PIPELINE
+}
+
+#ifdef THREADED_PIPELINE
+////////////////////////////////////////////////////////////////////
+//     Function: Pipeline::add_cycler
+//       Access: Public
+//  Description: Adds the indicated cycler to the list of cyclers
+//               associated with the pipeline.  This method only
+//               exists when true pipelining is configured on.
+////////////////////////////////////////////////////////////////////
+void Pipeline::
+add_cycler(PipelineCyclerTrueImpl *cycler) {
+  ReMutexHolder holder(_lock);
+  nassertv(!cycler->_dirty);
+  nassertv(!_cycling);
+  ++_num_cyclers;
+  cycler->insert_before(this);
+  
+#ifdef DEBUG_THREADS
+  inc_cycler_type(_all_cycler_types, cycler->get_parent_type(), 1);
+#endif
+}
+#endif  // THREADED_PIPELINE
+
+#ifdef THREADED_PIPELINE
+////////////////////////////////////////////////////////////////////
+//     Function: Pipeline::add_dirty_cycler
+//       Access: Public
+//  Description: Marks the indicated cycler as "dirty", meaning it
+//               will need to be cycled next frame.  This both adds it
+//               to the "dirty" set and also sets the "dirty" flag
+//               within the cycler.  This method only exists when true
+//               pipelining is configured on.
+////////////////////////////////////////////////////////////////////
+void Pipeline::
+add_dirty_cycler(PipelineCyclerTrueImpl *cycler) {
+  nassertv(cycler->_lock.debug_is_locked());
+
+  ReMutexHolder holder(_lock);
+  nassertv(!_cycling);
+  nassertv(!cycler->_dirty);
+  cycler->_dirty = true;
+
+  bool inserted = _dirty_cyclers.insert(cycler).second;
+  nassertv(inserted);
+
+#ifdef DEBUG_THREADS
+  inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), 1);
+#endif
+}
+#endif  // THREADED_PIPELINE
+
+#ifdef THREADED_PIPELINE
+////////////////////////////////////////////////////////////////////
+//     Function: Pipeline::remove_cycler
+//       Access: Public
+//  Description: Removes the indicated cycler from the list of cyclers
+//               associated with the pipeline.  This method only
+//               exists when true pipelining is configured on.
+////////////////////////////////////////////////////////////////////
+void Pipeline::
+remove_cycler(PipelineCyclerTrueImpl *cycler) {
+  nassertv(cycler->_lock.debug_is_locked());
+
+  ReMutexHolder holder(_lock);
+  nassertv(!_cycling);
+
+  --_num_cyclers;
+  cycler->remove_from_list();
+
+#ifdef DEBUG_THREADS
+  inc_cycler_type(_all_cycler_types, cycler->get_parent_type(), -1);
+#endif
+
+  if (cycler->_dirty) {
+    cycler->_dirty = false;
+    Cyclers::iterator ci = _dirty_cyclers.find(cycler);
+    nassertv(ci != _dirty_cyclers.end());
+    _dirty_cyclers.erase(ci);
+#ifdef DEBUG_THREADS
+    inc_cycler_type(_dirty_cycler_types, cycler->get_parent_type(), -1);
+#endif
+  }
+}
+#endif  // THREADED_PIPELINE
+
+#if defined(THREADED_PIPELINE) && defined(DEBUG_THREADS) 
+////////////////////////////////////////////////////////////////////
+//     Function: Pipeline::iterate_all_cycler_types
+//       Access: Public
+//  Description: Walks through the list of all the different
+//               PipelineCycler types in the universe.  For each one,
+//               calls the indicated callback function with the
+//               TypeHandle of the respective type (actually, the
+//               result of cycler::get_parent_type()) and the count of
+//               pipeline cyclers of that type.  Mainly used for
+//               PStats reporting.
+////////////////////////////////////////////////////////////////////
+void Pipeline::
+iterate_all_cycler_types(CallbackFunc *func, void *data) const {
+  ReMutexHolder holder(_lock);
+  TypeCount::const_iterator ci;
+  for (ci = _all_cycler_types.begin(); ci != _all_cycler_types.end(); ++ci) {
+    func((*ci).first, (*ci).second, data);
+  }
+}
+#endif  // THREADED_PIPELINE && DEBUG_THREADS
+
+#if defined(THREADED_PIPELINE) && defined(DEBUG_THREADS) 
+////////////////////////////////////////////////////////////////////
+//     Function: Pipeline::iterate_dirty_cycler_types
+//       Access: Public
+//  Description: Walks through the list of all the different
+//               PipelineCycler types, for only the dirty
+//               PipelineCyclers.  See also
+//               iterate_all_cycler_types().
+////////////////////////////////////////////////////////////////////
+void Pipeline::
+iterate_dirty_cycler_types(CallbackFunc *func, void *data) const {
+  ReMutexHolder holder(_lock);
+  TypeCount::const_iterator ci;
+  for (ci = _dirty_cycler_types.begin(); ci != _dirty_cycler_types.end(); ++ci) {
+    func((*ci).first, (*ci).second, data);
+  }
+}
+#endif  // THREADED_PIPELINE && DEBUG_THREADS
+
+////////////////////////////////////////////////////////////////////
+//     Function: Pipeline::make_render_pipeline
+//       Access: Private, Static
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void Pipeline::
+make_render_pipeline() {
+  ConfigVariableInt pipeline_stages
+    ("pipeline-stages", 1,
+     PRC_DESC("The initial number of stages in the render pipeline.  This is "
+              "only meaningful if threaded pipelining is compiled into "
+              "Panda.  In most cases, you should not set this at all anyway, "
+              "since the pipeline can automatically grow stages as needed, "
+              "but it will not remove stages automatically, and having more "
+              "pipeline stages than your application requires will incur "
+              "additional runtime overhead."));
+
+  nassertv(_render_pipeline == (Pipeline *)NULL);
+  _render_pipeline = new Pipeline("render", pipeline_stages);
+}
+
+#if defined(THREADED_PIPELINE) && defined(DEBUG_THREADS) 
+////////////////////////////////////////////////////////////////////
+//     Function: Pipeline::inc_cycler_type
+//       Access: Private, Static
+//  Description: Increments (or decrements, according to added) the
+//               value for TypeHandle in the indicated TypeCount map.
+//               This is used in DEBUG_THREADS mode to track the types
+//               of PipelineCyclers that are coming and going, mainly
+//               for PStats reporting.
+//
+//               It is assumed the lock is held during this call.
+////////////////////////////////////////////////////////////////////
+void Pipeline::
+inc_cycler_type(TypeCount &count, TypeHandle type, int addend) {
+  TypeCount::iterator ci = count.find(type);
+  if (ci == count.end()) {
+    ci = count.insert(TypeCount::value_type(type, 0)).first;
+  }
+  (*ci).second += addend;
+  nassertv((*ci).second >= 0);
+}
+#endif  // THREADED_PIPELINE && DEBUG_THREADS

+ 101 - 0
panda/src/express/pipeline.h

@@ -0,0 +1,101 @@
+// Filename: pipeline.h
+// Created by:  drose (21Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 PIPELINE_H
+#define PIPELINE_H
+
+#include "pandabase.h"
+#include "pipelineCyclerLinks.h"
+#include "namable.h"
+#include "pset.h"
+#include "reMutex.h"
+#include "reMutexHolder.h"
+
+struct PipelineCyclerTrueImpl;
+
+////////////////////////////////////////////////////////////////////
+//       Class : Pipeline
+// Description : This class manages a staged pipeline of data, for
+//               instance the render pipeline, so that each stage of
+//               the pipeline can simultaneously access different
+//               copies of the same data.  It actually maintains a
+//               collection of PipelineCycler objects, and manages the
+//               turning of all of them at once.
+//
+//               There is one default Pipeline object, the render
+//               pipeline.  Other specialty pipelines may be created
+//               as needed.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS Pipeline : public PipelineCyclerLinks, public Namable {
+public:
+  Pipeline(const string &name, int num_stages);
+  ~Pipeline();
+
+  INLINE static Pipeline *get_render_pipeline();
+
+  void cycle();
+
+  void set_num_stages(int num_stages);
+  INLINE void set_min_stages(int min_stages);
+  INLINE int get_num_stages() const;
+
+#ifdef THREADED_PIPELINE
+  void add_cycler(PipelineCyclerTrueImpl *cycler);
+  void add_dirty_cycler(PipelineCyclerTrueImpl *cycler);
+  void remove_cycler(PipelineCyclerTrueImpl *cycler);
+
+  INLINE int get_num_cyclers() const;
+  INLINE int get_num_dirty_cyclers() const;
+  
+#ifdef DEBUG_THREADS
+  typedef void CallbackFunc(TypeHandle type, int count, void *data);
+  void iterate_all_cycler_types(CallbackFunc *func, void *data) const;
+  void iterate_dirty_cycler_types(CallbackFunc *func, void *data) const;
+#endif  // DEBUG_THREADS
+
+#endif  // THREADED_PIPELINE
+
+private:
+  int _num_stages;
+
+  static void make_render_pipeline();
+  static Pipeline *_render_pipeline;
+
+#ifdef THREADED_PIPELINE
+  int _num_cyclers;
+  typedef pset<PipelineCyclerTrueImpl *> Cyclers;
+  Cyclers _dirty_cyclers;
+
+#ifdef DEBUG_THREADS
+  typedef pmap<TypeHandle, int> TypeCount;
+  TypeCount _all_cycler_types, _dirty_cycler_types;
+  
+  static void inc_cycler_type(TypeCount &count, TypeHandle type, int addend);
+#endif  // DEBUG_THREADS
+
+  // This is true only during cycle().
+  bool _cycling;
+
+  ReMutex _lock;
+#endif  // THREADED_PIPELINE
+};
+
+#include "pipeline.I"
+
+#endif
+

+ 287 - 0
panda/src/express/pipelineCycler.I

@@ -0,0 +1,287 @@
+// Filename: pipelineCycler.I
+// Created by:  drose (21Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+#ifdef DO_PIPELINING
+// The following implementations are to support the
+// PipelineCyclerDummyImpl or the PipelineCyclerTrueImpl.
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::Constructor (dummy or true)
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE PipelineCycler<CycleDataType>::
+PipelineCycler(Pipeline *pipeline) :
+  PipelineCyclerBase(new CycleDataType, pipeline)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::Copy Constructor (dummy or true)
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE PipelineCycler<CycleDataType>::
+PipelineCycler(const PipelineCycler<CycleDataType> &copy) :
+  PipelineCyclerBase(copy)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::Copy Assignment (dummy or true)
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE void PipelineCycler<CycleDataType>::
+operator = (const PipelineCycler<CycleDataType> &copy) {
+  PipelineCyclerBase::operator = (copy);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::read (dummy or true)
+//       Access: Public
+//  Description: See PipelineCyclerBase::read().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *PipelineCycler<CycleDataType>::
+read() const {
+  return (const CycleDataType *)PipelineCyclerBase::read();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::read (dummy or true)
+//       Access: Public
+//  Description: See PipelineCyclerBase::read_stage().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *PipelineCycler<CycleDataType>::
+read_stage(int n) const {
+  return (const CycleDataType *)PipelineCyclerBase::read_stage(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::write (dummy or true)
+//       Access: Public
+//  Description: See PipelineCyclerBase::write().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+write() {
+  return (CycleDataType *)PipelineCyclerBase::write();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::elevate_read (dummy or true)
+//       Access: Public
+//  Description: See PipelineCyclerBase::elevate_read().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+elevate_read(const CycleDataType *pointer) {
+  return (CycleDataType *)PipelineCyclerBase::elevate_read(pointer);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::elevate_read_upstream (dummy or true)
+//       Access: Public
+//  Description: See PipelineCyclerBase::elevate_read_upstream().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+elevate_read_upstream(const CycleDataType *pointer, bool force_to_0) {
+  return (CycleDataType *)PipelineCyclerBase::elevate_read_upstream(pointer, force_to_0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::elevate_read_stage (dummy or true)
+//       Access: Public
+//  Description: See PipelineCyclerBase::elevate_read_stage().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+elevate_read_stage(int n, const CycleDataType *pointer) {
+  return (CycleDataType *)PipelineCyclerBase::elevate_read_stage(n, pointer);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::write_upstream (dummy or true)
+//       Access: Public
+//  Description: See PipelineCyclerBase::write_upstream().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+write_upstream(bool force_to_0) {
+  return (CycleDataType *)PipelineCyclerBase::write_upstream(force_to_0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::write_stage (dummy or true)
+//       Access: Public
+//  Description: See PipelineCyclerBase::write_stage().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+write_stage(int n) {
+  return (CycleDataType *)PipelineCyclerBase::write_stage(n);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::cheat (dummy or true)
+//       Access: Public
+//  Description: Returns a pointer without counting it.  This is only
+//               intended for use as the return value for certain
+//               nassertr() functions, so the application can recover
+//               after a failure to manage the read and write pointers
+//               correctly.  You should never call this function
+//               directly.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+cheat() const {
+  return (CycleDataType *)PipelineCyclerBase::cheat();
+}
+
+#else  // !DO_PIPELINING
+// The following implementations are provided for when pipelining is
+// not compiled in.  They are trivial functions that do as little as
+// possible.
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::Constructor (trivial)
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE PipelineCycler<CycleDataType>::
+PipelineCycler(Pipeline *pipeline) :
+  PipelineCyclerBase(&_typed_data, pipeline)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::Copy Constructor (trivial)
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE PipelineCycler<CycleDataType>::
+PipelineCycler(const PipelineCycler<CycleDataType> &copy) :
+  PipelineCyclerBase(&_typed_data),
+  _typed_data(copy._typed_data)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::Copy Assignment (trivial)
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE void PipelineCycler<CycleDataType>::
+operator = (const PipelineCycler<CycleDataType> &copy) {
+  _typed_data = copy._typed_data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::read (trivial)
+//       Access: Public
+//  Description: See PipelineCyclerBase::read().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *PipelineCycler<CycleDataType>::
+read() const {
+  return &_typed_data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::write (trivial)
+//       Access: Public
+//  Description: See PipelineCyclerBase::write().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+write() {
+  return &_typed_data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::elevate_read (trivial)
+//       Access: Public
+//  Description: See PipelineCyclerBase::elevate_read().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+elevate_read(const CycleDataType *) {
+  return &_typed_data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::elevate_read_upstream (trivial)
+//       Access: Public
+//  Description: See PipelineCyclerBase::elevate_read_upstream().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+elevate_read_upstream(const CycleDataType *, bool) {
+  return &_typed_data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::write_upstream (trivial)
+//       Access: Public
+//  Description: See PipelineCyclerBase::write_upstream().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+write_upstream(bool) {
+  return &_typed_data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::write_stage (trivial)
+//       Access: Public
+//  Description: See PipelineCyclerBase::write_stage().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+write_stage(int) {
+  return &_typed_data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::cheat (trivial)
+//       Access: Public
+//  Description: Returns a pointer without counting it.  This is only
+//               intended for use as the return value for certain
+//               nassertr() functions, so the application can recover
+//               after a failure to manage the read and write pointers
+//               correctly.  You should never call this function
+//               directly.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+cheat() const {
+  return (CycleDataType *)&_typed_data;
+}
+
+
+#endif   // DO_PIPELINING

+ 19 - 0
panda/src/express/pipelineCycler.cxx

@@ -0,0 +1,19 @@
+// Filename: pipelineCycler.cxx
+// Created by:  drose (21Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "pipelineCycler.h"

+ 171 - 0
panda/src/express/pipelineCycler.h

@@ -0,0 +1,171 @@
+// Filename: pipelineCycler.h
+// Created by:  drose (21Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 PIPELINECYCLER_H
+#define PIPELINECYCLER_H
+
+#include "pandabase.h"
+#include "pipelineCyclerBase.h"
+#include "cyclerHolder.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : PipelineCycler
+// Description : This class maintains different copies of a page of
+//               data between stages of the graphics pipeline (or any
+//               other pipelining context).
+//
+//               The class object maintains up to n copies of a
+//               CycleData structure, one for each stage of the
+//               pipeline.  The head of the pipeline is responsible
+//               for making changes to its copy, which are then cycled
+//               through the pipeline at each frame.
+//
+//               To access the data, you must first ask for a readable
+//               pointer.  In order to make changes to the data, you
+//               must ask for a writable pointer.  Both kinds of
+//               pointers should be released when you are done, as a
+//               sanity check.  The CycleDataReader and
+//               CycleDataWriter classes transparently handle this.
+//
+//               If pipelining support is not enabled at compile time
+//               (that is, SUPPORT_PIPELINING is not defined), this
+//               object compiles to a minimum object that presents the
+//               same interface but with minimal runtime overhead.
+//               (Actually, this isn't true yet, but it will be one
+//               day.)
+//
+//               We define this as a struct instead of a class to
+//               guarantee byte placement within the object, so that
+//               (particularly for the trivial implementation) the
+//               inherited struct's data is likely to be placed by the
+//               compiler at the "this" pointer.
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+struct PipelineCycler : public PipelineCyclerBase {
+public:
+  INLINE PipelineCycler(Pipeline *pipeline = NULL);
+  INLINE PipelineCycler(const PipelineCycler<CycleDataType> &copy);
+  INLINE void operator = (const PipelineCycler<CycleDataType> &copy);
+
+  INLINE const CycleDataType *read() const;
+  INLINE const CycleDataType *read_stage(int n) const;
+  INLINE CycleDataType *write();
+  INLINE CycleDataType *elevate_read(const CycleDataType *pointer);
+  INLINE CycleDataType *elevate_read_upstream(const CycleDataType *pointer, bool force_to_0);
+  INLINE CycleDataType *elevate_read_stage(int n, const CycleDataType *pointer);
+  INLINE CycleDataType *write_upstream(bool force_to_0);
+  INLINE CycleDataType *write_stage(int n);
+
+  INLINE CycleDataType *cheat() const;
+
+#ifndef DO_PIPELINING
+private:
+  // If we are *not* compiling in support for pipelining, we just
+  // store the CycleData object right here.  No pointers needed.
+  CycleDataType _typed_data;
+#endif  // !DO_PIPELINING
+};
+
+// These macros are handy for iterating through the set of pipeline
+// stages.  They're particularly useful for updating cache values
+// upstream of the current stage, or for removing bad pointers from
+// all stages of the pipeline.  In each case, the variable
+// pipeline_stage is defined within the loop to be the current stage
+// of the pipeline traversed by the loop.
+#ifdef DO_PIPELINING
+
+// Iterates through all of the pipeline stages upstream of the current
+// stage, but not including the current stage.
+#define OPEN_ITERATE_UPSTREAM_ONLY(cycler) {                        \
+    CyclerHolder cholder(cycler);                                   \
+    int pipeline_stage;                                             \
+    for (pipeline_stage = Thread::get_current_pipeline_stage() - 1; \
+         pipeline_stage >= 0;                                       \
+         --pipeline_stage)
+
+#define CLOSE_ITERATE_UPSTREAM_ONLY(cycler)     \
+  }
+
+// Iterates through all of the pipeline stages upstream of the current
+// stage, and including the current stage.
+#define OPEN_ITERATE_CURRENT_AND_UPSTREAM(cycler) {                 \
+    CyclerHolder cholder(cycler);                                   \
+    int pipeline_stage;                                             \
+    for (pipeline_stage = Thread::get_current_pipeline_stage();	    \
+         pipeline_stage >= 0;                                       \
+         --pipeline_stage)
+
+#define CLOSE_ITERATE_CURRENT_AND_UPSTREAM(cycler)      \
+  }
+
+// As above, but without holding the cycler lock during the loop.
+#define OPEN_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(cycler) {	    \
+    int pipeline_stage;                                             \
+    for (pipeline_stage = Thread::get_current_pipeline_stage();	    \
+         pipeline_stage >= 0;                                       \
+         --pipeline_stage)
+
+#define CLOSE_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(cycler)	\
+  }
+
+// Iterates through all of the pipeline stages.
+#define OPEN_ITERATE_ALL_STAGES(cycler) {                           \
+    int pipeline_stage;                                             \
+    for (pipeline_stage = (cycler).get_num_stages() - 1;            \
+         pipeline_stage >= 0;                                       \
+         --pipeline_stage)
+
+#define CLOSE_ITERATE_ALL_STAGES(cycler)        \
+  }
+
+#else  // DO_PIPELINING
+
+// These are trivial implementations of the above macros, defined when
+// pipelining is not enabled, that simply operate on stage 0 without
+// bothering to create a for loop.
+#define OPEN_ITERATE_UPSTREAM_ONLY(cycler)      \
+  if (false) {                                  \
+    const int pipeline_stage = -1;                   
+
+#define CLOSE_ITERATE_UPSTREAM_ONLY(cycler)     \
+  }
+
+#define OPEN_ITERATE_CURRENT_AND_UPSTREAM(cycler) {                     \
+    const int pipeline_stage = 0;                                       \
+    
+#define CLOSE_ITERATE_CURRENT_AND_UPSTREAM(cycler)      \
+  }
+
+#define OPEN_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(cycler) {		\
+    const int pipeline_stage = 0;                                       \
+    
+#define CLOSE_ITERATE_CURRENT_AND_UPSTREAM_NOLOCK(cycler)	\
+  }
+
+#define OPEN_ITERATE_ALL_STAGES(cycler) {                               \
+    const int pipeline_stage = 0;                                       \
+    
+#define CLOSE_ITERATE_ALL_STAGES(cycler)        \
+  }
+
+#endif  // DO_PIPELINING
+
+#include "pipelineCycler.I"
+
+#endif
+

+ 52 - 0
panda/src/express/pipelineCyclerBase.h

@@ -0,0 +1,52 @@
+// Filename: pipelineCyclerBase.h
+// Created by:  drose (21Feb02)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 PIPELINECYCLERBASE_H
+#define PIPELINECYCLERBASE_H
+
+#include "pandabase.h"
+
+#ifdef DO_PIPELINING
+
+#ifdef HAVE_THREADS
+
+// With DO_PIPELINING and threads available, we want the true cycler
+// implementation.
+#include "pipelineCyclerTrueImpl.h"
+typedef PipelineCyclerTrueImpl PipelineCyclerBase;
+
+#else  // HAVE_THREADS
+
+// With DO_PIPELINING but no threads available, we want the dummy,
+// self-validating cycler implementation.
+#include "pipelineCyclerDummyImpl.h"
+typedef PipelineCyclerDummyImpl PipelineCyclerBase;
+
+#endif // HAVE_THREADS
+
+#else  // DO_PIPELINING
+
+// Without DO_PIPELINING, we only want the trivial, do-nothing
+// implementation.
+#include "pipelineCyclerTrivialImpl.h"
+typedef PipelineCyclerTrivialImpl PipelineCyclerBase;
+
+#endif  // DO_PIPELINING
+
+#endif
+

+ 420 - 0
panda/src/express/pipelineCyclerDummyImpl.I

@@ -0,0 +1,420 @@
+// Filename: pipelineCyclerDummyImpl.I
+// Created by:  drose (31Jan06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: PipelineCyclerDummyImpl::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PipelineCyclerDummyImpl::
+PipelineCyclerDummyImpl(CycleData *initial_data, Pipeline *pipeline) :
+  _data(initial_data),
+  _pipeline(pipeline),
+  _read_count(0),
+  _write_count(0),
+  _locked(false)
+{
+  if (_pipeline == (Pipeline *)NULL) {
+    _pipeline = Pipeline::get_render_pipeline();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::Copy Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PipelineCyclerDummyImpl::
+PipelineCyclerDummyImpl(const PipelineCyclerDummyImpl &copy) :
+  _data(copy._data->make_copy()),
+  _pipeline(copy._pipeline),
+  _read_count(0),
+  _write_count(0),
+  _locked(false)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::Copy Assignment
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerDummyImpl::
+operator = (const PipelineCyclerDummyImpl &copy) {
+  nassertv(_read_count == 0 && _write_count == 0);
+  _data = copy._data->make_copy();
+  _pipeline = copy._pipeline;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PipelineCyclerDummyImpl::
+~PipelineCyclerDummyImpl() {
+  nassertv(_read_count == 0 && _write_count == 0 && !_locked);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::lock
+//       Access: Public
+//  Description: Grabs an overall lock on the cycler.  Release it with
+//               a call to release().  This lock should be held while
+//               walking the list of stages.
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerDummyImpl::
+lock() {
+  nassertv(!_locked);
+  _locked = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::release
+//       Access: Public
+//  Description: Release the overall lock on the cycler that was
+//               grabbed via lock().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerDummyImpl::
+release() {
+  nassertv(_locked);
+  _locked = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::read
+//       Access: Public
+//  Description: Returns a const CycleData pointer, filled with the
+//               data for the current stage of the pipeline as seen by
+//               this thread.  This pointer should eventually be
+//               released by calling release_read().
+//
+//               There should be no outstanding write pointers on the
+//               data when this function is called.
+////////////////////////////////////////////////////////////////////
+INLINE const CycleData *PipelineCyclerDummyImpl::
+read() const {
+  // This function isn't truly const, but it doesn't change the data
+  // in any meaningful way, so we pretend it is.
+  ((PipelineCyclerDummyImpl *)this)->_read_count++;
+
+  // It's not an error to grab a read pointer while someone else holds
+  // a read or a write pointer.
+  return _data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::increment_read
+//       Access: Public
+//  Description: Increments the count on a pointer previously
+//               retrieved by read(); now the pointer will need to be
+//               released twice.
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerDummyImpl::
+increment_read(const CycleData *pointer) const {
+  // This function isn't truly const, but it doesn't change the data
+  // in any meaningful way, so we pretend it is.
+  nassertv(pointer == _data);
+  nassertv(_read_count > 0);
+  ((PipelineCyclerDummyImpl *)this)->_read_count++;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::release_read
+//       Access: Public
+//  Description: Releases a pointer previously obtained via a call to
+//               read().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerDummyImpl::
+release_read(const CycleData *pointer) const {
+  // This function isn't truly const, but it doesn't change the data
+  // in any meaningful way, so we pretend it is.
+  nassertv(pointer == _data);
+  nassertv(_read_count > 0);
+  ((PipelineCyclerDummyImpl *)this)->_read_count--;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::write
+//       Access: Public
+//  Description: Returns a non-const CycleData pointer, filled with a
+//               unique copy of the data for the current stage of the
+//               pipeline as seen by this thread.  This pointer may
+//               now be used to write to the data, and that copy of
+//               the data will be propagated to all later stages of the
+//               pipeline.  This pointer should eventually be released
+//               by calling release_write().
+//
+//               There may only be one outstanding write pointer on a
+//               given stage at a time, and if there is a write
+//               pointer there may be no read pointers on the same
+//               stage (but see elevate_read).
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerDummyImpl::
+write() {
+  _write_count++;
+
+  // It's an error to grab a write pointer while someone else holds a
+  // read pointer, because doing so may invalidate the read pointer.
+  nassertr(_read_count == 0, _data);
+
+  // It's not an error to do this while someone else holds a write
+  // pointer, however.
+
+  return _data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::elevate_read
+//       Access: Public
+//  Description: Elevates a currently-held read pointer into a write
+//               pointer.  This may or may not change the value of the
+//               pointer.  It is only valid to do this if this is the
+//               only currently-outstanding read pointer on the
+//               current stage.
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerDummyImpl::
+elevate_read(const CycleData *pointer) {
+  release_read(pointer);
+  return write();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::elevate_read_upstream
+//       Access: Public
+//  Description: Elevates a currently-held read pointer into a write
+//               pointer, like elevate_read(), but also propagates the
+//               pointer back to upstream stages, like
+//               write_upstream().
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerDummyImpl::
+elevate_read_upstream(const CycleData *pointer, bool force_to_0) {
+  release_read(pointer);
+  return write_upstream(force_to_0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::increment_write
+//       Access: Public
+//  Description: Increments the count on a pointer previously
+//               retrieved by write(); now the pointer will need to be
+//               released twice.
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerDummyImpl::
+increment_write(CycleData *pointer) const {
+  // This function isn't truly const, but it doesn't change the data
+  // in any meaningful way, so we pretend it is.
+  nassertv(pointer == _data);
+  nassertv(_write_count > 0);
+  ((PipelineCyclerDummyImpl *)this)->_write_count++;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::release_write
+//       Access: Public
+//  Description: Releases a pointer previously obtained via a call to
+//               write().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerDummyImpl::
+release_write(CycleData *pointer) {
+  nassertv(pointer == _data);
+  nassertv(_write_count > 0);
+  _write_count--;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::get_num_stages
+//       Access: Public
+//  Description: Returns the number of stages in the pipeline.
+////////////////////////////////////////////////////////////////////
+INLINE int PipelineCyclerDummyImpl::
+get_num_stages() {
+  return 1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::read_stage
+//       Access: Public
+//  Description: Returns a const CycleData pointer, filled with the
+//               data for the indicated stage of the pipeline.  This
+//               pointer should eventually be released by calling
+//               release_read_stage().
+//
+//               There should be no outstanding write pointers on the
+//               data when this function is called.
+////////////////////////////////////////////////////////////////////
+INLINE const CycleData *PipelineCyclerDummyImpl::
+read_stage(int n) const {
+  // This function isn't truly const, but it doesn't change the data
+  // in any meaningful way, so we pretend it is.
+  nassertr(n == 0, NULL);
+  ((PipelineCyclerDummyImpl *)this)->_read_count++;
+
+  // It's not an error to grab a read pointer while someone else holds
+  // a read or a write pointer.
+  return _data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::release_read_stage
+//       Access: Public
+//  Description: Releases a pointer previously obtained via a call to
+//               read_stage().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerDummyImpl::
+release_read_stage(int n, const CycleData *pointer) const {
+  // This function isn't truly const, but it doesn't change the data
+  // in any meaningful way, so we pretend it is.
+  nassertv(n == 0);
+  nassertv(pointer == _data);
+  nassertv(_read_count > 0);
+  ((PipelineCyclerDummyImpl *)this)->_read_count--;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::write_upstream
+//       Access: Public
+//  Description: This special variant on write() will automatically
+//               propagate changes back to upstream pipeline stages.
+//               If force_to_0 is false, then it propagates back only
+//               as long as the CycleData pointers are equivalent,
+//               guaranteeing that it does not modify upstream data
+//               (other than the modification that will be performed
+//               by the code that returns this pointer).  This is
+//               particularly appropriate for minor updates, where it
+//               doesn't matter much if the update is lost, such as
+//               storing a cached value.
+//
+//               If force_to_0 is dummy, then the CycleData pointer for
+//               the current pipeline stage is propagated all the way
+//               back up to stage 0; after this call, there will be
+//               only one CycleData pointer that is duplicated in all
+//               stages between stage 0 and the current stage.  This
+//               may undo some recent changes that were made
+//               independently at pipeline stage 0 (or any other
+//               upstream stage).  However, it guarantees that the
+//               change that is to be applied at this pipeline stage
+//               will stick.  This is slightly dangerous because of
+//               the risk of losing upstream changes; generally, this
+//               should only be done when you are confident that there
+//               are no upstream changes to be lost (for instance, for
+//               an object that has been recently created).
+////////////////////////////////////////////////////////////////////
+CycleData *PipelineCyclerDummyImpl::
+write_upstream(bool) {
+  _write_count++;
+  return _data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::write_stage
+//       Access: Public
+//  Description: Returns a pointer suitable for writing to the nth
+//               stage of the pipeline.  This is for special
+//               applications that need to update the entire pipeline
+//               at once (for instance, to remove an invalid pointer).
+//               This pointer should later be released with
+//               release_write_stage().
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerDummyImpl::
+write_stage(int n) {
+  nassertr(n == 0, (CycleData *)NULL);
+  _write_count++;
+  return _data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::elevate_read_stage
+//       Access: Public
+//  Description: Elevates a currently-held read pointer into a write
+//               pointer.  This may or may not change the value of the
+//               pointer.  It is only valid to do this if this is the
+//               only currently-outstanding read pointer on the
+//               current stage.
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerDummyImpl::
+elevate_read_stage(int n, const CycleData *pointer) {
+  nassertr(n == 0, NULL);
+  release_read(pointer);
+  return write();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::release_write_stage
+//       Access: Public
+//  Description: Releases a pointer previously obtained via a call to
+//               write_stage().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerDummyImpl::
+release_write_stage(int n, CycleData *pointer) {
+  nassertv(n == 0 && pointer == _data);
+  nassertv(_write_count > 0);
+  _write_count--;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::get_parent_type
+//       Access: Public
+//  Description: Returns the type of object that owns this cycler, as
+//               reported by CycleData::get_parent_type().
+////////////////////////////////////////////////////////////////////
+INLINE TypeHandle PipelineCyclerDummyImpl::
+get_parent_type() const {
+  return _data->get_parent_type();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::cheat
+//       Access: Public
+//  Description: Returns a pointer without counting it.  This is only
+//               intended for use as the return value for certain
+//               nassertr() functions, so the application can recover
+//               after a failure to manage the read and write pointers
+//               correctly.  You should never call this function
+//               directly.
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerDummyImpl::
+cheat() const {
+  return _data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::get_read_count
+//       Access: Public
+//  Description: Returns the number of handles currently outstanding
+//               to read the current stage of the data.  This should
+//               only be used for debugging purposes.
+////////////////////////////////////////////////////////////////////
+INLINE int PipelineCyclerDummyImpl::
+get_read_count() const {
+  return _read_count;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::get_write_count
+//       Access: Public
+//  Description: Returns the number of handles currently outstanding
+//               to read the current stage of the data.  This will
+//               normally only be either 0 or 1.  This should only be
+//               used for debugging purposes.
+////////////////////////////////////////////////////////////////////
+INLINE int PipelineCyclerDummyImpl::
+get_write_count() const {
+  return _write_count;
+}

+ 19 - 0
panda/src/express/pipelineCyclerDummyImpl.cxx

@@ -0,0 +1,19 @@
+// Filename: pipelineCyclerDummyImpl.cxx
+// Created by:  drose (31Jan06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "pipelineCyclerDummyImpl.h"

+ 94 - 0
panda/src/express/pipelineCyclerDummyImpl.h

@@ -0,0 +1,94 @@
+// Filename: pipelineCyclerDummyImpl.h
+// Created by:  drose (31Jan06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 PIPELINECYCLERDUMMYIMPL_H
+#define PIPELINECYCLERDUMMYIMPL_H
+
+#include "pandabase.h"
+
+#if defined(DO_PIPELINING) && !defined(HAVE_THREADS)
+
+#include "cycleData.h"
+#include "pipeline.h"
+#include "pointerTo.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : PipelineCyclerDummyImpl
+// Description : This is a simple, single-threaded-only implementation
+//               of PipelineCyclerBase.  It is only compiled when
+//               DO_PIPELINING is defined, but threading is not
+//               available, which is usually the case only in
+//               development mode.
+//
+//               This implmentation is similar in principle to
+//               PipelineCyclerTrivialImpl, except it does basic
+//               sanity checking to ensure that you use the interface
+//               in a reasonable way consistent with its design (e.g.,
+//               read() is balanced with release_read(), etc.).
+//
+//               This is defined as a struct instead of a class,
+//               mainly to be consistent with
+//               PipelineCyclerTrivialImpl.
+////////////////////////////////////////////////////////////////////
+struct EXPCL_PANDAEXPRESS PipelineCyclerDummyImpl {
+public:
+  INLINE PipelineCyclerDummyImpl(CycleData *initial_data, Pipeline *pipeline = NULL);
+  INLINE PipelineCyclerDummyImpl(const PipelineCyclerDummyImpl &copy);
+  INLINE void operator = (const PipelineCyclerDummyImpl &copy);
+  INLINE ~PipelineCyclerDummyImpl();
+
+  INLINE void lock();
+  INLINE void release();
+
+  INLINE const CycleData *read() const;
+  INLINE void increment_read(const CycleData *pointer) const;
+  INLINE void release_read(const CycleData *pointer) const;
+
+  INLINE CycleData *write();
+  INLINE CycleData *elevate_read(const CycleData *pointer);
+  INLINE CycleData *elevate_read_upstream(const CycleData *pointer, bool force_to_0);
+  INLINE void increment_write(CycleData *pointer) const;
+  INLINE void release_write(CycleData *pointer);
+
+  INLINE int get_num_stages();
+  INLINE const CycleData *read_stage(int n) const;
+  INLINE void release_read_stage(int n, const CycleData *pointer) const;
+  INLINE CycleData *write_upstream(bool force_to_0);
+  INLINE CycleData *write_stage(int n);
+  INLINE CycleData *elevate_read_stage(int n, const CycleData *pointer);
+  INLINE void release_write_stage(int n, CycleData *pointer);
+
+  INLINE TypeHandle get_parent_type() const;
+
+  INLINE CycleData *cheat() const;
+  INLINE int get_read_count() const;
+  INLINE int get_write_count() const;
+
+private:
+  PT(CycleData) _data;
+  Pipeline *_pipeline;
+  short _read_count, _write_count;
+  short _locked;
+};
+
+#include "pipelineCyclerDummyImpl.I"
+
+#endif  // DO_PIPELINING && !HAVE_THREADS
+
+#endif
+

+ 83 - 0
panda/src/express/pipelineCyclerLinks.I

@@ -0,0 +1,83 @@
+// Filename: pipelineCyclerLinks.I
+// Created by:  drose (16Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+#ifdef THREADED_PIPELINE
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerLinks::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PipelineCyclerLinks::
+PipelineCyclerLinks() {
+#ifndef NDEBUG
+  _next = NULL;
+  _prev = NULL;
+#endif
+}
+#endif  // THREADED_PIPELINE
+
+#ifdef THREADED_PIPELINE
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerLinks::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PipelineCyclerLinks::
+~PipelineCyclerLinks() {
+  nassertv(_next == NULL && _prev == NULL);
+}
+#endif  // THREADED_PIPELINE
+
+#ifdef THREADED_PIPELINE
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerLinks::remove_from_list
+//       Access: Private
+//  Description: Removes a PipelineCyclerLinks record from the
+//               doubly-linked list.
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerLinks::
+remove_from_list() {
+  nassertv(_prev->_next == this && _next->_prev == this);
+  _prev->_next = _next;
+  _next->_prev = _prev;
+#ifndef NDEBUG
+  _next = NULL;
+  _prev = NULL;
+#endif
+}
+#endif  // THREADED_PIPELINE
+
+#ifdef THREADED_PIPELINE
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerLinks::insert_before
+//       Access: Private
+//  Description: Adds a PipelineCyclerLinks record before the indicated
+//               node in the doubly-linked list.
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerLinks::
+insert_before(PipelineCyclerLinks *node) {
+  nassertv(node->_prev->_next == node && node->_next->_prev == node);
+  nassertv(_prev == (PipelineCyclerLinks *)NULL &&
+           _next == (PipelineCyclerLinks *)NULL);
+  _prev = node->_prev;
+  _next = node;
+  _prev->_next = this;
+  node->_prev = this;
+}
+#endif  // THREADED_PIPELINE

+ 55 - 0
panda/src/express/pipelineCyclerLinks.h

@@ -0,0 +1,55 @@
+// Filename: pipelineCyclerLinks.h
+// Created by:  drose (16Feb06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 PIPELINECYCLERLINKS_H
+#define PIPELINECYCLERLINKS_H
+
+#include "pandabase.h"
+#include "selectThreadImpl.h"  // for THREADED_PIPELINE definition
+#include "notify.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : PipelineCyclerLinks
+// Description : This just stores the pointers to implement a
+//               doubly-linked list of PipelineCyclers for a
+//               particular Pipeline object.  We use a hand-rolled
+//               linked list rather than any STL container, because we
+//               want PipelineCyclers to be able to add and remove
+//               themselves from this list very quickly.
+//
+//               These pointers are inherited from this separate class
+//               so the Pipeline object itself can be the root of the
+//               linked list.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDAEXPRESS PipelineCyclerLinks {
+protected:
+#ifdef THREADED_PIPELINE
+  INLINE PipelineCyclerLinks();
+  INLINE ~PipelineCyclerLinks();
+
+  INLINE void remove_from_list();
+  INLINE void insert_before(PipelineCyclerLinks *node);
+
+  PipelineCyclerLinks *_prev, *_next;
+#endif
+  friend class Pipeline;
+};
+
+#include "pipelineCyclerLinks.I"
+
+#endif

+ 396 - 0
panda/src/express/pipelineCyclerTrivialImpl.I

@@ -0,0 +1,396 @@
+// Filename: pipelineCyclerTrivialImpl.I
+// Created by:  drose (31Jan06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: PipelineCyclerTrivialImpl::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PipelineCyclerTrivialImpl::
+PipelineCyclerTrivialImpl(CycleData *initial_data, Pipeline *) {
+  // In the trivial implementation, a derived class (the
+  // PipelineCycler template class) stores the CycleData object
+  // directly within itself, and since we have no data members or
+  // virtual functions, we get away with assuming the pointer is the
+  // same as the 'this' pointer.
+
+  // If this turns out not to be true on a particular platform, we
+  // will have to store the pointer in this class, for a little bit of
+  // extra overhead.
+#ifdef SIMPLE_STRUCT_POINTERS
+  nassertv(initial_data == (CycleData *)this);
+#else
+  _data = initial_data;
+#endif  // SIMPLE_STRUCT_POINTERS
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::Copy Constructor
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PipelineCyclerTrivialImpl::
+PipelineCyclerTrivialImpl(const PipelineCyclerTrivialImpl &) {
+  // The copy constructor for the PipelineCyclerTrivialImpl case
+  // doesn't work.  Don't try to use it.  The PipelineCycler template
+  // class is #ifdeffed appropriately to call the normal constructor
+  // instead.
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::Copy Assignment
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrivialImpl::
+operator = (const PipelineCyclerTrivialImpl &) {
+  // The copy assignment operator for the PipelineCyclerTrivialImpl
+  // case doesn't work.  Don't try to use it.  The PipelineCycler
+  // template class is #ifdeffed appropriately not to call this
+  // method.
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PipelineCyclerTrivialImpl::
+~PipelineCyclerTrivialImpl() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::lock
+//       Access: Public
+//  Description: Grabs an overall lock on the cycler.  Release it with
+//               a call to release().  This lock should be held while
+//               walking the list of stages.
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrivialImpl::
+lock() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::release
+//       Access: Public
+//  Description: Release the overall lock on the cycler that was
+//               grabbed via lock().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrivialImpl::
+release() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::read
+//       Access: Public
+//  Description: Returns a const CycleData pointer, filled with the
+//               data for the current stage of the pipeline as seen by
+//               this thread.  This pointer should eventually be
+//               released by calling release_read().
+////////////////////////////////////////////////////////////////////
+INLINE const CycleData *PipelineCyclerTrivialImpl::
+read() const {
+#ifdef SIMPLE_STRUCT_POINTERS
+  return (const CycleData *)this;
+#else
+  return _data;
+#endif  // SIMPLE_STRUCT_POINTERS
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::increment_read
+//       Access: Public
+//  Description: Increments the count on a pointer previously
+//               retrieved by read(); now the pointer will need to be
+//               released twice.
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrivialImpl::
+increment_read(const CycleData *) const {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::release_read
+//       Access: Public
+//  Description: Releases a pointer previously obtained via a call to
+//               read().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrivialImpl::
+release_read(const CycleData *) const {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::write
+//       Access: Public
+//  Description: Returns a non-const CycleData pointer, filled with a
+//               unique copy of the data for the current stage of the
+//               pipeline as seen by this thread.  This pointer may
+//               now be used to write to the data, and that copy of
+//               the data will be propagated to all later stages of the
+//               pipeline.  This pointer should eventually be released
+//               by calling release_write().
+//
+//               There may only be one outstanding write pointer on a
+//               given stage at a time, and if there is a write
+//               pointer there may be no read pointers on the same
+//               stage (but see elevate_read).
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerTrivialImpl::
+write() {
+#ifdef SIMPLE_STRUCT_POINTERS
+  return (CycleData *)this;
+#else
+  return _data;
+#endif  // SIMPLE_STRUCT_POINTERS
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::elevate_read
+//       Access: Public
+//  Description: Elevates a currently-held read pointer into a write
+//               pointer.  This may or may not change the value of the
+//               pointer.  It is only valid to do this if this is the
+//               only currently-outstanding read pointer on the
+//               current stage.
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerTrivialImpl::
+elevate_read(const CycleData *) {
+#ifdef SIMPLE_STRUCT_POINTERS
+  return (CycleData *)this;
+#else
+  return _data;
+#endif  // SIMPLE_STRUCT_POINTERS
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::elevate_read_upstream
+//       Access: Public
+//  Description: Elevates a currently-held read pointer into a write
+//               pointer, like elevate_read(), but also propagates the
+//               pointer back to upstream stages, like
+//               write_upstream().
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerTrivialImpl::
+elevate_read_upstream(const CycleData *, bool) {
+#ifdef SIMPLE_STRUCT_POINTERS
+  return (CycleData *)this;
+#else
+  return _data;
+#endif  // SIMPLE_STRUCT_POINTERS
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::increment_write
+//       Access: Public
+//  Description: Increments the count on a pointer previously
+//               retrieved by write(); now the pointer will need to be
+//               released twice.
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrivialImpl::
+increment_write(CycleData *) const {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::release_write
+//       Access: Public
+//  Description: Releases a pointer previously obtained via a call to
+//               write().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrivialImpl::
+release_write(CycleData *) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::get_num_stages
+//       Access: Public
+//  Description: Returns the number of stages in the pipeline.
+////////////////////////////////////////////////////////////////////
+INLINE int PipelineCyclerTrivialImpl::
+get_num_stages() {
+  return 1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::read_stage
+//       Access: Public
+//  Description: Returns a const CycleData pointer, filled with the
+//               data for the indicated pipeline stage.  This pointer
+//               should eventually be released by calling
+//               release_read().
+////////////////////////////////////////////////////////////////////
+INLINE const CycleData *PipelineCyclerTrivialImpl::
+read_stage(int) const {
+#ifdef SIMPLE_STRUCT_POINTERS
+  return (const CycleData *)this;
+#else
+  return _data;
+#endif  // SIMPLE_STRUCT_POINTERS
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::release_read_stage
+//       Access: Public
+//  Description: Releases a pointer previously obtained via a call to
+//               read_stage().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrivialImpl::
+release_read_stage(int, const CycleData *) const {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::write_upstream
+//       Access: Public
+//  Description: This special variant on write() will automatically
+//               propagate changes back to upstream pipeline stages.
+//               If force_to_0 is false, then it propagates back only
+//               as long as the CycleData pointers are equivalent,
+//               guaranteeing that it does not modify upstream data
+//               (other than the modification that will be performed
+//               by the code that returns this pointer).  This is
+//               particularly appropriate for minor updates, where it
+//               doesn't matter much if the update is lost, such as
+//               storing a cached value.
+//
+//               If force_to_0 is trivial, then the CycleData pointer for
+//               the current pipeline stage is propagated all the way
+//               back up to stage 0; after this call, there will be
+//               only one CycleData pointer that is duplicated in all
+//               stages between stage 0 and the current stage.  This
+//               may undo some recent changes that were made
+//               independently at pipeline stage 0 (or any other
+//               upstream stage).  However, it guarantees that the
+//               change that is to be applied at this pipeline stage
+//               will stick.  This is slightly dangerous because of
+//               the risk of losing upstream changes; generally, this
+//               should only be done when you are confident that there
+//               are no upstream changes to be lost (for instance, for
+//               an object that has been recently created).
+////////////////////////////////////////////////////////////////////
+CycleData *PipelineCyclerTrivialImpl::
+write_upstream(bool) {
+#ifdef SIMPLE_STRUCT_POINTERS
+  return (CycleData *)this;
+#else
+  return _data;
+#endif  // SIMPLE_STRUCT_POINTERS
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::write_stage
+//       Access: Public
+//  Description: Returns a pointer suitable for writing to the nth
+//               stage of the pipeline.  This is for special
+//               applications that need to update the entire pipeline
+//               at once (for instance, to remove an invalid pointer).
+//               This pointer should later be released with
+//               release_write_stage().
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerTrivialImpl::
+write_stage(int) {
+#ifdef SIMPLE_STRUCT_POINTERS
+  return (CycleData *)this;
+#else
+  return _data;
+#endif  // SIMPLE_STRUCT_POINTERS
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::elevate_read_stage
+//       Access: Public
+//  Description: Elevates a currently-held read pointer into a write
+//               pointer.  This may or may not change the value of the
+//               pointer.  It is only valid to do this if this is the
+//               only currently-outstanding read pointer on the
+//               current stage.
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerTrivialImpl::
+elevate_read_stage(int, const CycleData *) {
+#ifdef SIMPLE_STRUCT_POINTERS
+  return (CycleData *)this;
+#else
+  return _data;
+#endif  // SIMPLE_STRUCT_POINTERS
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::release_write_stage
+//       Access: Public
+//  Description: Releases a pointer previously obtained via a call to
+//               write_stage().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrivialImpl::
+release_write_stage(int, CycleData *) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::get_parent_type
+//       Access: Public
+//  Description: Returns the type of object that owns this cycler, as
+//               reported by CycleData::get_parent_type().
+////////////////////////////////////////////////////////////////////
+INLINE TypeHandle PipelineCyclerTrivialImpl::
+get_parent_type() const {
+  return cheat()->get_parent_type();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::cheat
+//       Access: Public
+//  Description: Returns a pointer without counting it.  This is only
+//               intended for use as the return value for certain
+//               nassertr() functions, so the application can recover
+//               after a failure to manage the read and write pointers
+//               correctly.  You should never call this function
+//               directly.
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerTrivialImpl::
+cheat() const {
+#ifdef SIMPLE_STRUCT_POINTERS
+  return (CycleData *)this;
+#else
+  return _data;
+#endif  // SIMPLE_STRUCT_POINTERS
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::get_read_count
+//       Access: Public
+//  Description: Returns the number of handles currently outstanding
+//               to read the current stage of the data.  This should
+//               only be used for debugging purposes.
+////////////////////////////////////////////////////////////////////
+INLINE int PipelineCyclerTrivialImpl::
+get_read_count() const {
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::get_write_count
+//       Access: Public
+//  Description: Returns the number of handles currently outstanding
+//               to read the current stage of the data.  This will
+//               normally only be either 0 or 1.  This should only be
+//               used for debugging purposes.
+////////////////////////////////////////////////////////////////////
+INLINE int PipelineCyclerTrivialImpl::
+get_write_count() const {
+  return 0;
+}
+

+ 19 - 0
panda/src/express/pipelineCyclerTrivialImpl.cxx

@@ -0,0 +1,19 @@
+// Filename: pipelineCyclerTrivialImpl.cxx
+// Created by:  drose (31Jan06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "pipelineCyclerTrivialImpl.h"

+ 100 - 0
panda/src/express/pipelineCyclerTrivialImpl.h

@@ -0,0 +1,100 @@
+// Filename: pipelineCyclerTrivialImpl.h
+// Created by:  drose (31Jan06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 PIPELINECYCLERTRIVIALIMPL_H
+#define PIPELINECYCLERTRIVIALIMPL_H
+
+#include "pandabase.h"
+
+#ifndef DO_PIPELINING
+
+#include "cycleData.h"
+
+class Pipeline;
+
+////////////////////////////////////////////////////////////////////
+//       Class : PipelineCyclerTrivialImpl
+// Description : This is the trivial, non-threaded implementation of
+//               PipelineCyclerBase.  It is only compiled when
+//               DO_PIPELINING is not defined (which usually implies
+//               that threading is not available).
+//
+//               This implementation is designed to do as little as
+//               possible, and to compile to nothing, or almost
+//               nothing.  It doesn't actually support pipelining in
+//               any way.  It doesn't even perform any sanity checks
+//               to speak of.  It's designed for a strictly
+//               single-threaded application, and its purpose is to be
+//               as low-overhead as possible.
+//
+//               We define this as a struct instead of a class to
+//               emphasize the importance of byte placement within the
+//               object, so that the inherited struct's data is likely
+//               to be placed by the compiler at the "this" pointer.
+////////////////////////////////////////////////////////////////////
+struct EXPCL_PANDAEXPRESS PipelineCyclerTrivialImpl {
+public:
+  INLINE PipelineCyclerTrivialImpl(CycleData *initial_data, Pipeline *pipeline = NULL);
+private:
+  INLINE PipelineCyclerTrivialImpl(const PipelineCyclerTrivialImpl &copy);
+  INLINE void operator = (const PipelineCyclerTrivialImpl &copy);
+public:
+  INLINE ~PipelineCyclerTrivialImpl();
+
+  INLINE void lock();
+  INLINE void release();
+
+  INLINE const CycleData *read() const;
+  INLINE void increment_read(const CycleData *pointer) const;
+  INLINE void release_read(const CycleData *pointer) const;
+
+  INLINE CycleData *write();
+  INLINE CycleData *elevate_read(const CycleData *pointer);
+  INLINE CycleData *elevate_read_upstream(const CycleData *pointer, bool force_to_0);
+  INLINE void increment_write(CycleData *pointer) const;
+  INLINE void release_write(CycleData *pointer);
+
+  INLINE int get_num_stages();
+  INLINE const CycleData *read_stage(int n) const;
+  INLINE void release_read_stage(int n, const CycleData *pointer) const;
+  INLINE CycleData *write_upstream(bool force_to_0);
+  INLINE CycleData *write_stage(int n);
+  INLINE CycleData *elevate_read_stage(int n, const CycleData *pointer);
+  INLINE void release_write_stage(int n, CycleData *pointer);
+
+  INLINE TypeHandle get_parent_type() const;
+
+  INLINE CycleData *cheat() const;
+  INLINE int get_read_count() const;
+  INLINE int get_write_count() const;
+
+  // In a trivial implementation, we only need to store the CycleData
+  // pointer.  Actually, we don't even need to do that, if we're lucky
+  // and the compiler doesn't do anything funny with the struct
+  // layout.
+  #ifndef SIMPLE_STRUCT_POINTERS
+  CycleData *_data;
+  #endif  // SIMPLE_STRUCT_POINTERS
+};
+
+#include "pipelineCyclerTrivialImpl.I"
+
+#endif  // !DO_PIPELINING
+
+#endif
+

+ 381 - 0
panda/src/express/pipelineCyclerTrueImpl.I

@@ -0,0 +1,381 @@
+// Filename: pipelineCyclerTrueImpl.I
+// Created by:  drose (31Jan06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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: PipelineCyclerTrueImpl::lock
+//       Access: Public
+//  Description: Grabs an overall lock on the cycler.  Release it with
+//               a call to release().  This lock should be held while
+//               walking the list of stages.
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrueImpl::
+lock() {
+  _lock.lock();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::release
+//       Access: Public
+//  Description: Release the overall lock on the cycler that was
+//               grabbed via lock().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrueImpl::
+release() {
+  _lock.release();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::read
+//       Access: Public
+//  Description: Returns a const CycleData pointer, filled with the
+//               data for the current stage of the pipeline as seen by
+//               this thread.  This pointer should eventually be
+//               released by calling release_read().
+//
+//               There should be no outstanding write pointers on the
+//               data when this function is called.
+////////////////////////////////////////////////////////////////////
+INLINE const CycleData *PipelineCyclerTrueImpl::
+read() const {
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
+  _lock.lock();
+  return _data[pipeline_stage];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::increment_read
+//       Access: Public
+//  Description: Increments the count on a pointer previously
+//               retrieved by read(); now the pointer will need to be
+//               released twice.
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrueImpl::
+increment_read(const CycleData *pointer) const {
+#ifndef NDEBUG
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages);
+  nassertv(_data[pipeline_stage] == pointer);
+#endif
+  _lock.lock();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::release_read
+//       Access: Public
+//  Description: Releases a pointer previously obtained via a call to
+//               read().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrueImpl::
+release_read(const CycleData *pointer) const {
+#ifndef NDEBUG
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages);
+  nassertv(_data[pipeline_stage] == pointer);
+#endif
+  _lock.release();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::write
+//       Access: Public
+//  Description: Returns a non-const CycleData pointer, filled with a
+//               unique copy of the data for the current stage of the
+//               pipeline as seen by this thread.  This pointer may
+//               now be used to write to the data, and that copy of
+//               the data will be propagated to all later stages of the
+//               pipeline.  This pointer should eventually be released
+//               by calling release_write().
+//
+//               There may only be one outstanding write pointer on a
+//               given stage at a time, and if there is a write
+//               pointer there may be no read pointers on the same
+//               stage (but see elevate_read).
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerTrueImpl::
+write() {
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  return write_stage(pipeline_stage);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::elevate_read
+//       Access: Public
+//  Description: Elevates a currently-held read pointer into a write
+//               pointer.  This may or may not change the value of the
+//               pointer.  It is only valid to do this if this is the
+//               only currently-outstanding read pointer on the
+//               current stage.
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerTrueImpl::
+elevate_read(const CycleData *pointer) {
+#ifndef NDEBUG
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
+  nassertr(_data[pipeline_stage] == pointer, NULL);
+#endif
+  CycleData *new_pointer = write();
+  _lock.release();
+  return new_pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::elevate_read_upstream
+//       Access: Public
+//  Description: Elevates a currently-held read pointer into a write
+//               pointer, like elevate_read(), but also propagates the
+//               pointer back to upstream stages, like
+//               write_upstream().
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerTrueImpl::
+elevate_read_upstream(const CycleData *pointer, bool force_to_0) {
+#ifndef NDEBUG
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
+  nassertr(_data[pipeline_stage] == pointer, NULL);
+#endif
+  CycleData *new_pointer = write_upstream(force_to_0);
+  _lock.release();
+  return new_pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::increment_write
+//       Access: Public
+//  Description: Increments the count on a pointer previously
+//               retrieved by write(); now the pointer will need to be
+//               released twice.
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrueImpl::
+increment_write(CycleData *pointer) const {
+#ifndef NDEBUG
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages);
+  nassertv(_data[pipeline_stage] == pointer);
+#endif
+  _lock.lock();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::release_write
+//       Access: Public
+//  Description: Releases a pointer previously obtained via a call to
+//               write().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrueImpl::
+release_write(CycleData *pointer) {
+#ifdef NDEBUG
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  return release_write_stage(pipeline_stage, pointer);
+#else
+  _lock.release();
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::get_num_stages
+//       Access: Public
+//  Description: Returns the number of stages in the pipeline.
+////////////////////////////////////////////////////////////////////
+INLINE int PipelineCyclerTrueImpl::
+get_num_stages() {
+  return _num_stages;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::read_stage
+//       Access: Public
+//  Description: Returns a const CycleData pointer, filled with the
+//               data for the indicated stage of the pipeline.  This
+//               pointer should eventually be released by calling
+//               release_read_stage().
+//
+//               There should be no outstanding write pointers on the
+//               data when this function is called.
+////////////////////////////////////////////////////////////////////
+INLINE const CycleData *PipelineCyclerTrueImpl::
+read_stage(int n) const {
+  nassertr(n >= 0 && n < _num_stages, NULL);
+  _lock.lock();
+  return _data[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::release_read_stage
+//       Access: Public
+//  Description: Releases a pointer previously obtained via a call to
+//               read_stage().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrueImpl::
+release_read_stage(int n, const CycleData *pointer) const {
+#ifndef NDEBUG
+  nassertv(n >= 0 && n < _num_stages);
+  nassertv(_data[n] == pointer);
+#endif
+  _lock.release();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::elevate_read_stage
+//       Access: Public
+//  Description: Elevates a currently-held read pointer into a write
+//               pointer.  This may or may not change the value of the
+//               pointer.  It is only valid to do this if this is the
+//               only currently-outstanding read pointer on the
+//               indicated stage.
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerTrueImpl::
+elevate_read_stage(int n, const CycleData *pointer) {
+#ifndef NDEBUG
+  nassertr(n >= 0 && n < _num_stages, NULL);
+  nassertr(_data[n] == pointer, NULL);
+#endif
+  CycleData *new_pointer = write_stage(n);
+  _lock.release();
+  return new_pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::release_write_stage
+//       Access: Public
+//  Description: Releases a pointer previously obtained via a call to
+//               write_stage().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrueImpl::
+release_write_stage(int n, CycleData *pointer) {
+#ifndef NDEBUG
+  nassertv(n >= 0 && n < _num_stages);
+  nassertv(_data[n] == pointer);
+#endif
+  _lock.release();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::get_parent_type
+//       Access: Public
+//  Description: Returns the type of object that owns this cycler, as
+//               reported by CycleData::get_parent_type().
+////////////////////////////////////////////////////////////////////
+INLINE TypeHandle PipelineCyclerTrueImpl::
+get_parent_type() const {
+  return _data[0]->get_parent_type();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::cheat
+//       Access: Public
+//  Description: Returns a pointer without counting it.  This is only
+//               intended for use as the return value for certain
+//               nassertr() functions, so the application can recover
+//               after a failure to manage the read and write pointers
+//               correctly.  You should never call this function
+//               directly.
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerTrueImpl::
+cheat() const {
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
+  return _data[pipeline_stage];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::get_read_count
+//       Access: Public
+//  Description: Returns the number of handles currently outstanding
+//               to read the current stage of the data.  This should
+//               only be used for debugging purposes.
+////////////////////////////////////////////////////////////////////
+INLINE int PipelineCyclerTrueImpl::
+get_read_count() const {
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::get_write_count
+//       Access: Public
+//  Description: Returns the number of handles currently outstanding
+//               to read the current stage of the data.  This will
+//               normally only be either 0 or 1.  This should only be
+//               used for debugging purposes.
+////////////////////////////////////////////////////////////////////
+INLINE int PipelineCyclerTrueImpl::
+get_write_count() const {
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::cycle_2
+//       Access: Private
+//  Description: This is a special implementation of cycle() for the
+//               special case of just two stages to the pipeline.  It
+//               does the same thing as cycle(), but is a little bit
+//               faster because it knows there are exactly two stages.
+////////////////////////////////////////////////////////////////////
+INLINE PT(CycleData) PipelineCyclerTrueImpl::
+cycle_2() {
+  PT(CycleData) last_val = _data[1];
+  nassertr(_lock.debug_is_locked(), last_val);
+  nassertr(_dirty, last_val);
+  nassertr(_num_stages == 2, last_val);
+
+  _data[1] = _data[0];
+
+  // No longer dirty.
+  _dirty = false;
+  return last_val;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::cycle_3
+//       Access: Private
+//  Description: This is a special implementation of cycle() for the
+//               special case of exactly three stages to the pipeline.
+//               It does the same thing as cycle(), but is a little
+//               bit faster because it knows there are exactly three
+//               stages.
+////////////////////////////////////////////////////////////////////
+INLINE PT(CycleData) PipelineCyclerTrueImpl::
+cycle_3() {
+  PT(CycleData) last_val = _data[2];
+  nassertr(_lock.debug_is_locked(), last_val);
+  nassertr(_dirty, last_val);
+  nassertr(_num_stages == 3, last_val);
+
+  _data[2] = _data[1];
+  _data[1] = _data[0];
+
+  if (_data[2] == _data[1]) {
+    // No longer dirty.
+    _dirty = false;
+  }
+
+  return last_val;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::CyclerMutex::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PipelineCyclerTrueImpl::CyclerMutex::
+CyclerMutex(PipelineCyclerTrueImpl *cycler) {
+#ifdef DEBUG_THREADS
+  _cycler = cycler;
+#endif
+}

+ 350 - 0
panda/src/express/pipelineCyclerTrueImpl.cxx

@@ -0,0 +1,350 @@
+// Filename: pipelineCyclerTrueImpl.cxx
+// Created by:  drose (31Jan06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "pipelineCyclerTrueImpl.h"
+
+#ifdef THREADED_PIPELINE
+
+#include "config_express.h"
+#include "pipeline.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+PipelineCyclerTrueImpl::
+PipelineCyclerTrueImpl(CycleData *initial_data, Pipeline *pipeline) :
+  _pipeline(pipeline),
+  _dirty(false),
+  _lock(this)
+{
+  if (_pipeline == (Pipeline *)NULL) {
+    _pipeline = Pipeline::get_render_pipeline();
+  }
+
+  _num_stages = _pipeline->get_num_stages();
+  _data = new PT(CycleData)[_num_stages];
+  for (int i = 0; i < _num_stages; ++i) {
+    _data[i] = initial_data;
+  }
+
+  _pipeline->add_cycler(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::Copy Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+PipelineCyclerTrueImpl::
+PipelineCyclerTrueImpl(const PipelineCyclerTrueImpl &copy) :
+  _pipeline(copy._pipeline),
+  _dirty(false),
+  _lock(this)
+{
+  ReMutexHolder holder(_lock);
+  ReMutexHolder holder2(copy._lock);
+  
+  _num_stages = _pipeline->get_num_stages();
+  nassertv(_num_stages == copy._num_stages);
+  _data = new PT(CycleData)[_num_stages];
+  
+  // It's no longer critically important that we preserve pointerwise
+  // equivalence between different stages in the copy, but it doesn't
+  // cost much and might be a little more efficient, so we do it
+  // anyway.
+  typedef pmap<CycleData *, PT(CycleData) > Pointers;
+  Pointers pointers;
+  
+  for (int i = 0; i < _num_stages; ++i) {
+    PT(CycleData) &new_pt = pointers[copy._data[i]];
+    if (new_pt == NULL) {
+      new_pt = copy._data[i]->make_copy();
+    }
+    _data[i] = new_pt;
+  }
+
+  _pipeline->add_cycler(this);
+  if (copy._dirty) {
+    _pipeline->add_dirty_cycler(this);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::Copy Assignment
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void PipelineCyclerTrueImpl::
+operator = (const PipelineCyclerTrueImpl &copy) {
+  ReMutexHolder holder1(_lock);
+  ReMutexHolder holder2(copy._lock);
+  nassertv(get_parent_type() == copy.get_parent_type());
+
+  typedef pmap<CycleData *, PT(CycleData) > Pointers;
+  Pointers pointers;
+
+  for (int i = 0; i < _num_stages; ++i) {
+    PT(CycleData) &new_pt = pointers[copy._data[i]];
+    if (new_pt == NULL) {
+      new_pt = copy._data[i]->make_copy();
+    }
+    _data[i] = new_pt;
+  }
+
+  if (copy._dirty && !_dirty) {
+    _pipeline->add_dirty_cycler(this);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+PipelineCyclerTrueImpl::
+~PipelineCyclerTrueImpl() {
+  ReMutexHolder holder(_lock);
+
+  _pipeline->remove_cycler(this);
+
+  delete[] _data;
+  _data = NULL;
+  _num_stages = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::write_upstream
+//       Access: Public
+//  Description: This special variant on write() will automatically
+//               propagate changes back to upstream pipeline stages.
+//               If force_to_0 is false, then it propagates back only
+//               as long as the CycleData pointers are equivalent,
+//               guaranteeing that it does not modify upstream data
+//               (other than the modification that will be performed
+//               by the code that returns this pointer).  This is
+//               particularly appropriate for minor updates, where it
+//               doesn't matter much if the update is lost, such as
+//               storing a cached value.
+//
+//               If force_to_0 is true, then the CycleData pointer for
+//               the current pipeline stage is propagated all the way
+//               back up to stage 0; after this call, there will be
+//               only one CycleData pointer that is duplicated in all
+//               stages between stage 0 and the current stage.  This
+//               may undo some recent changes that were made
+//               independently at pipeline stage 0 (or any other
+//               upstream stage).  However, it guarantees that the
+//               change that is to be applied at this pipeline stage
+//               will stick.  This is slightly dangerous because of
+//               the risk of losing upstream changes; generally, this
+//               should only be done when you are confident that there
+//               are no upstream changes to be lost (for instance, for
+//               an object that has been recently created).
+////////////////////////////////////////////////////////////////////
+CycleData *PipelineCyclerTrueImpl::
+write_upstream(bool force_to_0) {
+  _lock.lock();
+  
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+
+#ifndef NDEBUG
+  nassertd(pipeline_stage >= 0 && pipeline_stage < _num_stages) {
+    _lock.release();
+    return NULL;
+  }
+#endif  // NDEBUG
+
+  CycleData *old_data = _data[pipeline_stage];
+
+  if (old_data->get_ref_count() != 1 || force_to_0) {
+    // Count the number of references before the current stage, and
+    // the number of references remaining other than those.
+    int external_count = old_data->get_ref_count() - 1;
+    int k = pipeline_stage - 1;
+    while (k >= 0 && _data[k] == old_data) {
+      --k;
+      --external_count;
+    }
+    
+    if (external_count > 0) {
+      // There are references other than the ones before this stage in
+      // the pipeline; perform a copy-on-write.
+      PT(CycleData) new_data = old_data->make_copy();
+      
+      k = pipeline_stage - 1;
+      while (k >= 0 && (_data[k] == old_data || force_to_0)) {
+        _data[k] = new_data;
+        --k;
+      }
+      
+      _data[pipeline_stage] = new_data;
+      
+      if (k >= 0 || pipeline_stage + 1 < _num_stages) {
+        // Now we have differences between some of the data pointers,
+        // which makes us "dirty".
+        if (!_dirty) {
+          _pipeline->add_dirty_cycler(this);
+        }
+      }
+
+    } else if (k >= 0 && force_to_0) {
+      // There are no external pointers, so no need to copy-on-write,
+      // but the current pointer doesn't go all the way back.  Make it
+      // do so.
+      while (k >= 0) {
+        _data[k] = old_data;
+        --k;
+      }
+    }
+  }
+
+  return _data[pipeline_stage];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::write_stage
+//       Access: Public
+//  Description: Returns a pointer suitable for writing to the nth
+//               stage of the pipeline.  This is for special
+//               applications that need to update the entire pipeline
+//               at once (for instance, to remove an invalid pointer).
+//               This pointer should later be released with
+//               release_write_stage().
+////////////////////////////////////////////////////////////////////
+CycleData *PipelineCyclerTrueImpl::
+write_stage(int pipeline_stage) {
+  _lock.lock();
+
+#ifndef NDEBUG
+  nassertd(pipeline_stage >= 0 && pipeline_stage < _num_stages) {
+    _lock.release();
+    return NULL;
+  }
+#endif  // NDEBUG
+
+  CycleData *old_data = _data[pipeline_stage];
+
+  if (old_data->get_ref_count() != 1) {
+    // Copy-on-write.
+    _data[pipeline_stage] = old_data->make_copy();
+
+    // Now we have differences between some of the data pointers, so
+    // we're "dirty".  Mark it so.
+    if (!_dirty) {
+      _pipeline->add_dirty_cycler(this);
+    }
+  }
+
+  return _data[pipeline_stage];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::cycle
+//       Access: Private
+//  Description: Cycles the data between frames.  This is only called
+//               from Pipeline::cycle(), and presumably it is only
+//               called if the cycler is "dirty".
+//
+//               At the conclusion of this method, the cycler should
+//               clear its dirty flag if it is no longer "dirty"--that
+//               is, if all of the pipeline pointers are the same.
+//
+//               The return value is the CycleData pointer which fell
+//               off the end of the cycle.  If this is allowed to
+//               destruct immediately, there may be side-effects that
+//               cascade through the system, so the caller may choose
+//               to hold the pointer until it can safely be released
+//               later.
+////////////////////////////////////////////////////////////////////
+PT(CycleData) PipelineCyclerTrueImpl::
+cycle() {
+  PT(CycleData) last_val = _data[_num_stages - 1];
+  nassertr(_lock.debug_is_locked(), last_val);
+  nassertr(_dirty, last_val);
+
+  int i;
+  for (i = _num_stages - 1; i > 0; --i) {
+    _data[i] = _data[i - 1];
+  }
+
+  for (i = 1; i < _num_stages; ++i) {
+    if (_data[i] != _data[i - 1]) {
+      // Still dirty.
+      return last_val;
+    }
+  }
+
+  // No longer dirty.
+  _dirty = false;
+  return last_val;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::set_num_stages
+//       Access: Private
+//  Description: Changes the number of stages in the cycler.  This is
+//               only called from Pipeline::set_num_stages();
+////////////////////////////////////////////////////////////////////
+void PipelineCyclerTrueImpl::
+set_num_stages(int num_stages) {
+  nassertv(_lock.debug_is_locked());
+
+  if (num_stages <= _num_stages) {
+    // Don't bother to reallocate the array smaller; we just won't use
+    // the rest of the array.
+    for (int i = _num_stages; i < num_stages; ++i) {
+      _data[i].clear();
+    }
+
+    _num_stages = num_stages;
+    
+
+  } else {
+    // To increase the array, we must reallocate it larger.
+    PT(CycleData) *new_data = new PT(CycleData)[num_stages];
+    int i;
+    for (i = 0; i < _num_stages; ++i) {
+      new_data[i] = _data[i];
+    }
+    for (i = _num_stages; i < num_stages; ++i) {
+      new_data[i] = _data[_num_stages - 1];
+    }
+    delete[] _data;
+
+    _num_stages = num_stages;
+    _data = new_data;
+  }
+}
+
+#ifdef DEBUG_THREADS
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::CyclerMutex::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void PipelineCyclerTrueImpl::CyclerMutex::
+output(ostream &out) const {
+  _cycler->cheat()->output(out);
+}
+#endif  // DEBUG_THREADS
+
+#endif  // THREADED_PIPELINE
+
+

+ 124 - 0
panda/src/express/pipelineCyclerTrueImpl.h

@@ -0,0 +1,124 @@
+// Filename: pipelineCyclerTrueImpl.h
+// Created by:  drose (31Jan06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 PIPELINECYCLERTRUEIMPL_H
+#define PIPELINECYCLERTRUEIMPL_H
+
+#include "pandabase.h"
+
+#ifdef THREADED_PIPELINE
+
+#include "pipelineCyclerLinks.h"
+#include "cycleData.h"
+#include "pointerTo.h"
+#include "thread.h"
+#include "reMutex.h"
+#include "reMutexHolder.h"
+
+class Pipeline;
+
+////////////////////////////////////////////////////////////////////
+//       Class : PipelineCyclerTrueImpl
+// Description : This is the true, threaded implementation of
+//               PipelineCyclerBase.  It is only compiled when
+//               threading is available and DO_PIPELINING is defined.
+//
+//               This implementation is designed to do the actual work
+//               of cycling the data through a pipeline, and returning
+//               the actual CycleData appropriate to the current
+//               thread's pipeline stage.
+//
+//               This is defined as a struct instead of a class,
+//               mainly to be consistent with
+//               PipelineCyclerTrivialImpl.
+////////////////////////////////////////////////////////////////////
+struct EXPCL_PANDAEXPRESS PipelineCyclerTrueImpl : public PipelineCyclerLinks {
+private:
+  PipelineCyclerTrueImpl();
+public:
+  PipelineCyclerTrueImpl(CycleData *initial_data, Pipeline *pipeline = NULL);
+  PipelineCyclerTrueImpl(const PipelineCyclerTrueImpl &copy);
+  void operator = (const PipelineCyclerTrueImpl &copy);
+  ~PipelineCyclerTrueImpl();
+
+  INLINE void lock();
+  INLINE void release();
+
+  INLINE const CycleData *read() const;
+  INLINE void increment_read(const CycleData *pointer) const;
+  INLINE void release_read(const CycleData *pointer) const;
+
+  INLINE CycleData *write();
+  INLINE CycleData *elevate_read(const CycleData *pointer);
+  INLINE CycleData *elevate_read_upstream(const CycleData *pointer, bool force_to_0);
+  INLINE void increment_write(CycleData *pointer) const;
+  INLINE void release_write(CycleData *pointer);
+
+  INLINE int get_num_stages();
+  INLINE const CycleData *read_stage(int n) const;
+  INLINE void release_read_stage(int n, const CycleData *pointer) const;
+  CycleData *write_upstream(bool force_to_0);
+  CycleData *write_stage(int pipeline_stage);
+  INLINE CycleData *elevate_read_stage(int n, const CycleData *pointer);
+  INLINE void release_write_stage(int n, CycleData *pointer);
+
+  INLINE TypeHandle get_parent_type() const;
+
+  INLINE CycleData *cheat() const;
+  INLINE int get_read_count() const;
+  INLINE int get_write_count() const;
+
+public:
+  // We redefine the ReMutex class, solely so we can define the
+  // output() operator.  This is only useful for debugging, but does
+  // no harm in the production case.
+  class CyclerMutex : public ReMutex {
+  public:
+    INLINE CyclerMutex(PipelineCyclerTrueImpl *cycler);
+
+#ifdef DEBUG_THREADS
+    virtual void output(ostream &out) const;
+    PipelineCyclerTrueImpl *_cycler;
+#endif  // DEBUG_THREADS
+  };
+
+private:
+  PT(CycleData) cycle();
+  INLINE PT(CycleData) cycle_2();
+  INLINE PT(CycleData) cycle_3();
+  void set_num_stages(int num_stages);
+
+private:
+  Pipeline *_pipeline;
+
+  // An array of PT(CycleData) objects.
+  PT(CycleData) *_data;
+  int _num_stages;
+  bool _dirty;
+
+  CyclerMutex _lock;
+
+  friend class Pipeline;
+};
+
+#include "pipelineCyclerTrueImpl.I"
+
+#endif  // THREADED_PIPELINE
+
+#endif
+