Просмотр исходного кода

first pass at cheesy pipelining

David Rose 20 лет назад
Родитель
Сommit
629a2f087a
35 измененных файлов с 1635 добавлено и 767 удалено
  1. 5 0
      panda/src/display/graphicsEngine.cxx
  2. 2 0
      panda/src/express/config_express.cxx
  3. 18 1
      panda/src/express/externalThread.cxx
  4. 3 1
      panda/src/express/externalThread.h
  5. 2 1
      panda/src/express/mainThread.h
  6. 104 71
      panda/src/express/thread.I
  7. 43 2
      panda/src/express/thread.cxx
  8. 17 7
      panda/src/express/thread.h
  9. 13 0
      panda/src/express/threadDummyImpl.I
  10. 1 0
      panda/src/express/threadDummyImpl.h
  11. 17 1
      panda/src/express/threadNsprImpl.I
  12. 1 0
      panda/src/express/threadNsprImpl.h
  13. 4 4
      panda/src/gobj/geomVertexFormat.cxx
  14. 0 27
      panda/src/pgraph/pandaNode.I
  15. 94 28
      panda/src/pgraph/pandaNode.cxx
  16. 8 1
      panda/src/pgraph/pandaNode.h
  17. 1 1
      panda/src/pstatclient/pStatThread.cxx
  18. 11 3
      panda/src/putil/Sources.pp
  19. 1 2
      panda/src/putil/bamReader.h
  20. 1 1
      panda/src/putil/bamWriter.h
  21. 77 3
      panda/src/putil/pipeline.cxx
  22. 20 2
      panda/src/putil/pipeline.h
  23. 12 14
      panda/src/putil/pipelineCycler.I
  24. 0 527
      panda/src/putil/pipelineCyclerBase.I
  25. 17 53
      panda/src/putil/pipelineCyclerBase.h
  26. 275 0
      panda/src/putil/pipelineCyclerDummyImpl.I
  27. 3 16
      panda/src/putil/pipelineCyclerDummyImpl.cxx
  28. 83 0
      panda/src/putil/pipelineCyclerDummyImpl.h
  29. 261 0
      panda/src/putil/pipelineCyclerTrivialImpl.I
  30. 19 0
      panda/src/putil/pipelineCyclerTrivialImpl.cxx
  31. 89 0
      panda/src/putil/pipelineCyclerTrivialImpl.h
  32. 196 0
      panda/src/putil/pipelineCyclerTrueImpl.I
  33. 140 0
      panda/src/putil/pipelineCyclerTrueImpl.cxx
  34. 94 0
      panda/src/putil/pipelineCyclerTrueImpl.h
  35. 3 1
      panda/src/putil/putil_composite2.cxx

+ 5 - 0
panda/src/display/graphicsEngine.cxx

@@ -33,6 +33,8 @@
 #include "cullFaceAttrib.h"
 #include "string_utils.h"
 #include "geomCacheManager.h"
+#include "thread.h"
+#include "pipeline.h"
 #include "throw_event.h"
 
 #if defined(WIN32)
@@ -1413,6 +1415,9 @@ get_window_renderer(const string &name) {
   }
 
   PT(RenderThread) thread = new RenderThread(name, this);
+  thread->set_pipeline_stage(1);
+  Pipeline::get_render_pipeline()->set_num_stages(2);
+
   thread->start(TP_normal, true, true);
   _threads[name] = thread;
 

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

@@ -20,6 +20,7 @@
 #include "config_express.h"
 #include "datagram.h"
 #include "mainThread.h"
+#include "externalThread.h"
 #include "referenceCount.h"
 #include "textEncoder.h"
 #include "thread.h"
@@ -169,6 +170,7 @@ init_libexpress() {
 
   Datagram::init_type();
   MainThread::init_type();
+  ExternalThread::init_type();
   ReferenceCount::init_type();
   TextEncoder::init_type();
   Thread::init_type();

+ 18 - 1
panda/src/express/externalThread.cxx

@@ -23,12 +23,29 @@ TypeHandle ExternalThread::_type_handle;
 ////////////////////////////////////////////////////////////////////
 //     Function: ExternalThread::Constructor
 //       Access: Private
-//  Description: 
+//  Description: This constructor is used to create the one global
+//               ExternalThread object that is shared by all
+//               externally-created threads that are not specifically
+//               bound to a particular Thread object.
 ////////////////////////////////////////////////////////////////////
 ExternalThread::
 ExternalThread() : Thread("External", "External") {
   _started = true;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: ExternalThread::Constructor
+//       Access: Private
+//  Description: This constructor is used to create the particular
+//               Thread object for each external thread that is bound
+//               via Thread::bind_thread().
+////////////////////////////////////////////////////////////////////
+ExternalThread::
+ExternalThread(const string &name, const string &sync_name) : 
+  Thread(name, sync_name)
+{
+  _started = true;
+}
  
 ////////////////////////////////////////////////////////////////////
 //     Function: ExternalThread::thread_main

+ 3 - 1
panda/src/express/externalThread.h

@@ -31,12 +31,14 @@
 class EXPCL_PANDAEXPRESS ExternalThread : public Thread {
 private:
   ExternalThread();
+  ExternalThread(const string &name, const string &sync_name);
   virtual void thread_main();
 
-public:
+PUBLISHED:
   static TypeHandle get_class_type() {
     return _type_handle;
   }
+public:
   static void init_type() {
     Thread::init_type();
     register_type(_type_handle, "ExternalThread",

+ 2 - 1
panda/src/express/mainThread.h

@@ -33,10 +33,11 @@ private:
   MainThread();
   virtual void thread_main();
 
-public:
+PUBLISHED:
   static TypeHandle get_class_type() {
     return _type_handle;
   }
+public:
   static void init_type() {
     Thread::init_type();
     register_type(_type_handle, "MainThread",

+ 104 - 71
panda/src/express/thread.I

@@ -19,7 +19,7 @@
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Thread::Constructor
-//       Access: Public
+//       Access: Protected
 //  Description: Creates a new Thread object, but does not
 //               immediately start executing it.  This gives the
 //               caller a chance to store it in a PT(Thread) object,
@@ -43,6 +43,7 @@ Thread(const string &name, const string &sync_name) :
 {
   _started = false;
   _pstats_index = -1;
+  _pipeline_stage = 0;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -67,7 +68,7 @@ operator = (const Thread &copy) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Thread::get_name
-//       Access: Public
+//       Access: Published
 //  Description: Returns the name of the thread.  Each thread should
 //               probably have a unique name, but this is not enforced
 //               (and not strictly required).
@@ -79,7 +80,7 @@ get_name() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Thread::get_sync_name
-//       Access: Public
+//       Access: Published
 //  Description: Returns the sync name of the thread.  This name
 //               collects threads into "sync groups", which are
 //               expected to run synchronously.  This is mainly used
@@ -94,7 +95,7 @@ get_sync_name() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Thread::get_pstats_index
-//       Access: Public
+//       Access: Published
 //  Description: Returns the PStats index associated with this thread,
 //               or -1 if no index has yet been associated with this
 //               thread.  This is used internally by the PStatClient;
@@ -107,7 +108,7 @@ get_pstats_index() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Thread::set_pstats_index
-//       Access: Public
+//       Access: Published
 //  Description: Stores a PStats index to be associated with this
 //               thread.  This is used internally by the PStatClient;
 //               you should not need to call this directly.
@@ -118,78 +119,40 @@ set_pstats_index(int pstats_index) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Thread::start
-//       Access: Public
-//  Description: Starts the thread executing.  It is only valid to
-//               call this once.
-//
-//               The thread will begin executing its thread_main()
-//               function, and will terminate when thread_main()
-//               returns.
-//
-//               priority is intended as a hint to the relative
-//               importance of this thread, and global should be set
-//               true if the thread will perform a lot of blocking
-//               I/O, or false otherwise (see the NSPR documentation
-//               on global vs. local threads for more on this).  Both
-//               of these parameters may be ignored by the thread
-//               implementation.
-//
-//               joinable should be set true if you intend to call
-//               join() to wait for the thread to terminate, or false
-//               if you don't care and you will never call join().
-//
-//               The return value is true if the thread is
-//               successfully started, false otherwise.
+//     Function: Thread::get_pipeline_stage
+//       Access: Published
+//  Description: Returns the Pipeline stage number associated with
+//               this thread.  The default stage is 0 if no stage is
+//               specified otherwise.  See set_pipeline_stage().
 ////////////////////////////////////////////////////////////////////
-INLINE bool Thread::
-start(ThreadPriority priority, bool global, bool joinable) {
-  nassertr(!_started, false);
-  _started = _impl.start(priority, global, joinable);
-  return _started;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Thread::interrupt
-//       Access: Public
-//  Description: Sends an interrupt message to the thread.  This will
-//               interrupt any blocking-type system calls the thread
-//               may be waiting on, such as I/O, so that the thread
-//               may continue some other processing.  The specific
-//               behavior is implementation dependent.
-////////////////////////////////////////////////////////////////////
-INLINE void Thread::
-interrupt() {
-  _impl.interrupt();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Thread::join
-//       Access: Public
-//  Description: Blocks the calling process until the thread
-//               terminates.  If the thread has already terminated,
-//               this returns immediately.
-////////////////////////////////////////////////////////////////////
-INLINE void Thread::
-join() {
-  _impl.join();
+INLINE int Thread::
+get_pipeline_stage() const {
+  return _pipeline_stage;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Thread::prepare_for_exit
-//       Access: Public
-//  Description: Should be called by the main thread just before
-//               exiting the program, this blocks until any remaining
-//               thread cleanup has finished.
+//     Function: Thread::set_pipeline_stage
+//       Access: Published
+//  Description: Specifies the Pipeline stage number associated with
+//               this thread.  The default stage is 0 if no stage is
+//               specified otherwise.
+//
+//               This must be a value in the range [0
+//               .. pipeline->get_num_stages() - 1].  It specifies the
+//               values that this thread observes for all pipelined
+//               data.  Typically, an application thread will leave
+//               this at 0, but a render thread may set it to 1 or 2
+//               (to operate on the previous frame's data, or the
+//               second previous frame's data).
 ////////////////////////////////////////////////////////////////////
 INLINE void Thread::
-prepare_for_exit() {
-  ThreadImpl::prepare_for_exit();
+set_pipeline_stage(int pipeline_stage) {
+  _pipeline_stage = pipeline_stage;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Thread::get_main_thread
-//       Access: Public, Static
+//       Access: Published, Static
 //  Description: Returns a pointer to the "main" Thread object--this
 //               is the Thread that started the whole process.
 ////////////////////////////////////////////////////////////////////
@@ -203,7 +166,7 @@ get_main_thread() {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Thread::get_external_thread
-//       Access: Public, Static
+//       Access: Published, Static
 //  Description: Returns a pointer to the "external" Thread
 //               object--this is a special Thread object that
 //               corresponds to any thread spawned outside of Panda's
@@ -220,7 +183,7 @@ get_external_thread() {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Thread::get_current_thread
-//       Access: Public, Static
+//       Access: Published, Static
 //  Description: Returns a pointer to the currently-executing Thread
 //               object.  If this is called from the main thread, this
 //               will return the same value as get_main_thread().
@@ -246,7 +209,7 @@ get_current_thread() {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Thread::is_threading_supported
-//       Access: Public, Static
+//       Access: Published, Static
 //  Description: Returns true if a real threading library is available
 //               that supports threads, or false if no threading
 //               library is available (and Thread::start() will always
@@ -259,7 +222,7 @@ is_threading_supported() {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Thread::sleep
-//       Access: Public, Static
+//       Access: Published, Static
 //  Description: Suspends the current thread for at least the
 //               indicated amount of time.  It might be suspended for
 //               longer.
@@ -269,6 +232,76 @@ sleep(double seconds) {
   ThreadImpl::sleep(seconds);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Thread::start
+//       Access: Public
+//  Description: Starts the thread executing.  It is only valid to
+//               call this once.
+//
+//               The thread will begin executing its thread_main()
+//               function, and will terminate when thread_main()
+//               returns.
+//
+//               priority is intended as a hint to the relative
+//               importance of this thread, and global should be set
+//               true if the thread will perform a lot of blocking
+//               I/O, or false otherwise (see the NSPR documentation
+//               on global vs. local threads for more on this).  Both
+//               of these parameters may be ignored by the thread
+//               implementation.
+//
+//               joinable should be set true if you intend to call
+//               join() to wait for the thread to terminate, or false
+//               if you don't care and you will never call join().
+//
+//               The return value is true if the thread is
+//               successfully started, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool Thread::
+start(ThreadPriority priority, bool global, bool joinable) {
+  nassertr(!_started, false);
+  _started = _impl.start(priority, global, joinable);
+  return _started;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Thread::interrupt
+//       Access: Public
+//  Description: Sends an interrupt message to the thread.  This will
+//               interrupt any blocking-type system calls the thread
+//               may be waiting on, such as I/O, so that the thread
+//               may continue some other processing.  The specific
+//               behavior is implementation dependent.
+////////////////////////////////////////////////////////////////////
+INLINE void Thread::
+interrupt() {
+  _impl.interrupt();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Thread::join
+//       Access: Public
+//  Description: Blocks the calling process until the thread
+//               terminates.  If the thread has already terminated,
+//               this returns immediately.
+////////////////////////////////////////////////////////////////////
+INLINE void Thread::
+join() {
+  _impl.join();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Thread::prepare_for_exit
+//       Access: Public
+//  Description: Should be called by the main thread just before
+//               exiting the program, this blocks until any remaining
+//               thread cleanup has finished.
+////////////////////////////////////////////////////////////////////
+INLINE void Thread::
+prepare_for_exit() {
+  ThreadImpl::prepare_for_exit();
+}
+
 INLINE ostream &
 operator << (ostream &out, const Thread &thread) {
   thread.output(out);

+ 43 - 2
panda/src/express/thread.cxx

@@ -26,16 +26,57 @@ TypeHandle Thread::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Thread::Destructor
-//       Access: Public, Virtual
+//       Access: Published, Virtual
 //  Description:
 ////////////////////////////////////////////////////////////////////
 Thread::
 ~Thread() {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Thread::bind_thread
+//       Access: Published, Static
+//  Description: Returns a new Panda Thread object associated with the
+//               current thread (which has been created externally).
+//               This can be used to bind a unique Panda Thread object
+//               with an external thread, such as a new Python thread.
+//
+//               It is particularly useful to bind a Panda Thread
+//               object to an external thread for the purposes of
+//               PStats monitoring.  Without this call, each external
+//               thread will be assigned the same global
+//               ExternalThread object, which means they will all
+//               appear in the same PStats graph.
+//
+//               It is the caller's responsibility to save the
+//               returned Thread pointer for the lifetime of the
+//               external thread.  It is an error for the Thread
+//               pointer to destruct while the external thread is
+//               still in the system.
+//
+//               It is also an error to call this method from the main
+//               thread, or twice within a given thread, unless it is
+//               given the same name each time (in which case the same
+//               pointer will be returned each time).
+////////////////////////////////////////////////////////////////////
+PT(Thread) Thread::
+bind_thread(const string &name, const string &sync_name) {
+  Thread *current_thread = get_current_thread();
+  if (current_thread != get_external_thread()) {
+    // This thread already has an associated thread.
+    nassertr(current_thread->get_name() == name && 
+             current_thread->get_sync_name() == sync_name, current_thread);
+    return current_thread;
+  }
+
+  PT(Thread) thread = new ExternalThread(name, sync_name);
+  ThreadImpl::bind_thread(thread);
+  return thread;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Thread::output
-//       Access: Public, Virtual
+//       Access: Published, Virtual
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void Thread::

+ 17 - 7
panda/src/express/thread.h

@@ -21,6 +21,7 @@
 
 #include "pandabase.h"
 #include "typedReferenceCount.h"
+#include "pointerTo.h"
 #include "threadPriority.h"
 #include "threadImpl.h"
 #include "notify.h"
@@ -38,8 +39,10 @@
 //               are referencing it.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDAEXPRESS Thread : public TypedReferenceCount {
-public:
+protected:
   INLINE Thread(const string &name, const string &sync_name);
+
+PUBLISHED:
   virtual ~Thread();
 
 private:
@@ -49,18 +52,17 @@ private:
 protected:
   virtual void thread_main()=0;
 
-public:
+PUBLISHED:
+  static PT(Thread) bind_thread(const string &name, const string &sync_name);
+
   INLINE const string &get_name() const;
   INLINE const string &get_sync_name() const;
 
   INLINE int get_pstats_index() const;
   INLINE void set_pstats_index(int pstats_index);
 
-  INLINE bool start(ThreadPriority priority, bool global, bool joinable);
-  INLINE void interrupt();
-  INLINE void join();
-
-  INLINE static void prepare_for_exit();
+  INLINE int get_pipeline_stage() const;
+  INLINE void set_pipeline_stage(int pipeline_stage);
 
   INLINE static Thread *get_main_thread();
   INLINE static Thread *get_external_thread();
@@ -70,6 +72,13 @@ public:
 
   virtual void output(ostream &out) const;
 
+public:
+  INLINE bool start(ThreadPriority priority, bool global, bool joinable);
+  INLINE void interrupt();
+  INLINE void join();
+
+  INLINE static void prepare_for_exit();
+
 private:
   static void init_main_thread();
   static void init_external_thread();
@@ -80,6 +89,7 @@ protected:
   string _sync_name;
   ThreadImpl _impl;
   int _pstats_index;
+  int _pipeline_stage;
 
 private:
   static Thread *_main_thread;

+ 13 - 0
panda/src/express/threadDummyImpl.I

@@ -72,6 +72,19 @@ INLINE void ThreadDummyImpl::
 prepare_for_exit() {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadDummyImpl::bind_thread
+//       Access: Public, Static
+//  Description: Associates the indicated Thread object with the
+//               currently-executing thread.  You should not call this
+//               directly; use Thread::bind_thread() instead.
+////////////////////////////////////////////////////////////////////
+INLINE void ThreadDummyImpl::
+bind_thread(Thread *thread) {
+  // This method shouldn't be called in the non-threaded case.
+  nassertv(false);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ThreadDummyImpl::is_threading_supported
 //       Access: Public, Static

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

@@ -53,6 +53,7 @@ public:
   INLINE static void prepare_for_exit();
 
   static Thread *get_current_thread();
+  INLINE static void bind_thread(Thread *thread);
   INLINE static bool is_threading_supported();
   INLINE static void sleep(double seconds);
 

+ 17 - 1
panda/src/express/threadNsprImpl.I

@@ -41,7 +41,7 @@ prepare_for_exit() {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: ThreadNsprImpl::Constructor
+//     Function: ThreadNsprImpl::get_current_thread
 //       Access: Public, Static
 //  Description: 
 ////////////////////////////////////////////////////////////////////
@@ -53,6 +53,22 @@ get_current_thread() {
   return (Thread *)PR_GetThreadPrivate(_pt_ptr_index);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: ThreadNsprImpl::bind_thread
+//       Access: Public, Static
+//  Description: Associates the indicated Thread object with the
+//               currently-executing thread.  You should not call this
+//               directly; use Thread::bind_thread() instead.
+////////////////////////////////////////////////////////////////////
+INLINE void ThreadNsprImpl::
+bind_thread(Thread *thread) {
+  if (!_got_pt_ptr_index) {
+    init_pt_ptr_index();
+  }
+  PRStatus result = PR_SetThreadPrivate(_pt_ptr_index, thread);
+  nassertv(result == PR_SUCCESS);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ThreadNsprImpl::is_threading_supported
 //       Access: Public, Static

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

@@ -49,6 +49,7 @@ public:
   INLINE static void prepare_for_exit();
 
   INLINE static Thread *get_current_thread();
+  INLINE static void bind_thread(Thread *thread);
   INLINE static bool is_threading_supported();
   INLINE static void sleep(double seconds);
 

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

@@ -799,7 +799,7 @@ Registry() {
                             (InternalName::get_vertex(), 3,
                              NT_float32, C_point,
                              InternalName::get_normal(), 3,
-                             NT_float32, C_point,
+                             NT_float32, C_vector,
                              InternalName::get_color(), 1,
                              NT_packed_dabc, C_color));
 
@@ -815,7 +815,7 @@ Registry() {
                               (InternalName::get_vertex(), 3,
                                NT_float32, C_point,
                                InternalName::get_normal(), 3,
-                               NT_float32, C_point,
+                               NT_float32, C_vector,
                                InternalName::get_color(), 1,
                                NT_packed_dabc, C_color,
                                InternalName::get_texcoord(), 2,
@@ -834,7 +834,7 @@ Registry() {
                             (InternalName::get_vertex(), 3,
                              NT_float32, C_point,
                              InternalName::get_normal(), 3,
-                             NT_float32, C_point,
+                             NT_float32, C_vector,
                              InternalName::get_color(), 4,
                              NT_uint8, C_color));
 
@@ -850,7 +850,7 @@ Registry() {
                               (InternalName::get_vertex(), 3,
                                NT_float32, C_point,
                                InternalName::get_normal(), 3,
-                               NT_float32, C_point,
+                               NT_float32, C_vector,
                                InternalName::get_color(), 4,
                                NT_uint8, C_color,
                                InternalName::get_texcoord(), 2,

+ 0 - 27
panda/src/pgraph/pandaNode.I

@@ -125,33 +125,6 @@ CData() {
   _fixed_internal_bound = false;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: PandaNode::CData::Copy Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE PandaNode::CData::
-CData(const PandaNode::CData &copy) :
-  _down(copy._down),
-  _stashed(copy._stashed),
-  _up(copy._up),
-  _paths(copy._paths),
-  _state(copy._state),
-  _effects(copy._effects),
-  _transform(copy._transform),
-  _prev_transform(copy._prev_transform),
-  _tag_data(copy._tag_data),
-  _draw_mask(copy._draw_mask),
-  _into_collide_mask(copy._into_collide_mask),
-  _fixed_internal_bound(copy._fixed_internal_bound)
-{
-  _net_collide_mask = CollideMask::all_off();
-
-  // Note that this copy constructor is not used by the PandaNode copy
-  // constructor!  Any elements that must be copied between nodes
-  // should also be explicitly copied there.
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::Children::Constructor
 //       Access: Public

+ 94 - 28
panda/src/pgraph/pandaNode.cxx

@@ -55,6 +55,53 @@ TypeHandle PandaNode::_type_handle;
 //
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::CData::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+PandaNode::CData::
+CData(const PandaNode::CData &copy) :
+  _down(copy._down),
+  _stashed(copy._stashed),
+  _up(copy._up),
+  _paths(copy._paths),
+  _state(copy._state),
+  _effects(copy._effects),
+  _transform(copy._transform),
+  _prev_transform(copy._prev_transform),
+  _tag_data(copy._tag_data),
+  _draw_mask(copy._draw_mask),
+  _into_collide_mask(copy._into_collide_mask),
+  _net_collide_mask(copy._net_collide_mask),
+  _off_clip_planes(copy._off_clip_planes),
+  _fixed_internal_bound(copy._fixed_internal_bound)
+{
+  // Note that this copy constructor is not used by the PandaNode copy
+  // constructor!  Any elements that must be copied between nodes
+  // should also be explicitly copied there.
+
+#ifdef HAVE_PYTHON
+  // Copy and increment all of the Python objects held by the other
+  // node.
+  _python_tag_data = _python_tag_data;
+  inc_py_refs();
+#endif  // HAVE_PYTHON
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::CData::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+PandaNode::CData::
+~CData() {
+#ifdef HAVE_PYTHON
+  // Free all of the Python objects held by this node.
+  dec_py_refs();
+#endif  // HAVE_PYTHON
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::CData::make_copy
 //       Access: Public, Virtual
@@ -160,9 +207,47 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   }
 }
 
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::CData::inc_py_refs
+//       Access: Public
+//  Description: Increments the reference counts on all held Python
+//               objects.
+////////////////////////////////////////////////////////////////////
+void PandaNode::CData::
+inc_py_refs() {
+  PythonTagData::const_iterator ti;
+  for (ti = _python_tag_data.begin();
+       ti != _python_tag_data.end();
+       ++ti) {
+    PyObject *value = (*ti).second;
+    Py_XINCREF(value);
+  }
+}
+#endif  // HAVE_PYTHON
+
+#ifdef HAVE_PYTHON
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::CData::dec_py_refs
+//       Access: Public
+//  Description: Decrements the reference counts on all held Python
+//               objects.
+////////////////////////////////////////////////////////////////////
+void PandaNode::CData::
+dec_py_refs() {
+  PythonTagData::const_iterator ti;
+  for (ti = _python_tag_data.begin();
+       ti != _python_tag_data.end();
+       ++ti) {
+    PyObject *value = (*ti).second;
+    Py_XDECREF(value);
+  }
+}
+#endif  // HAVE_PYTHON
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::CData::write_up_list
-//       Access: Private
+//       Access: Public
 //  Description: Writes the indicated list of parent node pointers to
 //               the datagram.
 ////////////////////////////////////////////////////////////////////
@@ -198,7 +283,7 @@ write_up_list(const PandaNode::Up &up_list,
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::CData::write_down_list
-//       Access: Private
+//       Access: Public
 //  Description: Writes the indicated list of child node pointers to
 //               the datagram.
 ////////////////////////////////////////////////////////////////////
@@ -222,7 +307,7 @@ write_down_list(const PandaNode::Down &down_list,
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::CData::complete_up_list
-//       Access: Private
+//       Access: Public
 //  Description: Calls complete_pointers() on the list of parent node
 //               pointers.
 ////////////////////////////////////////////////////////////////////
@@ -254,7 +339,7 @@ complete_up_list(PandaNode::Up &up_list,
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::CData::complete_down_list
-//       Access: Private
+//       Access: Public
 //  Description: Calls complete_pointers() on the list of child node
 //               pointers.
 ////////////////////////////////////////////////////////////////////
@@ -279,7 +364,7 @@ complete_down_list(PandaNode::Down &down_list,
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::CData::fillin_up_list
-//       Access: Private
+//       Access: Public
 //  Description: Reads the indicated list parent node pointers from
 //               the datagram (or at least calls read_pointer() for
 //               each one).
@@ -298,7 +383,7 @@ fillin_up_list(PandaNode::Up &up_list,
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::CData::fillin_down_list
-//       Access: Private
+//       Access: Public
 //  Description: Reads the indicated list child node pointers from
 //               the datagram (or at least calls read_pointer() for
 //               each one).
@@ -370,20 +455,6 @@ PandaNode::
 #endif  // NDEBUG
 
   remove_all_children();
-
-#ifdef HAVE_PYTHON
-  {
-    // Free all of the Python objects held by this node.
-    CDReader cdata(_cycler);
-    PythonTagData::const_iterator ti;
-    for (ti = cdata->_python_tag_data.begin();
-         ti != cdata->_python_tag_data.end();
-         ++ti) {
-      PyObject *value = (*ti).second;
-      Py_XDECREF(value);
-    }
-  }
-#endif  // HAVE_PYTHON
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -418,21 +489,16 @@ PandaNode(const PandaNode &copy) :
   cdata->_tag_data = copy_cdata->_tag_data;
   cdata->_draw_mask = copy_cdata->_draw_mask;
   cdata->_into_collide_mask = copy_cdata->_into_collide_mask;
+  cdata->_net_collide_mask = CollideMask::all_off();
+  cdata->_off_clip_planes = NULL;
   cdata->_fixed_internal_bound = copy_cdata->_fixed_internal_bound;
 
 #ifdef HAVE_PYTHON
   // Copy and increment all of the Python objects held by the other
   // node.
   cdata->_python_tag_data = copy_cdata->_python_tag_data;
-  PythonTagData::const_iterator ti;
-  for (ti = cdata->_python_tag_data.begin();
-       ti != cdata->_python_tag_data.end();
-       ++ti) {
-    PyObject *value = (*ti).second;
-    Py_XINCREF(value);
-  }
+  cdata->inc_py_refs();
 #endif  // HAVE_PYTHON
-
 }
 
 ////////////////////////////////////////////////////////////////////

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

@@ -315,12 +315,19 @@ private:
   class EXPCL_PANDA CData : public CycleData {
   public:
     INLINE CData();
-    INLINE CData(const CData &copy);
+    CData(const CData &copy);
+    virtual ~CData();
+
     virtual CycleData *make_copy() const;
     virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
     virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
     virtual void fillin(DatagramIterator &scan, BamReader *manager);
 
+#ifdef HAVE_PYTHON
+    void inc_py_refs();
+    void dec_py_refs();
+#endif
+
     void write_up_list(const Up &up_list,
                        BamWriter *manager, Datagram &dg) const;
     void write_down_list(const Down &down_list,

+ 1 - 1
panda/src/pstatclient/pStatThread.cxx

@@ -25,7 +25,7 @@
 //  Description: Returns the Panda Thread object associated with this
 //               particular PStatThread.
 ////////////////////////////////////////////////////////////////////
-INLINE Thread *PStatThread::
+Thread *PStatThread::
 get_thread() const {
   return _client->get_thread_object(_index);
 }

+ 11 - 3
panda/src/putil/Sources.pp

@@ -48,7 +48,10 @@
     nodePointerTo.h nodePointerTo.I \
     pipeline.h pipeline.I \
     pipelineCycler.h pipelineCycler.I \
-    pipelineCyclerBase.h pipelineCyclerBase.I \
+    pipelineCyclerBase.h  \
+    pipelineCyclerDummyImpl.h pipelineCyclerDummyImpl.I \
+    pipelineCyclerTrivialImpl.h pipelineCyclerTrivialImpl.I \
+    pipelineCyclerTrueImpl.h pipelineCyclerTrueImpl.I \
     pta_double.h \
     pta_float.h pta_int.h \
     string_utils.I string_utils.N string_utils.h \
@@ -88,7 +91,9 @@
     nodePointerTo.cxx \
     pipeline.cxx \
     pipelineCycler.cxx \
-    pipelineCyclerBase.cxx \
+    pipelineCyclerDummyImpl.cxx \
+    pipelineCyclerTrivialImpl.cxx \
+    pipelineCyclerTrueImpl.cxx \
     pta_double.cxx pta_float.cxx \
     pta_int.cxx pta_ushort.cxx \
     string_utils.cxx timedCycle.cxx typedWritable.cxx \
@@ -138,7 +143,10 @@
     nodePointerTo.h nodePointerTo.I \
     pipeline.h pipeline.I \
     pipelineCycler.h pipelineCycler.I \
-    pipelineCyclerBase.h pipelineCyclerBase.I \
+    pipelineCyclerBase.h \
+    pipelineCyclerDummyImpl.h pipelineCyclerDummyImpl.I \
+    pipelineCyclerTrivialImpl.h pipelineCyclerTrivialImpl.I \
+    pipelineCyclerTrueImpl.h pipelineCyclerTrueImpl.I \
     pta_double.h \
     pta_float.h pta_int.h pta_ushort.h \
     string_utils.I \

+ 1 - 2
panda/src/putil/bamReader.h

@@ -32,11 +32,10 @@
 #include "pset.h"
 #include "pmap.h"
 #include "dcast.h"
+#include "pipelineCyclerBase.h"
 
 #include <algorithm>
 
-struct PipelineCyclerBase;
-
 
 // A handy macro for reading PointerToArrays.
 #define READ_PTA(Manager, source, Read_func, array)   \

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

@@ -28,8 +28,8 @@
 #include "pset.h"
 #include "pmap.h"
 #include "vector_int.h"
+#include "pipelineCyclerBase.h"
 
-struct PipelineCyclerBase;
 
 // A handy macro for writing PointerToArrays.
 #define WRITE_PTA(Manager, dest, Write_func, array)  \

+ 77 - 3
panda/src/putil/pipeline.cxx

@@ -17,6 +17,7 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "pipeline.h"
+#include "pipelineCyclerTrueImpl.h"
 
 Pipeline *Pipeline::_render_pipeline = (Pipeline *)NULL;
 
@@ -29,26 +30,100 @@ Pipeline::
 Pipeline(const string &name) :
   Namable(name)
 {
+  _num_stages = 1;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Pipeline::Destructor
-//       Access: Public, Virtual
+//       Access: Public
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 Pipeline::
 ~Pipeline() {
+#if defined(DO_PIPELINING) && defined(HAVE_THREADS)
+  nassertv(_cyclers.empty());
+#endif  // DO_PIPELINING && HAVE_THREADS
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Pipeline::cycle
-//       Access: Public, Virtual
+//       Access: Public
 //  Description: Flows all the pipeline data down to the next stage.
 ////////////////////////////////////////////////////////////////////
 void Pipeline::
 cycle() {
+#if defined(DO_PIPELINING) && defined(HAVE_THREADS)
+  Cyclers::iterator ci;
+  for (ci = _cyclers.begin(); ci != _cyclers.end(); ++ci) {
+    (*ci)->cycle();
+  }
+#endif  // DO_PIPELINING && HAVE_THREADS
+}
+
+////////////////////////////////////////////////////////////////////
+//     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);
+  if (num_stages != _num_stages) {
+    _num_stages = num_stages;
+
+#if defined(DO_PIPELINING) && defined(HAVE_THREADS)
+    Cyclers::iterator ci;
+    for (ci = _cyclers.begin(); ci != _cyclers.end(); ++ci) {
+      (*ci)->set_num_stages(num_stages);
+    }
+#endif  // DO_PIPELINING && HAVE_THREADS
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Pipeline::get_num_stages
+//       Access: Public
+//  Description: Returns the number of stages required for the
+//               pipeline.
+////////////////////////////////////////////////////////////////////
+int Pipeline::
+get_num_stages() const {
+  return _num_stages;
 }
 
+
+#if defined(DO_PIPELINING) && defined(HAVE_THREADS)
+////////////////////////////////////////////////////////////////////
+//     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) {
+  bool inserted = _cyclers.insert(cycler).second;
+  nassertv(inserted);
+}
+#endif  // DO_PIPELINING && HAVE_THREADS
+
+#if defined(DO_PIPELINING) && defined(HAVE_THREADS)
+////////////////////////////////////////////////////////////////////
+//     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) {
+  Cyclers::iterator ci = _cyclers.find(cycler);
+  nassertv(ci != _cyclers.end());
+  _cyclers.erase(ci);
+}
+#endif  // DO_PIPELINING && HAVE_THREADS
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Pipeline::make_render_pipeline
 //       Access: Private, Static
@@ -59,4 +134,3 @@ make_render_pipeline() {
   nassertv(_render_pipeline == (Pipeline *)NULL);
   _render_pipeline = new Pipeline("render");
 }
-

+ 20 - 2
panda/src/putil/pipeline.h

@@ -21,6 +21,9 @@
 
 #include "pandabase.h"
 #include "namable.h"
+#include "pset.h"
+
+class PipelineCyclerTrueImpl;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : Pipeline
@@ -38,15 +41,30 @@
 class EXPCL_PANDA Pipeline : public Namable {
 public:
   Pipeline(const string &name);
-  virtual ~Pipeline();
+  ~Pipeline();
 
   INLINE static Pipeline *get_render_pipeline();
 
-  virtual void cycle();
+  void cycle();
+
+  void set_num_stages(int num_stages);
+  int get_num_stages() const;
+
+#if defined(DO_PIPELINING) && defined(HAVE_THREADS)
+  void add_cycler(PipelineCyclerTrueImpl *cycler);
+  void remove_cycler(PipelineCyclerTrueImpl *cycler);
+#endif  // DO_PIPELINING && HAVE_THREADS
 
 private:
+  int _num_stages;
+
   static void make_render_pipeline();
   static Pipeline *_render_pipeline;
+
+#if defined(DO_PIPELINING) && defined(HAVE_THREADS)
+  typedef pset<PipelineCyclerTrueImpl *> Cyclers;
+  Cyclers _cyclers;
+#endif  // DO_PIPELINING && HAVE_THREADS
 };
 
 #include "pipeline.I"

+ 12 - 14
panda/src/putil/pipelineCycler.I

@@ -18,11 +18,11 @@
 
 
 #ifdef DO_PIPELINING
-// The following implementations are to support compiled-in pipeline
-// sanity checks.
+// The following implementations are to support the
+// PipelineCyclerDummyImpl or the PipelineCyclerTrueImpl.
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCycler::Constructor (sanity-check)
+//     Function: PipelineCycler::Constructor (dummy or true)
 //       Access: Public
 //  Description: 
 ////////////////////////////////////////////////////////////////////
@@ -34,20 +34,19 @@ PipelineCycler(Pipeline *pipeline) :
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCycler::Copy Constructor (sanity-check)
+//     Function: PipelineCycler::Copy Constructor (dummy or true)
 //       Access: Public
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 template<class CycleDataType>
 INLINE PipelineCycler<CycleDataType>::
 PipelineCycler(const PipelineCycler<CycleDataType> &copy) :
-  // In the DO_PIPELINING copy constructor, the pointer value is ignored.
-  PipelineCyclerBase(NULL, copy)
+  PipelineCyclerBase(copy)
 {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCycler::Copy Assignment (sanity-check)
+//     Function: PipelineCycler::Copy Assignment (dummy or true)
 //       Access: Public
 //  Description: 
 ////////////////////////////////////////////////////////////////////
@@ -58,7 +57,7 @@ operator = (const PipelineCycler<CycleDataType> &copy) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCycler::read (sanity-check)
+//     Function: PipelineCycler::read (dummy or true)
 //       Access: Public
 //  Description: See PipelineCyclerBase::read().
 ////////////////////////////////////////////////////////////////////
@@ -69,7 +68,7 @@ read() const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCycler::write (sanity-check)
+//     Function: PipelineCycler::write (dummy or true)
 //       Access: Public
 //  Description: See PipelineCyclerBase::write().
 ////////////////////////////////////////////////////////////////////
@@ -80,7 +79,7 @@ write() {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCycler::elevate_read (sanity-check)
+//     Function: PipelineCycler::elevate_read (dummy or true)
 //       Access: Public
 //  Description: See PipelineCyclerBase::elevate_read().
 ////////////////////////////////////////////////////////////////////
@@ -91,7 +90,7 @@ elevate_read(const CycleDataType *pointer) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCycler::write_stage (sanity-check)
+//     Function: PipelineCycler::write_stage (dummy or true)
 //       Access: Public
 //  Description: See PipelineCyclerBase::write_stage().
 ////////////////////////////////////////////////////////////////////
@@ -102,7 +101,7 @@ write_stage(int n) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCycler::cheat (sanity-check)
+//     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
@@ -142,7 +141,7 @@ PipelineCycler(Pipeline *pipeline) :
 template<class CycleDataType>
 INLINE PipelineCycler<CycleDataType>::
 PipelineCycler(const PipelineCycler<CycleDataType> &copy) :
-  PipelineCyclerBase(&_typed_data, copy),
+  PipelineCyclerBase(&_typed_data),
   _typed_data(copy._typed_data)
 {
 }
@@ -155,7 +154,6 @@ PipelineCycler(const PipelineCycler<CycleDataType> &copy) :
 template<class CycleDataType>
 INLINE void PipelineCycler<CycleDataType>::
 operator = (const PipelineCycler<CycleDataType> &copy) {
-  PipelineCyclerBase::operator = (copy);
   _typed_data = copy._typed_data;
 }
 

+ 0 - 527
panda/src/putil/pipelineCyclerBase.I

@@ -1,527 +0,0 @@
-// Filename: pipelineCyclerBase.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 compiled-in pipeline
-// sanity checks.
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::Constructor (sanity-check)
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE PipelineCyclerBase::
-PipelineCyclerBase(CycleData *initial_data, Pipeline *pipeline) :
-  _data(initial_data),
-  _pipeline(pipeline),
-  _read_count(0),
-  _write_count(0)
-{
-  if (_pipeline == (Pipeline *)NULL) {
-    _pipeline = Pipeline::get_render_pipeline();
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::Copy Constructor (sanity-check)
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE PipelineCyclerBase::
-PipelineCyclerBase(CycleData *, const PipelineCyclerBase &copy) :
-  _data(copy._data->make_copy()),
-  _pipeline(copy._pipeline),
-  _read_count(0),
-  _write_count(0)
-{
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::Copy Assignment (sanity-check)
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE void PipelineCyclerBase::
-operator = (const PipelineCyclerBase &copy) {
-  nassertv(_read_count == 0 && _write_count == 0);
-  _data = copy._data->make_copy();
-  _pipeline = copy._pipeline;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::Destructor (sanity-check)
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE PipelineCyclerBase::
-~PipelineCyclerBase() {
-  nassertv(_read_count == 0 && _write_count == 0);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::read (sanity-check)
-//       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 *PipelineCyclerBase::
-read() const {
-  // This function isn't truly const, but it doesn't change the data
-  // in any meaningful way, so we pretend it is.
-  ((PipelineCyclerBase *)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: PipelineCyclerBase::increment_read (sanity-check)
-//       Access: Public
-//  Description: Increments the count on a pointer previously
-//               retrieved by read(); now the pointer will need to be
-//               released twice.
-////////////////////////////////////////////////////////////////////
-INLINE void PipelineCyclerBase::
-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);
-  ((PipelineCyclerBase *)this)->_read_count++;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::release_read (sanity-check)
-//       Access: Public
-//  Description: Releases a pointer previously obtained via a call to
-//               read().
-////////////////////////////////////////////////////////////////////
-INLINE void PipelineCyclerBase::
-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);
-  ((PipelineCyclerBase *)this)->_read_count--;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::write (sanity-check)
-//       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 propagate 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 *PipelineCyclerBase::
-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: PipelineCyclerBase::elevate_read (sanity-check)
-//       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 *PipelineCyclerBase::
-elevate_read(const CycleData *pointer) {
-  release_read(pointer);
-  return write();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::release_write (sanity-check)
-//       Access: Public
-//  Description: Releases a pointer previously obtained via a call to
-//               write().
-////////////////////////////////////////////////////////////////////
-INLINE void PipelineCyclerBase::
-release_write(CycleData *pointer) {
-  nassertv(pointer == _data);
-  nassertv(_write_count > 0);
-  _write_count--;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::get_num_stages (sanity-check)
-//       Access: Public
-//  Description: Returns the number of stages in the pipeline.
-////////////////////////////////////////////////////////////////////
-INLINE int PipelineCyclerBase::
-get_num_stages() {
-  return 1;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::is_stage_unique (sanity-check)
-//       Access: Public
-//  Description: Returns true if the nth stage is a different pointer
-//               than the previous stage, or false if its pointer is
-//               shared with the previous one.
-////////////////////////////////////////////////////////////////////
-INLINE bool PipelineCyclerBase::
-is_stage_unique(int n) const {
-  nassertr(n == 0, false);
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::write_stage (sanity-check)
-//       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 *PipelineCyclerBase::
-write_stage(int n) {
-  nassertr(n == 0, (CycleData *)NULL);
-  _write_count++;
-  return _data;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::release_write_stage (sanity-check)
-//       Access: Public
-//  Description: Releases a pointer previously obtained via a call to
-//               write_stage().
-////////////////////////////////////////////////////////////////////
-INLINE void PipelineCyclerBase::
-release_write_stage(int n, CycleData *pointer) {
-  nassertv(n == 0 && pointer == _data);
-  nassertv(_write_count > 0);
-  _write_count--;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::cheat (sanity-check)
-//       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 *PipelineCyclerBase::
-cheat() const {
-  return _data;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::get_read_count (sanity-check)
-//       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 PipelineCyclerBase::
-get_read_count() const {
-  return _read_count;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::get_write_count (sanity-check)
-//       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 PipelineCyclerBase::
-get_write_count() const {
-  return _write_count;
-}
-
-#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: PipelineCyclerBase::Constructor (trivial)
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE PipelineCyclerBase::
-PipelineCyclerBase(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: PipelineCyclerBase::Copy Constructor (trivial)
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE PipelineCyclerBase::
-PipelineCyclerBase(CycleData *initial_data, const PipelineCyclerBase &) {
-  // The copy constructor for the DO_PIPELINING case is 
-#ifdef SIMPLE_STRUCT_POINTERS
-  nassertv(initial_data == (CycleData *)this);
-#else
-  _data = initial_data;
-#endif  // SIMPLE_STRUCT_POINTERS
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::Copy Assignment (trivial)
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE void PipelineCyclerBase::
-operator = (const PipelineCyclerBase &) {
-  // We don't reassign the _data pointer here; we assume it was set
-  // correctly previously.
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::Destructor (trivial)
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE PipelineCyclerBase::
-~PipelineCyclerBase() {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::read (trivial)
-//       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 *PipelineCyclerBase::
-read() const {
-#ifdef SIMPLE_STRUCT_POINTERS
-  return (const CycleData *)this;
-#else
-  return _data;
-#endif  // SIMPLE_STRUCT_POINTERS
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::increment_read (trivial)
-//       Access: Public
-//  Description: Increments the count on a pointer previously
-//               retrieved by read(); now the pointer will need to be
-//               released twice.
-////////////////////////////////////////////////////////////////////
-INLINE void PipelineCyclerBase::
-increment_read(const CycleData *) const {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::release_read (trivial)
-//       Access: Public
-//  Description: Releases a pointer previously obtained via a call to
-//               read().
-////////////////////////////////////////////////////////////////////
-INLINE void PipelineCyclerBase::
-release_read(const CycleData *) const {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::write (trivial)
-//       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 propagate 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 *PipelineCyclerBase::
-write() {
-#ifdef SIMPLE_STRUCT_POINTERS
-  return (CycleData *)this;
-#else
-  return _data;
-#endif  // SIMPLE_STRUCT_POINTERS
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::elevate_read (trivial)
-//       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 *PipelineCyclerBase::
-elevate_read(const CycleData *) {
-#ifdef SIMPLE_STRUCT_POINTERS
-  return (CycleData *)this;
-#else
-  return _data;
-#endif  // SIMPLE_STRUCT_POINTERS
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::release_write (trivial)
-//       Access: Public
-//  Description: Releases a pointer previously obtained via a call to
-//               write().
-////////////////////////////////////////////////////////////////////
-INLINE void PipelineCyclerBase::
-release_write(CycleData *) {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::get_num_stages (trivial)
-//       Access: Public
-//  Description: Returns the number of stages in the pipeline.
-////////////////////////////////////////////////////////////////////
-INLINE int PipelineCyclerBase::
-get_num_stages() {
-  return 1;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::is_stage_unique (trivial)
-//       Access: Public
-//  Description: Returns true if the nth stage is a different pointer
-//               than the previous stage, or false if its pointer is
-//               shared with the previous one.
-////////////////////////////////////////////////////////////////////
-INLINE bool PipelineCyclerBase::
-is_stage_unique(int n) const {
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::write_stage (trivial)
-//       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 *PipelineCyclerBase::
-write_stage(int) {
-  return (CycleData *)this;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::release_write_stage (trivial)
-//       Access: Public
-//  Description: Releases a pointer previously obtained via a call to
-//               write_stage().
-////////////////////////////////////////////////////////////////////
-INLINE void PipelineCyclerBase::
-release_write_stage(int, CycleData *) {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::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.
-////////////////////////////////////////////////////////////////////
-INLINE CycleData *PipelineCyclerBase::
-cheat() const {
-#ifdef SIMPLE_STRUCT_POINTERS
-  return (CycleData *)this;
-#else
-  return _data;
-#endif  // SIMPLE_STRUCT_POINTERS
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::get_read_count (trivial)
-//       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 PipelineCyclerBase::
-get_read_count() const {
-  return 0;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerBase::get_write_count (trivial)
-//       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 PipelineCyclerBase::
-get_write_count() const {
-  return 0;
-}
-
-
-#endif   // DO_PIPELINING

+ 17 - 53
panda/src/putil/pipelineCyclerBase.h

@@ -21,68 +21,32 @@
 
 #include "pandabase.h"
 
-#include "cycleData.h"
-#include "pipeline.h"
-#include "pointerTo.h"
+#ifdef DO_PIPELINING
 
-////////////////////////////////////////////////////////////////////
-//       Class : PipelineCyclerBase
-// Description : This is the non-template part of the implementation
-//               of PipelineCycler.  See PipelineCycler.
-//
-//               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.
-////////////////////////////////////////////////////////////////////
-struct EXPCL_PANDA PipelineCyclerBase {
-public:
-  INLINE PipelineCyclerBase(CycleData *initial_data, Pipeline *pipeline = NULL);
-  INLINE PipelineCyclerBase(CycleData *initial_data, const PipelineCyclerBase &copy);
-  INLINE void operator = (const PipelineCyclerBase &copy);
-  INLINE ~PipelineCyclerBase();
+#ifdef HAVE_THREADS
 
-  INLINE const CycleData *read() const;
-  INLINE void increment_read(const CycleData *pointer) const;
-  INLINE void release_read(const CycleData *pointer) const;
+// With DO_PIPELINING and threads available, we want the true cycler
+// implementation.
+#include "pipelineCyclerTrueImpl.h"
+typedef PipelineCyclerTrueImpl PipelineCyclerBase;
 
-  INLINE CycleData *write();
-  INLINE CycleData *elevate_read(const CycleData *pointer);
-  INLINE void release_write(CycleData *pointer);
+#else  // HAVE_THREADS
 
-  INLINE int get_num_stages();
-  INLINE bool is_stage_unique(int n) const;
-  INLINE CycleData *write_stage(int n);
-  INLINE void release_write_stage(int n, CycleData *pointer);
+// With DO_PIPELINING but no threads available, we want the dummy,
+// self-validating cycler implementation.
+#include "pipelineCyclerDummyImpl.h"
+typedef PipelineCyclerDummyImpl PipelineCyclerBase;
 
-  INLINE CycleData *cheat() const;
-  INLINE int get_read_count() const;
-  INLINE int get_write_count() const;
+#endif // HAVE_THREADS
 
-#ifdef DO_PIPELINING
-  // This private data is only stored here if we have pipelining
-  // compiled in.  Actually, this particular data is only used for
-  // sanity checking the pipelining code; it doesn't do anything
-  // useful.
-private:
-  PT(CycleData) _data;
-  Pipeline *_pipeline;
-  short _read_count, _write_count;
+#else  // DO_PIPELINING
 
-#else  // !DO_PIPELINING
-  // 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
+// Without DO_PIPELINING, we only want the trivial, do-nothing
+// implementation.
+#include "pipelineCyclerTrivialImpl.h"
+typedef PipelineCyclerTrivialImpl PipelineCyclerBase;
 
 #endif  // DO_PIPELINING
-};
-
-#include "pipelineCyclerBase.I"
 
 #endif
 

+ 275 - 0
panda/src/putil/pipelineCyclerDummyImpl.I

@@ -0,0 +1,275 @@
+// 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 (sanity-check)
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PipelineCyclerDummyImpl::
+PipelineCyclerDummyImpl(CycleData *initial_data, Pipeline *pipeline) :
+  _data(initial_data),
+  _pipeline(pipeline),
+  _read_count(0),
+  _write_count(0)
+{
+  if (_pipeline == (Pipeline *)NULL) {
+    _pipeline = Pipeline::get_render_pipeline();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::Copy Constructor (sanity-check)
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PipelineCyclerDummyImpl::
+PipelineCyclerDummyImpl(const PipelineCyclerDummyImpl &copy) :
+  _data(copy._data->make_copy()),
+  _pipeline(copy._pipeline),
+  _read_count(0),
+  _write_count(0)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::Copy Assignment (sanity-check)
+//       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 (sanity-check)
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PipelineCyclerDummyImpl::
+~PipelineCyclerDummyImpl() {
+  nassertv(_read_count == 0 && _write_count == 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::read (sanity-check)
+//       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 (sanity-check)
+//       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 (sanity-check)
+//       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 (sanity-check)
+//       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 (sanity-check)
+//       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::release_write (sanity-check)
+//       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 (sanity-check)
+//       Access: Public
+//  Description: Returns the number of stages in the pipeline.
+////////////////////////////////////////////////////////////////////
+INLINE int PipelineCyclerDummyImpl::
+get_num_stages() {
+  return 1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::is_stage_unique (sanity-check)
+//       Access: Public
+//  Description: Returns true if the nth stage is a different pointer
+//               than the previous stage, or false if its pointer is
+//               shared with the previous one.
+////////////////////////////////////////////////////////////////////
+INLINE bool PipelineCyclerDummyImpl::
+is_stage_unique(int n) const {
+  nassertr(n == 0, false);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::write_stage (sanity-check)
+//       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::release_write_stage (sanity-check)
+//       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::cheat (sanity-check)
+//       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 (sanity-check)
+//       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 (sanity-check)
+//       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;
+}

+ 3 - 16
panda/src/putil/pipelineCyclerBase.cxx → panda/src/putil/pipelineCyclerDummyImpl.cxx

@@ -1,5 +1,5 @@
-// Filename: pipelineCyclerBase.cxx
-// Created by:  drose (21Feb02)
+// Filename: pipelineCyclerDummyImpl.cxx
+// Created by:  drose (31Jan06)
 //
 ////////////////////////////////////////////////////////////////////
 //
@@ -16,17 +16,4 @@
 //
 ////////////////////////////////////////////////////////////////////
 
-#include "pipelineCyclerBase.h"
-
-
-#ifdef DO_PIPELINING
-// The following implementations are to support compiled-in pipeline
-// sanity checks.
-
-
-#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.
-
-#endif
+#include "pipelineCyclerDummyImpl.h"

+ 83 - 0
panda/src/putil/pipelineCyclerDummyImpl.h

@@ -0,0 +1,83 @@
+// 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_PANDA PipelineCyclerDummyImpl {
+public:
+  INLINE PipelineCyclerDummyImpl(CycleData *initial_data, Pipeline *pipeline = NULL);
+  INLINE PipelineCyclerDummyImpl(const PipelineCyclerDummyImpl &copy);
+  INLINE void operator = (const PipelineCyclerDummyImpl &copy);
+  INLINE ~PipelineCyclerDummyImpl();
+
+  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 void release_write(CycleData *pointer);
+
+  INLINE int get_num_stages();
+  INLINE bool is_stage_unique(int n) const;
+  INLINE CycleData *write_stage(int n);
+  INLINE void release_write_stage(int n, CycleData *pointer);
+
+  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;
+};
+
+#include "pipelineCyclerDummyImpl.I"
+
+#endif  // DO_PIPELINING && !HAVE_THREADS
+
+#endif
+

+ 261 - 0
panda/src/putil/pipelineCyclerTrivialImpl.I

@@ -0,0 +1,261 @@
+// 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 (trivial)
+//       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 (trivial)
+//       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 (trivial)
+//       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 (trivial)
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE PipelineCyclerTrivialImpl::
+~PipelineCyclerTrivialImpl() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::read (trivial)
+//       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 (trivial)
+//       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 (trivial)
+//       Access: Public
+//  Description: Releases a pointer previously obtained via a call to
+//               read().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrivialImpl::
+release_read(const CycleData *) const {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::write (trivial)
+//       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 (trivial)
+//       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::release_write (trivial)
+//       Access: Public
+//  Description: Releases a pointer previously obtained via a call to
+//               write().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrivialImpl::
+release_write(CycleData *) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::get_num_stages (trivial)
+//       Access: Public
+//  Description: Returns the number of stages in the pipeline.
+////////////////////////////////////////////////////////////////////
+INLINE int PipelineCyclerTrivialImpl::
+get_num_stages() {
+  return 1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::is_stage_unique (trivial)
+//       Access: Public
+//  Description: Returns true if the nth stage is a different pointer
+//               than the previous stage, or false if its pointer is
+//               shared with the previous one.
+////////////////////////////////////////////////////////////////////
+INLINE bool PipelineCyclerTrivialImpl::
+is_stage_unique(int n) const {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::write_stage (trivial)
+//       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) {
+  return (CycleData *)this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::release_write_stage (trivial)
+//       Access: Public
+//  Description: Releases a pointer previously obtained via a call to
+//               write_stage().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrivialImpl::
+release_write_stage(int, CycleData *) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::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.
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerTrivialImpl::
+cheat() const {
+#ifdef SIMPLE_STRUCT_POINTERS
+  return (CycleData *)this;
+#else
+  return _data;
+#endif  // SIMPLE_STRUCT_POINTERS
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::get_read_count (trivial)
+//       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 (trivial)
+//       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/putil/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"

+ 89 - 0
panda/src/putil/pipelineCyclerTrivialImpl.h

@@ -0,0 +1,89 @@
+// 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
+
+class CycleData;
+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_PANDA 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 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 void release_write(CycleData *pointer);
+
+  INLINE int get_num_stages();
+  INLINE bool is_stage_unique(int n) const;
+  INLINE CycleData *write_stage(int n);
+  INLINE void release_write_stage(int n, CycleData *pointer);
+
+  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
+

+ 196 - 0
panda/src/putil/pipelineCyclerTrueImpl.I

@@ -0,0 +1,196 @@
+// 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::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_thread()->get_pipeline_stage();
+  nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
+  return _data[pipeline_stage]._cycle_data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     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 {
+}
+
+////////////////////////////////////////////////////////////////////
+//     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 {
+}
+
+////////////////////////////////////////////////////////////////////
+//     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_thread()->get_pipeline_stage();
+  nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
+  return _data[pipeline_stage]._cycle_data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     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) {
+  return (CycleData *)pointer;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::release_write
+//       Access: Public
+//  Description: Releases a pointer previously obtained via a call to
+//               write().
+////////////////////////////////////////////////////////////////////
+INLINE void PipelineCyclerTrueImpl::
+release_write(CycleData *pointer) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     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::is_stage_unique
+//       Access: Public
+//  Description: Returns true if the nth stage is a different pointer
+//               than the previous stage, or false if its pointer is
+//               shared with the previous one.
+////////////////////////////////////////////////////////////////////
+INLINE bool PipelineCyclerTrueImpl::
+is_stage_unique(int n) const {
+  nassertr(n >= 0 && n < _num_stages, false);
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     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().
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerTrueImpl::
+write_stage(int n) {
+  nassertr(n >= 0 && n < _num_stages, (CycleData *)NULL);
+  return _data[n]._cycle_data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     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) {
+  nassertv(n >= 0 && n < _num_stages);
+}
+
+////////////////////////////////////////////////////////////////////
+//     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_thread()->get_pipeline_stage();
+  nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
+  return _data[pipeline_stage]._cycle_data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     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;
+}

+ 140 - 0
panda/src/putil/pipelineCyclerTrueImpl.cxx

@@ -0,0 +1,140 @@
+// 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"
+
+#if defined(DO_PIPELINING) && defined(HAVE_THREADS)
+
+#include "pipeline.h"
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+PipelineCyclerTrueImpl::
+PipelineCyclerTrueImpl(CycleData *initial_data, Pipeline *pipeline) :
+  _pipeline(pipeline)
+{
+  if (_pipeline == (Pipeline *)NULL) {
+    _pipeline = Pipeline::get_render_pipeline();
+  }
+  _pipeline->add_cycler(this);
+
+  _num_stages = _pipeline->get_num_stages();
+  _data = new StageData[_num_stages];
+  for (int i = 0; i < _num_stages; ++i) {
+    _data[i]._cycle_data = initial_data->make_copy();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::Copy Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+PipelineCyclerTrueImpl::
+PipelineCyclerTrueImpl(const PipelineCyclerTrueImpl &copy) :
+  _pipeline(copy._pipeline)
+{
+  _pipeline->add_cycler(this);
+
+  _num_stages = _pipeline->get_num_stages();
+  nassertv(_num_stages == copy._num_stages);
+  _data = new StageData[_num_stages];
+  for (int i = 0; i < _num_stages; ++i) {
+    _data[i]._cycle_data = copy._data[i]._cycle_data->make_copy();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::Copy Assignment
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void PipelineCyclerTrueImpl::
+operator = (const PipelineCyclerTrueImpl &copy) {
+  nassertv(_num_stages == copy._num_stages);
+  for (int i = 0; i < _num_stages; ++i) {
+    _data[i]._cycle_data = copy._data[i]._cycle_data->make_copy();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+PipelineCyclerTrueImpl::
+~PipelineCyclerTrueImpl() {
+  _pipeline->remove_cycler(this);
+
+  delete[] _data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::cycle
+//       Access: Private
+//  Description: Cycles the data between frames.  This is only called
+//               from Pipeline::cycle().
+////////////////////////////////////////////////////////////////////
+void PipelineCyclerTrueImpl::
+cycle() {
+  for (int i = _num_stages - 1; i > 0; --i) {
+    _data[i] = _data[i - 1];
+  }
+  _data[0]._cycle_data = _data[0]._cycle_data->make_copy();
+}
+
+////////////////////////////////////////////////////////////////////
+//     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) {
+  if (num_stages <= _num_stages) {
+    // Don't bother to reallocate the array.
+    for (int i = _num_stages; i < num_stages; ++i) {
+      _data[i]._cycle_data.clear();
+    }
+
+    _num_stages = num_stages;
+    
+
+  } else {
+    // Increase the array.
+    StageData *new_data = new StageData[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]._cycle_data = _data[_num_stages - 1]._cycle_data->make_copy();
+    }
+    delete[] _data;
+
+    _num_stages = num_stages;
+    _data = new_data;
+  }
+}
+
+#endif  // DO_PIPELINING && HAVE_THREADS
+

+ 94 - 0
panda/src/putil/pipelineCyclerTrueImpl.h

@@ -0,0 +1,94 @@
+// 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"
+
+#if defined(DO_PIPELINING) && defined(HAVE_THREADS)
+
+#include "cycleData.h"
+#include "pointerTo.h"
+#include "thread.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_PANDA PipelineCyclerTrueImpl {
+public:
+  PipelineCyclerTrueImpl(CycleData *initial_data, Pipeline *pipeline = NULL);
+  PipelineCyclerTrueImpl(const PipelineCyclerTrueImpl &copy);
+  void operator = (const PipelineCyclerTrueImpl &copy);
+  ~PipelineCyclerTrueImpl();
+
+  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 void release_write(CycleData *pointer);
+
+  INLINE int get_num_stages();
+  INLINE bool is_stage_unique(int n) const;
+  INLINE CycleData *write_stage(int n);
+  INLINE void release_write_stage(int n, CycleData *pointer);
+
+  INLINE CycleData *cheat() const;
+  INLINE int get_read_count() const;
+  INLINE int get_write_count() const;
+
+private:
+  void cycle();
+  void set_num_stages(int num_stages);
+
+private:
+  Pipeline *_pipeline;
+
+  class StageData {
+  public:
+    PT(CycleData) _cycle_data;
+  };
+
+  StageData *_data;
+  int _num_stages;
+
+  friend class Pipeline;
+};
+
+#include "pipelineCyclerTrueImpl.I"
+
+#endif  // DO_PIPELINING && HAVE_THREADS
+
+#endif
+

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

@@ -6,7 +6,9 @@
 #include "nodePointerTo.cxx"
 #include "pipeline.cxx"
 #include "pipelineCycler.cxx"
-#include "pipelineCyclerBase.cxx"
+#include "pipelineCyclerDummyImpl.cxx"
+#include "pipelineCyclerTrivialImpl.cxx"
+#include "pipelineCyclerTrueImpl.cxx"
 #include "pta_double.cxx"
 #include "pta_float.cxx"
 #include "pta_int.cxx"