Browse Source

better windows tau support; better threaded DeletedChain support; beginning PipelineReader classes

David Rose 19 years ago
parent
commit
57338ee24d
94 changed files with 3634 additions and 1542 deletions
  1. 1 0
      direct/src/distributed/cConnectionRepository.h
  2. 2 0
      direct/src/distributed/cDistributedSmoothNodeBase.cxx
  3. 1 1
      dtool/pptempl/Depends.pp
  4. 1 1
      dtool/pptempl/Global.gmsvc.pp
  5. 1 1
      dtool/pptempl/Global.nmake.pp
  6. 7 0
      dtool/pptempl/Template.gmsvc.pp
  7. 1 1
      dtool/src/dtoolbase/atomicAdjust.h
  8. 42 19
      dtool/src/dtoolbase/deletedChain.T
  9. 19 3
      dtool/src/dtoolbase/deletedChain.h
  10. 6 0
      dtool/src/dtoolbase/dtoolbase.cxx
  11. 16 5
      dtool/src/dtoolbase/dtoolbase_cc.h
  12. 2 0
      dtool/src/dtoolbase/numeric_types.h
  13. 8 3
      dtool/src/prc/notify.cxx
  14. 15 13
      panda/src/display/graphicsStateGuardian.cxx
  15. 12 9
      panda/src/display/graphicsStateGuardian.h
  16. 2 1
      panda/src/downloader/extractor.h
  17. 1 0
      panda/src/downloadertools/pzip.cxx
  18. 116 114
      panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx
  19. 8 8
      panda/src/dxgsg8/dxGraphicsStateGuardian8.h
  20. 127 125
      panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx
  21. 8 8
      panda/src/dxgsg9/dxGraphicsStateGuardian9.h
  22. 2 2
      panda/src/dxgsg9/dxShaderContext9.cxx
  23. 16 0
      panda/src/express/referenceCount.I
  24. 14 0
      panda/src/express/referenceCount.cxx
  25. 2 0
      panda/src/express/referenceCount.h
  26. 155 148
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  27. 14 13
      panda/src/glstuff/glGraphicsStateGuardian_src.h
  28. 12 12
      panda/src/glstuff/glImmediateModeSender_src.cxx
  29. 8 7
      panda/src/glstuff/glImmediateModeSender_src.h
  30. 10 16
      panda/src/glstuff/glShaderContext_src.cxx
  31. 168 0
      panda/src/gobj/geom.I
  32. 64 61
      panda/src/gobj/geom.cxx
  33. 43 5
      panda/src/gobj/geom.h
  34. 2 2
      panda/src/gobj/geomCacheManager.cxx
  35. 2 2
      panda/src/gobj/geomLines.cxx
  36. 2 1
      panda/src/gobj/geomLines.h
  37. 2 2
      panda/src/gobj/geomLinestrips.cxx
  38. 2 1
      panda/src/gobj/geomLinestrips.h
  39. 2 2
      panda/src/gobj/geomPoints.cxx
  40. 2 1
      panda/src/gobj/geomPoints.h
  41. 281 42
      panda/src/gobj/geomPrimitive.I
  42. 118 84
      panda/src/gobj/geomPrimitive.cxx
  43. 67 9
      panda/src/gobj/geomPrimitive.h
  44. 2 2
      panda/src/gobj/geomTriangles.cxx
  45. 2 1
      panda/src/gobj/geomTriangles.h
  46. 2 2
      panda/src/gobj/geomTrifans.cxx
  47. 2 1
      panda/src/gobj/geomTrifans.h
  48. 2 2
      panda/src/gobj/geomTristrips.cxx
  49. 2 1
      panda/src/gobj/geomTristrips.h
  50. 278 1
      panda/src/gobj/geomVertexArrayData.I
  51. 72 105
      panda/src/gobj/geomVertexArrayData.cxx
  52. 79 3
      panda/src/gobj/geomVertexArrayData.h
  53. 412 65
      panda/src/gobj/geomVertexData.I
  54. 437 351
      panda/src/gobj/geomVertexData.cxx
  55. 134 30
      panda/src/gobj/geomVertexData.h
  56. 86 39
      panda/src/gobj/geomVertexReader.I
  57. 33 7
      panda/src/gobj/geomVertexReader.cxx
  58. 10 2
      panda/src/gobj/geomVertexReader.h
  59. 83 39
      panda/src/gobj/geomVertexWriter.I
  60. 33 7
      panda/src/gobj/geomVertexWriter.cxx
  61. 10 2
      panda/src/gobj/geomVertexWriter.h
  62. 5 3
      panda/src/gobj/shaderContext.cxx
  63. 11 30
      panda/src/gsgbase/graphicsStateGuardianBase.h
  64. 4 0
      panda/src/mathutil/mersenne.h
  65. 2 1
      panda/src/mathutil/perlinNoise.h
  66. 2 1
      panda/src/parametrics/nurbsCurveDrawer.h
  67. 1 1
      panda/src/particlesystem/spriteParticleRenderer.cxx
  68. 3 2
      panda/src/particlesystem/spriteParticleRenderer.h
  69. 3 1
      panda/src/pgraph/Sources.pp
  70. 2 1
      panda/src/pgraph/geomTransformer.cxx
  71. 0 3
      panda/src/pgraph/shaderPool.cxx
  72. 22 0
      panda/src/pipeline/Sources.pp
  73. 4 4
      panda/src/pipeline/config_pipeline.cxx
  74. 62 0
      panda/src/pipeline/cycleDataStageWriter.I
  75. 5 0
      panda/src/pipeline/cycleDataStageWriter.h
  76. 1 3
      panda/src/pipeline/mutexDebug.cxx
  77. 66 0
      panda/src/pipeline/pipelineCycler.I
  78. 3 0
      panda/src/pipeline/pipelineCycler.h
  79. 35 0
      panda/src/pipeline/pipelineCyclerDummyImpl.I
  80. 3 0
      panda/src/pipeline/pipelineCyclerDummyImpl.h
  81. 37 0
      panda/src/pipeline/pipelineCyclerTrivialImpl.I
  82. 3 0
      panda/src/pipeline/pipelineCyclerTrivialImpl.h
  83. 56 0
      panda/src/pipeline/pipelineCyclerTrueImpl.I
  84. 42 66
      panda/src/pipeline/pipelineCyclerTrueImpl.cxx
  85. 3 1
      panda/src/pipeline/pipelineCyclerTrueImpl.h
  86. 1 0
      panda/src/pipeline/select.tau
  87. 142 0
      panda/src/pipeline/test_delete.cxx
  88. 2 2
      panda/src/pipeline/test_diners.cxx
  89. 7 1
      panda/src/pipeline/threadPosixImpl.cxx
  90. 37 32
      panda/src/pipeline/threadWin32Impl.cxx
  91. 3 3
      panda/src/pstatclient/pStatClient.cxx
  92. 1 2
      panda/src/putil/datagramInputFile.cxx
  93. 1 1
      panda/src/windisplay/Sources.pp
  94. 2 2
      pandatool/src/bam/bamToEgg.cxx

+ 1 - 0
direct/src/distributed/cConnectionRepository.h

@@ -26,6 +26,7 @@
 #include "dcFile.h"
 #include "dcField.h"  // to pick up Python.h
 #include "pStatCollector.h"
+#include "datagramIterator.h"
 
 #ifdef HAVE_NSPR
 #include "queuedConnectionManager.h"

+ 2 - 0
direct/src/distributed/cDistributedSmoothNodeBase.cxx

@@ -279,6 +279,8 @@ finish_send_update(DCPacker &packer) {
   nassertv(clock_delta != NULL);
   double delta = PyFloat_AsDouble(clock_delta);
   Py_DECREF(clock_delta);
+#else
+  static const double delta = 0.0f;
 #endif  // HAVE_PYTHON
 
   double local_time = ClockObject::get_global_clock()->get_frame_time();

+ 1 - 1
dtool/pptempl/Depends.pp

@@ -87,7 +87,7 @@
       #push 1 $[file]_obj
     #end file
 
-    #if $[USE_SINGLE_COMPOSITE_SOURCEFILE]
+    #if $[and $[not $[DONT_COMBINE]],$[or $[USE_SINGLE_COMPOSITE_SOURCEFILE],$[and $[USE_TAU],$[WINDOWS_PLATFORM]]]]
       #if $[> $[words $[cxx_sources]], 1]
         // If we have multiple C++ files, put them together into one
         // composite file.

+ 1 - 1
dtool/pptempl/Global.gmsvc.pp

@@ -121,7 +121,7 @@
 // '#defer extra_cflags $[extra_cflags] /STUFF' will never work because extra_cflags hasnt been
 // defined yet, so this just evaluates the reference to null and removes the reference and the
 // the defining extra_cflags in individual sources.pp's will not picked up.  use END_FLAGS instead
-#defer extra_cflags /EHsc /Zm500 /DWIN32_VC /DWIN32 $[WARNING_LEVEL_FLAG] $[END_CFLAGS]
+#defer extra_cflags /EHsc /Zm500 /DWIN32_VC /DWIN32=1 $[WARNING_LEVEL_FLAG] $[END_CFLAGS]
 
 #if $[direct_tau]
 #define tau_ipath $[TAU_ROOT]/include

+ 1 - 1
dtool/pptempl/Global.nmake.pp

@@ -125,7 +125,7 @@
 // '#defer extra_cflags $[extra_cflags] /STUFF' will never work because extra_cflags hasnt been
 // defined yet, so this just evaluates the reference to null and removes the reference and the
 // the defining extra_cflags in individual sources.pp's will not picked up.  use END_FLAGS instead
-#defer extra_cflags /EHsc /Zm300 /DWIN32_VC /DWIN32 $[WARNING_LEVEL_FLAG] $[END_CFLAGS]
+#defer extra_cflags /EHsc /Zm300 /DWIN32_VC /DWIN32=1 $[WARNING_LEVEL_FLAG] $[END_CFLAGS]
 
 #defer DECYGWINED_INC_PATHLIST_ARGS $[decygwin %,/I"%",$[EXTRA_INCPATH] $[ipath] $[WIN32_PLATFORMSDK_INCPATH]]
 #defer MAIN_C_COMPILE_ARGS /nologo /c $[DECYGWINED_INC_PATHLIST_ARGS] $[flags] $[extra_cflags] "$[osfilename $[source]]"

+ 7 - 0
dtool/pptempl/Template.gmsvc.pp

@@ -183,7 +183,14 @@
 /* ################################# DO NOT EDIT ########################### */
 
 #foreach file $[$[composite_file]_sources]
+#if $[USE_TAU]
+// For the benefit of Tau, we copy the source file verbatim into the
+// composite file.  (Tau doesn't instrument files picked up via #include.)
+#copy $[DIRPREFIX]$[file]
+
+#else
 ##include "$[file]"
+#endif  // USE_TAU
 #end file
 
 #end $[composite_file]

+ 1 - 1
dtool/src/dtoolbase/atomicAdjust.h

@@ -27,7 +27,7 @@
 #include "atomicAdjustDummyImpl.h"
 typedef AtomicAdjustDummyImpl AtomicAdjust;
 
-#elif defined(__i386__) || defined(_M_IX86)
+#elif  defined(__i386__) || defined(_M_IX86)
 // For an i386 architecture, we'll always use the i386 implementation.
 // It should be safe for any OS, and it might be a bit faster than
 // any OS-provided calls.

+ 42 - 19
dtool/src/dtoolbase/deletedChain.T

@@ -17,9 +17,9 @@
 ////////////////////////////////////////////////////////////////////
 
 template<class Type>
-TYPENAME DeletedChain<Type>::ObjectNode *DeletedChain<Type>::_deleted_chain;
+TVOLATILE TYPENAME DeletedChain<Type>::ObjectNode * TVOLATILE DeletedChain<Type>::_deleted_chain;
 
-#ifndef HAVE_ATOMIC_COMPARE_AND_EXCHANGE_PTR
+#ifndef DELETED_CHAIN_USE_ATOMIC_EXCHANGE
 template<class Type>
 MutexImpl DeletedChain<Type>::_lock;
 #endif
@@ -35,38 +35,54 @@ allocate(size_t size) {
   TAU_PROFILE("Type *DeletedChain<Type>::allocate(size_t)", " ", TAU_USER);
   assert(size <= sizeof(Type));
 
-#ifndef HAVE_ATOMIC_COMPARE_AND_EXCHANGE_PTR
+  TVOLATILE ObjectNode *obj;
+
+#ifndef DELETED_CHAIN_USE_ATOMIC_EXCHANGE
   _lock.lock();
   if (_deleted_chain != (ObjectNode *)NULL) {
-    ObjectNode *obj = _deleted_chain;
+    obj = _deleted_chain;
     _deleted_chain = _deleted_chain->_next;
     _lock.release();
+#ifndef NDEBUG
+    assert(obj->_flag == ((PN_int32)obj ^ deleted_chain_flag_hash));
+    obj->_flag = 0;
+#endif  // NDEBUG
     return (Type *)obj;
   }
   _lock.release();
 
-#else  // HAVE_ATOMIC_COMPARE_AND_EXCHANGE_PTR
-  ObjectNode *obj = _deleted_chain;
+#else  // DELETED_CHAIN_USE_ATOMIC_EXCHANGE
+  obj = _deleted_chain;
   while (obj != (ObjectNode *)NULL) {
-    ObjectNode *result = (ObjectNode *)AtomicAdjust::compare_and_exchange_ptr((void *&)_deleted_chain, (void *)obj, (void *)obj->_next);
+    TVOLATILE ObjectNode *next = obj->_next;
+    TVOLATILE ObjectNode *result = (ObjectNode *)AtomicAdjust::compare_and_exchange_ptr((void * TVOLATILE &)_deleted_chain, (void *)obj, (void *)next);
     if (result == obj) {
       // We got it.
+#ifndef NDEBUG
+      assert(obj->_flag == ((PN_int32)obj ^ deleted_chain_flag_hash));
+      obj->_flag = 0;
+#endif  // NDEBUG
       return (Type *)obj;
     }
     // Someone else grabbed the top link first.  Try again.
     obj = _deleted_chain;
   }
 
-#endif  // HAVE_ATOMIC_COMPARE_AND_EXCHANGE_PTR
+#endif  // DELETED_CHAIN_USE_ATOMIC_EXCHANGE
 
   // If we get here, the deleted_chain is empty; we have to allocate a
   // new object from the system pool.
 
 #ifdef DO_MEMORY_USAGE
-  return (Type *)(*global_operator_new)(sizeof(Type));
+  obj = (ObjectNode *)(*global_operator_new)(max(sizeof(Type), sizeof(ObjectNode)));
 #else
-  return (Type *)malloc(sizeof(Type));
+  obj = (ObjectNode *)malloc(max(sizeof(Type), sizeof(ObjectNode)));
 #endif
+#ifndef NDEBUG
+  obj->_flag = 0;
+#endif  // NDEBUG
+
+  return (Type *)obj;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -78,25 +94,32 @@ template<class Type>
 INLINE void DeletedChain<Type>::
 deallocate(Type *ptr) {
   TAU_PROFILE("void DeletedChain<Type>::deallocate(Type *)", " ", TAU_USER);
-#ifndef HAVE_ATOMIC_COMPARE_AND_EXCHANGE_PTR
+  TVOLATILE ObjectNode *obj = (ObjectNode *)ptr;
+
+#ifndef NDEBUG
+  assert(obj->_flag != ((PN_int32)obj ^ deleted_chain_flag_hash));
+  obj->_flag = (PN_int32)obj ^ deleted_chain_flag_hash;
+#endif  // NDEBUG
+
+#ifndef DELETED_CHAIN_USE_ATOMIC_EXCHANGE
   _lock.lock();
 
-  ObjectNode *obj = (ObjectNode *)ptr;
   obj->_next = _deleted_chain;
   _deleted_chain = obj;
 
   _lock.release();
 
-#else  // HAVE_ATOMIC_COMPARE_AND_EXCHANGE_PTR
+#else  // DELETED_CHAIN_USE_ATOMIC_EXCHANGE
 
-  ObjectNode *obj = (ObjectNode *)ptr;
-  ObjectNode *result;
+  TVOLATILE ObjectNode *result;
+  TVOLATILE ObjectNode *next;
 
   do {
-    obj->_next = _deleted_chain;
-    result = (ObjectNode *)AtomicAdjust::compare_and_exchange_ptr((void *&)_deleted_chain, (void *)obj->_next, (void *)obj);
+    next = _deleted_chain;
+    obj->_next = next;
+    result = (ObjectNode *)AtomicAdjust::compare_and_exchange_ptr((void * TVOLATILE &)_deleted_chain, (void *)next, (void *)obj);
     // Keep trying until no one else got to _deleted_chain first.
-  } while (result != obj->_next);
+  } while (result != next);
 
-#endif  // HAVE_ATOMIC_COMPARE_AND_EXCHANGE_PTR
+#endif  // DELETED_CHAIN_USE_ATOMIC_EXCHANGE
 }

+ 19 - 3
dtool/src/dtoolbase/deletedChain.h

@@ -23,8 +23,19 @@
 
 #include "mutexImpl.h"
 #include "atomicAdjust.h"
+#include "numeric_types.h"
 #include <assert.h>
 
+#ifdef HAVE_ATOMIC_COMPARE_AND_EXCHANGE_PTR
+// Actually, there appears to be a (maybe fatal) flaw in our
+//implementation of DeletedChain via the atomic exchange operation.
+//Specifically, a pointer may be removed from the head of the chain,
+//then the same pointer reinserted in the chain, while another thread
+//is waiting; and that thread will not detect the change.  For now,
+//then, let's not use this implmentation, and fall back to the mutex.
+//#define DELETED_CHAIN_USE_ATOMIC_EXCHANGE
+#endif
+
 ////////////////////////////////////////////////////////////////////
 //       Class : DeletedChain
 // Description : This template class can be used to provide faster
@@ -60,7 +71,10 @@ public:
 private:
   class ObjectNode {
   public:
-    ObjectNode *_next;
+    TVOLATILE ObjectNode * TVOLATILE _next;
+#ifndef NDEBUG
+    TVOLATILE PN_int32 _flag;
+#endif
   };
 
   // Ideally, the compiler and linker will unify all references to
@@ -68,15 +82,17 @@ private:
   // However, if the compiler fails to do this (*cough* Microsoft), it
   // won't be a big deal; it just means there will be multiple
   // unrelated chains of deleted objects for a particular type.
-  static ObjectNode *_deleted_chain;
+  static TVOLATILE ObjectNode * TVOLATILE _deleted_chain;
 
-#ifndef HAVE_ATOMIC_COMPARE_AND_EXCHANGE_PTR
+#ifndef DELETED_CHAIN_USE_ATOMIC_EXCHANGE
   // If we don't have atomic compare-and-exchange, we need to use a
   // Mutex to protect the above linked list.
   static MutexImpl _lock;
 #endif
 };
 
+static const PN_int32 deleted_chain_flag_hash = 0x12345678;
+
 // Place this macro within a class definition to define appropriate
 // operator new and delete methods that take advantage of
 // DeletedChain.

+ 6 - 0
dtool/src/dtoolbase/dtoolbase.cxx

@@ -18,6 +18,12 @@
 
 #include "dtoolbase.h"
 
+#if defined(USE_TAU) && defined(WIN32)
+// Hack around tau's lack of DLL export declarations for Profiler class.
+bool __tau_shutdown = false;
+#endif
+
+
 /////////////////////////////////////////////////////////////////////
 //
 // Memory manager: DLMALLOC

+ 16 - 5
dtool/src/dtoolbase/dtoolbase_cc.h

@@ -185,15 +185,18 @@ INLINE void operator delete[](void *ptr) {
 
 #if defined(USE_TAU) && defined(WIN32)
 // Hack around tau's lack of DLL export declarations for Profiler class.
+extern EXPCL_DTOOL bool __tau_shutdown;
 class EXPCL_DTOOL TauProfile {
 public:
-  TauProfile(char *name, char *type, int group, char *group_name) {
-    _tautimer = NULL;
-    Tau_profile_c_timer(&_tautimer, name, type, group, group_name);
+  TauProfile(void *&tautimer, char *name, char *type, int group, char *group_name) {
+    Tau_profile_c_timer(&tautimer, name, type, group, group_name);
+    _tautimer = tautimer;
     TAU_PROFILE_START(_tautimer); 
   }
   ~TauProfile() {
-    TAU_PROFILE_STOP(_tautimer);
+    if (!__tau_shutdown) {
+      TAU_PROFILE_STOP(_tautimer);
+    }
   }
 
 private:
@@ -201,7 +204,15 @@ private:
 };
 
 #undef TAU_PROFILE
-#define TAU_PROFILE(name, type, group) TauProfile _taupr(name, type, group, #group)
+#define TAU_PROFILE(name, type, group) \
+  static void *__tautimer; \
+  TauProfile __taupr(__tautimer, name, type, group, #group)
+
+#undef TAU_PROFILE_EXIT
+#define TAU_PROFILE_EXIT(msg) \
+  __tau_shutdown = true; \
+  Tau_exit(msg);
+
 #endif  // USE_TAU
 
 #endif  // GLOBAL_OPERATOR_NEW_EXCEPTIONS

+ 2 - 0
dtool/src/dtoolbase/numeric_types.h

@@ -19,6 +19,8 @@
 #ifndef NUMERIC_TYPES_H
 #define NUMERIC_TYPES_H
 
+#include "dtoolbase.h"
+
 // This header file defines a number of typedefs that correspond to
 // the various numeric types for unsigned and signed numbers of
 // various widths.

+ 8 - 3
dtool/src/prc/notify.cxx

@@ -452,12 +452,17 @@ assert_failure(const char *expression, int line,
     // doesn't seem to work properly either, since we don't seem to
     // get a reliable stack trace.
 
-    // Guess we'll still have to force a segfault.
+    // The old reliable int 3 works (at least on an Intel platform) if
+    // you are already running within a debugger.  But it doesn't
+    // offer to bring up a debugger otherwise.
+
+    // So we'll force a segfault, which works every time.
     int *ptr = (int *)NULL;
     *ptr = 1;
-#else
+
+#else  // WIN32
     abort();
-#endif
+#endif  // WIN32
   }
 
   return true;

+ 15 - 13
panda/src/display/graphicsStateGuardian.cxx

@@ -88,7 +88,8 @@ GraphicsStateGuardian(const FrameBufferProperties &properties,
   
   set_coordinate_system(get_default_coordinate_system());
 
-  _current_display_region = (DisplayRegion*)0L;
+  _data_reader = (GeomVertexDataPipelineReader *)NULL;
+  _current_display_region = (DisplayRegion*)NULL;
   _current_stereo_channel = Lens::SC_mono;
   _current_lens = (Lens *)NULL;
   _projection_mat = TransformState::make_identity();
@@ -1253,12 +1254,13 @@ finish_decal() {
 //               are ok, false to abort this group of primitives.
 ////////////////////////////////////////////////////////////////////
 bool GraphicsStateGuardian::
-begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
-                      const GeomVertexData *data) {
+begin_draw_primitives(const GeomPipelineReader *geom_reader, 
+                      const GeomMunger *munger,
+                      const GeomVertexDataPipelineReader *data_reader) {
   _munger = munger;
-  _vertex_data = data;
-  nassertr(geom->check_valid(data), false);
-  return _vertex_data->has_vertex();
+  _data_reader = data_reader;
+  nassertr(geom_reader->check_valid(data_reader), false);
+  return _data_reader->has_vertex();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1267,7 +1269,7 @@ begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
 //  Description: Draws a series of disconnected triangles.
 ////////////////////////////////////////////////////////////////////
 void GraphicsStateGuardian::
-draw_triangles(const GeomTriangles *) {
+draw_triangles(const GeomPrimitivePipelineReader *) {
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1276,7 +1278,7 @@ draw_triangles(const GeomTriangles *) {
 //  Description: Draws a series of triangle strips.
 ////////////////////////////////////////////////////////////////////
 void GraphicsStateGuardian::
-draw_tristrips(const GeomTristrips *primitive) {
+draw_tristrips(const GeomPrimitivePipelineReader *) {
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1285,7 +1287,7 @@ draw_tristrips(const GeomTristrips *primitive) {
 //  Description: Draws a series of triangle fans.
 ////////////////////////////////////////////////////////////////////
 void GraphicsStateGuardian::
-draw_trifans(const GeomTrifans *primitive) {
+draw_trifans(const GeomPrimitivePipelineReader *) {
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1294,7 +1296,7 @@ draw_trifans(const GeomTrifans *primitive) {
 //  Description: Draws a series of disconnected line segments.
 ////////////////////////////////////////////////////////////////////
 void GraphicsStateGuardian::
-draw_lines(const GeomLines *) {
+draw_lines(const GeomPrimitivePipelineReader *) {
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1303,7 +1305,7 @@ draw_lines(const GeomLines *) {
 //  Description: Draws a series of line strips.
 ////////////////////////////////////////////////////////////////////
 void GraphicsStateGuardian::
-draw_linestrips(const GeomLinestrips *primitive) {
+draw_linestrips(const GeomPrimitivePipelineReader *) {
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1312,7 +1314,7 @@ draw_linestrips(const GeomLinestrips *primitive) {
 //  Description: Draws a series of disconnected points.
 ////////////////////////////////////////////////////////////////////
 void GraphicsStateGuardian::
-draw_points(const GeomPoints *) {
+draw_points(const GeomPrimitivePipelineReader *) {
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1325,7 +1327,7 @@ draw_points(const GeomPoints *) {
 void GraphicsStateGuardian::
 end_draw_primitives() {
   _munger = NULL;
-  _vertex_data = NULL;
+  _data_reader = NULL;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 12 - 9
panda/src/display/graphicsStateGuardian.h

@@ -193,15 +193,15 @@ public:
   virtual CPT(RenderState) begin_decal_base_second();
   virtual void finish_decal();
 
-  virtual bool begin_draw_primitives(const Geom *geom, 
+  virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader, 
                                      const GeomMunger *munger,
-                                     const GeomVertexData *vertex_data);
-  virtual void draw_triangles(const GeomTriangles *primitive);
-  virtual void draw_tristrips(const GeomTristrips *primitive);
-  virtual void draw_trifans(const GeomTrifans *primitive);
-  virtual void draw_lines(const GeomLines *primitive);
-  virtual void draw_linestrips(const GeomLinestrips *primitive);
-  virtual void draw_points(const GeomPoints *primitive);
+                                     const GeomVertexDataPipelineReader *data_reader);
+  virtual void draw_triangles(const GeomPrimitivePipelineReader *reader);
+  virtual void draw_tristrips(const GeomPrimitivePipelineReader *reader);
+  virtual void draw_trifans(const GeomPrimitivePipelineReader *reader);
+  virtual void draw_lines(const GeomPrimitivePipelineReader *reader);
+  virtual void draw_linestrips(const GeomPrimitivePipelineReader *reader);
+  virtual void draw_points(const GeomPrimitivePipelineReader *reader);
   virtual void end_draw_primitives();
 
   INLINE bool reset_if_new();
@@ -270,8 +270,11 @@ protected:
   CPT(RenderState) _state_rs;
   CPT(RenderState) _target_rs;
   CPT(TransformState) _internal_transform;
+
+  // These are set by begin_draw_primitives(), and are only valid
+  // between begin_draw_primitives() and end_draw_primitives().
   CPT(GeomMunger) _munger;
-  CPT(GeomVertexData) _vertex_data;
+  const GeomVertexDataPipelineReader *_data_reader;
 
   unsigned int _color_write_mask;
   Colorf _color_clear_value;

+ 2 - 1
panda/src/downloader/extractor.h

@@ -23,6 +23,7 @@
 #include "buffer.h"
 #include "multifile.h"
 #include "pointerTo.h"
+#include "vector_int.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : Extractor
@@ -63,7 +64,7 @@ private:
 
   Filename _extract_dir;
 
-  typedef pvector<int> Requests;
+  typedef vector_int Requests;
   Requests _requests;
   size_t _requests_total_length;
   

+ 1 - 0
panda/src/downloadertools/pzip.cxx

@@ -19,6 +19,7 @@
 #include "filename.h"
 #include "zStream.h"
 #include "pnotify.h"
+#include "config_util.h"
 
 #ifndef HAVE_GETOPT
   #include "gnu_getopt.h"

+ 116 - 114
panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx

@@ -788,24 +788,26 @@ end_frame() {
 //               are ok, false to abort this group of primitives.
 ////////////////////////////////////////////////////////////////////
 bool DXGraphicsStateGuardian8::
-begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
-                      const GeomVertexData *vertex_data) {
-  if (!GraphicsStateGuardian::begin_draw_primitives(geom, munger, vertex_data)) {
+begin_draw_primitives(const GeomPipelineReader *geom_reader, 
+                      const GeomMunger *munger,
+                      const GeomVertexDataPipelineReader *data_reader) {
+  if (!GraphicsStateGuardian::begin_draw_primitives(geom_reader, munger, 
+                                                    data_reader)) {
     return false;
   }
-  nassertr(_vertex_data != (GeomVertexData *)NULL, false);
+  nassertr(_data_reader != (GeomVertexDataPipelineReader *)NULL, false);
 
-  const GeomVertexFormat *format = _vertex_data->get_format();
+  const GeomVertexFormat *format = _data_reader->get_format();
 
   // The munger should have put the FVF data in the first array.
-  const GeomVertexArrayData *data = _vertex_data->get_array(0);
+  const GeomVertexArrayDataPipelineReader *data = _data_reader->get_array_reader(0);
 
-  VertexBufferContext *vbc = ((GeomVertexArrayData *)data)->prepare_now(get_prepared_objects(), this);
+  VertexBufferContext *vbc = ((GeomVertexArrayData *)(data->get_object()))->prepare_now(get_prepared_objects(), this);
   nassertr(vbc != (VertexBufferContext *)NULL, false);
   apply_vertex_buffer(vbc);
 
   const GeomVertexAnimationSpec &animation =
-    vertex_data->get_format()->get_animation();
+    data_reader->get_format()->get_animation();
   if (animation.get_animation_type() == Geom::AT_hardware) {
     // Set up vertex blending.
     switch (animation.get_num_transforms()) {
@@ -836,7 +838,7 @@ begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
       _d3d_device->SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE);
     }
 
-    const TransformTable *table = vertex_data->get_transform_table();
+    const TransformTable *table = data_reader->get_transform_table();
     if (table != (TransformTable *)NULL) {
       for (int i = 0; i < table->get_num_transforms(); i++) {
         LMatrix4f mat;
@@ -859,14 +861,14 @@ begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
       _vertex_blending_enabled = false;
     }
 
-    if (_transform_stale && !_vertex_data->is_vertex_transformed()) {
+    if (_transform_stale && !_data_reader->is_vertex_transformed()) {
       const D3DMATRIX *d3d_mat = (const D3DMATRIX *)_internal_transform->get_mat().get_data();
       _d3d_device->SetTransform(D3DTS_WORLD, d3d_mat);
       _transform_stale = false;
     }
   }
 
-  if (_vertex_data->is_vertex_transformed()) {
+  if (_data_reader->is_vertex_transformed()) {
     // If the vertex data claims to be already transformed into clip
     // coordinates, wipe out the current projection and modelview
     // matrix (so we don't attempt to transform it again).
@@ -895,53 +897,53 @@ begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
 //  Description: Draws a series of disconnected triangles.
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian8::
-draw_triangles(const GeomTriangles *primitive) {
+draw_triangles(const GeomPrimitivePipelineReader *reader) {
   PStatTimer timer(_draw_primitive_pcollector);
-  _vertices_tri_pcollector.add_level(primitive->get_num_vertices());
+  _vertices_tri_pcollector.add_level(reader->get_num_vertices());
   _primitive_batches_tri_pcollector.add_level(1);
-  if (primitive->is_indexed()) {
-    int min_vertex = dx_broken_max_index ? 0 : primitive->get_min_vertex();
-    int max_vertex = primitive->get_max_vertex();
+  if (reader->is_indexed()) {
+    int min_vertex = dx_broken_max_index ? 0 : reader->get_min_vertex();
+    int max_vertex = reader->get_max_vertex();
 
     if (_active_vbuffer != NULL) {
       // Indexed, vbuffers.
-      IndexBufferContext *ibc = ((GeomPrimitive *)primitive)->prepare_now(get_prepared_objects(), this);
+      IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
       nassertv(ibc != (IndexBufferContext *)NULL);
       apply_index_buffer(ibc);
 
       _d3d_device->DrawIndexedPrimitive
         (D3DPT_TRIANGLELIST,
          min_vertex, max_vertex - min_vertex + 1,
-         0, primitive->get_num_primitives());
+         0, reader->get_num_primitives());
 
     } else {
       // Indexed, client arrays.
-      D3DFORMAT index_type = get_index_type(primitive->get_index_type());
+      D3DFORMAT index_type = get_index_type(reader->get_index_type());
       draw_indexed_primitive_up
         (D3DPT_TRIANGLELIST,
          min_vertex, max_vertex,
-         primitive->get_num_primitives(),
-         primitive->get_data(),
+         reader->get_num_primitives(),
+         reader->get_data(),
          index_type,
-         _vertex_data->get_array(0)->get_data(),
-         _vertex_data->get_format()->get_array(0)->get_stride());
+         _data_reader->get_array_reader(0)->get_data(),
+         _data_reader->get_format()->get_array(0)->get_stride());
     }
   } else {
     if (_active_vbuffer != NULL) {
       // Nonindexed, vbuffers.
       _d3d_device->DrawPrimitive
         (D3DPT_TRIANGLELIST,
-         primitive->get_first_vertex(),
-         primitive->get_num_primitives());
+         reader->get_first_vertex(),
+         reader->get_num_primitives());
 
     } else {
       // Nonindexed, client arrays.
 
-      draw_primitive_up(D3DPT_TRIANGLELIST, primitive->get_num_primitives(),
-                        primitive->get_first_vertex(),
-                        primitive->get_num_vertices(),
-                        _vertex_data->get_array(0)->get_data(),
-                        _vertex_data->get_format()->get_array(0)->get_stride());
+      draw_primitive_up(D3DPT_TRIANGLELIST, reader->get_num_primitives(),
+                        reader->get_first_vertex(),
+                        reader->get_num_vertices(),
+                        _data_reader->get_array_reader(0)->get_data(),
+                        _data_reader->get_format()->get_array(0)->get_stride());
     }
   }
 }
@@ -952,77 +954,77 @@ draw_triangles(const GeomTriangles *primitive) {
 //  Description: Draws a series of triangle strips.
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian8::
-draw_tristrips(const GeomTristrips *primitive) {
+draw_tristrips(const GeomPrimitivePipelineReader *reader) {
   PStatTimer timer(_draw_primitive_pcollector);
   if (connect_triangle_strips && _current_fill_mode != RenderModeAttrib::M_wireframe) {
     // One long triangle strip, connected by the degenerate vertices
     // that have already been set up within the primitive.
-    _vertices_tristrip_pcollector.add_level(primitive->get_num_vertices());
+    _vertices_tristrip_pcollector.add_level(reader->get_num_vertices());
     _primitive_batches_tristrip_pcollector.add_level(1);
-    if (primitive->is_indexed()) {
-      int min_vertex = dx_broken_max_index ? 0 : primitive->get_min_vertex();
-      int max_vertex = primitive->get_max_vertex();
+    if (reader->is_indexed()) {
+      int min_vertex = dx_broken_max_index ? 0 : reader->get_min_vertex();
+      int max_vertex = reader->get_max_vertex();
 
       if (_active_vbuffer != NULL) {
         // Indexed, vbuffers, one line triangle strip.
-        IndexBufferContext *ibc = ((GeomPrimitive *)primitive)->prepare_now(get_prepared_objects(), this);
+        IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
         nassertv(ibc != (IndexBufferContext *)NULL);
         apply_index_buffer(ibc);
 
         _d3d_device->DrawIndexedPrimitive
           (D3DPT_TRIANGLESTRIP,
            min_vertex, max_vertex - min_vertex + 1,
-           0, primitive->get_num_vertices() - 2);
+           0, reader->get_num_vertices() - 2);
 
       } else {
         // Indexed, client arrays, one long triangle strip.
-        D3DFORMAT index_type = get_index_type(primitive->get_index_type());
+        D3DFORMAT index_type = get_index_type(reader->get_index_type());
         draw_indexed_primitive_up
           (D3DPT_TRIANGLESTRIP,
            min_vertex, max_vertex,
-           primitive->get_num_vertices() - 2,
-           primitive->get_data(), index_type,
-           _vertex_data->get_array(0)->get_data(),
-           _vertex_data->get_format()->get_array(0)->get_stride());
+           reader->get_num_vertices() - 2,
+           reader->get_data(), index_type,
+           _data_reader->get_array_reader(0)->get_data(),
+           _data_reader->get_format()->get_array(0)->get_stride());
       }
     } else {
       if (_active_vbuffer != NULL) {
         // Nonindexed, vbuffers, one long triangle strip.
         _d3d_device->DrawPrimitive
           (D3DPT_TRIANGLESTRIP,
-           primitive->get_first_vertex(),
-           primitive->get_num_vertices() - 2);
+           reader->get_first_vertex(),
+           reader->get_num_vertices() - 2);
 
       } else {
         // Indexed, client arrays, one long triangle strip.
         draw_primitive_up(D3DPT_TRIANGLESTRIP,
-                          primitive->get_num_vertices() - 2,
-                          primitive->get_first_vertex(),
-                          primitive->get_num_vertices(),
-                          _vertex_data->get_array(0)->get_data(),
-                          _vertex_data->get_format()->get_array(0)->get_stride());
+                          reader->get_num_vertices() - 2,
+                          reader->get_first_vertex(),
+                          reader->get_num_vertices(),
+                          _data_reader->get_array_reader(0)->get_data(),
+                          _data_reader->get_format()->get_array(0)->get_stride());
       }
     }
 
   } else {
     // Send the individual triangle strips, stepping over the
     // degenerate vertices.
-    CPTA_int ends = primitive->get_ends();
+    CPTA_int ends = reader->get_ends();
     _primitive_batches_tristrip_pcollector.add_level(ends.size());
 
-    if (primitive->is_indexed()) {
-      CPTA_int ends = primitive->get_ends();
-      int index_stride = primitive->get_index_stride();
+    if (reader->is_indexed()) {
+      CPTA_int ends = reader->get_ends();
+      int index_stride = reader->get_index_stride();
       _primitive_batches_tristrip_pcollector.add_level(ends.size());
 
-      GeomVertexReader mins(primitive->get_mins(), 0);
-      GeomVertexReader maxs(primitive->get_maxs(), 0);
-      nassertv(primitive->get_mins()->get_num_rows() == (int)ends.size() &&
-               primitive->get_maxs()->get_num_rows() == (int)ends.size());
+      GeomVertexReader mins(reader->get_mins(), 0);
+      GeomVertexReader maxs(reader->get_maxs(), 0);
+      nassertv(reader->get_mins()->get_num_rows() == (int)ends.size() &&
+               reader->get_maxs()->get_num_rows() == (int)ends.size());
 
       if (_active_vbuffer != NULL) {
         // Indexed, vbuffers, individual triangle strips.
-        IndexBufferContext *ibc = ((GeomPrimitive *)primitive)->prepare_now(get_prepared_objects(), this);
+        IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
         nassertv(ibc != (IndexBufferContext *)NULL);
         apply_index_buffer(ibc);
 
@@ -1041,10 +1043,10 @@ draw_tristrips(const GeomTristrips *primitive) {
 
       } else {
         // Indexed, client arrays, individual triangle strips.
-        CPTA_uchar array_data = _vertex_data->get_array(0)->get_data();
-        int stride = _vertex_data->get_format()->get_array(0)->get_stride();
-        CPTA_uchar vertices = primitive->get_data();
-        D3DFORMAT index_type = get_index_type(primitive->get_index_type());
+        CPTA_uchar array_data = _data_reader->get_array_reader(0)->get_data();
+        int stride = _data_reader->get_format()->get_array(0)->get_stride();
+        CPTA_uchar vertices = reader->get_data();
+        D3DFORMAT index_type = get_index_type(reader->get_index_type());
 
         unsigned int start = 0;
         for (size_t i = 0; i < ends.size(); i++) {
@@ -1062,7 +1064,7 @@ draw_tristrips(const GeomTristrips *primitive) {
         }
       }
     } else {
-      unsigned int first_vertex = primitive->get_first_vertex();
+      unsigned int first_vertex = reader->get_first_vertex();
 
       if (_active_vbuffer != NULL) {
         // Nonindexed, vbuffers, individual triangle strips.
@@ -1078,8 +1080,8 @@ draw_tristrips(const GeomTristrips *primitive) {
 
       } else {
         // Nonindexed, client arrays, individual triangle strips.
-        CPTA_uchar array_data = _vertex_data->get_array(0)->get_data();
-        int stride = _vertex_data->get_format()->get_array(0)->get_stride();
+        CPTA_uchar array_data = _data_reader->get_array_reader(0)->get_data();
+        int stride = _data_reader->get_format()->get_array(0)->get_stride();
 
         unsigned int start = 0;
         for (size_t i = 0; i < ends.size(); i++) {
@@ -1102,27 +1104,27 @@ draw_tristrips(const GeomTristrips *primitive) {
 //  Description: Draws a series of triangle fans.
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian8::
-draw_trifans(const GeomTrifans *primitive) {
+draw_trifans(const GeomPrimitivePipelineReader *reader) {
   PStatTimer timer(_draw_primitive_pcollector);
-  CPTA_int ends = primitive->get_ends();
+  CPTA_int ends = reader->get_ends();
   _primitive_batches_trifan_pcollector.add_level(ends.size());
 
-  if (primitive->is_indexed()) {
-    int min_vertex = dx_broken_max_index ? 0 : primitive->get_min_vertex();
-    int max_vertex = primitive->get_max_vertex();
+  if (reader->is_indexed()) {
+    int min_vertex = dx_broken_max_index ? 0 : reader->get_min_vertex();
+    int max_vertex = reader->get_max_vertex();
 
     // Send the individual triangle fans.  There's no connecting fans
     // with degenerate vertices, so no worries about that.
-    int index_stride = primitive->get_index_stride();
+    int index_stride = reader->get_index_stride();
 
-    GeomVertexReader mins(primitive->get_mins(), 0);
-    GeomVertexReader maxs(primitive->get_maxs(), 0);
-    nassertv(primitive->get_mins()->get_num_rows() == (int)ends.size() &&
-             primitive->get_maxs()->get_num_rows() == (int)ends.size());
+    GeomVertexReader mins(reader->get_mins(), 0);
+    GeomVertexReader maxs(reader->get_maxs(), 0);
+    nassertv(reader->get_mins()->get_num_rows() == (int)ends.size() &&
+             reader->get_maxs()->get_num_rows() == (int)ends.size());
 
     if (_active_vbuffer != NULL) {
       // Indexed, vbuffers.
-      IndexBufferContext *ibc = ((GeomPrimitive *)primitive)->prepare_now(get_prepared_objects(), this);
+      IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
       nassertv(ibc != (IndexBufferContext *)NULL);
       apply_index_buffer(ibc);
 
@@ -1141,10 +1143,10 @@ draw_trifans(const GeomTrifans *primitive) {
 
     } else {
       // Indexed, client arrays.
-      CPTA_uchar array_data = _vertex_data->get_array(0)->get_data();
-      int stride = _vertex_data->get_format()->get_array(0)->get_stride();
-      CPTA_uchar vertices = primitive->get_data();
-      D3DFORMAT index_type = get_index_type(primitive->get_index_type());
+      CPTA_uchar array_data = _data_reader->get_array_reader(0)->get_data();
+      int stride = _data_reader->get_format()->get_array(0)->get_stride();
+      CPTA_uchar vertices = reader->get_data();
+      D3DFORMAT index_type = get_index_type(reader->get_index_type());
 
       unsigned int start = 0;
       for (size_t i = 0; i < ends.size(); i++) {
@@ -1162,7 +1164,7 @@ draw_trifans(const GeomTrifans *primitive) {
       }
     }
   } else {
-    unsigned int first_vertex = primitive->get_first_vertex();
+    unsigned int first_vertex = reader->get_first_vertex();
 
     if (_active_vbuffer != NULL) {
       // Nonindexed, vbuffers.
@@ -1178,8 +1180,8 @@ draw_trifans(const GeomTrifans *primitive) {
 
     } else {
       // Nonindexed, client arrays.
-      CPTA_uchar array_data = _vertex_data->get_array(0)->get_data();
-      int stride = _vertex_data->get_format()->get_array(0)->get_stride();
+      CPTA_uchar array_data = _data_reader->get_array_reader(0)->get_data();
+      int stride = _data_reader->get_format()->get_array(0)->get_stride();
 
       unsigned int start = 0;
       for (size_t i = 0; i < ends.size(); i++) {
@@ -1201,54 +1203,54 @@ draw_trifans(const GeomTrifans *primitive) {
 //  Description: Draws a series of disconnected line segments.
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian8::
-draw_lines(const GeomLines *primitive) {
+draw_lines(const GeomPrimitivePipelineReader *reader) {
   PStatTimer timer(_draw_primitive_pcollector);
-  _vertices_other_pcollector.add_level(primitive->get_num_vertices());
+  _vertices_other_pcollector.add_level(reader->get_num_vertices());
   _primitive_batches_other_pcollector.add_level(1);
 
-  if (primitive->is_indexed()) {
-    int min_vertex = dx_broken_max_index ? 0 : primitive->get_min_vertex();
-    int max_vertex = primitive->get_max_vertex();
+  if (reader->is_indexed()) {
+    int min_vertex = dx_broken_max_index ? 0 : reader->get_min_vertex();
+    int max_vertex = reader->get_max_vertex();
 
     if (_active_vbuffer != NULL) {
       // Indexed, vbuffers.
-      IndexBufferContext *ibc = ((GeomPrimitive *)primitive)->prepare_now(get_prepared_objects(), this);
+      IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
       nassertv(ibc != (IndexBufferContext *)NULL);
       apply_index_buffer(ibc);
 
       _d3d_device->DrawIndexedPrimitive
         (D3DPT_LINELIST,
          min_vertex, max_vertex - min_vertex + 1,
-         0, primitive->get_num_primitives());
+         0, reader->get_num_primitives());
 
     } else {
       // Indexed, client arrays.
-      D3DFORMAT index_type = get_index_type(primitive->get_index_type());
+      D3DFORMAT index_type = get_index_type(reader->get_index_type());
 
       draw_indexed_primitive_up
         (D3DPT_LINELIST,
          min_vertex, max_vertex,
-         primitive->get_num_primitives(),
-         primitive->get_data(),
+         reader->get_num_primitives(),
+         reader->get_data(),
          index_type,
-         _vertex_data->get_array(0)->get_data(),
-         _vertex_data->get_format()->get_array(0)->get_stride());
+         _data_reader->get_array_reader(0)->get_data(),
+         _data_reader->get_format()->get_array(0)->get_stride());
     }
   } else {
     if (_active_vbuffer != NULL) {
       // Nonindexed, vbuffers.
       _d3d_device->DrawPrimitive
         (D3DPT_LINELIST,
-         primitive->get_first_vertex(),
-         primitive->get_num_primitives());
+         reader->get_first_vertex(),
+         reader->get_num_primitives());
 
     } else {
       // Nonindexed, client arrays.
-      draw_primitive_up(D3DPT_LINELIST, primitive->get_num_primitives(),
-                        primitive->get_first_vertex(),
-                        primitive->get_num_vertices(),
-                        _vertex_data->get_array(0)->get_data(),
-                        _vertex_data->get_format()->get_array(0)->get_stride());
+      draw_primitive_up(D3DPT_LINELIST, reader->get_num_primitives(),
+                        reader->get_first_vertex(),
+                        reader->get_num_vertices(),
+                        _data_reader->get_array_reader(0)->get_data(),
+                        _data_reader->get_format()->get_array(0)->get_stride());
     }
   }
 }
@@ -1259,7 +1261,7 @@ draw_lines(const GeomLines *primitive) {
 //  Description: Draws a series of line strips.
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian8::
-draw_linestrips(const GeomLinestrips *primitive) {
+draw_linestrips(const GeomPrimitivePipelineReader *reader) {
   PStatTimer timer(_draw_primitive_pcollector);
 }
 
@@ -1269,29 +1271,29 @@ draw_linestrips(const GeomLinestrips *primitive) {
 //  Description: Draws a series of disconnected points.
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian8::
-draw_points(const GeomPoints *primitive) {
+draw_points(const GeomPrimitivePipelineReader *reader) {
   PStatTimer timer(_draw_primitive_pcollector);
-  _vertices_other_pcollector.add_level(primitive->get_num_vertices());
+  _vertices_other_pcollector.add_level(reader->get_num_vertices());
   _primitive_batches_other_pcollector.add_level(1);
 
   // The munger should have protected us from indexed points--DirectX
   // doesn't support them.
-  nassertv(!primitive->is_indexed());
+  nassertv(!reader->is_indexed());
 
   if (_active_vbuffer != NULL) {
     // Nonindexed, vbuffers.
     _d3d_device->DrawPrimitive
       (D3DPT_POINTLIST,
-       primitive->get_first_vertex(),
-       primitive->get_num_primitives());
+       reader->get_first_vertex(),
+       reader->get_num_primitives());
 
   } else {
     // Nonindexed, client arrays.
-    draw_primitive_up(D3DPT_POINTLIST, primitive->get_num_primitives(),
-                      primitive->get_first_vertex(),
-                      primitive->get_num_vertices(),
-                      _vertex_data->get_array(0)->get_data(),
-                      _vertex_data->get_format()->get_array(0)->get_stride());
+    draw_primitive_up(D3DPT_POINTLIST, reader->get_num_primitives(),
+                      reader->get_first_vertex(),
+                      reader->get_num_vertices(),
+                      _data_reader->get_array_reader(0)->get_data(),
+                      _data_reader->get_format()->get_array(0)->get_stride());
   }
 }
 
@@ -1312,7 +1314,7 @@ end_draw_primitives() {
     _vertex_blending_enabled = false;
   }
 
-  if (_vertex_data->is_vertex_transformed()) {
+  if (_data_reader->is_vertex_transformed()) {
     // Restore the projection matrix that we wiped out above.
     _d3d_device->SetTransform(D3DTS_PROJECTION,
                               (D3DMATRIX*)_projection_mat->get_mat().get_data());

+ 8 - 8
panda/src/dxgsg8/dxGraphicsStateGuardian8.h

@@ -75,15 +75,15 @@ public:
   virtual void end_scene();
   virtual void end_frame();
 
-  virtual bool begin_draw_primitives(const Geom *geom,
+  virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader, 
                                      const GeomMunger *munger,
-                                     const GeomVertexData *vertex_data);
-  virtual void draw_triangles(const GeomTriangles *primitive);
-  virtual void draw_tristrips(const GeomTristrips *primitive);
-  virtual void draw_trifans(const GeomTrifans *primitive);
-  virtual void draw_lines(const GeomLines *primitive);
-  virtual void draw_linestrips(const GeomLinestrips *primitive);
-  virtual void draw_points(const GeomPoints *primitive);
+                                     const GeomVertexDataPipelineReader *data_reader);
+  virtual void draw_triangles(const GeomPrimitivePipelineReader *reader);
+  virtual void draw_tristrips(const GeomPrimitivePipelineReader *reader);
+  virtual void draw_trifans(const GeomPrimitivePipelineReader *reader);
+  virtual void draw_lines(const GeomPrimitivePipelineReader *reader);
+  virtual void draw_linestrips(const GeomPrimitivePipelineReader *reader);
+  virtual void draw_points(const GeomPrimitivePipelineReader *reader);
   virtual void end_draw_primitives();
 
   virtual void framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr,

+ 127 - 125
panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx

@@ -1144,12 +1144,14 @@ DBG_S dxgsg9_cat.debug ( ) << "@@@@@@@@@@ end_frame \n"; DBG_E
 //               are ok, false to abort this group of primitives.
 ////////////////////////////////////////////////////////////////////
 bool DXGraphicsStateGuardian9::
-begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
-                      const GeomVertexData *vertex_data) {
-  if (!GraphicsStateGuardian::begin_draw_primitives(geom, munger, vertex_data)) {
+begin_draw_primitives(const GeomPipelineReader *geom_reader, 
+                      const GeomMunger *munger,
+                      const GeomVertexDataPipelineReader *data_reader) {
+  if (!GraphicsStateGuardian::begin_draw_primitives(geom_reader, munger, 
+                                                    data_reader)) {
     return false;
   }
-  nassertr(_vertex_data != (GeomVertexData *)NULL, false);
+  nassertr(_data_reader != (GeomVertexDataPipelineReader *)NULL, false);
 
   DBG_SH5 dxgsg9_cat.debug ( ) << "begin_draw_primitives\n"; DBG_E
 
@@ -1173,9 +1175,9 @@ begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
   _vertex_array_shader_expansion = _current_shader_expansion;
   _vertex_array_shader_context = _current_shader_context;
 
-  const GeomVertexFormat *format = _vertex_data->get_format ( );
-  const GeomVertexArrayData *data;
-  int number_of_arrays = _vertex_data -> get_num_arrays ( );
+  const GeomVertexFormat *format = _data_reader->get_format ( );
+  const GeomVertexArrayDataPipelineReader *data;
+  int number_of_arrays = _data_reader -> get_num_arrays ( );
 
   if (_current_shader_context && number_of_arrays > 1) {
 
@@ -1198,7 +1200,7 @@ begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
       // find the one array with the minimum number of elements if possible
       {
         for (index = 0; index < number_of_arrays; index++) {
-          data = _vertex_data -> get_array (index);
+          data = _data_reader -> get_array_reader (index);
 
           const GeomVertexArrayFormat *array_format = data->get_array_format();
           int number_of_columns = array_format->get_num_columns();
@@ -1227,7 +1229,7 @@ begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
         // ugh slow, need to find which one
         for (index = first_index; index < number_of_arrays; index++)
         {
-          data = _vertex_data -> get_array (index);
+          data = _data_reader -> get_array_reader (index);
 
           const GeomVertexArrayFormat *array_format = data->get_array_format();
           int number_of_columns = array_format->get_num_columns();
@@ -1247,15 +1249,15 @@ vertex_element_array -> vertex_element_type_array;
           }
         }
 
-// since the check is not implemented yet use first_index for now
-data = _vertex_data -> get_array (first_index);
+        // since the check is not implemented yet use first_index for now
+        data = _data_reader -> get_array_reader (first_index);
 
         match = true;
       }
       else
       {
         if (first_index >= 0) {
-          data = _vertex_data -> get_array (first_index);
+          data = _data_reader -> get_array_reader (first_index);
           match = true;
         }
       }
@@ -1268,7 +1270,7 @@ data = _vertex_data -> get_array (first_index);
         dxgsg9_cat.error ( ) << "could not find matching vertex element data for vertex shader\n";
 
         // just use the 0 array
-        data = _vertex_data->get_array(0);
+        data = _data_reader->get_array_reader(0);
       }
     }
     else {
@@ -1278,15 +1280,15 @@ data = _vertex_data -> get_array (first_index);
   }
   else {
     // The munger should have put the FVF data in the first array.
-    data = _vertex_data->get_array(0);
+    data = _data_reader->get_array_reader(0);
   }
 
-  VertexBufferContext *vbc = ((GeomVertexArrayData *)data)->prepare_now(get_prepared_objects(), this);
+  VertexBufferContext *vbc = ((GeomVertexArrayData *)(data->get_object()))->prepare_now(get_prepared_objects(), this);
   nassertr(vbc != (VertexBufferContext *)NULL, false);
   apply_vertex_buffer(vbc, _current_shader_context);
 
   const GeomVertexAnimationSpec &animation =
-    vertex_data->get_format()->get_animation();
+    data_reader->get_format()->get_animation();
   if (animation.get_animation_type() == Geom::AT_hardware) {
     // Set up vertex blending.
     switch (animation.get_num_transforms()) {
@@ -1317,7 +1319,7 @@ data = _vertex_data -> get_array (first_index);
       set_render_state(D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE);
     }
 
-    const TransformTable *table = vertex_data->get_transform_table();
+    const TransformTable *table = data_reader->get_transform_table();
     if (table != (TransformTable *)NULL) {
       for (int i = 0; i < table->get_num_transforms(); i++) {
         LMatrix4f mat;
@@ -1340,14 +1342,14 @@ data = _vertex_data -> get_array (first_index);
       _vertex_blending_enabled = false;
     }
 
-    if (_transform_stale && !_vertex_data->is_vertex_transformed()) {
+    if (_transform_stale && !_data_reader->is_vertex_transformed()) {
       const D3DMATRIX *d3d_mat = (const D3DMATRIX *)_internal_transform->get_mat().get_data();
       _d3d_device->SetTransform(D3DTS_WORLD, d3d_mat);
       _transform_stale = false;
     }
   }
 
-  if (_vertex_data->is_vertex_transformed()) {
+  if (_data_reader->is_vertex_transformed()) {
     // If the vertex data claims to be already transformed into clip
     // coordinates, wipe out the current projection and modelview
     // matrix (so we don't attempt to transform it again).
@@ -1376,20 +1378,20 @@ data = _vertex_data -> get_array (first_index);
 //  Description: Draws a series of disconnected triangles.
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian9::
-draw_triangles(const GeomTriangles *primitive) {
+draw_triangles(const GeomPrimitivePipelineReader *reader) {
   PStatTimer timer(_draw_primitive_pcollector);
 
 //  DBG_SH5 dxgsg9_cat.debug ( ) << "draw_triangles 1\n"; DBG_E
 
-  _vertices_tri_pcollector.add_level(primitive->get_num_vertices());
+  _vertices_tri_pcollector.add_level(reader->get_num_vertices());
   _primitive_batches_tri_pcollector.add_level(1);
-  if (primitive->is_indexed()) {
-    int min_vertex = dx_broken_max_index ? 0 : primitive->get_min_vertex();
-    int max_vertex = primitive->get_max_vertex();
+  if (reader->is_indexed()) {
+    int min_vertex = dx_broken_max_index ? 0 : reader->get_min_vertex();
+    int max_vertex = reader->get_max_vertex();
 
     if (_active_vbuffer != NULL) {
       // Indexed, vbuffers.
-      IndexBufferContext *ibc = ((GeomPrimitive *)primitive)->prepare_now(get_prepared_objects(), this);
+      IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
       nassertv(ibc != (IndexBufferContext *)NULL);
       apply_index_buffer(ibc);
 
@@ -1398,22 +1400,22 @@ draw_triangles(const GeomTriangles *primitive) {
       _d3d_device->DrawIndexedPrimitive
         (D3DPT_TRIANGLELIST, 0,
          min_vertex, max_vertex - min_vertex + 1,
-         0, primitive->get_num_primitives());
+         0, reader->get_num_primitives());
 
     } else {
       // Indexed, client arrays.
 
 //DBG_SH2 dxgsg9_cat.debug ( ) << "draw_indexed_primitive_up \n"; DBG_E
 
-      D3DFORMAT index_type = get_index_type(primitive->get_index_type());
+      D3DFORMAT index_type = get_index_type(reader->get_index_type());
       draw_indexed_primitive_up
         (D3DPT_TRIANGLELIST,
          min_vertex, max_vertex,
-         primitive->get_num_primitives(),
-         primitive->get_data(),
+         reader->get_num_primitives(),
+         reader->get_data(),
          index_type,
-         _vertex_data->get_array(0)->get_data(),
-         _vertex_data->get_format()->get_array(0)->get_stride());
+         _data_reader->get_array_reader(0)->get_data(),
+         _data_reader->get_format()->get_array(0)->get_stride());
     }
   } else {
     if (_active_vbuffer != NULL) {
@@ -1423,19 +1425,19 @@ draw_triangles(const GeomTriangles *primitive) {
 
       _d3d_device->DrawPrimitive
         (D3DPT_TRIANGLELIST,
-         primitive->get_first_vertex(),
-         primitive->get_num_primitives());
+         reader->get_first_vertex(),
+         reader->get_num_primitives());
 
     } else {
       // Nonindexed, client arrays.
 
 //DBG_SH2 dxgsg9_cat.debug ( ) << "draw_primitive_up \n"; DBG_E
 
-      draw_primitive_up(D3DPT_TRIANGLELIST, primitive->get_num_primitives(),
-      primitive->get_first_vertex(),
-      primitive->get_num_vertices(),
-      _vertex_data->get_array(0)->get_data(),
-      _vertex_data->get_format()->get_array(0)->get_stride());
+      draw_primitive_up(D3DPT_TRIANGLELIST, reader->get_num_primitives(),
+      reader->get_first_vertex(),
+      reader->get_num_vertices(),
+      _data_reader->get_array_reader(0)->get_data(),
+      _data_reader->get_format()->get_array(0)->get_stride());
     }
   }
 
@@ -1448,7 +1450,7 @@ draw_triangles(const GeomTriangles *primitive) {
 //  Description: Draws a series of triangle strips.
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian9::
-draw_tristrips(const GeomTristrips *primitive) {
+draw_tristrips(const GeomPrimitivePipelineReader *reader) {
   PStatTimer timer(_draw_primitive_pcollector);
 
   DBG_SH5 dxgsg9_cat.debug ( ) << "draw_tristrips\n"; DBG_E
@@ -1456,80 +1458,80 @@ draw_tristrips(const GeomTristrips *primitive) {
   if (connect_triangle_strips && _current_fill_mode != RenderModeAttrib::M_wireframe) {
     // One long triangle strip, connected by the degenerate vertices
     // that have already been set up within the primitive.
-    _vertices_tristrip_pcollector.add_level(primitive->get_num_vertices());
+    _vertices_tristrip_pcollector.add_level(reader->get_num_vertices());
     _primitive_batches_tristrip_pcollector.add_level(1);
-    if (primitive->is_indexed()) {
-      int min_vertex = dx_broken_max_index ? 0 : primitive->get_min_vertex();
-      int max_vertex = primitive->get_max_vertex();
+    if (reader->is_indexed()) {
+      int min_vertex = dx_broken_max_index ? 0 : reader->get_min_vertex();
+      int max_vertex = reader->get_max_vertex();
 
       if (_active_vbuffer != NULL) {
         // Indexed, vbuffers, one line triangle strip.
-        IndexBufferContext *ibc = ((GeomPrimitive *)primitive)->prepare_now(get_prepared_objects(), this);
+        IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
         nassertv(ibc != (IndexBufferContext *)NULL);
         apply_index_buffer(ibc);
 
-//dxgsg9_cat.error ( ) << "DrawIndexedPrimitive D3DPT_TRIANGLESTRIP VERTICES: " << primitive->get_num_vertices ( ) << "\n";
+//dxgsg9_cat.error ( ) << "DrawIndexedPrimitive D3DPT_TRIANGLESTRIP VERTICES: " << reader->get_num_vertices ( ) << "\n";
 
         _d3d_device->DrawIndexedPrimitive
           (D3DPT_TRIANGLESTRIP, 0,
            min_vertex, max_vertex - min_vertex + 1,
-           0, primitive->get_num_vertices() - 2);
+           0, reader->get_num_vertices() - 2);
 
       } else {
 
-//dxgsg9_cat.error ( ) << "draw_indexed_primitive_up D3DPT_TRIANGLESTRIP VERTICES: " << primitive->get_num_vertices ( ) << "\n";
+//dxgsg9_cat.error ( ) << "draw_indexed_primitive_up D3DPT_TRIANGLESTRIP VERTICES: " << reader->get_num_vertices ( ) << "\n";
 
         // Indexed, client arrays, one long triangle strip.
-        D3DFORMAT index_type = get_index_type(primitive->get_index_type());
+        D3DFORMAT index_type = get_index_type(reader->get_index_type());
         draw_indexed_primitive_up
           (D3DPT_TRIANGLESTRIP,
            min_vertex, max_vertex,
-           primitive->get_num_vertices() - 2,
-           primitive->get_data(), index_type,
-           _vertex_data->get_array(0)->get_data(),
-           _vertex_data->get_format()->get_array(0)->get_stride());
+           reader->get_num_vertices() - 2,
+           reader->get_data(), index_type,
+           _data_reader->get_array_reader(0)->get_data(),
+           _data_reader->get_format()->get_array(0)->get_stride());
       }
     } else {
       if (_active_vbuffer != NULL) {
         // Nonindexed, vbuffers, one long triangle strip.
 
-//dxgsg9_cat.error ( ) << "DrawPrimitive D3DPT_TRIANGLESTRIP " << primitive->get_first_vertex ( ) << " VERTICES: " << primitive->get_num_vertices ( ) << "\n";
+//dxgsg9_cat.error ( ) << "DrawPrimitive D3DPT_TRIANGLESTRIP " << reader->get_first_vertex ( ) << " VERTICES: " << reader->get_num_vertices ( ) << "\n";
 
         _d3d_device->DrawPrimitive
           (D3DPT_TRIANGLESTRIP,
-           primitive->get_first_vertex(),
-           primitive->get_num_vertices() - 2);
+           reader->get_first_vertex(),
+           reader->get_num_vertices() - 2);
 
       } else {
         // Indexed, client arrays, one long triangle strip.
         draw_primitive_up(D3DPT_TRIANGLESTRIP,
-        primitive->get_num_vertices() - 2,
-        primitive->get_first_vertex(),
-        primitive->get_num_vertices(),
-        _vertex_data->get_array(0)->get_data(),
-        _vertex_data->get_format()->get_array(0)->get_stride());
+        reader->get_num_vertices() - 2,
+        reader->get_first_vertex(),
+        reader->get_num_vertices(),
+        _data_reader->get_array_reader(0)->get_data(),
+        _data_reader->get_format()->get_array(0)->get_stride());
       }
     }
 
   } else {
     // Send the individual triangle strips, stepping over the
     // degenerate vertices.
-    CPTA_int ends = primitive->get_ends();
+    CPTA_int ends = reader->get_ends();
     _primitive_batches_tristrip_pcollector.add_level(ends.size());
 
-    if (primitive->is_indexed()) {
-      CPTA_int ends = primitive->get_ends();
-      int index_stride = primitive->get_index_stride();
+    if (reader->is_indexed()) {
+      CPTA_int ends = reader->get_ends();
+      int index_stride = reader->get_index_stride();
       _primitive_batches_tristrip_pcollector.add_level(ends.size());
 
-      GeomVertexReader mins(primitive->get_mins(), 0);
-      GeomVertexReader maxs(primitive->get_maxs(), 0);
-      nassertv(primitive->get_mins()->get_num_rows() == (int)ends.size() &&
-               primitive->get_maxs()->get_num_rows() == (int)ends.size());
+      GeomVertexReader mins(reader->get_mins(), 0);
+      GeomVertexReader maxs(reader->get_maxs(), 0);
+      nassertv(reader->get_mins()->get_num_rows() == (int)ends.size() &&
+               reader->get_maxs()->get_num_rows() == (int)ends.size());
 
       if (_active_vbuffer != NULL) {
         // Indexed, vbuffers, individual triangle strips.
-        IndexBufferContext *ibc = ((GeomPrimitive *)primitive)->prepare_now(get_prepared_objects(), this);
+        IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
         nassertv(ibc != (IndexBufferContext *)NULL);
         apply_index_buffer(ibc);
 
@@ -1549,10 +1551,10 @@ draw_tristrips(const GeomTristrips *primitive) {
 
       } else {
         // Indexed, client arrays, individual triangle strips.
-        CPTA_uchar array_data = _vertex_data->get_array(0)->get_data();
-        int stride = _vertex_data->get_format()->get_array(0)->get_stride();
-        CPTA_uchar vertices = primitive->get_data();
-        D3DFORMAT index_type = get_index_type(primitive->get_index_type());
+        CPTA_uchar array_data = _data_reader->get_array_reader(0)->get_data();
+        int stride = _data_reader->get_format()->get_array(0)->get_stride();
+        CPTA_uchar vertices = reader->get_data();
+        D3DFORMAT index_type = get_index_type(reader->get_index_type());
 
         unsigned int start = 0;
         for (size_t i = 0; i < ends.size(); i++) {
@@ -1570,7 +1572,7 @@ draw_tristrips(const GeomTristrips *primitive) {
         }
       }
     } else {
-      unsigned int first_vertex = primitive->get_first_vertex();
+      unsigned int first_vertex = reader->get_first_vertex();
 
       if (_active_vbuffer != NULL) {
         // Nonindexed, vbuffers, individual triangle strips.
@@ -1586,8 +1588,8 @@ draw_tristrips(const GeomTristrips *primitive) {
 
       } else {
         // Nonindexed, client arrays, individual triangle strips.
-        CPTA_uchar array_data = _vertex_data->get_array(0)->get_data();
-        int stride = _vertex_data->get_format()->get_array(0)->get_stride();
+        CPTA_uchar array_data = _data_reader->get_array_reader(0)->get_data();
+        int stride = _data_reader->get_format()->get_array(0)->get_stride();
 
         unsigned int start = 0;
         for (size_t i = 0; i < ends.size(); i++) {
@@ -1610,30 +1612,30 @@ draw_tristrips(const GeomTristrips *primitive) {
 //  Description: Draws a series of triangle fans.
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian9::
-draw_trifans(const GeomTrifans *primitive) {
+draw_trifans(const GeomPrimitivePipelineReader *reader) {
   PStatTimer timer(_draw_primitive_pcollector);
 
   DBG_SH5 dxgsg9_cat.debug ( ) << "draw_trifans\n"; DBG_E
 
-  CPTA_int ends = primitive->get_ends();
+  CPTA_int ends = reader->get_ends();
   _primitive_batches_trifan_pcollector.add_level(ends.size());
 
-  if (primitive->is_indexed()) {
-    int min_vertex = dx_broken_max_index ? 0 : primitive->get_min_vertex();
-    int max_vertex = primitive->get_max_vertex();
+  if (reader->is_indexed()) {
+    int min_vertex = dx_broken_max_index ? 0 : reader->get_min_vertex();
+    int max_vertex = reader->get_max_vertex();
 
     // Send the individual triangle fans.  There's no connecting fans
     // with degenerate vertices, so no worries about that.
-    int index_stride = primitive->get_index_stride();
+    int index_stride = reader->get_index_stride();
 
-    GeomVertexReader mins(primitive->get_mins(), 0);
-    GeomVertexReader maxs(primitive->get_maxs(), 0);
-    nassertv(primitive->get_mins()->get_num_rows() == (int)ends.size() &&
-             primitive->get_maxs()->get_num_rows() == (int)ends.size());
+    GeomVertexReader mins(reader->get_mins(), 0);
+    GeomVertexReader maxs(reader->get_maxs(), 0);
+    nassertv(reader->get_mins()->get_num_rows() == (int)ends.size() &&
+             reader->get_maxs()->get_num_rows() == (int)ends.size());
 
     if (_active_vbuffer != NULL) {
       // Indexed, vbuffers.
-      IndexBufferContext *ibc = ((GeomPrimitive *)primitive)->prepare_now(get_prepared_objects(), this);
+      IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
       nassertv(ibc != (IndexBufferContext *)NULL);
       apply_index_buffer(ibc);
 
@@ -1652,10 +1654,10 @@ draw_trifans(const GeomTrifans *primitive) {
 
     } else {
       // Indexed, client arrays.
-      CPTA_uchar array_data = _vertex_data->get_array(0)->get_data();
-      int stride = _vertex_data->get_format()->get_array(0)->get_stride();
-      CPTA_uchar vertices = primitive->get_data();
-      D3DFORMAT index_type = get_index_type(primitive->get_index_type());
+      CPTA_uchar array_data = _data_reader->get_array_reader(0)->get_data();
+      int stride = _data_reader->get_format()->get_array(0)->get_stride();
+      CPTA_uchar vertices = reader->get_data();
+      D3DFORMAT index_type = get_index_type(reader->get_index_type());
 
       unsigned int start = 0;
       for (size_t i = 0; i < ends.size(); i++) {
@@ -1673,7 +1675,7 @@ draw_trifans(const GeomTrifans *primitive) {
       }
     }
   } else {
-    unsigned int first_vertex = primitive->get_first_vertex();
+    unsigned int first_vertex = reader->get_first_vertex();
 
     if (_active_vbuffer != NULL) {
       // Nonindexed, vbuffers.
@@ -1689,8 +1691,8 @@ draw_trifans(const GeomTrifans *primitive) {
 
     } else {
       // Nonindexed, client arrays.
-      CPTA_uchar array_data = _vertex_data->get_array(0)->get_data();
-      int stride = _vertex_data->get_format()->get_array(0)->get_stride();
+      CPTA_uchar array_data = _data_reader->get_array_reader(0)->get_data();
+      int stride = _data_reader->get_format()->get_array(0)->get_stride();
 
       unsigned int start = 0;
       for (size_t i = 0; i < ends.size(); i++) {
@@ -1712,18 +1714,18 @@ draw_trifans(const GeomTrifans *primitive) {
 //  Description: Draws a series of disconnected line segments.
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian9::
-draw_lines(const GeomLines *primitive) {
+draw_lines(const GeomPrimitivePipelineReader *reader) {
   PStatTimer timer(_draw_primitive_pcollector);
-  _vertices_other_pcollector.add_level(primitive->get_num_vertices());
+  _vertices_other_pcollector.add_level(reader->get_num_vertices());
   _primitive_batches_other_pcollector.add_level(1);
 
-  if (primitive->is_indexed()) {
-    int min_vertex = dx_broken_max_index ? 0 : primitive->get_min_vertex();
-    int max_vertex = primitive->get_max_vertex();
+  if (reader->is_indexed()) {
+    int min_vertex = dx_broken_max_index ? 0 : reader->get_min_vertex();
+    int max_vertex = reader->get_max_vertex();
 
     if (_active_vbuffer != NULL) {
       // Indexed, vbuffers.
-      IndexBufferContext *ibc = ((GeomPrimitive *)primitive)->prepare_now(get_prepared_objects(), this);
+      IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
       nassertv(ibc != (IndexBufferContext *)NULL);
       apply_index_buffer(ibc);
 
@@ -1731,36 +1733,36 @@ draw_lines(const GeomLines *primitive) {
         (D3DPT_LINELIST,
      0,
          min_vertex, max_vertex - min_vertex + 1,
-         0, primitive->get_num_primitives());
+         0, reader->get_num_primitives());
 
     } else {
       // Indexed, client arrays.
-      D3DFORMAT index_type = get_index_type(primitive->get_index_type());
+      D3DFORMAT index_type = get_index_type(reader->get_index_type());
 
       draw_indexed_primitive_up
         (D3DPT_LINELIST,
          min_vertex, max_vertex,
-         primitive->get_num_primitives(),
-         primitive->get_data(),
+         reader->get_num_primitives(),
+         reader->get_data(),
          index_type,
-         _vertex_data->get_array(0)->get_data(),
-         _vertex_data->get_format()->get_array(0)->get_stride());
+         _data_reader->get_array_reader(0)->get_data(),
+         _data_reader->get_format()->get_array(0)->get_stride());
     }
   } else {
     if (_active_vbuffer != NULL) {
       // Nonindexed, vbuffers.
       _d3d_device->DrawPrimitive
         (D3DPT_LINELIST,
-         primitive->get_first_vertex(),
-         primitive->get_num_primitives());
+         reader->get_first_vertex(),
+         reader->get_num_primitives());
 
     } else {
       // Nonindexed, client arrays.
-      draw_primitive_up(D3DPT_LINELIST, primitive->get_num_primitives(),
-      primitive->get_first_vertex(),
-      primitive->get_num_vertices(),
-      _vertex_data->get_array(0)->get_data(),
-      _vertex_data->get_format()->get_array(0)->get_stride());
+      draw_primitive_up(D3DPT_LINELIST, reader->get_num_primitives(),
+      reader->get_first_vertex(),
+      reader->get_num_vertices(),
+      _data_reader->get_array_reader(0)->get_data(),
+      _data_reader->get_format()->get_array(0)->get_stride());
     }
   }
 }
@@ -1771,7 +1773,7 @@ draw_lines(const GeomLines *primitive) {
 //  Description: Draws a series of line strips.
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian9::
-draw_linestrips(const GeomLinestrips *primitive) {
+draw_linestrips(const GeomPrimitivePipelineReader *reader) {
   PStatTimer timer(_draw_primitive_pcollector);
 }
 
@@ -1781,29 +1783,29 @@ draw_linestrips(const GeomLinestrips *primitive) {
 //  Description: Draws a series of disconnected points.
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian9::
-draw_points(const GeomPoints *primitive) {
+draw_points(const GeomPrimitivePipelineReader *reader) {
   PStatTimer timer(_draw_primitive_pcollector);
-  _vertices_other_pcollector.add_level(primitive->get_num_vertices());
+  _vertices_other_pcollector.add_level(reader->get_num_vertices());
   _primitive_batches_other_pcollector.add_level(1);
 
   // The munger should have protected us from indexed points--DirectX
   // doesn't support them.
-  nassertv(!primitive->is_indexed());
+  nassertv(!reader->is_indexed());
 
   if (_active_vbuffer != NULL) {
     // Nonindexed, vbuffers.
     _d3d_device->DrawPrimitive
       (D3DPT_POINTLIST,
-       primitive->get_first_vertex(),
-       primitive->get_num_primitives());
+       reader->get_first_vertex(),
+       reader->get_num_primitives());
 
   } else {
     // Nonindexed, client arrays.
-    draw_primitive_up(D3DPT_POINTLIST, primitive->get_num_primitives(),
-          primitive->get_first_vertex(),
-          primitive->get_num_vertices(),
-          _vertex_data->get_array(0)->get_data(),
-          _vertex_data->get_format()->get_array(0)->get_stride());
+    draw_primitive_up(D3DPT_POINTLIST, reader->get_num_primitives(),
+                      reader->get_first_vertex(),
+                      reader->get_num_vertices(),
+                      _data_reader->get_array_reader(0)->get_data(),
+                      _data_reader->get_format()->get_array(0)->get_stride());
   }
 }
 
@@ -1824,7 +1826,7 @@ end_draw_primitives() {
     _vertex_blending_enabled = false;
   }
 
-  if (_vertex_data->is_vertex_transformed()) {
+  if (_data_reader->is_vertex_transformed()) {
     // Restore the projection matrix that we wiped out above.
     _d3d_device->SetTransform(D3DTS_PROJECTION,
                               (D3DMATRIX*)_projection_mat->get_mat().get_data());

+ 8 - 8
panda/src/dxgsg9/dxGraphicsStateGuardian9.h

@@ -111,15 +111,15 @@ public:
   virtual void end_scene();
   virtual void end_frame();
 
-  virtual bool begin_draw_primitives(const Geom *geom,
+  virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader, 
                                      const GeomMunger *munger,
-                                     const GeomVertexData *vertex_data);
-  virtual void draw_triangles(const GeomTriangles *primitive);
-  virtual void draw_tristrips(const GeomTristrips *primitive);
-  virtual void draw_trifans(const GeomTrifans *primitive);
-  virtual void draw_lines(const GeomLines *primitive);
-  virtual void draw_linestrips(const GeomLinestrips *primitive);
-  virtual void draw_points(const GeomPoints *primitive);
+                                     const GeomVertexDataPipelineReader *data_reader);
+  virtual void draw_triangles(const GeomPrimitivePipelineReader *reader);
+  virtual void draw_tristrips(const GeomPrimitivePipelineReader *reader);
+  virtual void draw_trifans(const GeomPrimitivePipelineReader *reader);
+  virtual void draw_lines(const GeomPrimitivePipelineReader *reader);
+  virtual void draw_linestrips(const GeomPrimitivePipelineReader *reader);
+  virtual void draw_points(const GeomPrimitivePipelineReader *reader);
   virtual void end_draw_primitives();
 
   virtual void framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr,

+ 2 - 2
panda/src/dxgsg9/dxShaderContext9.cxx

@@ -714,7 +714,7 @@ update_shader_vertex_arrays(CLP(ShaderContext) *prev, GSG *gsg)
     {
       if (_vertex_element_array == 0) {
         bool error;
-        const GeomVertexArrayData *array_data;
+        const GeomVertexArrayDataPipelineReader *array_reader;
         Geom::NumericType numeric_type;
         int start, stride, num_values;
         int nvarying = _var_spec.size();
@@ -744,7 +744,7 @@ update_shader_vertex_arrays(CLP(ShaderContext) *prev, GSG *gsg)
               }
             }
           }
-          if (gsg->_vertex_data->get_array_info(name, array_data, num_values, numeric_type, start, stride)) {
+          if (gsg->_data_reader->get_array_info(name, array_reader, num_values, numeric_type, start, stride)) {
 
             if (false) {
 

+ 16 - 0
panda/src/express/referenceCount.I

@@ -252,6 +252,22 @@ test_ref_count_integrity() const {
 #endif
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: ReferenceCount::test_ref_count_nonzero
+//       Access: Published
+//  Description: Does some easy checks to make sure that the reference
+//               count isn't zero, or completely bogus.  Returns true
+//               if ok, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool ReferenceCount::
+test_ref_count_nonzero() const {
+#ifndef NDEBUG
+  return do_test_ref_count_nonzero();
+#else
+  return true;
+#endif
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ReferenceCount::local_object
 //       Access: Public

+ 14 - 0
panda/src/express/referenceCount.cxx

@@ -50,6 +50,20 @@ do_test_ref_count_integrity() const {
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: ReferenceCount::do_test_ref_count_nonzero
+//       Access: Protected
+//  Description: Returns true if the reference count is nonzero, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+bool ReferenceCount::
+do_test_ref_count_nonzero() const {
+  nassertr(do_test_ref_count_integrity(), false);
+  nassertr(_ref_count > 0, false);
+
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ReferenceCount::create_weak_list
 //       Access: Private

+ 2 - 0
panda/src/express/referenceCount.h

@@ -55,6 +55,7 @@ PUBLISHED:
   INLINE bool unref() const;
 
   INLINE bool test_ref_count_integrity() const;
+  INLINE bool test_ref_count_nonzero() const;
 
 public:
   INLINE void local_object();
@@ -66,6 +67,7 @@ public:
 
 protected:
   bool do_test_ref_count_integrity() const;
+  bool do_test_ref_count_nonzero() const;
 
 private:
   void create_weak_list();

+ 155 - 148
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -1356,23 +1356,24 @@ end_frame() {
 //               are ok, false to abort this group of primitives.
 ////////////////////////////////////////////////////////////////////
 bool CLP(GraphicsStateGuardian)::
-begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
-                      const GeomVertexData *vertex_data) {
+begin_draw_primitives(const GeomPipelineReader *geom_reader,
+                      const GeomMunger *munger,
+                      const GeomVertexDataPipelineReader *data_reader) {
 #ifndef NDEBUG
   if (GLCAT.is_spam()) {
-    GLCAT.spam() << "begin_draw_primitives: " << *vertex_data << "\n";
+    GLCAT.spam() << "begin_draw_primitives: " << *(data_reader->get_object()) << "\n";
   }
 #endif  // NDEBUG
 
-  if (!GraphicsStateGuardian::begin_draw_primitives(geom, munger, vertex_data)) {
+  if (!GraphicsStateGuardian::begin_draw_primitives(geom_reader, munger, data_reader)) {
     return false;
   }
-  nassertr(_vertex_data != (GeomVertexData *)NULL, false);
+  nassertr(_data_reader != (GeomVertexDataPipelineReader *)NULL, false);
 
   _geom_display_list = 0;
 
   if (_auto_antialias_mode) {
-    switch (geom->get_primitive_type()) {
+    switch (geom_reader->get_primitive_type()) {
     case GeomPrimitive::PT_polygons:
       setup_antialias_polygon();
       break;
@@ -1396,7 +1397,7 @@ begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
   }
 
   const GeomVertexAnimationSpec &animation =
-    _vertex_data->get_format()->get_animation();
+    _data_reader->get_format()->get_animation();
   bool hardware_animation = (animation.get_animation_type() == Geom::AT_hardware);
   if (hardware_animation) {
     // Set up the transform matrices for vertex blending.
@@ -1404,7 +1405,7 @@ begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
     GLP(Enable)(GL_VERTEX_BLEND_ARB);
     _glVertexBlendARB(animation.get_num_transforms());
 
-    const TransformTable *table = _vertex_data->get_transform_table();
+    const TransformTable *table = _data_reader->get_transform_table();
     if (table != (TransformTable *)NULL) {
       if (animation.get_indexed_transforms()) {
         nassertr(_supports_matrix_palette, false);
@@ -1481,7 +1482,7 @@ begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
     }
   }
 
-  if (_vertex_data->is_vertex_transformed()) {
+  if (_data_reader->is_vertex_transformed()) {
     // If the vertex data claims to be already transformed into clip
     // coordinates, wipe out the current projection and modelview
     // matrix (so we don't attempt to transform it again).
@@ -1493,8 +1494,8 @@ begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
     GLP(LoadIdentity)();
   }
 
-  if (geom->get_usage_hint() == Geom::UH_static &&
-      _vertex_data->get_usage_hint() == Geom::UH_static &&
+  if (geom_reader->get_usage_hint() == Geom::UH_static &&
+      _data_reader->get_usage_hint() == Geom::UH_static &&
       display_lists && (!hardware_animation || display_list_animation)) {
     // If the geom claims to be totally static, try to build it into
     // a display list.
@@ -1518,11 +1519,11 @@ begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
       _current_ibuffer_index = 0;
     }
 
-    GeomContext *gc = ((Geom *)geom)->prepare_now(get_prepared_objects(), this);
+    GeomContext *gc = ((Geom *)geom_reader->get_object())->prepare_now(get_prepared_objects(), this);
     nassertr(gc != (GeomContext *)NULL, false);
     CLP(GeomContext) *ggc = DCAST(CLP(GeomContext), gc);
     const CLP(GeomMunger) *gmunger = DCAST(CLP(GeomMunger), _munger);
-    UpdateSeq modified = max(geom->get_modified(), _vertex_data->get_modified());
+    UpdateSeq modified = max(geom_reader->get_modified(), _data_reader->get_modified());
     if (ggc->get_display_list(_geom_display_list, gmunger, modified)) {
       // If it hasn't been modified, just play the display list again.
       if (GLCAT.is_spam()) {
@@ -1563,8 +1564,8 @@ begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
     // Count up the number of vertices used by primitives in the Geom,
     // for PStats reporting.
     ggc->_num_verts = 0;
-    for (int i = 0; i < geom->get_num_primitives(); i++) {
-      ggc->_num_verts += geom->get_primitive(i)->get_num_vertices();
+    for (int i = 0; i < geom_reader->get_num_primitives(); i++) {
+      ggc->_num_verts += geom_reader->get_primitive(i)->get_num_vertices();
     }
 #endif
   }
@@ -1610,19 +1611,18 @@ begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
 //               for setting up their own vertex arrays.
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsStateGuardian)::
-update_standard_vertex_arrays()
-{
+update_standard_vertex_arrays() {
   const GeomVertexAnimationSpec &animation =
-    _vertex_data->get_format()->get_animation();
+    _data_reader->get_format()->get_animation();
   bool hardware_animation = (animation.get_animation_type() == Geom::AT_hardware);
 #ifdef SUPPORT_IMMEDIATE_MODE
   if (_use_sender) {
     // We must use immediate mode to render primitives.
     _sender.clear();
 
-    _sender.add_column(_vertex_data, InternalName::get_normal(),
+    _sender.add_column(_data_reader, InternalName::get_normal(),
                        NULL, NULL, GLP(Normal3f), NULL);
-    if (!_sender.add_column(_vertex_data, InternalName::get_color(),
+    if (!_sender.add_column(_data_reader, InternalName::get_color(),
                             NULL, NULL, GLP(Color3f), GLP(Color4f))) {
       // If we didn't have a color column, the item color is white.
       GLP(Color4f)(1.0f, 1.0f, 1.0f, 1.0f);
@@ -1647,13 +1647,13 @@ update_standard_vertex_arrays()
         if (stage_index == 0) {
           // Use the original functions for stage 0, in case we don't
           // support multitexture.
-          _sender.add_column(_vertex_data, name,
+          _sender.add_column(_data_reader, name,
                              GLP(TexCoord1f), GLP(TexCoord2f),
                              GLP(TexCoord3f), GLP(TexCoord4f));
 
         } else {
           // Other stages require the multitexture functions.
-          _sender.add_texcoord_column(_vertex_data, name, stage_index,
+          _sender.add_texcoord_column(_data_reader, name, stage_index,
                                       _glMultiTexCoord1f, _glMultiTexCoord2f,
                                       _glMultiTexCoord3f, _glMultiTexCoord4f);
         }
@@ -1673,12 +1673,12 @@ update_standard_vertex_arrays()
     if (_supports_vertex_blend) {
       if (hardware_animation) {
         // Issue the weights and/or transform indices for vertex blending.
-        _sender.add_vector_column(_vertex_data, InternalName::get_transform_weight(),
+        _sender.add_vector_column(_data_reader, InternalName::get_transform_weight(),
                                   _glWeightfvARB);
 
         if (animation.get_indexed_transforms()) {
           // Issue the matrix palette indices.
-          _sender.add_vector_uint_column(_vertex_data, InternalName::get_transform_index(),
+          _sender.add_vector_uint_column(_data_reader, InternalName::get_transform_index(),
                                         _glMatrixIndexuivARB);
         }
       }
@@ -1686,22 +1686,22 @@ update_standard_vertex_arrays()
 
     // We must add vertex last, because glVertex3f() is the key
     // function call that actually issues the vertex.
-    _sender.add_column(_vertex_data, InternalName::get_vertex(),
+    _sender.add_column(_data_reader, InternalName::get_vertex(),
                        NULL, GLP(Vertex2f), GLP(Vertex3f), GLP(Vertex4f));
 
   } else
 #endif  // SUPPORT_IMMEDIATE_MODE
   {
     // We may use vertex arrays or buffers to render primitives.
-    const GeomVertexArrayData *array_data;
+    const GeomVertexArrayDataPipelineReader *array_reader;
     int num_values;
     Geom::NumericType numeric_type;
     int start;
     int stride;
 
-    if (_vertex_data->get_normal_info(array_data, numeric_type,
+    if (_data_reader->get_normal_info(array_reader, numeric_type,
                                       start, stride)) {
-      const unsigned char *client_pointer = setup_array_data(array_data);
+      const unsigned char *client_pointer = setup_array_data(array_reader);
       GLP(NormalPointer)(get_numeric_type(numeric_type), stride,
                          client_pointer + start);
       GLP(EnableClientState)(GL_NORMAL_ARRAY);
@@ -1709,10 +1709,10 @@ update_standard_vertex_arrays()
       GLP(DisableClientState)(GL_NORMAL_ARRAY);
     }
 
-    if (_vertex_data->get_color_info(array_data, num_values, numeric_type,
+    if (_data_reader->get_color_info(array_reader, num_values, numeric_type,
                                      start, stride) &&
         numeric_type != Geom::NT_packed_dabc) {
-      const unsigned char *client_pointer = setup_array_data(array_data);
+      const unsigned char *client_pointer = setup_array_data(array_reader);
       GLP(ColorPointer)(num_values, get_numeric_type(numeric_type),
                         stride, client_pointer + start);
       GLP(EnableClientState)(GL_COLOR_ARRAY);
@@ -1742,10 +1742,10 @@ update_standard_vertex_arrays()
         // texcoords issued for it.
         const InternalName *name = stage->get_texcoord_name();
 
-        if (_vertex_data->get_array_info(name, array_data, num_values,
+        if (_data_reader->get_array_info(name, array_reader, num_values,
                                          numeric_type, start, stride)) {
           // The vertex data does have texcoords for this stage.
-          const unsigned char *client_pointer = setup_array_data(array_data);
+          const unsigned char *client_pointer = setup_array_data(array_reader);
           GLP(TexCoordPointer)(num_values, get_numeric_type(numeric_type),
                                stride, client_pointer + start);
           GLP(EnableClientState)(GL_TEXTURE_COORD_ARRAY);
@@ -1774,10 +1774,10 @@ update_standard_vertex_arrays()
     if (_supports_vertex_blend) {
       if (hardware_animation) {
         // Issue the weights and/or transform indices for vertex blending.
-        if (_vertex_data->get_array_info(InternalName::get_transform_weight(),
-                                         array_data, num_values, numeric_type,
+        if (_data_reader->get_array_info(InternalName::get_transform_weight(),
+                                         array_reader, num_values, numeric_type,
                                          start, stride)) {
-          const unsigned char *client_pointer = setup_array_data(array_data);
+          const unsigned char *client_pointer = setup_array_data(array_reader);
           _glWeightPointerARB(num_values, get_numeric_type(numeric_type),
                               stride, client_pointer + start);
           GLP(EnableClientState)(GL_WEIGHT_ARRAY_ARB);
@@ -1787,10 +1787,10 @@ update_standard_vertex_arrays()
 
         if (animation.get_indexed_transforms()) {
           // Issue the matrix palette indices.
-          if (_vertex_data->get_array_info(InternalName::get_transform_index(),
-                                           array_data, num_values, numeric_type,
+          if (_data_reader->get_array_info(InternalName::get_transform_index(),
+                                           array_reader, num_values, numeric_type,
                                            start, stride)) {
-            const unsigned char *client_pointer = setup_array_data(array_data);
+            const unsigned char *client_pointer = setup_array_data(array_reader);
             _glMatrixIndexPointerARB(num_values, get_numeric_type(numeric_type),
                                      stride, client_pointer + start);
             GLP(EnableClientState)(GL_MATRIX_INDEX_ARRAY_ARB);
@@ -1809,9 +1809,9 @@ update_standard_vertex_arrays()
 
     // There's no requirement that we add vertices last, but we do
     // anyway.
-    if (_vertex_data->get_vertex_info(array_data, num_values, numeric_type,
+    if (_data_reader->get_vertex_info(array_reader, num_values, numeric_type,
                                       start, stride)) {
-      const unsigned char *client_pointer = setup_array_data(array_data);
+      const unsigned char *client_pointer = setup_array_data(array_reader);
       GLP(VertexPointer)(num_values, get_numeric_type(numeric_type),
                          stride, client_pointer + start);
       GLP(EnableClientState)(GL_VERTEX_ARRAY);
@@ -1865,38 +1865,39 @@ disable_standard_vertex_arrays()
 //  Description: Draws a series of disconnected triangles.
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsStateGuardian)::
-draw_triangles(const GeomTriangles *primitive) {
+draw_triangles(const GeomPrimitivePipelineReader *reader) {
   PStatTimer timer(_draw_primitive_pcollector);
 
 #ifndef NDEBUG
   if (GLCAT.is_spam()) {
-    GLCAT.spam() << "draw_triangles: " << *primitive << "\n";
+    GLCAT.spam() << "draw_triangles: " << *(reader->get_object()) << "\n";
   }
 #endif  // NDEBUG
 
 #ifdef SUPPORT_IMMEDIATE_MODE
   if (_use_sender) {
-    draw_immediate_simple_primitives(primitive, GL_TRIANGLES);
+    draw_immediate_simple_primitives(reader, GL_TRIANGLES);
 
   } else
 #endif  // SUPPORT_IMMEDIATE_MODE
   {
-    _vertices_tri_pcollector.add_level(primitive->get_num_vertices());
+    int num_vertices = reader->get_num_vertices();
+    _vertices_tri_pcollector.add_level(num_vertices);
     _primitive_batches_tri_pcollector.add_level(1);
 
-    if (primitive->is_indexed()) {
-      const unsigned char *client_pointer = setup_primitive(primitive);
+    if (reader->is_indexed()) {
+      const unsigned char *client_pointer = setup_primitive(reader);
 
       _glDrawRangeElements(GL_TRIANGLES,
-                           primitive->get_min_vertex(),
-                           primitive->get_max_vertex(),
-                           primitive->get_num_vertices(),
-                           get_numeric_type(primitive->get_index_type()),
+                           reader->get_min_vertex(),
+                           reader->get_max_vertex(),
+                           num_vertices,
+                           get_numeric_type(reader->get_index_type()),
                            client_pointer);
     } else {
       GLP(DrawArrays)(GL_TRIANGLES,
-                      primitive->get_first_vertex(),
-                      primitive->get_num_vertices());
+                      reader->get_first_vertex(),
+                      num_vertices);
     }
   }
 
@@ -1909,20 +1910,20 @@ draw_triangles(const GeomTriangles *primitive) {
 //  Description: Draws a series of triangle strips.
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsStateGuardian)::
-draw_tristrips(const GeomTristrips *primitive) {
+draw_tristrips(const GeomPrimitivePipelineReader *reader) {
   PStatTimer timer(_draw_primitive_pcollector);
 
   report_my_gl_errors();
 
 #ifndef NDEBUG
   if (GLCAT.is_spam()) {
-    GLCAT.spam() << "draw_tristrips: " << *primitive << "\n";
+    GLCAT.spam() << "draw_tristrips: " << *(reader->get_object()) << "\n";
   }
 #endif  // NDEBUG
 
 #ifdef SUPPORT_IMMEDIATE_MODE
   if (_use_sender) {
-    draw_immediate_composite_primitives(primitive, GL_TRIANGLE_STRIP);
+    draw_immediate_composite_primitives(reader, GL_TRIANGLE_STRIP);
 
   } else
 #endif  // SUPPORT_IMMEDIATE_MODE
@@ -1930,35 +1931,36 @@ draw_tristrips(const GeomTristrips *primitive) {
     if (connect_triangle_strips && _render_mode != RenderModeAttrib::M_wireframe) {
       // One long triangle strip, connected by the degenerate vertices
       // that have already been set up within the primitive.
-      _vertices_tristrip_pcollector.add_level(primitive->get_num_vertices());
+      int num_vertices = reader->get_num_vertices();
+      _vertices_tristrip_pcollector.add_level(num_vertices);
       _primitive_batches_tristrip_pcollector.add_level(1);
-      if (primitive->is_indexed()) {
-        const unsigned char *client_pointer = setup_primitive(primitive);
+      if (reader->is_indexed()) {
+        const unsigned char *client_pointer = setup_primitive(reader);
         _glDrawRangeElements(GL_TRIANGLE_STRIP,
-                             primitive->get_min_vertex(),
-                             primitive->get_max_vertex(),
-                             primitive->get_num_vertices(),
-                             get_numeric_type(primitive->get_index_type()),
+                             reader->get_min_vertex(),
+                             reader->get_max_vertex(),
+                             num_vertices,
+                             get_numeric_type(reader->get_index_type()),
                              client_pointer);
       } else {
         GLP(DrawArrays)(GL_TRIANGLE_STRIP,
-                        primitive->get_first_vertex(),
-                        primitive->get_num_vertices());
+                        reader->get_first_vertex(),
+                        num_vertices);
       }
 
     } else {
       // Send the individual triangle strips, stepping over the
       // degenerate vertices.
-      CPTA_int ends = primitive->get_ends();
+      CPTA_int ends = reader->get_ends();
 
       _primitive_batches_tristrip_pcollector.add_level(ends.size());
-      if (primitive->is_indexed()) {
-        const unsigned char *client_pointer = setup_primitive(primitive);
-        int index_stride = primitive->get_index_stride();
-        GeomVertexReader mins(primitive->get_mins(), 0);
-        GeomVertexReader maxs(primitive->get_maxs(), 0);
-        nassertv(primitive->get_mins()->get_num_rows() == (int)ends.size() &&
-                 primitive->get_maxs()->get_num_rows() == (int)ends.size());
+      if (reader->is_indexed()) {
+        const unsigned char *client_pointer = setup_primitive(reader);
+        int index_stride = reader->get_index_stride();
+        GeomVertexReader mins(reader->get_mins(), 0);
+        GeomVertexReader maxs(reader->get_maxs(), 0);
+        nassertv(reader->get_mins()->get_num_rows() == (int)ends.size() &&
+                 reader->get_maxs()->get_num_rows() == (int)ends.size());
 
         unsigned int start = 0;
         for (size_t i = 0; i < ends.size(); i++) {
@@ -1966,13 +1968,13 @@ draw_tristrips(const GeomTristrips *primitive) {
           _glDrawRangeElements(GL_TRIANGLE_STRIP,
                                mins.get_data1i(), maxs.get_data1i(),
                                ends[i] - start,
-                               get_numeric_type(primitive->get_index_type()),
+                               get_numeric_type(reader->get_index_type()),
                                client_pointer + start * index_stride);
           start = ends[i] + 2;
         }
       } else {
         unsigned int start = 0;
-        int first_vertex = primitive->get_first_vertex();
+        int first_vertex = reader->get_first_vertex();
         for (size_t i = 0; i < ends.size(); i++) {
           _vertices_tristrip_pcollector.add_level(ends[i] - start);
           GLP(DrawArrays)(GL_TRIANGLE_STRIP, first_vertex + start,
@@ -1992,45 +1994,45 @@ draw_tristrips(const GeomTristrips *primitive) {
 //  Description: Draws a series of triangle fans.
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsStateGuardian)::
-draw_trifans(const GeomTrifans *primitive) {
+draw_trifans(const GeomPrimitivePipelineReader *reader) {
   PStatTimer timer(_draw_primitive_pcollector);
 #ifndef NDEBUG
   if (GLCAT.is_spam()) {
-    GLCAT.spam() << "draw_trifans: " << *primitive << "\n";
+    GLCAT.spam() << "draw_trifans: " << *(reader->get_object()) << "\n";
   }
 #endif  // NDEBUG
 
 #ifdef SUPPORT_IMMEDIATE_MODE
   if (_use_sender) {
-    draw_immediate_composite_primitives(primitive, GL_TRIANGLE_FAN);
+    draw_immediate_composite_primitives(reader, GL_TRIANGLE_FAN);
   } else
 #endif  // SUPPORT_IMMEDIATE_MODE
   {
     // Send the individual triangle fans.  There's no connecting fans
     // with degenerate vertices, so no worries about that.
-    CPTA_int ends = primitive->get_ends();
+    CPTA_int ends = reader->get_ends();
 
     _primitive_batches_trifan_pcollector.add_level(ends.size());
-    if (primitive->is_indexed()) {
-      const unsigned char *client_pointer = setup_primitive(primitive);
-      int index_stride = primitive->get_index_stride();
-      GeomVertexReader mins(primitive->get_mins(), 0);
-      GeomVertexReader maxs(primitive->get_maxs(), 0);
-      nassertv(primitive->get_mins()->get_num_rows() == (int)ends.size() &&
-               primitive->get_maxs()->get_num_rows() == (int)ends.size());
+    if (reader->is_indexed()) {
+      const unsigned char *client_pointer = setup_primitive(reader);
+      int index_stride = reader->get_index_stride();
+      GeomVertexReader mins(reader->get_mins(), 0);
+      GeomVertexReader maxs(reader->get_maxs(), 0);
+      nassertv(reader->get_mins()->get_num_rows() == (int)ends.size() &&
+               reader->get_maxs()->get_num_rows() == (int)ends.size());
 
       unsigned int start = 0;
       for (size_t i = 0; i < ends.size(); i++) {
         _vertices_trifan_pcollector.add_level(ends[i] - start);
         _glDrawRangeElements(GL_TRIANGLE_FAN,
                              mins.get_data1i(), maxs.get_data1i(), ends[i] - start,
-                             get_numeric_type(primitive->get_index_type()),
+                             get_numeric_type(reader->get_index_type()),
                              client_pointer + start * index_stride);
         start = ends[i];
       }
     } else {
       unsigned int start = 0;
-      int first_vertex = primitive->get_first_vertex();
+      int first_vertex = reader->get_first_vertex();
       for (size_t i = 0; i < ends.size(); i++) {
         _vertices_trifan_pcollector.add_level(ends[i] - start);
         GLP(DrawArrays)(GL_TRIANGLE_FAN, first_vertex + start,
@@ -2049,35 +2051,36 @@ draw_trifans(const GeomTrifans *primitive) {
 //  Description: Draws a series of disconnected line segments.
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsStateGuardian)::
-draw_lines(const GeomLines *primitive) {
+draw_lines(const GeomPrimitivePipelineReader *reader) {
   PStatTimer timer(_draw_primitive_pcollector);
 #ifndef NDEBUG
   if (GLCAT.is_spam()) {
-    GLCAT.spam() << "draw_lines: " << *primitive << "\n";
+    GLCAT.spam() << "draw_lines: " << *(reader->get_object()) << "\n";
   }
 #endif  // NDEBUG
 
 #ifdef SUPPORT_IMMEDIATE_MODE
   if (_use_sender) {
-    draw_immediate_simple_primitives(primitive, GL_LINES);
+    draw_immediate_simple_primitives(reader, GL_LINES);
   } else
 #endif  // SUPPORT_IMMEDIATE_MODE
   {
-    _vertices_other_pcollector.add_level(primitive->get_num_vertices());
+    int num_vertices = reader->get_num_vertices();
+    _vertices_other_pcollector.add_level(num_vertices);
     _primitive_batches_other_pcollector.add_level(1);
 
-    if (primitive->is_indexed()) {
-      const unsigned char *client_pointer = setup_primitive(primitive);
+    if (reader->is_indexed()) {
+      const unsigned char *client_pointer = setup_primitive(reader);
       _glDrawRangeElements(GL_LINES,
-                           primitive->get_min_vertex(),
-                           primitive->get_max_vertex(),
-                           primitive->get_num_vertices(),
-                           get_numeric_type(primitive->get_index_type()),
+                           reader->get_min_vertex(),
+                           reader->get_max_vertex(),
+                           num_vertices,
+                           get_numeric_type(reader->get_index_type()),
                            client_pointer);
     } else {
       GLP(DrawArrays)(GL_LINES,
-                      primitive->get_first_vertex(),
-                      primitive->get_num_vertices());
+                      reader->get_first_vertex(),
+                      num_vertices);
     }
   }
 
@@ -2090,11 +2093,11 @@ draw_lines(const GeomLines *primitive) {
 //  Description: Draws a series of line strips.
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsStateGuardian)::
-draw_linestrips(const GeomLinestrips *primitive) {
+draw_linestrips(const GeomPrimitivePipelineReader *reader) {
   PStatTimer timer(_draw_primitive_pcollector);
 #ifndef NDEBUG
   if (GLCAT.is_spam()) {
-    GLCAT.spam() << "draw_linestrips: " << *primitive << "\n";
+    GLCAT.spam() << "draw_linestrips: " << *(reader->get_object()) << "\n";
   }
 #endif  // NDEBUG
 
@@ -2106,35 +2109,36 @@ draw_linestrips(const GeomLinestrips *primitive) {
 //  Description: Draws a series of disconnected points.
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsStateGuardian)::
-draw_points(const GeomPoints *primitive) {
+draw_points(const GeomPrimitivePipelineReader *reader) {
   PStatTimer timer(_draw_primitive_pcollector);
 #ifndef NDEBUG
   if (GLCAT.is_spam()) {
-    GLCAT.spam() << "draw_points: " << *primitive << "\n";
+    GLCAT.spam() << "draw_points: " << *(reader->get_object()) << "\n";
   }
 #endif  // NDEBUG
 
 #ifdef SUPPORT_IMMEDIATE_MODE
   if (_use_sender) {
-    draw_immediate_simple_primitives(primitive, GL_POINTS);
+    draw_immediate_simple_primitives(reader, GL_POINTS);
   } else
 #endif  // SUPPORT_IMMEDIATE_MODE
   {
-    _vertices_other_pcollector.add_level(primitive->get_num_vertices());
+    int num_vertices = reader->get_num_vertices();
+    _vertices_other_pcollector.add_level(num_vertices);
     _primitive_batches_other_pcollector.add_level(1);
 
-    if (primitive->is_indexed()) {
-      const unsigned char *client_pointer = setup_primitive(primitive);
+    if (reader->is_indexed()) {
+      const unsigned char *client_pointer = setup_primitive(reader);
       _glDrawRangeElements(GL_POINTS,
-                           primitive->get_min_vertex(),
-                           primitive->get_max_vertex(),
-                           primitive->get_num_vertices(),
-                           get_numeric_type(primitive->get_index_type()),
+                           reader->get_min_vertex(),
+                           reader->get_max_vertex(),
+                           num_vertices,
+                           get_numeric_type(reader->get_index_type()),
                            client_pointer);
     } else {
       GLP(DrawArrays)(GL_POINTS,
-                      primitive->get_first_vertex(),
-                      primitive->get_num_vertices());
+                      reader->get_first_vertex(),
+                      num_vertices);
     }
   }
 
@@ -2176,7 +2180,7 @@ end_draw_primitives() {
     GLP(LoadMatrixf)(_internal_transform->get_mat().get_data());
   }
 
-  if (_vertex_data->is_vertex_transformed()) {
+  if (_data_reader->is_vertex_transformed()) {
     // Restore the matrices that we pushed above.
     GLP(MatrixMode)(GL_PROJECTION);
     GLP(PopMatrix)();
@@ -2704,19 +2708,19 @@ release_vertex_buffer(VertexBufferContext *vbc) {
 //               memory.
 //
 //               If the buffer object is bound, this function returns
-//               NULL (reprsenting the start of the buffer object in
+//               NULL (representing the start of the buffer object in
 //               server memory); if the buffer object is not bound,
 //               this function returns the pointer to the data array
 //               in client memory, that is, the data array passed in.
 ////////////////////////////////////////////////////////////////////
 const unsigned char *CLP(GraphicsStateGuardian)::
-setup_array_data(const GeomVertexArrayData *data) {
+setup_array_data(const GeomVertexArrayDataPipelineReader *array_reader) {
   if (!_supports_buffers) {
     // No support for buffer objects; always render from client.
-    return data->get_data();
+    return array_reader->get_data();
   }
   if (!vertex_buffers || _geom_display_list != 0 ||
-      data->get_usage_hint() == Geom::UH_client) {
+      array_reader->get_usage_hint() == Geom::UH_client) {
     // The array specifies client rendering only, or buffer objects
     // are configured off.
     if (_current_vbuffer_index != 0) {
@@ -2727,12 +2731,12 @@ setup_array_data(const GeomVertexArrayData *data) {
       _glBindBuffer(GL_ARRAY_BUFFER, 0);
       _current_vbuffer_index = 0;
     }
-    return data->get_data();
+    return array_reader->get_data();
   }
 
   // Prepare the buffer object and bind it.
-  VertexBufferContext *vbc = ((GeomVertexArrayData *)data)->prepare_now(get_prepared_objects(), this);
-  nassertr(vbc != (VertexBufferContext *)NULL, data->get_data());
+  VertexBufferContext *vbc = ((GeomVertexArrayData *)array_reader->get_object())->prepare_now(get_prepared_objects(), this);
+  nassertr(vbc != (VertexBufferContext *)NULL, array_reader->get_data());
   apply_vertex_buffer(vbc);
 
   // NULL is the OpenGL convention for the first byte of the buffer object.
@@ -2780,9 +2784,10 @@ prepare_index_buffer(GeomPrimitive *data) {
 //               rendering.
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsStateGuardian)::
-apply_index_buffer(IndexBufferContext *ibc) {
+apply_index_buffer(IndexBufferContext *ibc,
+                   const GeomPrimitivePipelineReader *reader) {
   nassertv(_supports_buffers);
-  nassertv(ibc->get_data()->get_modified() != UpdateSeq::initial());
+  nassertv(reader->get_modified() != UpdateSeq::initial());
 
   CLP(IndexBufferContext) *gibc = DCAST(CLP(IndexBufferContext), ibc);
 
@@ -2798,7 +2803,7 @@ apply_index_buffer(IndexBufferContext *ibc) {
 
   if (gibc->was_modified()) {
     PStatTimer timer(_load_index_buffer_pcollector);
-    int num_bytes = gibc->get_data()->get_data_size_bytes();
+    int num_bytes = reader->get_data_size_bytes();
     if (GLCAT.is_spam()) {
       GLCAT.spam()
         << "copying " << num_bytes
@@ -2807,12 +2812,12 @@ apply_index_buffer(IndexBufferContext *ibc) {
     if (num_bytes != 0) {
       if (gibc->changed_size() || gibc->changed_usage_hint()) {
         _glBufferData(GL_ELEMENT_ARRAY_BUFFER, num_bytes,
-                      gibc->get_data()->get_data(),
-                      get_usage(gibc->get_data()->get_usage_hint()));
+                      reader->get_data(),
+                      get_usage(reader->get_usage_hint()));
 
       } else {
         _glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, num_bytes,
-                         gibc->get_data()->get_data());
+                         reader->get_data());
       }
       _data_transferred_pcollector.add_level(num_bytes);
     }
@@ -2877,13 +2882,13 @@ release_index_buffer(IndexBufferContext *ibc) {
 //               in client memory, that is, the data array passed in.
 ////////////////////////////////////////////////////////////////////
 const unsigned char *CLP(GraphicsStateGuardian)::
-setup_primitive(const GeomPrimitive *data) {
+setup_primitive(const GeomPrimitivePipelineReader *reader) {
   if (!_supports_buffers) {
     // No support for buffer objects; always render from client.
-    return data->get_data();
+    return reader->get_data();
   }
   if (!vertex_buffers || _geom_display_list != 0 ||
-      data->get_usage_hint() == Geom::UH_client) {
+      reader->get_usage_hint() == Geom::UH_client) {
     // The array specifies client rendering only, or buffer objects
     // are configured off.
     if (_current_ibuffer_index != 0) {
@@ -2894,13 +2899,13 @@ setup_primitive(const GeomPrimitive *data) {
       _glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
       _current_ibuffer_index = 0;
     }
-    return data->get_data();
+    return reader->get_data();
   }
 
   // Prepare the buffer object and bind it.
-  IndexBufferContext *ibc = ((GeomPrimitive *)data)->prepare_now(get_prepared_objects(), this);
-  nassertr(ibc != (IndexBufferContext *)NULL, data->get_data());
-  apply_index_buffer(ibc);
+  IndexBufferContext *ibc = ((GeomPrimitive *)reader->get_object())->prepare_now(get_prepared_objects(), this);
+  nassertr(ibc != (IndexBufferContext *)NULL, reader->get_data());
+  apply_index_buffer(ibc, reader);
 
   // NULL is the OpenGL convention for the first byte of the buffer object.
   return NULL;
@@ -3926,19 +3931,20 @@ bind_light(Spotlight *light_obj, const NodePath &light, int light_id) {
 //               primitives of the indicated type.
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsStateGuardian)::
-draw_immediate_simple_primitives(const GeomPrimitive *primitive, GLenum mode) {
-  _vertices_immediate_pcollector.add_level(primitive->get_num_vertices());
+draw_immediate_simple_primitives(const GeomPrimitivePipelineReader *reader, GLenum mode) {
+  int num_vertices = reader->get_num_vertices();
+  _vertices_immediate_pcollector.add_level(num_vertices);
   GLP(Begin)(mode);
 
-  if (primitive->is_indexed()) {
-    for (int v = 0; v < primitive->get_num_vertices(); ++v) {
-      _sender.set_vertex(primitive->get_vertex(v));
+  if (reader->is_indexed()) {
+    for (int v = 0; v < num_vertices; ++v) {
+      _sender.set_vertex(reader->get_vertex(v));
       _sender.issue_vertex();
     }
 
   } else {
-    _sender.set_vertex(primitive->get_first_vertex());
-    for (int v = 0; v < primitive->get_num_vertices(); ++v) {
+    _sender.set_vertex(reader->get_first_vertex());
+    for (int v = 0; v < num_vertices; ++v) {
       _sender.issue_vertex();
     }
   }
@@ -3957,12 +3963,13 @@ draw_immediate_simple_primitives(const GeomPrimitive *primitive, GLenum mode) {
 //               several begin/end groups.
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsStateGuardian)::
-draw_immediate_composite_primitives(const GeomPrimitive *primitive, GLenum mode) {
-  _vertices_immediate_pcollector.add_level(primitive->get_num_vertices());
-  CPTA_int ends = primitive->get_ends();
-  int num_unused_vertices_per_primitive = primitive->get_num_unused_vertices_per_primitive();
+draw_immediate_composite_primitives(const GeomPrimitivePipelineReader *reader, GLenum mode) {
+  int num_vertices = reader->get_num_vertices();
+  _vertices_immediate_pcollector.add_level(num_vertices);
+  CPTA_int ends = reader->get_ends();
+  int num_unused_vertices_per_primitive = reader->get_object()->get_num_unused_vertices_per_primitive();
 
-  if (primitive->is_indexed()) {
+  if (reader->is_indexed()) {
     int begin = 0;
     CPTA_int::const_iterator ei;
     for (ei = ends.begin(); ei != ends.end(); ++ei) {
@@ -3970,7 +3977,7 @@ draw_immediate_composite_primitives(const GeomPrimitive *primitive, GLenum mode)
 
       GLP(Begin)(mode);
       for (int v = begin; v < end; ++v) {
-        _sender.set_vertex(primitive->get_vertex(v));
+        _sender.set_vertex(reader->get_vertex(v));
         _sender.issue_vertex();
       }
       GLP(End)();
@@ -3979,7 +3986,7 @@ draw_immediate_composite_primitives(const GeomPrimitive *primitive, GLenum mode)
     }
 
   } else {
-    _sender.set_vertex(primitive->get_first_vertex());
+    _sender.set_vertex(reader->get_first_vertex());
     int begin = 0;
     CPTA_int::const_iterator ei;
     for (ei = ends.begin(); ei != ends.end(); ++ei) {

+ 14 - 13
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -96,15 +96,15 @@ public:
   virtual bool begin_frame();
   virtual void end_frame();
 
-  virtual bool begin_draw_primitives(const Geom *geom, 
+  virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader, 
                                      const GeomMunger *munger,
-                                     const GeomVertexData *vertex_data);
-  virtual void draw_triangles(const GeomTriangles *primitive);
-  virtual void draw_tristrips(const GeomTristrips *primitive);
-  virtual void draw_trifans(const GeomTrifans *primitive);
-  virtual void draw_lines(const GeomLines *primitive);
-  virtual void draw_linestrips(const GeomLinestrips *primitive);
-  virtual void draw_points(const GeomPoints *primitive);
+                                     const GeomVertexDataPipelineReader *data_reader);
+  virtual void draw_triangles(const GeomPrimitivePipelineReader *reader);
+  virtual void draw_tristrips(const GeomPrimitivePipelineReader *reader);
+  virtual void draw_trifans(const GeomPrimitivePipelineReader *reader);
+  virtual void draw_lines(const GeomPrimitivePipelineReader *reader);
+  virtual void draw_linestrips(const GeomPrimitivePipelineReader *reader);
+  virtual void draw_points(const GeomPrimitivePipelineReader *reader);
   virtual void end_draw_primitives();
 
   INLINE bool draw_display_list(GeomContext *gc);
@@ -124,12 +124,13 @@ public:
   virtual VertexBufferContext *prepare_vertex_buffer(GeomVertexArrayData *data);
   void apply_vertex_buffer(VertexBufferContext *vbc);
   virtual void release_vertex_buffer(VertexBufferContext *vbc);
-  const unsigned char *setup_array_data(const GeomVertexArrayData *data);
+  const unsigned char *setup_array_data(const GeomVertexArrayDataPipelineReader *data);
 
   virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data);
-  void apply_index_buffer(IndexBufferContext *ibc);
+  void apply_index_buffer(IndexBufferContext *ibc,
+                          const GeomPrimitivePipelineReader *reader);
   virtual void release_index_buffer(IndexBufferContext *ibc);
-  const unsigned char *setup_primitive(const GeomPrimitive *data);
+  const unsigned char *setup_primitive(const GeomPrimitivePipelineReader *reader);
 
   virtual void begin_occlusion_query();
   virtual PT(OcclusionQueryContext) end_occlusion_query();
@@ -159,8 +160,8 @@ public:
   const float *get_light_color(Light *light) const;
 
 #ifdef SUPPORT_IMMEDIATE_MODE
-  void draw_immediate_simple_primitives(const GeomPrimitive *primitive, GLenum mode);
-  void draw_immediate_composite_primitives(const GeomPrimitive *primitive, GLenum mode);
+  void draw_immediate_simple_primitives(const GeomPrimitivePipelineReader *reader, GLenum mode);
+  void draw_immediate_composite_primitives(const GeomPrimitivePipelineReader *reader, GLenum mode);
 #endif  // SUPPORT_IMMEDIATE_MODE
 
   INLINE static bool report_errors(int line, const char *source_file);

+ 12 - 12
panda/src/glstuff/glImmediateModeSender_src.cxx

@@ -89,10 +89,10 @@ issue_vertex() {
 //               named column doesn't exist in the vertex data).
 ////////////////////////////////////////////////////////////////////
 bool CLP(ImmediateModeSender)::
-add_column(const GeomVertexData *vertex_data, const InternalName *name,
+add_column(const GeomVertexDataPipelineReader *data_reader, const InternalName *name,
            Func1f *func1f, Func2f *func2f, Func3f *func3f, Func4f *func4f) {
-  if (vertex_data->has_column(name)) {
-    GeomVertexReader *reader = new GeomVertexReader(vertex_data, name);
+  if (data_reader->has_column(name)) {
+    GeomVertexReader *reader = new GeomVertexReader(data_reader, name);
     ComponentSender *sender = NULL;
     const GeomVertexColumn *column = reader->get_column();
     switch (column->get_num_components()) {
@@ -151,12 +151,12 @@ add_column(const GeomVertexData *vertex_data, const InternalName *name,
 //               named column doesn't exist in the vertex data).
 ////////////////////////////////////////////////////////////////////
 bool CLP(ImmediateModeSender)::
-add_texcoord_column(const GeomVertexData *vertex_data, 
+add_texcoord_column(const GeomVertexDataPipelineReader *data_reader, 
                     const InternalName *name, int stage_index,
                     TexcoordFunc1f *func1f, TexcoordFunc2f *func2f, 
                     TexcoordFunc3f *func3f, TexcoordFunc4f *func4f) {
-  if (vertex_data->has_column(name)) {
-    GeomVertexReader *reader = new GeomVertexReader(vertex_data, name);
+  if (data_reader->has_column(name)) {
+    GeomVertexReader *reader = new GeomVertexReader(data_reader, name);
     ComponentSender *sender = NULL;
     const GeomVertexColumn *column = reader->get_column();
     switch (column->get_num_components()) {
@@ -209,10 +209,10 @@ add_texcoord_column(const GeomVertexData *vertex_data,
 //               named column doesn't exist in the vertex data).
 ////////////////////////////////////////////////////////////////////
 bool CLP(ImmediateModeSender)::
-add_vector_column(const GeomVertexData *vertex_data, const InternalName *name,
+add_vector_column(const GeomVertexDataPipelineReader *data_reader, const InternalName *name,
                   VectorFunc *func) {
-  if (vertex_data->has_column(name)) {
-    GeomVertexReader *reader = new GeomVertexReader(vertex_data, name);
+  if (data_reader->has_column(name)) {
+    GeomVertexReader *reader = new GeomVertexReader(data_reader, name);
     ComponentSender *sender = NULL;
     const GeomVertexColumn *column = reader->get_column();
     switch (column->get_num_components()) {
@@ -262,10 +262,10 @@ add_vector_column(const GeomVertexData *vertex_data, const InternalName *name,
 //               named column doesn't exist in the vertex data).
 ////////////////////////////////////////////////////////////////////
 bool CLP(ImmediateModeSender)::
-add_vector_uint_column(const GeomVertexData *vertex_data, 
+add_vector_uint_column(const GeomVertexDataPipelineReader *data_reader, 
                        const InternalName *name, VectorUintFunc *func) {
-  if (vertex_data->has_column(name)) {
-    GeomVertexReader *reader = new GeomVertexReader(vertex_data, name);
+  if (data_reader->has_column(name)) {
+    GeomVertexReader *reader = new GeomVertexReader(data_reader, name);
     ComponentSender *sender = NULL;
     const GeomVertexColumn *column = reader->get_column();
     switch (column->get_num_components()) {

+ 8 - 7
panda/src/glstuff/glImmediateModeSender_src.h

@@ -58,17 +58,18 @@ public:
   typedef void APIENTRY VectorFunc(GLint, const GLfloat *);
   typedef void APIENTRY VectorUintFunc(GLint, const GLuint *);
 
-  bool add_column(const GeomVertexData *vertex_data, const InternalName *name,
-                  Func1f *func1f, Func2f *func2f, Func3f *func3f, Func4f *func4f);
-  bool add_texcoord_column(const GeomVertexData *vertex_data, 
+  bool add_column(const GeomVertexDataPipelineReader *data_reader, 
+                  const InternalName *name, Func1f *func1f, 
+                  Func2f *func2f, Func3f *func3f, Func4f *func4f);
+  bool add_texcoord_column(const GeomVertexDataPipelineReader *data_reader, 
                            const InternalName *name, int stage_index,
                            TexcoordFunc1f *func1f, TexcoordFunc2f *func2f, 
                            TexcoordFunc3f *func3f, TexcoordFunc4f *func4f);
 
-  bool add_vector_column(const GeomVertexData *vertex_data, const InternalName *name,
-                         VectorFunc *func);
-  bool add_vector_uint_column(const GeomVertexData *vertex_data, const InternalName *name,
-                              VectorUintFunc *func);
+  bool add_vector_column(const GeomVertexDataPipelineReader *data_reader, 
+                         const InternalName *name, VectorFunc *func);
+  bool add_vector_uint_column(const GeomVertexDataPipelineReader *data_reader, 
+                              const InternalName *name, VectorUintFunc *func);
 
   void add_sender(ComponentSender *sender);
 

+ 10 - 16
panda/src/glstuff/glShaderContext_src.cxx

@@ -419,8 +419,7 @@ bind(GSG *gsg) {
 //  Description: This function disables a currently-bound shader.
 ////////////////////////////////////////////////////////////////////
 void CLP(ShaderContext)::
-unbind()
-{
+unbind() {
 #ifdef HAVE_CGGL
   if (_cg_context != 0) {
     cgGLDisableProfile(_cg_profile[SHADER_type_vert]);
@@ -445,8 +444,7 @@ unbind()
 //               transforms.
 ////////////////////////////////////////////////////////////////////
 void CLP(ShaderContext)::
-issue_parameters(GSG *gsg, bool altered)
-{
+issue_parameters(GSG *gsg, bool altered) {
 #ifdef HAVE_CGGL
   if (_cg_context == 0) {
     return;
@@ -482,8 +480,7 @@ issue_parameters(GSG *gsg, bool altered)
 //  Description: Disable all the vertex arrays used by this shader.
 ////////////////////////////////////////////////////////////////////
 void CLP(ShaderContext)::
-disable_shader_vertex_arrays(GSG *gsg)
-{
+disable_shader_vertex_arrays(GSG *gsg) {
 #ifdef HAVE_CGGL
   if (_cg_context == 0) {
     return;
@@ -508,8 +505,7 @@ disable_shader_vertex_arrays(GSG *gsg)
 //               reenable them.  We may optimize this someday.
 ////////////////////////////////////////////////////////////////////
 void CLP(ShaderContext)::
-update_shader_vertex_arrays(CLP(ShaderContext) *prev, GSG *gsg)
-{
+update_shader_vertex_arrays(CLP(ShaderContext) *prev, GSG *gsg) {
   if (prev) prev->disable_shader_vertex_arrays(gsg);
 #ifdef HAVE_CGGL
   if (_cg_context == 0) {
@@ -522,7 +518,7 @@ update_shader_vertex_arrays(CLP(ShaderContext) *prev, GSG *gsg)
   } else
 #endif // SUPPORT_IMMEDIATE_MODE
   {
-    const GeomVertexArrayData *array_data;
+    const GeomVertexArrayDataPipelineReader *array_reader;
     Geom::NumericType numeric_type;
     int start, stride, num_values;
     int nvarying = _var_spec.size();
@@ -543,10 +539,10 @@ update_shader_vertex_arrays(CLP(ShaderContext) *prev, GSG *gsg)
           }
         }
       }
-      if (gsg->_vertex_data->get_array_info(name,
-                                            array_data, num_values, numeric_type,
+      if (gsg->_data_reader->get_array_info(name,
+                                            array_reader, num_values, numeric_type,
                                             start, stride)) {
-        const unsigned char *client_pointer = gsg->setup_array_data(array_data);
+        const unsigned char *client_pointer = gsg->setup_array_data(array_reader);
         cgGLSetParameterPointer(p,
                                 num_values, gsg->get_numeric_type(numeric_type),
                                 stride, client_pointer + start);
@@ -565,8 +561,7 @@ update_shader_vertex_arrays(CLP(ShaderContext) *prev, GSG *gsg)
 //  Description: Disable all the texture bindings used by this shader.
 ////////////////////////////////////////////////////////////////////
 void CLP(ShaderContext)::
-disable_shader_texture_bindings(GSG *gsg)
-{
+disable_shader_texture_bindings(GSG *gsg) {
 #ifdef HAVE_CGGL
   if (_cg_context == 0) {
     return;
@@ -602,8 +597,7 @@ disable_shader_texture_bindings(GSG *gsg)
 //               reenable them.  We may optimize this someday.
 ////////////////////////////////////////////////////////////////////
 void CLP(ShaderContext)::
-update_shader_texture_bindings(CLP(ShaderContext) *prev, GSG *gsg)
-{
+update_shader_texture_bindings(CLP(ShaderContext) *prev, GSG *gsg) {
   if (prev) prev->disable_shader_texture_bindings(gsg);
 #ifdef HAVE_CGGL
   if (_cg_context == 0) {

+ 168 - 0
panda/src/gobj/geom.I

@@ -461,6 +461,174 @@ CData(const Geom::CData &copy) :
 {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPipelineReader::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GeomPipelineReader::
+GeomPipelineReader(const Geom *object, int pipeline_stage) :
+  _object(object),
+  _pipeline_stage(pipeline_stage),
+  _cdata(object->_cycler.read_stage(pipeline_stage))
+{
+  nassertv(_object->test_ref_count_nonzero());
+#ifdef DO_PIPELINING
+  nassertv(_cdata->test_ref_count_nonzero());
+#endif  // DO_PIPELINING
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPipelineReader::Copy Constructor
+//       Access: Private
+//  Description: Don't attempt to copy these objects.
+////////////////////////////////////////////////////////////////////
+INLINE GeomPipelineReader::
+GeomPipelineReader(const GeomPipelineReader &) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPipelineReader::Copy Assignment Operator
+//       Access: Private
+//  Description: Don't attempt to copy these objects.
+////////////////////////////////////////////////////////////////////
+INLINE void GeomPipelineReader::
+operator = (const GeomPipelineReader &) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPipelineReader::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GeomPipelineReader::
+~GeomPipelineReader() {
+  nassertv(_object->test_ref_count_nonzero());
+#ifdef DO_PIPELINING
+  nassertv(_cdata->test_ref_count_nonzero());
+#endif  // DO_PIPELINING
+  _object->_cycler.release_read_stage(_pipeline_stage, _cdata);
+  _object = NULL;
+  _cdata = NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPipelineReader::get_object
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE const Geom *GeomPipelineReader::
+get_object() const {
+  return _object;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPipelineReader::get_pipeline_stage
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE int GeomPipelineReader::
+get_pipeline_stage() const {
+  return _pipeline_stage;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPipelineReader::check_usage_hint
+//       Access: Public
+//  Description: Ensures that the Geom's usage_hint cache has been
+//               computed.
+////////////////////////////////////////////////////////////////////
+INLINE void GeomPipelineReader::
+check_usage_hint() const {
+  if (!_cdata->_got_usage_hint) {
+    ((Geom *)_object)->reset_usage_hint((Geom::CData *)_cdata);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPipelineReader::get_primitive_type
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE GeomPipelineReader::PrimitiveType GeomPipelineReader::
+get_primitive_type() const {
+  return _cdata->_primitive_type;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPipelineReader::get_shade_model
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE GeomPipelineReader::ShadeModel GeomPipelineReader::
+get_shade_model() const {
+  return _cdata->_shade_model;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPipelineReader::get_geom_rendering
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE int GeomPipelineReader::
+get_geom_rendering() const {
+  return _cdata->_geom_rendering;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPipelineReader::get_usage_hint
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE GeomPipelineReader::UsageHint GeomPipelineReader::
+get_usage_hint() const {
+  nassertr(_cdata->_got_usage_hint, UH_static);
+  return _cdata->_usage_hint;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPipelineReader::get_vertex_data
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CPT(GeomVertexData) GeomPipelineReader::
+get_vertex_data() const {
+  return _cdata->_data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPipelineReader::get_num_primitives
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE int GeomPipelineReader::
+get_num_primitives() const {
+  return _cdata->_primitives.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPipelineReader::get_primitive
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE const GeomPrimitive *GeomPipelineReader::
+get_primitive(int i) const {
+  nassertr(i >= 0 && i < (int)_cdata->_primitives.size(), NULL);
+  return _cdata->_primitives[i];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPipelineReader::get_modified
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE UpdateSeq GeomPipelineReader::
+get_modified() const {
+  return _cdata->_modified;
+}
+
 INLINE ostream &
 operator << (ostream &out, const Geom &obj) {
   obj.output(out);

+ 64 - 61
panda/src/gobj/geom.cxx

@@ -685,18 +685,11 @@ transform_vertices(const LMatrix4f &mat) {
 ////////////////////////////////////////////////////////////////////
 bool Geom::
 check_valid() const {
-  CDReader cdata(_cycler);
-
-  Primitives::const_iterator pi;
-  for (pi = cdata->_primitives.begin(); 
-       pi != cdata->_primitives.end();
-       ++pi) {
-    if (!(*pi)->check_valid(cdata->_data)) {
-      return false;
-    }
-  }
-
-  return true;
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  GeomPipelineReader geom_reader(this, pipeline_stage);
+  GeomVertexDataPipelineReader data_reader(geom_reader.get_vertex_data(), pipeline_stage);
+  data_reader.check_array_readers();
+  return geom_reader.check_valid(&data_reader);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -709,18 +702,11 @@ check_valid() const {
 ////////////////////////////////////////////////////////////////////
 bool Geom::
 check_valid(const GeomVertexData *vertex_data) const {
-  CDReader cdata(_cycler);
-
-  Primitives::const_iterator pi;
-  for (pi = cdata->_primitives.begin(); 
-       pi != cdata->_primitives.end();
-       ++pi) {
-    if (!(*pi)->check_valid(vertex_data)) {
-      return false;
-    }
-  }
-
-  return true;
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  GeomPipelineReader geom_reader(this, pipeline_stage);
+  GeomVertexDataPipelineReader data_reader(vertex_data, pipeline_stage);
+  data_reader.check_array_readers();
+  return geom_reader.check_valid(&data_reader);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -945,21 +931,14 @@ prepare_now(PreparedGraphicsObjects *prepared_objects,
 void Geom::
 draw(GraphicsStateGuardianBase *gsg, const GeomMunger *munger,
      const GeomVertexData *vertex_data) const {
-  CDReader cdata(_cycler);
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  GeomPipelineReader geom_reader(this, pipeline_stage);
+  geom_reader.check_usage_hint();
 
-#ifdef DO_PIPELINING
-  // Make sure the usage_hint is already updated before we start to
-  // draw, so we don't end up with a circular lock if the GSG asks us
-  // to update this while we're holding the read lock.
-  if (!cdata->_got_usage_hint) {
-    {
-      CDWriter cdataw(((Geom *)this)->_cycler, cdata, false);
-      ((Geom *)this)->reset_usage_hint(cdataw);
-      do_draw(gsg, munger, vertex_data, cdataw);
-    }
-  } else
-#endif  // DO_PIPELINING
-    do_draw(gsg, munger, vertex_data, cdata);
+  GeomVertexDataPipelineReader data_reader(vertex_data, pipeline_stage);
+  data_reader.check_array_readers();
+
+  geom_reader.draw(gsg, munger, &data_reader);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1083,29 +1062,6 @@ check_will_be_valid(const GeomVertexData *vertex_data) const {
 
   return true;
 }
-  
-////////////////////////////////////////////////////////////////////
-//     Function: Geom::do_draw
-//       Access: Private
-//  Description: The private implementation of draw().
-////////////////////////////////////////////////////////////////////
-void Geom::
-do_draw(GraphicsStateGuardianBase *gsg, const GeomMunger *munger,
-	const GeomVertexData *vertex_data, const Geom::CData *cdata) const {
-  PStatTimer timer(_draw_primitive_setup_pcollector);
-  if (gsg->begin_draw_primitives(this, munger, vertex_data)) {
-    Primitives::const_iterator pi;
-    for (pi = cdata->_primitives.begin(); 
-         pi != cdata->_primitives.end();
-         ++pi) {
-      const GeomPrimitive *primitive = (*pi);
-      if (primitive->get_num_vertices() != 0) {
-        (*pi)->draw(gsg);
-      }
-    }
-    gsg->end_draw_primitives();
-  }
-}
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Geom::reset_usage_hint
@@ -1374,3 +1330,50 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   _got_usage_hint = false;
   _modified = Geom::get_next_modified();
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPipelineReader::check_valid
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool GeomPipelineReader::
+check_valid(const GeomVertexDataPipelineReader *data_reader) const {
+  Geom::Primitives::const_iterator pi;
+  for (pi = _cdata->_primitives.begin(); 
+       pi != _cdata->_primitives.end();
+       ++pi) {
+    const GeomPrimitive *primitive = (*pi);
+    GeomPrimitivePipelineReader reader(primitive, _pipeline_stage);
+    reader.check_minmax();
+    if (!reader.check_valid(data_reader)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+  
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPipelineReader::draw
+//       Access: Public
+//  Description: The implementation of Geom::draw().
+////////////////////////////////////////////////////////////////////
+void GeomPipelineReader::
+draw(GraphicsStateGuardianBase *gsg, const GeomMunger *munger,
+     const GeomVertexDataPipelineReader *data_reader) const {
+  PStatTimer timer(Geom::_draw_primitive_setup_pcollector);
+  if (gsg->begin_draw_primitives(this, munger, data_reader)) {
+    Geom::Primitives::const_iterator pi;
+    for (pi = _cdata->_primitives.begin(); 
+         pi != _cdata->_primitives.end();
+         ++pi) {
+      const GeomPrimitive *primitive = (*pi);
+      GeomPrimitivePipelineReader reader(primitive, _pipeline_stage);
+      if (reader.get_num_vertices() != 0) {
+        reader.check_minmax();
+        primitive->draw(gsg, &reader);
+      }
+    }
+    gsg->end_draw_primitives();
+  }
+}

+ 43 - 5
panda/src/gobj/geom.h

@@ -156,11 +156,6 @@ private:
   void clear_prepared(PreparedGraphicsObjects *prepared_objects);
   bool check_will_be_valid(const GeomVertexData *vertex_data) const;
 
-  void do_draw(GraphicsStateGuardianBase *gsg, 
-	       const GeomMunger *munger,
-	       const GeomVertexData *vertex_data,
-	       const CData *cdata) const;
-
   void reset_usage_hint(CData *cdata);
   void reset_geom_rendering(CData *cdata);
 
@@ -296,9 +291,52 @@ private:
   friend class CacheEntry;
   friend class GeomMunger;
   friend class GeomContext;
+  friend class GeomPipelineReader;
   friend class PreparedGraphicsObjects;
 };
 
+////////////////////////////////////////////////////////////////////
+//       Class : GeomPipelineReader
+// Description : Encapsulates the data from a Geom,
+//               pre-fetched for one stage of the pipeline.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA GeomPipelineReader : public GeomEnums {
+public:
+  INLINE GeomPipelineReader(const Geom *object, int pipeline_stage);
+private:
+  INLINE GeomPipelineReader(const GeomPipelineReader &copy);
+  INLINE void operator = (const GeomPipelineReader &copy);
+
+public:
+  INLINE ~GeomPipelineReader();
+  ALLOC_DELETED_CHAIN(GeomPipelineReader);
+
+  INLINE const Geom *get_object() const;
+  INLINE int get_pipeline_stage() const;
+
+  INLINE void check_usage_hint() const;
+
+  INLINE PrimitiveType get_primitive_type() const;
+  INLINE ShadeModel get_shade_model() const;
+  INLINE int get_geom_rendering() const;
+  INLINE UsageHint get_usage_hint() const;
+  INLINE CPT(GeomVertexData) get_vertex_data() const;
+  INLINE int get_num_primitives() const;
+  INLINE const GeomPrimitive *get_primitive(int i) const;
+
+  INLINE UpdateSeq get_modified() const;
+
+  bool check_valid(const GeomVertexDataPipelineReader *data_reader) const;
+
+  void draw(GraphicsStateGuardianBase *gsg, const GeomMunger *munger,
+            const GeomVertexDataPipelineReader *data_reader) const;
+
+private:
+  const Geom *_object;
+  int _pipeline_stage;
+  const Geom::CData *_cdata;
+};
+
 INLINE ostream &operator << (ostream &out, const Geom &obj);
 
 #include "geom.I"

+ 2 - 2
panda/src/gobj/geomCacheManager.cxx

@@ -35,8 +35,8 @@ PStatCollector GeomCacheManager::_geom_cache_evict_pcollector("Geom cache operat
 ////////////////////////////////////////////////////////////////////
 GeomCacheManager::
 GeomCacheManager() :
-  _total_size(0),
-  _lock("GeomCacheManager")
+  _lock("GeomCacheManager"),
+  _total_size(0)
 {
   // We deliberately hang on to this pointer forever.
   _list = new GeomCacheEntry;

+ 2 - 2
panda/src/gobj/geomLines.cxx

@@ -117,8 +117,8 @@ get_min_num_vertices_per_primitive() const {
 //               primitive.
 ////////////////////////////////////////////////////////////////////
 void GeomLines::
-draw(GraphicsStateGuardianBase *gsg) const {
-  gsg->draw_lines(this);
+draw(GraphicsStateGuardianBase *gsg, const GeomPrimitivePipelineReader *reader) const {
+  gsg->draw_lines(reader);
 }
 
 ////////////////////////////////////////////////////////////////////

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

@@ -40,7 +40,8 @@ public:
   virtual int get_min_num_vertices_per_primitive() const;
 
 public:
-  virtual void draw(GraphicsStateGuardianBase *gsg) const;
+  virtual void draw(GraphicsStateGuardianBase *gsg,
+                    const GeomPrimitivePipelineReader *reader) const;
 
 protected:
   virtual CPT(GeomVertexArrayData) rotate_impl() const;

+ 2 - 2
panda/src/gobj/geomLinestrips.cxx

@@ -117,8 +117,8 @@ get_min_num_vertices_per_primitive() const {
 //               primitive.
 ////////////////////////////////////////////////////////////////////
 void GeomLinestrips::
-draw(GraphicsStateGuardianBase *gsg) const {
-  gsg->draw_linestrips(this);
+draw(GraphicsStateGuardianBase *gsg, const GeomPrimitivePipelineReader *reader) const {
+  gsg->draw_linestrips(reader);
 }
 
 ////////////////////////////////////////////////////////////////////

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

@@ -39,7 +39,8 @@ public:
   virtual int get_min_num_vertices_per_primitive() const;
 
 public:
-  virtual void draw(GraphicsStateGuardianBase *gsg) const;
+  virtual void draw(GraphicsStateGuardianBase *gsg,
+                    const GeomPrimitivePipelineReader *reader) const;
 
 protected:
   virtual CPT(GeomPrimitive) decompose_impl() const;

+ 2 - 2
panda/src/gobj/geomPoints.cxx

@@ -134,8 +134,8 @@ get_min_num_vertices_per_primitive() const {
 //               primitive.
 ////////////////////////////////////////////////////////////////////
 void GeomPoints::
-draw(GraphicsStateGuardianBase *gsg) const {
-  gsg->draw_points(this);
+draw(GraphicsStateGuardianBase *gsg, const GeomPrimitivePipelineReader *reader) const {
+  gsg->draw_points(reader);
 }
 
 ////////////////////////////////////////////////////////////////////

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

@@ -41,7 +41,8 @@ public:
   virtual int get_min_num_vertices_per_primitive() const;
 
 public:
-  virtual void draw(GraphicsStateGuardianBase *gsg) const;
+  virtual void draw(GraphicsStateGuardianBase *gsg,
+                    const GeomPrimitivePipelineReader *reader) const;
 
 public:
   static void register_with_read_factory();

+ 281 - 42
panda/src/gobj/geomPrimitive.I

@@ -111,8 +111,23 @@ is_composite() const {
 ////////////////////////////////////////////////////////////////////
 INLINE bool GeomPrimitive::
 is_indexed() const {
-  CDReader cdata(_cycler);
-  return (cdata->_vertices != (GeomVertexArrayData *)NULL);
+  GeomPrimitivePipelineReader reader(this, Thread::get_current_pipeline_stage());
+  return reader.is_indexed();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitive::get_first_vertex
+//       Access: Published
+//  Description: Returns the first vertex number referenced by the
+//               primitive.  This is particularly important in the
+//               case of a nonindexed primitive, in which case
+//               get_first_vertex() and get_num_vertices() completely
+//               define the extent of the vertex range.
+////////////////////////////////////////////////////////////////////
+INLINE int GeomPrimitive::
+get_first_vertex() const {
+  GeomPrimitivePipelineReader reader(this, Thread::get_current_pipeline_stage());
+  return reader.get_first_vertex();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -123,12 +138,32 @@ is_indexed() const {
 ////////////////////////////////////////////////////////////////////
 INLINE int GeomPrimitive::
 get_num_vertices() const {
-  CDReader cdata(_cycler);
-  if (cdata->_vertices == (GeomVertexArrayData *)NULL) {
-    return cdata->_num_vertices;
-  } else {
-    return cdata->_vertices->get_num_rows();
-  }
+  GeomPrimitivePipelineReader reader(this, Thread::get_current_pipeline_stage());
+  return reader.get_num_vertices();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitive::get_vertex
+//       Access: Published
+//  Description: Returns the ith vertex index in the table.
+////////////////////////////////////////////////////////////////////
+INLINE int GeomPrimitive::
+get_vertex(int i) const {
+  GeomPrimitivePipelineReader reader(this, Thread::get_current_pipeline_stage());
+  return reader.get_vertex(i);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitive::get_num_primitives
+//       Access: Published
+//  Description: Returns the number of individual primitives stored
+//               within this object.  All primitives are the same
+//               type.
+////////////////////////////////////////////////////////////////////
+INLINE int GeomPrimitive::
+get_num_primitives() const {
+  GeomPrimitivePipelineReader reader(this, Thread::get_current_pipeline_stage());
+  return reader.get_num_primitives();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -179,14 +214,9 @@ get_primitive_num_faces(int n) const {
 ////////////////////////////////////////////////////////////////////
 INLINE int GeomPrimitive::
 get_min_vertex() const {
-  CDReader cdata(_cycler);
-  if (!cdata->_got_minmax) {
-    CDWriter cdataw(((GeomPrimitive *)this)->_cycler, cdata, false);
-    ((GeomPrimitive *)this)->recompute_minmax(cdataw);
-    return cdataw->_min_vertex;
-  }
-  
-  return cdata->_min_vertex;
+  GeomPrimitivePipelineReader reader(this, Thread::get_current_pipeline_stage());
+  reader.check_minmax();
+  return reader.get_min_vertex();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -197,13 +227,9 @@ get_min_vertex() const {
 ////////////////////////////////////////////////////////////////////
 INLINE int GeomPrimitive::
 get_max_vertex() const {
-  CDReader cdata(_cycler);
-  if (!cdata->_got_minmax) {
-    CDWriter cdataw(((GeomPrimitive *)this)->_cycler, cdata, false);
-    ((GeomPrimitive *)this)->recompute_minmax(cdataw);
-    return cdataw->_max_vertex;
-  }
-  return cdata->_max_vertex;
+  GeomPrimitivePipelineReader reader(this, Thread::get_current_pipeline_stage());
+  reader.check_minmax();
+  return reader.get_max_vertex();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -231,6 +257,24 @@ get_modified() const {
   return cdata->_modified;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitive::check_valid
+//       Access: Published
+//  Description: Verifies that the primitive only references vertices
+//               that actually exist within the indicated
+//               GeomVertexData.  Returns true if the primitive
+//               appears to be valid, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool GeomPrimitive::
+check_valid(const GeomVertexData *vertex_data) const {
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  GeomPrimitivePipelineReader reader(this, pipeline_stage);
+  reader.check_minmax();
+  GeomVertexDataPipelineReader data_reader(vertex_data, pipeline_stage);
+  data_reader.check_array_readers();
+  return reader.check_valid(&data_reader);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitive::get_vertices
 //       Access: Public
@@ -255,8 +299,8 @@ get_vertices() const {
 ////////////////////////////////////////////////////////////////////
 INLINE int GeomPrimitive::
 get_index_stride() const {
-  nassertr(is_indexed(), 0);
-  return get_vertices()->get_array_format()->get_stride();
+  GeomPrimitivePipelineReader reader(this, Thread::get_current_pipeline_stage());
+  return reader.get_index_stride();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -302,14 +346,9 @@ get_ends() const {
 ////////////////////////////////////////////////////////////////////
 INLINE const GeomVertexArrayData *GeomPrimitive::
 get_mins() const {
-  nassertr(is_indexed(), NULL);
-  CDReader cdata(_cycler);
-  if (!cdata->_got_minmax) {
-    CDWriter cdataw(((GeomPrimitive *)this)->_cycler, cdata, false);
-    ((GeomPrimitive *)this)->recompute_minmax(cdataw);
-    return cdataw->_mins;
-  }
-  return cdata->_mins;
+  GeomPrimitivePipelineReader reader(this, Thread::get_current_pipeline_stage());
+  reader.check_minmax();
+  return reader.get_mins();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -325,14 +364,9 @@ get_mins() const {
 ////////////////////////////////////////////////////////////////////
 INLINE const GeomVertexArrayData *GeomPrimitive::
 get_maxs() const {
-  nassertr(is_indexed(), NULL);
-  CDReader cdata(_cycler);
-  if (!cdata->_got_minmax) {
-    CDWriter cdataw(((GeomPrimitive *)this)->_cycler, cdata, false);
-    ((GeomPrimitive *)this)->recompute_minmax(cdataw);
-    return cdataw->_maxs;
-  }
-  return cdata->_maxs;
+  GeomPrimitivePipelineReader reader(this, Thread::get_current_pipeline_stage());
+  reader.check_minmax();
+  return reader.get_maxs();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -381,7 +415,7 @@ INLINE CPT(GeomVertexArrayFormat) GeomPrimitive::
 get_index_format() const {
   return GeomVertexArrayFormat::register_format
     (new GeomVertexArrayFormat(InternalName::get_index(), 1, 
-                                 get_index_type(), C_index));
+                               get_index_type(), C_index));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -435,6 +469,211 @@ CData(const GeomPrimitive::CData &copy) :
 {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::Copy Constructor
+//       Access: Private
+//  Description: Don't attempt to copy these objects.
+////////////////////////////////////////////////////////////////////
+INLINE GeomPrimitivePipelineReader::
+GeomPrimitivePipelineReader(const GeomPrimitivePipelineReader &) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::Copy Assignment Operator
+//       Access: Private
+//  Description: Don't attempt to copy these objects.
+////////////////////////////////////////////////////////////////////
+INLINE void GeomPrimitivePipelineReader::
+operator = (const GeomPrimitivePipelineReader &) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::get_object
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE const GeomPrimitive *GeomPrimitivePipelineReader::
+get_object() const {
+  return _object;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::get_pipeline_stage
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE int GeomPrimitivePipelineReader::
+get_pipeline_stage() const {
+  return _pipeline_stage;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::check_minmax
+//       Access: Public
+//  Description: Ensures that the primitive's minmax cache has been
+//               computed.
+////////////////////////////////////////////////////////////////////
+INLINE void GeomPrimitivePipelineReader::
+check_minmax() const {
+  if (!_cdata->_got_minmax) {
+    ((GeomPrimitive *)_object)->recompute_minmax((GeomPrimitive::CData *)_cdata);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::get_shade_model
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE GeomPrimitivePipelineReader::ShadeModel GeomPrimitivePipelineReader::
+get_shade_model() const {
+  return _cdata->_shade_model;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::get_usage_hint
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE GeomPrimitivePipelineReader::UsageHint GeomPrimitivePipelineReader::
+get_usage_hint() const {
+  return _cdata->_usage_hint;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::get_index_type
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE GeomPrimitivePipelineReader::NumericType GeomPrimitivePipelineReader::
+get_index_type() const {
+  return _cdata->_index_type;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::is_indexed
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool GeomPrimitivePipelineReader::
+is_indexed() const {
+  return (_cdata->_vertices != (GeomVertexArrayData *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::get_num_vertices
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE int GeomPrimitivePipelineReader::
+get_num_vertices() const {
+  if (_cdata->_vertices == (GeomVertexArrayData *)NULL) {
+    return _cdata->_num_vertices;
+  } else {
+    return _vertices_reader->get_num_rows();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::get_min_vertex
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE int GeomPrimitivePipelineReader::
+get_min_vertex() const {
+  nassertr(_cdata->_got_minmax, 0);
+  return _cdata->_min_vertex;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::get_max_vertex
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE int GeomPrimitivePipelineReader::
+get_max_vertex() const {
+  nassertr(_cdata->_got_minmax, 0);
+  return _cdata->_max_vertex;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::get_data_size_bytes
+//       Access: Published
+//  Description: Returns the number of bytes stored in the vertices
+//               array.
+////////////////////////////////////////////////////////////////////
+INLINE int GeomPrimitivePipelineReader::
+get_data_size_bytes() const {
+  return _vertices_reader->get_data_size_bytes();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::get_modified
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE UpdateSeq GeomPrimitivePipelineReader::
+get_modified() const {
+  return _cdata->_modified;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::get_index_stride
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE int GeomPrimitivePipelineReader::
+get_index_stride() const {
+  nassertr(is_indexed(), 0);
+  return _cdata->_vertices->get_array_format()->get_stride();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::get_data
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CPTA_uchar GeomPrimitivePipelineReader::
+get_data() const {
+  return _vertices_reader->get_data();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::get_ends
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CPTA_int GeomPrimitivePipelineReader::
+get_ends() const {
+  return _cdata->_ends;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::get_mins
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE const GeomVertexArrayData *GeomPrimitivePipelineReader::
+get_mins() const {
+  nassertr(is_indexed(), NULL);
+  nassertr(_cdata->_got_minmax, NULL);
+  return _cdata->_mins;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::get_maxs
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE const GeomVertexArrayData *GeomPrimitivePipelineReader::
+get_maxs() const {
+  nassertr(is_indexed(), NULL);
+  nassertr(_cdata->_got_minmax, NULL);
+  return _cdata->_maxs;
+}
+
 INLINE ostream &
 operator << (ostream &out, const GeomPrimitive &obj) {
   obj.output(out);

+ 118 - 84
panda/src/gobj/geomPrimitive.cxx

@@ -170,51 +170,6 @@ set_index_type(GeomPrimitive::NumericType index_type) {
   }
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomPrimitive::get_first_vertex
-//       Access: Published
-//  Description: Returns the first vertex number referenced by the
-//               primitive.  This is particularly important in the
-//               case of a nonindexed primitive, in which case
-//               get_first_vertex() and get_num_vertices() completely
-//               define the extent of the vertex range.
-////////////////////////////////////////////////////////////////////
-int GeomPrimitive::
-get_first_vertex() const {
-  CDReader cdata(_cycler);
-  if (cdata->_vertices == (GeomVertexArrayData *)NULL) {
-    return cdata->_first_vertex;
-  } else if (cdata->_vertices->get_num_rows() == 0) {
-    return 0;
-  } else {
-    GeomVertexReader index(cdata->_vertices, 0);
-    return index.get_data1i();
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomPrimitive::get_vertex
-//       Access: Published
-//  Description: Returns the ith vertex index in the table.
-////////////////////////////////////////////////////////////////////
-int GeomPrimitive::
-get_vertex(int i) const {
-  CDReader cdata(_cycler);
-
-  if (cdata->_vertices != (GeomVertexArrayData *)NULL) {
-    // The indexed case.
-    nassertr(i >= 0 && i < (int)cdata->_vertices->get_num_rows(), -1);
-
-    GeomVertexReader index(cdata->_vertices, 0);
-    index.set_row(i);
-    return index.get_data1i();
-
-  } else {
-    // The nonindexed case.
-    return cdata->_first_vertex + i;
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitive::add_vertex
 //       Access: Published
@@ -545,30 +500,6 @@ make_indexed() {
   do_make_indexed(cdata);
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomPrimitive::get_num_primitives
-//       Access: Published
-//  Description: Returns the number of individual primitives stored
-//               within this object.  All primitives are the same
-//               type.
-////////////////////////////////////////////////////////////////////
-int GeomPrimitive::
-get_num_primitives() const {
-  int num_vertices_per_primitive = get_num_vertices_per_primitive();
-
-  CDReader cdata(_cycler);
-  if (num_vertices_per_primitive == 0) {
-    // This is a complex primitive type like a triangle strip: each
-    // primitive uses a different number of vertices.
-    return cdata->_ends.size();
-
-  } else {
-    // This is a simple primitive type like a triangle: each primitive
-    // uses the same number of vertices.
-    return (get_num_vertices() / num_vertices_per_primitive);
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitive::get_primitive_start
 //       Access: Published
@@ -833,20 +764,6 @@ get_num_bytes() const {
   return num_bytes;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomPrimitive::check_valid
-//       Access: Published
-//  Description: Verifies that the primitive only references vertices
-//               that actually exist within the indicated
-//               GeomVertexData.  Returns true if the primitive
-//               appears to be valid, false otherwise.
-////////////////////////////////////////////////////////////////////
-bool GeomPrimitive::
-check_valid(const GeomVertexData *vertex_data) const {
-  return get_num_vertices() == 0 ||
-    get_max_vertex() < vertex_data->get_num_rows();
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitive::output
 //       Access: Published, Virtual
@@ -969,6 +886,8 @@ set_nonindexed_vertices(int first_vertex, int num_vertices) {
 
   cdata->_modified = Geom::get_next_modified();
   cdata->_got_minmax = false;
+
+  // Force the minmax to be recomputed.
   recompute_minmax(cdata);
 }
 
@@ -1357,7 +1276,7 @@ append_unused_vertices(GeomVertexArrayData *, int) {
 //               necessary.
 ////////////////////////////////////////////////////////////////////
 void GeomPrimitive::
-recompute_minmax(CData *cdata) {
+recompute_minmax(GeomPrimitive::CData *cdata) {
   if (cdata->_vertices == (GeomVertexArrayData *)NULL) {
     // In the nonindexed case, we don't need to do much (the
     // minmax is trivial).
@@ -1563,3 +1482,118 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   _modified = Geom::get_next_modified();
   _got_minmax = false;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+GeomPrimitivePipelineReader::
+GeomPrimitivePipelineReader(const GeomPrimitive *object, 
+                            int pipeline_stage) :
+  _object(object),
+  _pipeline_stage(pipeline_stage),
+  _cdata(object->_cycler.read_stage(pipeline_stage)),
+  _vertices_reader(NULL)
+{
+  nassertv(_object->test_ref_count_nonzero());
+#ifdef DO_PIPELINING
+  nassertv(_cdata->test_ref_count_nonzero());
+#endif  // DO_PIPELINING
+  if (_cdata->_vertices != (GeomVertexArrayData *)NULL) {
+    _vertices_reader =
+      new GeomVertexArrayDataPipelineReader(_cdata->_vertices, _pipeline_stage);
+    nassertv(_vertices_reader->get_object() == _cdata->_vertices);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+GeomPrimitivePipelineReader::
+~GeomPrimitivePipelineReader() {
+  if (_vertices_reader != (GeomVertexArrayDataPipelineReader *)NULL) {
+    nassertv(_vertices_reader->get_object() == _cdata->_vertices);
+    delete _vertices_reader;
+    _vertices_reader = NULL;
+  }
+  nassertv(_object->test_ref_count_nonzero());
+#ifdef DO_PIPELINING
+  nassertv(_cdata->test_ref_count_nonzero());
+#endif  // DO_PIPELINING
+  _object->_cycler.release_read_stage(_pipeline_stage, _cdata);
+  _object = NULL;
+  _cdata = NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::get_first_vertex
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int GeomPrimitivePipelineReader::
+get_first_vertex() const {
+  if (_cdata->_vertices == (GeomVertexArrayData *)NULL) {
+    return _cdata->_first_vertex;
+  } else if (_vertices_reader->get_num_rows() == 0) {
+    return 0;
+  } else {
+    GeomVertexReader index(_cdata->_vertices, 0);
+    return index.get_data1i();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::get_vertex
+//       Access: Public
+//  Description: Returns the ith vertex index in the table.
+////////////////////////////////////////////////////////////////////
+int GeomPrimitivePipelineReader::
+get_vertex(int i) const {
+  if (_cdata->_vertices != (GeomVertexArrayData *)NULL) {
+    // The indexed case.
+    nassertr(i >= 0 && i < _vertices_reader->get_num_rows(), -1);
+
+    GeomVertexReader index(_cdata->_vertices, 0);
+    index.set_row(i);
+    return index.get_data1i();
+
+  } else {
+    // The nonindexed case.
+    return _cdata->_first_vertex + i;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::get_num_primitives
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int GeomPrimitivePipelineReader::
+get_num_primitives() const {
+  int num_vertices_per_primitive = _object->get_num_vertices_per_primitive();
+
+  if (num_vertices_per_primitive == 0) {
+    // This is a complex primitive type like a triangle strip: each
+    // primitive uses a different number of vertices.
+    return _cdata->_ends.size();
+
+  } else {
+    // This is a simple primitive type like a triangle: each primitive
+    // uses the same number of vertices.
+    return (get_num_vertices() / num_vertices_per_primitive);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::check_valid
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+bool GeomPrimitivePipelineReader::
+check_valid(const GeomVertexDataPipelineReader *data_reader) const {
+  return get_num_vertices() == 0 ||
+    get_max_vertex() < data_reader->get_num_rows();
+}

+ 67 - 9
panda/src/gobj/geomPrimitive.h

@@ -22,6 +22,7 @@
 #include "pandabase.h"
 #include "geomEnums.h"
 #include "geomVertexArrayData.h"
+#include "geomVertexData.h"
 #include "typedWritableReferenceCount.h"
 #include "luse.h"
 #include "updateSeq.h"
@@ -31,14 +32,16 @@
 #include "cycleData.h"
 #include "cycleDataReader.h"
 #include "cycleDataWriter.h"
+#include "cycleDataStageReader.h"
+#include "cycleDataStageWriter.h"
 #include "pipelineCycler.h"
 #include "deletedChain.h"
 
-class GeomVertexData;
 class PreparedGraphicsObjects;
 class IndexBufferContext;
 class GraphicsStateGuardianBase;
 class FactoryParams;
+class GeomPrimitivePipelineReader;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : GeomPrimitive
@@ -95,9 +98,9 @@ PUBLISHED:
 
   INLINE bool is_composite() const;
   INLINE bool is_indexed() const;
-  int get_first_vertex() const;
+  INLINE int get_first_vertex() const;
   INLINE int get_num_vertices() const;
-  int get_vertex(int i) const;
+  INLINE int get_vertex(int i) const;
   void add_vertex(int vertex);
   INLINE void add_vertices(int v1, int v2);
   INLINE void add_vertices(int v1, int v2, int v3);
@@ -111,7 +114,7 @@ PUBLISHED:
   void pack_vertices(GeomVertexData *dest, const GeomVertexData *source);
   void make_indexed();
 
-  int get_num_primitives() const;
+  INLINE int get_num_primitives() const;
   int get_primitive_start(int n) const;
   int get_primitive_end(int n) const;
   int get_primitive_num_vertices(int n) const;
@@ -132,7 +135,7 @@ PUBLISHED:
   INLINE int get_data_size_bytes() const;
   INLINE UpdateSeq get_modified() const;
 
-  bool check_valid(const GeomVertexData *vertex_data) const;
+  INLINE bool check_valid(const GeomVertexData *vertex_data) const;
 
   virtual void output(ostream &out) const;
   virtual void write(ostream &out, int indent_level) const;
@@ -183,7 +186,8 @@ private:
   void clear_prepared(PreparedGraphicsObjects *prepared_objects);
 
 public:
-  virtual void draw(GraphicsStateGuardianBase *gsg) const=0;
+  virtual void draw(GraphicsStateGuardianBase *gsg,
+                    const GeomPrimitivePipelineReader *reader) const=0;
 
   void calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
                          bool &found_any, 
@@ -218,6 +222,7 @@ private:
     INLINE CData();
     INLINE CData(const CData &copy);
     ALLOC_DELETED_CHAIN(CData);
+    
     virtual CycleData *make_copy() const;
     virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
     virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
@@ -225,7 +230,7 @@ private:
     virtual TypeHandle get_parent_type() const {
       return GeomPrimitive::get_class_type();
     }
-
+    
     ShadeModel _shade_model;
     int _first_vertex;
     int _num_vertices;
@@ -236,16 +241,21 @@ private:
     PT(GeomVertexArrayData) _mins;
     PT(GeomVertexArrayData) _maxs;
     UpdateSeq _modified;
-
+    
     bool _got_minmax;
     unsigned int _min_vertex;
     unsigned int _max_vertex;
+    
+    friend class GeomPrimitive;
   };
 
   PipelineCycler<CData> _cycler;
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataWriter<CData> CDWriter;
-
+  typedef CycleDataStageReader<CData> CDStageReader;
+  typedef CycleDataStageWriter<CData> CDStageWriter;
+  
+private:
   static PStatCollector _decompose_pcollector;
   static PStatCollector _rotate_pcollector;
 
@@ -276,6 +286,54 @@ private:
 
   friend class Geom;
   friend class PreparedGraphicsObjects;
+  friend class GeomPrimitivePipelineReader;
+};
+
+////////////////////////////////////////////////////////////////////
+//       Class : GeomPrimitivePipelineReader
+// Description : Encapsulates the data from a GeomPrimitive,
+//               pre-fetched for one stage of the pipeline.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA GeomPrimitivePipelineReader : public GeomEnums {
+public:
+  GeomPrimitivePipelineReader(const GeomPrimitive *object, int pipeline_stage);
+private:
+  INLINE GeomPrimitivePipelineReader(const GeomPrimitivePipelineReader &copy);
+  INLINE void operator = (const GeomPrimitivePipelineReader &copy);
+
+public:
+  ~GeomPrimitivePipelineReader();
+  ALLOC_DELETED_CHAIN(GeomPrimitivePipelineReader);
+
+  INLINE const GeomPrimitive *get_object() const;
+  INLINE int get_pipeline_stage() const;
+
+  INLINE void check_minmax() const;
+
+  INLINE ShadeModel get_shade_model() const;
+  INLINE UsageHint get_usage_hint() const;
+  INLINE NumericType get_index_type() const;
+  INLINE bool is_indexed() const;
+  int get_first_vertex() const;
+  INLINE int get_num_vertices() const;
+  int get_vertex(int i) const;
+  int get_num_primitives() const;
+  INLINE int get_min_vertex() const;
+  INLINE int get_max_vertex() const;
+  INLINE int get_data_size_bytes() const;
+  INLINE UpdateSeq get_modified() const;
+  bool check_valid(const GeomVertexDataPipelineReader *data_reader) const;
+  INLINE int get_index_stride() const;
+  INLINE CPTA_uchar get_data() const;
+  INLINE CPTA_int get_ends() const;
+  INLINE const GeomVertexArrayData *get_mins() const;
+  INLINE const GeomVertexArrayData *get_maxs() const;
+  
+private:
+  const GeomPrimitive *_object;
+  int _pipeline_stage;
+  const GeomPrimitive::CData *_cdata;
+  GeomVertexArrayDataPipelineReader *_vertices_reader;
 };
 
 INLINE ostream &operator << (ostream &out, const GeomPrimitive &obj);

+ 2 - 2
panda/src/gobj/geomTriangles.cxx

@@ -105,8 +105,8 @@ get_num_vertices_per_primitive() const {
 //               primitive.
 ////////////////////////////////////////////////////////////////////
 void GeomTriangles::
-draw(GraphicsStateGuardianBase *gsg) const {
-  gsg->draw_triangles(this);
+draw(GraphicsStateGuardianBase *gsg, const GeomPrimitivePipelineReader *reader) const {
+  gsg->draw_triangles(reader);
 }
 
 ////////////////////////////////////////////////////////////////////

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

@@ -39,7 +39,8 @@ public:
   virtual int get_num_vertices_per_primitive() const;
 
 public:
-  virtual void draw(GraphicsStateGuardianBase *gsg) const;
+  virtual void draw(GraphicsStateGuardianBase *gsg,
+                    const GeomPrimitivePipelineReader *reader) const;
 
 protected:
   virtual CPT(GeomVertexArrayData) rotate_impl() const;

+ 2 - 2
panda/src/gobj/geomTrifans.cxx

@@ -105,8 +105,8 @@ get_geom_rendering() const {
 //               primitive.
 ////////////////////////////////////////////////////////////////////
 void GeomTrifans::
-draw(GraphicsStateGuardianBase *gsg) const {
-  gsg->draw_trifans(this);
+draw(GraphicsStateGuardianBase *gsg, const GeomPrimitivePipelineReader *reader) const {
+  gsg->draw_trifans(reader);
 }
 
 ////////////////////////////////////////////////////////////////////

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

@@ -38,7 +38,8 @@ public:
   virtual int get_geom_rendering() const;
 
 public:
-  virtual void draw(GraphicsStateGuardianBase *gsg) const;
+  virtual void draw(GraphicsStateGuardianBase *gsg,
+                    const GeomPrimitivePipelineReader *reader) const;
 
 protected:
   virtual CPT(GeomPrimitive) decompose_impl() const;

+ 2 - 2
panda/src/gobj/geomTristrips.cxx

@@ -120,8 +120,8 @@ get_num_unused_vertices_per_primitive() const {
 //               primitive.
 ////////////////////////////////////////////////////////////////////
 void GeomTristrips::
-draw(GraphicsStateGuardianBase *gsg) const {
-  gsg->draw_tristrips(this);
+draw(GraphicsStateGuardianBase *gsg, const GeomPrimitivePipelineReader *reader) const {
+  gsg->draw_tristrips(reader);
 }
 
 ////////////////////////////////////////////////////////////////////

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

@@ -39,7 +39,8 @@ public:
   virtual int get_num_unused_vertices_per_primitive() const;
 
 public:
-  virtual void draw(GraphicsStateGuardianBase *gsg) const;
+  virtual void draw(GraphicsStateGuardianBase *gsg,
+                    const GeomPrimitivePipelineReader *reader) const;
 
 protected:
   virtual CPT(GeomPrimitive) decompose_impl() const;

+ 278 - 1
panda/src/gobj/geomVertexArrayData.I

@@ -62,7 +62,35 @@ has_column(const InternalName *name) const {
 ////////////////////////////////////////////////////////////////////
 INLINE int GeomVertexArrayData::
 get_num_rows() const {
-  return get_data_size_bytes() / _array_format->get_stride();
+  GeomVertexArrayDataPipelineReader reader(this, Thread::get_current_pipeline_stage());
+  return reader.get_num_rows();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayData::set_num_rows
+//       Access: Published
+//  Description: Sets the length of the array to n rows.
+//               Normally, you would not call this directly, since all
+//               of the arrays in a particular GeomVertexData must
+//               have the same number of rows; instead, call
+//               GeomVertexData::set_num_rows().
+//
+//               The return value is true if the number of rows
+//               was changed, false if the object already contained n
+//               rows (or if there was some error).
+//
+//               The new vertex data is initialized to 0, including
+//               the "color" column (but see
+//               GeomVertexData::set_num_rows()).
+//
+//               Don't call this in a downstream thread unless you
+//               don't mind it blowing away other changes you might
+//               have recently made in an upstream thread.
+////////////////////////////////////////////////////////////////////
+INLINE bool GeomVertexArrayData::
+set_num_rows(int n) {
+  GeomVertexArrayDataPipelineWriter writer(this, Thread::get_current_pipeline_stage(), true);
+  return writer.set_num_rows(n);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -113,6 +141,39 @@ get_data() const {
   return cdata->_data;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayData::modify_data
+//       Access: Published
+//  Description: Returns a modifiable pointer to the actual vertex
+//               array, so that application code may directly
+//               manipulate it.  Use with caution.
+//
+//               Don't call this in a downstream thread unless you
+//               don't mind it blowing away other changes you might
+//               have recently made in an upstream thread.
+////////////////////////////////////////////////////////////////////
+INLINE PTA_uchar GeomVertexArrayData::
+modify_data() {
+  GeomVertexArrayDataPipelineWriter writer(this, Thread::get_current_pipeline_stage(), true);
+  return writer.modify_data();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayData::set_data
+//       Access: Published
+//  Description: Replaces the vertex data array with a completely new
+//               array.
+//
+//               Don't call this in a downstream thread unless you
+//               don't mind it blowing away other changes you might
+//               have recently made in an upstream thread.
+////////////////////////////////////////////////////////////////////
+INLINE void GeomVertexArrayData::
+set_data(CPTA_uchar array) {
+  GeomVertexArrayDataPipelineWriter writer(this, Thread::get_current_pipeline_stage(), true);
+  writer.set_data(array);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexArrayData::CData::Constructor
 //       Access: Public
@@ -137,6 +198,222 @@ CData(const GeomVertexArrayData::CData &copy) :
 {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayDataPipelineBase::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexArrayDataPipelineBase::
+GeomVertexArrayDataPipelineBase(GeomVertexArrayData *object, 
+                                int pipeline_stage,
+                                GeomVertexArrayData::CData *cdata) :
+  _object(object),
+  _pipeline_stage(pipeline_stage),
+  _cdata(cdata)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayDataPipelineBase::get_pipeline_stage
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE int GeomVertexArrayDataPipelineBase::
+get_pipeline_stage() const {
+  return _pipeline_stage;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayDataPipelineBase::get_array_format
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE const GeomVertexArrayFormat *GeomVertexArrayDataPipelineBase::
+get_array_format() const {
+  return _object->_array_format;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayDataPipelineBase::get_usage_hint
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexArrayDataPipelineBase::UsageHint GeomVertexArrayDataPipelineBase::
+get_usage_hint() const {
+  return _cdata->_usage_hint;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayDataPipelineBase::get_data
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE CPTA_uchar GeomVertexArrayDataPipelineBase::
+get_data() const {
+  return _cdata->_data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayDataPipelineBase::get_num_rows
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE int GeomVertexArrayDataPipelineBase::
+get_num_rows() const {
+  return get_data_size_bytes() / _object->_array_format->get_stride();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayDataPipelineBase::get_data_size_bytes
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE int GeomVertexArrayDataPipelineBase::
+get_data_size_bytes() const {
+  return _cdata->_data.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayDataPipelineBase::get_modified
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE UpdateSeq GeomVertexArrayDataPipelineBase::
+get_modified() const {
+  return _cdata->_modified;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayDataPipelineReader::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexArrayDataPipelineReader::
+GeomVertexArrayDataPipelineReader(const GeomVertexArrayData *object, 
+                                  int pipeline_stage) :
+  GeomVertexArrayDataPipelineBase((GeomVertexArrayData *)object,
+                                  pipeline_stage,
+                                  (GeomVertexArrayData::CData *)object->_cycler.read_stage(pipeline_stage))
+{
+  nassertv(_object->test_ref_count_nonzero());
+#ifdef DO_PIPELINING
+  nassertv(_cdata->test_ref_count_nonzero());
+#endif  // DO_PIPELINING
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayDataPipelineReader::Copy Constructor
+//       Access: Private
+//  Description: Don't attempt to copy these objects.
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexArrayDataPipelineReader::
+GeomVertexArrayDataPipelineReader(const GeomVertexArrayDataPipelineReader &copy) :
+  GeomVertexArrayDataPipelineBase(copy)
+{
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayDataPipelineReader::Copy Assignment Operator
+//       Access: Private
+//  Description: Don't attempt to copy these objects.
+////////////////////////////////////////////////////////////////////
+INLINE void GeomVertexArrayDataPipelineReader::
+operator = (const GeomVertexArrayDataPipelineReader &) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayDataPipelineReader::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexArrayDataPipelineReader::
+~GeomVertexArrayDataPipelineReader() {
+  nassertv(_object->test_ref_count_nonzero());
+#ifdef DO_PIPELINING
+  nassertv(_cdata->test_ref_count_nonzero());
+#endif  // DO_PIPELINING
+  _object->_cycler.release_read_stage(_pipeline_stage, _cdata);
+  _object = NULL;
+  _cdata = NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayDataPipelineReader::get_object
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE const GeomVertexArrayData *GeomVertexArrayDataPipelineReader::
+get_object() const {
+  return _object;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayDataPipelineWriter::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexArrayDataPipelineWriter::
+GeomVertexArrayDataPipelineWriter(GeomVertexArrayData *object, 
+                                  int pipeline_stage, bool force_to_0) :
+  GeomVertexArrayDataPipelineBase(object, pipeline_stage,
+                                  object->_cycler.write_stage_upstream(pipeline_stage, force_to_0))
+{
+  nassertv(_object->test_ref_count_nonzero());
+#ifdef DO_PIPELINING
+  nassertv(_cdata->test_ref_count_nonzero());
+#endif  // DO_PIPELINING
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayDataPipelineWriter::Copy Constructor
+//       Access: Private
+//  Description: Don't attempt to copy these objects.
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexArrayDataPipelineWriter::
+GeomVertexArrayDataPipelineWriter(const GeomVertexArrayDataPipelineWriter &copy) :
+  GeomVertexArrayDataPipelineBase(copy) 
+{
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayDataPipelineWriter::Copy Assignment Operator
+//       Access: Private
+//  Description: Don't attempt to copy these objects.
+////////////////////////////////////////////////////////////////////
+INLINE void GeomVertexArrayDataPipelineWriter::
+operator = (const GeomVertexArrayDataPipelineWriter &) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayDataPipelineWriter::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexArrayDataPipelineWriter::
+~GeomVertexArrayDataPipelineWriter() {
+  nassertv(_object->test_ref_count_nonzero());
+#ifdef DO_PIPELINING
+  nassertv(_cdata->test_ref_count_nonzero());
+#endif  // DO_PIPELINING
+  _object->_cycler.release_write_stage(_pipeline_stage, _cdata);
+  _object = NULL;
+  _cdata = NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayDataPipelineWriter::get_object
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexArrayData *GeomVertexArrayDataPipelineWriter::
+get_object() const {
+  return _object;
+}
+
 INLINE ostream &
 operator << (ostream &out, const GeomVertexArrayData &obj) {
   obj.output(out);

+ 72 - 105
panda/src/gobj/geomVertexArrayData.cxx

@@ -105,66 +105,6 @@ GeomVertexArrayData::
   release_all();
 }
 
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexArrayData::set_num_rows
-//       Access: Published
-//  Description: Sets the length of the array to n rows.
-//               Normally, you would not call this directly, since all
-//               of the arrays in a particular GeomVertexData must
-//               have the same number of rows; instead, call
-//               GeomVertexData::set_num_rows().
-//
-//               The return value is true if the number of rows
-//               was changed, false if the object already contained n
-//               rows (or if there was some error).
-//
-//               The new vertex data is initialized to 0, including
-//               the "color" column (but see
-//               GeomVertexData::set_num_rows()).
-//
-//               Don't call this in a downstream thread unless you
-//               don't mind it blowing away other changes you might
-//               have recently made in an upstream thread.
-////////////////////////////////////////////////////////////////////
-bool GeomVertexArrayData::
-set_num_rows(int n) {
-  CDWriter cdata(_cycler, true);
-
-  int stride = _array_format->get_stride();
-  int delta = n - (cdata->_data.size() / stride);
-  
-  if (delta != 0) {
-    if (cdata->_data.get_ref_count() > 1) {
-      // Copy-on-write: the data is already reffed somewhere else,
-      // so we're just going to make a copy.
-      PTA_uchar new_data;
-      new_data.reserve(n * stride);
-      new_data.insert(new_data.end(), n * stride, 0);
-      memcpy(new_data, cdata->_data, 
-             min((size_t)(n * stride), cdata->_data.size()));
-      cdata->_data = new_data;
-      
-    } else {
-      // We've got the only reference to the data, so we can change
-      // it directly.
-      if (delta > 0) {
-        cdata->_data.insert(cdata->_data.end(), delta * stride, 0);
-        
-      } else {
-        cdata->_data.erase(cdata->_data.begin() + n * stride, 
-                           cdata->_data.end());
-      }
-    }
-
-    cdata->_modified = Geom::get_next_modified();
-
-    return true;
-  }
-  
-  return false;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexArrayData::set_usage_hint
 //       Access: Published
@@ -202,51 +142,6 @@ write(ostream &out, int indent_level) const {
   _array_format->write_with_data(out, indent_level, this);
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexArrayData::modify_data
-//       Access: Published
-//  Description: Returns a modifiable pointer to the actual vertex
-//               array, so that application code may directly
-//               manipulate it.  Use with caution.
-//
-//               Don't call this in a downstream thread unless you
-//               don't mind it blowing away other changes you might
-//               have recently made in an upstream thread.
-////////////////////////////////////////////////////////////////////
-PTA_uchar GeomVertexArrayData::
-modify_data() {
-  // Perform copy-on-write: if the reference count on the vertex data
-  // is greater than 1, assume some other GeomVertexData has the same
-  // pointer, so make a copy of it first.
-  CDWriter cdata(_cycler, true);
-
-  if (cdata->_data.get_ref_count() > 1) {
-    PTA_uchar orig_data = cdata->_data;
-    cdata->_data = PTA_uchar();
-    cdata->_data.v() = orig_data.v();
-  }
-  cdata->_modified = Geom::get_next_modified();
-
-  return cdata->_data;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexArrayData::set_data
-//       Access: Published
-//  Description: Replaces the vertex data array with a completely new
-//               array.
-//
-//               Don't call this in a downstream thread unless you
-//               don't mind it blowing away other changes you might
-//               have recently made in an upstream thread.
-////////////////////////////////////////////////////////////////////
-void GeomVertexArrayData::
-set_data(CPTA_uchar array) {
-  CDWriter cdata(_cycler, true);
-  cdata->_data = (PTA_uchar &)array;
-  cdata->_modified = Geom::get_next_modified();
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexArrayData::prepare
 //       Access: Public
@@ -613,3 +508,75 @@ fillin(DatagramIterator &scan, BamReader *manager, void *extra_data) {
 
   _modified = Geom::get_next_modified();
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayDataPipelineWriter::set_num_rows
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool GeomVertexArrayDataPipelineWriter::
+set_num_rows(int n) {
+  int stride = _object->_array_format->get_stride();
+  int delta = n - (_cdata->_data.size() / stride);
+  
+  if (delta != 0) {
+    if (_cdata->_data.get_ref_count() > 1) {
+      // Copy-on-write: the data is already reffed somewhere else,
+      // so we're just going to make a copy.
+      PTA_uchar new_data;
+      new_data.reserve(n * stride);
+      new_data.insert(new_data.end(), n * stride, 0);
+      memcpy(new_data, _cdata->_data, 
+             min((size_t)(n * stride), _cdata->_data.size()));
+      _cdata->_data = new_data;
+      
+    } else {
+      // We've got the only reference to the data, so we can change
+      // it directly.
+      if (delta > 0) {
+        _cdata->_data.insert(_cdata->_data.end(), delta * stride, 0);
+        
+      } else {
+        _cdata->_data.erase(_cdata->_data.begin() + n * stride, 
+                           _cdata->_data.end());
+      }
+    }
+
+    _cdata->_modified = Geom::get_next_modified();
+
+    return true;
+  }
+  
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayDataPipelineWriter::modify_data
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+PTA_uchar GeomVertexArrayDataPipelineWriter::
+modify_data() {
+  // Perform copy-on-write: if the reference count on the vertex data
+  // is greater than 1, assume some other GeomVertexData has the same
+  // pointer, so make a copy of it first.
+  if (_cdata->_data.get_ref_count() > 1) {
+    PTA_uchar orig_data = _cdata->_data;
+    _cdata->_data = PTA_uchar();
+    _cdata->_data.v() = orig_data.v();
+  }
+  _cdata->_modified = Geom::get_next_modified();
+
+  return _cdata->_data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayDataPipelineWriter::set_data
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void GeomVertexArrayDataPipelineWriter::
+set_data(CPTA_uchar array) {
+  _cdata->_data = (PTA_uchar &)array;
+  _cdata->_modified = Geom::get_next_modified();
+}

+ 79 - 3
panda/src/gobj/geomVertexArrayData.h

@@ -76,7 +76,7 @@ PUBLISHED:
   INLINE bool has_column(const InternalName *name) const;
 
   INLINE int get_num_rows() const;
-  bool set_num_rows(int n);
+  INLINE bool set_num_rows(int n);
   INLINE void clear_rows();
 
   INLINE int get_data_size_bytes() const;
@@ -86,8 +86,8 @@ PUBLISHED:
   void write(ostream &out, int indent_level = 0) const;
 
   INLINE CPTA_uchar get_data() const;
-  PTA_uchar modify_data();
-  void set_data(CPTA_uchar data);
+  INLINE PTA_uchar modify_data();
+  INLINE void set_data(CPTA_uchar data);
 
 public:
   void prepare(PreparedGraphicsObjects *prepared_objects);
@@ -133,6 +133,8 @@ private:
     UsageHint _usage_hint;
     PTA_uchar _data;
     UpdateSeq _modified;
+
+    friend class GeomVertexArrayData;
   };
 
   PipelineCycler<CData> _cycler;
@@ -174,6 +176,80 @@ private:
   friend class GeomCacheManager;
   friend class GeomVertexData;
   friend class PreparedGraphicsObjects;
+  friend class GeomVertexArrayDataPipelineBase;
+  friend class GeomVertexArrayDataPipelineReader;
+  friend class GeomVertexArrayDataPipelineWriter;
+};
+
+////////////////////////////////////////////////////////////////////
+//       Class : GeomVertexArrayDataPipelineBase
+// Description : The common code from
+//               GeomVertexArrayDataPipelineReader and
+//               GeomVertexArrayDataPipelineWriter.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA GeomVertexArrayDataPipelineBase : public GeomEnums {
+protected:
+  INLINE GeomVertexArrayDataPipelineBase(GeomVertexArrayData *object, 
+                                         int pipeline_stage,
+                                         GeomVertexArrayData::CData *cdata);
+
+public:
+  INLINE int get_pipeline_stage() const;
+
+  INLINE const GeomVertexArrayFormat *get_array_format() const;
+
+  INLINE UsageHint get_usage_hint() const;
+  INLINE CPTA_uchar get_data() const;
+  INLINE int get_num_rows() const;
+  INLINE int get_data_size_bytes() const;
+  INLINE UpdateSeq get_modified() const;
+
+protected:
+  GeomVertexArrayData *_object;
+  int _pipeline_stage;
+  GeomVertexArrayData::CData *_cdata;
+};
+
+////////////////////////////////////////////////////////////////////
+//       Class : GeomVertexArrayDataPipelineReader
+// Description : Encapsulates the data from a GeomVertexArrayData,
+//               pre-fetched for one stage of the pipeline.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA GeomVertexArrayDataPipelineReader : public GeomVertexArrayDataPipelineBase {
+public:
+  INLINE GeomVertexArrayDataPipelineReader(const GeomVertexArrayData *object, int pipeline_stage);
+private:
+  INLINE GeomVertexArrayDataPipelineReader(const GeomVertexArrayDataPipelineReader &copy);
+  INLINE void operator = (const GeomVertexArrayDataPipelineReader &copy);
+
+public:
+  INLINE ~GeomVertexArrayDataPipelineReader();
+  ALLOC_DELETED_CHAIN(GeomVertexArrayDataPipelineReader);
+
+  INLINE const GeomVertexArrayData *get_object() const;
+};
+
+////////////////////////////////////////////////////////////////////
+//       Class : GeomVertexArrayDataPipelineWriter
+// Description : Encapsulates the data from a GeomVertexArrayData,
+//               pre-fetched for one stage of the pipeline.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA GeomVertexArrayDataPipelineWriter : public GeomVertexArrayDataPipelineBase {
+public:
+  INLINE GeomVertexArrayDataPipelineWriter(GeomVertexArrayData *object, int pipeline_stage, bool force_to_0);
+private:
+  INLINE GeomVertexArrayDataPipelineWriter(const GeomVertexArrayDataPipelineWriter &copy);
+  INLINE void operator = (const GeomVertexArrayDataPipelineWriter &copy);
+
+public:
+  INLINE ~GeomVertexArrayDataPipelineWriter();
+  ALLOC_DELETED_CHAIN(GeomVertexArrayDataPipelineWriter);
+
+  bool set_num_rows(int n);
+
+  INLINE GeomVertexArrayData *get_object() const;
+  PTA_uchar modify_data();
+  void set_data(CPTA_uchar data);
 };
 
 INLINE ostream &operator << (ostream &out, const GeomVertexArrayData &obj);

+ 412 - 65
panda/src/gobj/geomVertexData.I

@@ -29,17 +29,6 @@ get_name() const {
   return _name;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::get_format
-//       Access: Published
-//  Description: Returns a pointer to the GeomVertexFormat structure
-//               that defines this data.
-////////////////////////////////////////////////////////////////////
-INLINE const GeomVertexFormat *GeomVertexData::
-get_format() const {
-  return _format;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexData::get_usage_hint
 //       Access: Published
@@ -60,6 +49,18 @@ get_usage_hint() const {
   return cdata->_usage_hint;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexData::get_format
+//       Access: Published
+//  Description: Returns a pointer to the GeomVertexFormat structure
+//               that defines this data.
+////////////////////////////////////////////////////////////////////
+INLINE const GeomVertexFormat *GeomVertexData::
+get_format() const {
+  CDReader cdata(_cycler);
+  return cdata->_format;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexData::has_column
 //       Access: Published
@@ -69,7 +70,22 @@ get_usage_hint() const {
 ////////////////////////////////////////////////////////////////////
 INLINE bool GeomVertexData::
 has_column(const InternalName *name) const {
-  return _format->has_column(name);
+  CDReader cdata(_cycler);
+  return cdata->_format->has_column(name);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexData::get_num_rows
+//       Access: Published
+//  Description: Returns the number of rows stored within all the
+//               arrays.  All arrays store data for the same n
+//               rows.
+////////////////////////////////////////////////////////////////////
+INLINE int GeomVertexData::
+get_num_rows() const {
+  GeomVertexDataPipelineReader reader(this, Thread::get_current_pipeline_stage());
+  reader.check_array_readers();
+  return reader.get_num_rows();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -99,8 +115,9 @@ has_column(const InternalName *name) const {
 ////////////////////////////////////////////////////////////////////
 INLINE bool GeomVertexData::
 set_num_rows(int n) {
-  CDWriter cdata(_cycler, true);
-  return do_set_num_rows(n, cdata);
+  GeomVertexDataPipelineWriter writer(this, Thread::get_current_pipeline_stage(), true);
+  writer.check_array_writers();
+  return writer.set_num_rows(n);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -130,6 +147,26 @@ get_array(int i) const {
   return cdata->_arrays[i];
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexData::modify_array
+//       Access: Published
+//  Description: Returns a modifiable pointer to the indicated vertex
+//               array, so that application code may directly
+//               manipulate the data.  You should avoid changing
+//               the length of this array, since all of the arrays
+//               should be kept in sync--use set_num_rows()
+//               instead.
+//
+//               Don't call this in a downstream thread unless you
+//               don't mind it blowing away other changes you might
+//               have recently made in an upstream thread.
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexArrayData *GeomVertexData::
+modify_array(int i) {
+  GeomVertexDataPipelineWriter writer(this, Thread::get_current_pipeline_stage(), true);
+  return writer.modify_array(i);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexData::get_transform_table
 //       Access: Published
@@ -225,6 +262,18 @@ clear_slider_table() {
   set_slider_table(NULL);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexData::get_num_bytes
+//       Access: Published
+//  Description: Returns the total number of bytes consumed by the
+//               different arrays of the vertex data.
+////////////////////////////////////////////////////////////////////
+INLINE int GeomVertexData::
+get_num_bytes() const {
+  GeomVertexDataPipelineReader reader(this, Thread::get_current_pipeline_stage());
+  return reader.get_num_bytes();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexData::get_modified
 //       Access: Published
@@ -238,57 +287,6 @@ get_modified() const {
   return cdata->_modified;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::has_vertex
-//       Access: Public
-//  Description: Returns true if the data has a "vertex" column, false
-//               otherwise.
-////////////////////////////////////////////////////////////////////
-INLINE bool GeomVertexData::
-has_vertex() const {
-  return (_format->get_vertex_column() != (GeomVertexColumn *)NULL);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::is_vertex_transformed
-//       Access: Public
-//  Description: Returns true if the data has a "vertex" column and it
-//               is indicated as having been transformed into clip
-//               coordinates, false if there is no vertex column or if
-//               it contains ordinary 3-d pre-transformation points.
-////////////////////////////////////////////////////////////////////
-INLINE bool GeomVertexData::
-is_vertex_transformed() const {
-  const GeomVertexColumn *column = _format->get_vertex_column();
-  if (column != (GeomVertexColumn *)NULL) {
-    return column->get_contents() == C_clip_point;
-  }
-
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::has_normal
-//       Access: Public
-//  Description: Returns true if the data has a "normal" column, false
-//               otherwise.
-////////////////////////////////////////////////////////////////////
-INLINE bool GeomVertexData::
-has_normal() const {
-  return (_format->get_normal_column() != (GeomVertexColumn *)NULL);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::has_color
-//       Access: Public
-//  Description: Returns true if the data has a "color" column, false
-//               otherwise.
-////////////////////////////////////////////////////////////////////
-INLINE bool GeomVertexData::
-has_color() const {
-  return (_format->get_color_column() != (GeomVertexColumn *)NULL);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexData::pack_abcd
 //       Access: Public, Static
@@ -448,6 +446,7 @@ CData() :
 INLINE GeomVertexData::CData::
 CData(const GeomVertexData::CData &copy) :
   _usage_hint(copy._usage_hint),
+  _format(copy._format),
   _arrays(copy._arrays),
   _transform_table(copy._transform_table),
   _transform_blend_table(copy._transform_blend_table),
@@ -458,6 +457,354 @@ CData(const GeomVertexData::CData &copy) :
 {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineBase::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexDataPipelineBase::
+GeomVertexDataPipelineBase(GeomVertexData *object, 
+                             int pipeline_stage,
+                             GeomVertexData::CData *cdata) :
+  _object(object),
+  _pipeline_stage(pipeline_stage),
+  _cdata(cdata)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineBase::get_pipeline_stage
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE int GeomVertexDataPipelineBase::
+get_pipeline_stage() const {
+  return _pipeline_stage;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineBase::get_usage_hint
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexDataPipelineBase::UsageHint GeomVertexDataPipelineBase::
+get_usage_hint() const {
+  return _cdata->_usage_hint;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineBase::get_format
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE const GeomVertexFormat *GeomVertexDataPipelineBase::
+get_format() const {
+  return _cdata->_format;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineBase::has_column
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool GeomVertexDataPipelineBase::
+has_column(const InternalName *name) const {
+  return _cdata->_format->has_column(name);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineBase::get_num_arrays
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE int GeomVertexDataPipelineBase::
+get_num_arrays() const {
+  return _cdata->_arrays.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineBase::get_array
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE const GeomVertexArrayData *GeomVertexDataPipelineBase::
+get_array(int i) const {
+  nassertr(i >= 0 && i < (int)_cdata->_arrays.size(), NULL);
+  return _cdata->_arrays[i];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineBase::get_transform_table
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE const TransformTable *GeomVertexDataPipelineBase::
+get_transform_table() const {
+  return _cdata->_transform_table;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineBase::get_transform_blend_table
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE const TransformBlendTable *GeomVertexDataPipelineBase::
+get_transform_blend_table() const {
+  return _cdata->_transform_blend_table;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineBase::get_slider_table
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE const SliderTable *GeomVertexDataPipelineBase::
+get_slider_table() const {
+  return _cdata->_slider_table;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineBase::get_modified
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE UpdateSeq GeomVertexDataPipelineBase::
+get_modified() const {
+  return _cdata->_modified;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineReader::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexDataPipelineReader::
+GeomVertexDataPipelineReader(const GeomVertexData *object, 
+                             int pipeline_stage) :
+  GeomVertexDataPipelineBase((GeomVertexData *)object, pipeline_stage,
+                             (GeomVertexData::CData *)object->_cycler.read_stage(pipeline_stage)),
+  _got_array_readers(false)
+{
+  nassertv(_object->test_ref_count_nonzero());
+#ifdef DO_PIPELINING
+  nassertv(_cdata->test_ref_count_nonzero());
+#endif  // DO_PIPELINING
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineReader::Copy Constructor
+//       Access: Private
+//  Description: Don't attempt to copy these objects.
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexDataPipelineReader::
+GeomVertexDataPipelineReader(const GeomVertexDataPipelineReader &copy) : 
+  GeomVertexDataPipelineBase(copy)
+{
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineReader::Copy Assignment Operator
+//       Access: Private
+//  Description: Don't attempt to copy these objects.
+////////////////////////////////////////////////////////////////////
+INLINE void GeomVertexDataPipelineReader::
+operator = (const GeomVertexDataPipelineReader &) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineReader::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexDataPipelineReader::
+~GeomVertexDataPipelineReader() {
+  if (_got_array_readers) {
+    delete_array_readers();
+  }
+  nassertv(_object->test_ref_count_nonzero());
+#ifdef DO_PIPELINING
+  nassertv(_cdata->test_ref_count_nonzero());
+#endif  // DO_PIPELINING
+  _object->_cycler.release_read_stage(_pipeline_stage, _cdata);
+  _object = NULL;
+  _cdata = NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineReader::get_object
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE const GeomVertexData *GeomVertexDataPipelineReader::
+get_object() const {
+  return _object;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineReader::check_array_readers
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void GeomVertexDataPipelineReader::
+check_array_readers() const {
+  if (!_got_array_readers) {
+    ((GeomVertexDataPipelineReader *)this)->make_array_readers();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineReader::get_array_reader
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE const GeomVertexArrayDataPipelineReader *GeomVertexDataPipelineReader::
+get_array_reader(int i) const {
+  nassertr(_got_array_readers, NULL);
+  nassertr(i >= 0 && i < (int)_array_readers.size(), NULL);
+  return _array_readers[i];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineReader::has_vertex
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool GeomVertexDataPipelineReader::
+has_vertex() const {
+  return (_cdata->_format->get_vertex_column() != (GeomVertexColumn *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineReader::is_vertex_transformed
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool GeomVertexDataPipelineReader::
+is_vertex_transformed() const {
+  const GeomVertexColumn *column = _cdata->_format->get_vertex_column();
+  if (column != (GeomVertexColumn *)NULL) {
+    return column->get_contents() == C_clip_point;
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineReader::has_normal
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool GeomVertexDataPipelineReader::
+has_normal() const {
+  return (_cdata->_format->get_normal_column() != (GeomVertexColumn *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineReader::has_color
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool GeomVertexDataPipelineReader::
+has_color() const {
+  return (_cdata->_format->get_color_column() != (GeomVertexColumn *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineWriter::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexDataPipelineWriter::
+GeomVertexDataPipelineWriter(GeomVertexData *object, 
+                             int pipeline_stage, bool force_to_0) :
+  GeomVertexDataPipelineBase(object, pipeline_stage,
+                             object->_cycler.write_stage_upstream(pipeline_stage, force_to_0)),
+  _force_to_0(force_to_0),
+  _got_array_writers(false)
+{
+  nassertv(_object->test_ref_count_nonzero());
+#ifdef DO_PIPELINING
+  nassertv(_cdata->test_ref_count_nonzero());
+#endif  // DO_PIPELINING
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineWriter::Copy Constructor
+//       Access: Private
+//  Description: Don't attempt to copy these objects.
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexDataPipelineWriter::
+GeomVertexDataPipelineWriter(const GeomVertexDataPipelineWriter &copy) : 
+  GeomVertexDataPipelineBase(copy)
+{
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineWriter::Copy Assignment Operator
+//       Access: Private
+//  Description: Don't attempt to copy these objects.
+////////////////////////////////////////////////////////////////////
+INLINE void GeomVertexDataPipelineWriter::
+operator = (const GeomVertexDataPipelineWriter &) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineWriter::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexDataPipelineWriter::
+~GeomVertexDataPipelineWriter() {
+  if (_got_array_writers) {
+    delete_array_writers();
+  }
+  nassertv(_object->test_ref_count_nonzero());
+#ifdef DO_PIPELINING
+  nassertv(_cdata->test_ref_count_nonzero());
+#endif  // DO_PIPELINING
+  _object->_cycler.release_write_stage(_pipeline_stage, _cdata);
+  _object = NULL;
+  _cdata = NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineWriter::get_object
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexData *GeomVertexDataPipelineWriter::
+get_object() const {
+  return _object;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineWriter::check_array_writers
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void GeomVertexDataPipelineWriter::
+check_array_writers() const {
+  if (!_got_array_writers) {
+    ((GeomVertexDataPipelineWriter *)this)->make_array_writers();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineWriter::get_array_writer
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexArrayDataPipelineWriter *GeomVertexDataPipelineWriter::
+get_array_writer(int i) const {
+  nassertr(_got_array_writers, NULL);
+  nassertr(i >= 0 && i < (int)_array_writers.size(), NULL);
+  return _array_writers[i];
+}
+
 INLINE ostream &
 operator << (ostream &out, const GeomVertexData &obj) {
   obj.output(out);

+ 437 - 351
panda/src/gobj/geomVertexData.cxx

@@ -58,23 +58,22 @@ GeomVertexData(const string &name,
                const GeomVertexFormat *format,
                GeomVertexData::UsageHint usage_hint) :
   _name(name),
-  _format(format),
   _char_pcollector(PStatCollector(_animation_pcollector, name)),
   _skinning_pcollector(_char_pcollector, "Skinning"),
   _morphs_pcollector(_char_pcollector, "Morphs")
 {
-  nassertv(_format->is_registered());
-
-  set_usage_hint(usage_hint);
+  nassertv(format->is_registered());
 
   // Create some empty arrays as required by the format.
   // Let's ensure the vertex data gets set on all stages at once.
   OPEN_ITERATE_ALL_STAGES(_cycler) {
     CDStageWriter cdata(_cycler, pipeline_stage);
-    int num_arrays = _format->get_num_arrays();
+    cdata->_format = format;
+    cdata->_usage_hint = usage_hint;
+    int num_arrays = format->get_num_arrays();
     for (int i = 0; i < num_arrays; i++) {
       PT(GeomVertexArrayData) array = new GeomVertexArrayData
-        (_format->get_array(i), usage_hint);
+        (format->get_array(i), usage_hint);
       cdata->_arrays.push_back(array);
     }
   }
@@ -90,7 +89,6 @@ GeomVertexData::
 GeomVertexData(const GeomVertexData &copy) :
   TypedWritableReferenceCount(copy),
   _name(copy._name),
-  _format(copy._format),
   _cycler(copy._cycler),
   _char_pcollector(copy._char_pcollector),
   _skinning_pcollector(copy._skinning_pcollector),
@@ -118,13 +116,12 @@ GeomVertexData(const GeomVertexData &copy,
                const GeomVertexFormat *format) :
   TypedWritableReferenceCount(copy),
   _name(copy._name),
-  _format(format),
   _cycler(copy._cycler),
   _char_pcollector(copy._char_pcollector),
   _skinning_pcollector(copy._skinning_pcollector),
   _morphs_pcollector(copy._morphs_pcollector)
 {
-  nassertv(_format->is_registered());
+  nassertv(format->is_registered());
 
   // Create some empty arrays as required by the format.
   OPEN_ITERATE_ALL_STAGES(_cycler) {
@@ -132,10 +129,11 @@ GeomVertexData(const GeomVertexData &copy,
 
     UsageHint usage_hint = cdata->_usage_hint;
     cdata->_arrays.clear();
-    int num_arrays = _format->get_num_arrays();
+    cdata->_format = format;
+    int num_arrays = format->get_num_arrays();
     for (int i = 0; i < num_arrays; i++) {
       PT(GeomVertexArrayData) array = new GeomVertexArrayData
-        (_format->get_array(i), usage_hint);
+        (format->get_array(i), usage_hint);
       cdata->_arrays.push_back(array);
     }
 
@@ -161,7 +159,6 @@ operator = (const GeomVertexData &copy) {
   clear_cache();
 
   _name = copy._name;
-  _format = copy._format;
   _cycler = copy._cycler;
   _char_pcollector = copy._char_pcollector;
   _skinning_pcollector = copy._skinning_pcollector;
@@ -200,53 +197,6 @@ set_name(const string &name) {
   _morphs_pcollector = PStatCollector(_char_pcollector, "Morphs");
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::set_format
-//       Access: Published
-//  Description: Changes the format of the vertex data.  If the data
-//               is not empty, this will implicitly change every row
-//               to match the new format.
-//
-//               Don't call this in a downstream thread unless you
-//               don't mind it blowing away other changes you might
-//               have recently made in an upstream thread.
-////////////////////////////////////////////////////////////////////
-void GeomVertexData::
-set_format(const GeomVertexFormat *format) {
-  nassertv(format->is_registered());
-  if (format == _format) {
-    // Trivially no-op.
-    return;
-  }
-
-  CDWriter cdata(_cycler, true);
-
-  // Put the current data aside, so we can copy it back in below.
-  CPT(GeomVertexData) orig_data = new GeomVertexData(*this);
-
-  // Assign the new format.  This means clearing out all of our
-  // current arrays and replacing them with new, empty arrays.
-  _format = format;
-
-  UsageHint usage_hint = cdata->_usage_hint;
-  cdata->_arrays.clear();
-  int num_arrays = _format->get_num_arrays();
-  for (int i = 0; i < num_arrays; i++) {
-    PT(GeomVertexArrayData) array = new GeomVertexArrayData
-      (_format->get_array(i), usage_hint);
-    cdata->_arrays.push_back(array);
-  }
-
-  // Now copy the original data back in.  This will automatically
-  // convert it to the new format.
-  copy_from(orig_data, false);
-
-  clear_cache_stage();
-  cdata->_modified = Geom::get_next_modified();
-  cdata->_animated_vertices.clear();
-}
-
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexData::set_usage_hint
 //       Access: Published
@@ -278,24 +228,52 @@ set_usage_hint(GeomVertexData::UsageHint usage_hint) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::get_num_rows
+//     Function: GeomVertexData::set_format
 //       Access: Published
-//  Description: Returns the number of rows stored within all the
-//               arrays.  All arrays store data for the same n
-//               rows.
+//  Description: Changes the format of the vertex data.  If the data
+//               is not empty, this will implicitly change every row
+//               to match the new format.
+//
+//               Don't call this in a downstream thread unless you
+//               don't mind it blowing away other changes you might
+//               have recently made in an upstream thread.
 ////////////////////////////////////////////////////////////////////
-int GeomVertexData::
-get_num_rows() const {
+void GeomVertexData::
+set_format(const GeomVertexFormat *format) {
+  nassertv(format->is_registered());
+
   CDReader cdata(_cycler);
-  nassertr(_format->get_num_arrays() == (int)cdata->_arrays.size(), 0);
-  if (_format->get_num_arrays() == 0) {
-    // No arrays means no rows.  Weird but legal.
-    return 0;
+
+  if (format == cdata->_format) {
+    // Trivially no-op.
+    return;
   }
 
-  // Look up the answer on the first array (since any array will do).
-  int stride = _format->get_array(0)->get_stride();
-  return cdata->_arrays[0]->get_data_size_bytes() / stride;
+  CDWriter cdataw(_cycler, cdata, true);
+
+  // Put the current data aside, so we can copy it back in below.
+  CPT(GeomVertexData) orig_data = new GeomVertexData(*this);
+
+  // Assign the new format.  This means clearing out all of our
+  // current arrays and replacing them with new, empty arrays.
+  cdataw->_format = format;
+
+  UsageHint usage_hint = cdataw->_usage_hint;
+  cdataw->_arrays.clear();
+  int num_arrays = cdataw->_format->get_num_arrays();
+  for (int i = 0; i < num_arrays; i++) {
+    PT(GeomVertexArrayData) array = new GeomVertexArrayData
+      (cdataw->_format->get_array(i), usage_hint);
+    cdataw->_arrays.push_back(array);
+  }
+
+  // Now copy the original data back in.  This will automatically
+  // convert it to the new format.
+  copy_from(orig_data, false);
+
+  clear_cache_stage();
+  cdataw->_modified = Geom::get_next_modified();
+  cdataw->_animated_vertices.clear();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -312,7 +290,7 @@ get_num_rows() const {
 void GeomVertexData::
 clear_rows() {
   CDWriter cdata(_cycler, true);
-  nassertv(_format->get_num_arrays() == (int)cdata->_arrays.size());
+  nassertv(cdata->_format->get_num_arrays() == (int)cdata->_arrays.size());
 
   Arrays::iterator ai;
   for (ai = cdata->_arrays.begin();
@@ -328,38 +306,6 @@ clear_rows() {
   cdata->_animated_vertices.clear();
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::modify_array
-//       Access: Published
-//  Description: Returns a modifiable pointer to the indicated vertex
-//               array, so that application code may directly
-//               manipulate the data.  You should avoid changing
-//               the length of this array, since all of the arrays
-//               should be kept in sync--use set_num_rows()
-//               instead.
-//
-//               Don't call this in a downstream thread unless you
-//               don't mind it blowing away other changes you might
-//               have recently made in an upstream thread.
-////////////////////////////////////////////////////////////////////
-GeomVertexArrayData *GeomVertexData::
-modify_array(int i) {
-  // Perform copy-on-write: if the reference count on the vertex data
-  // is greater than 1, assume some other GeomVertexData has the same
-  // pointer, so make a copy of it first.
-  CDWriter cdata(_cycler, true);
-  nassertr(i >= 0 && i < (int)cdata->_arrays.size(), NULL);
-
-  if (cdata->_arrays[i]->get_ref_count() > 1) {
-    cdata->_arrays[i] = new GeomVertexArrayData(*cdata->_arrays[i]);
-  }
-  clear_cache_stage();
-  cdata->_modified = Geom::get_next_modified();
-  cdata->_animated_vertices_modified = UpdateSeq();
-
-  return cdata->_arrays[i];
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexData::set_array
 //       Access: Published
@@ -372,14 +318,10 @@ modify_array(int i) {
 //               don't mind it blowing away other changes you might
 //               have recently made in an upstream thread.
 ////////////////////////////////////////////////////////////////////
-void GeomVertexData::
+INLINE void GeomVertexData::
 set_array(int i, const GeomVertexArrayData *array) {
-  CDWriter cdata(_cycler, true);
-  nassertv(i >= 0 && i < (int)cdata->_arrays.size());
-  cdata->_arrays[i] = (GeomVertexArrayData *)array;
-  clear_cache_stage();
-  cdata->_modified = Geom::get_next_modified();
-  cdata->_animated_vertices_modified = UpdateSeq();
+  GeomVertexDataPipelineWriter writer(this, Thread::get_current_pipeline_stage(), true);
+  writer.set_array(i, array);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -483,26 +425,6 @@ set_slider_table(const SliderTable *table) {
   cdata->_animated_vertices_modified = UpdateSeq();
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::get_num_bytes
-//       Access: Published
-//  Description: Returns the total number of bytes consumed by the
-//               different arrays of the vertex data.
-////////////////////////////////////////////////////////////////////
-int GeomVertexData::
-get_num_bytes() const {
-  CDReader cdata(_cycler);
-  
-  int num_bytes = sizeof(GeomVertexData);
-
-  Arrays::const_iterator ai;
-  for (ai = cdata->_arrays.begin(); ai != cdata->_arrays.end(); ++ai) {
-    num_bytes += (*ai)->get_data_size_bytes();
-  }
-
-  return num_bytes;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexData::copy_from
 //       Access: Published
@@ -756,7 +678,7 @@ copy_row_from(int dest_row, const GeomVertexData *source,
 ////////////////////////////////////////////////////////////////////
 CPT(GeomVertexData) GeomVertexData::
 convert_to(const GeomVertexFormat *new_format) const {
-  if (new_format == _format) {
+  if (new_format == get_format()) {
     // Trivial case: no change is needed.
     return this;
   }
@@ -791,7 +713,7 @@ convert_to(const GeomVertexFormat *new_format) const {
   // Okay, convert the data to the new format.
   if (gobj_cat.is_debug()) {
     gobj_cat.debug()
-      << "Converting " << get_num_rows() << " rows from " << *_format
+      << "Converting " << get_num_rows() << " rows from " << *get_format()
       << " to " << *new_format << "\n";
   }
   PStatTimer timer(_convert_pcollector);
@@ -836,7 +758,7 @@ convert_to(const GeomVertexFormat *new_format) const {
 CPT(GeomVertexData) GeomVertexData::
 scale_color(const LVecBase4f &color_scale) const {
   const GeomVertexColumn *old_column = 
-    _format->get_column(InternalName::get_color());
+    get_format()->get_column(InternalName::get_color());
   if (old_column == (GeomVertexColumn *)NULL) {
     return this;
   }
@@ -868,7 +790,7 @@ CPT(GeomVertexData) GeomVertexData::
 scale_color(const LVecBase4f &color_scale, int num_components,
             GeomVertexData::NumericType numeric_type,
             GeomVertexData::Contents contents) const {
-  int old_color_array = _format->get_array_with(InternalName::get_color());
+  int old_color_array = get_format()->get_array_with(InternalName::get_color());
   if (old_color_array == -1) {
     // Oops, no color anyway.
     return set_color(color_scale, num_components, numeric_type, contents);
@@ -913,7 +835,7 @@ scale_color(const LVecBase4f &color_scale, int num_components,
 CPT(GeomVertexData) GeomVertexData::
 set_color(const Colorf &color) const {
   const GeomVertexColumn *old_column = 
-    _format->get_column(InternalName::get_color());
+    get_format()->get_column(InternalName::get_color());
   if (old_column == (GeomVertexColumn *)NULL) {
     return this;
   }
@@ -982,7 +904,7 @@ CPT(GeomVertexData) GeomVertexData::
 animate_vertices() const {
   CDReader cdata(_cycler);
 
-  if (_format->get_animation().get_animation_type() != AT_panda) {
+  if (cdata->_format->get_animation().get_animation_type() != AT_panda) {
     return this;
   }
 
@@ -1068,11 +990,12 @@ PT(GeomVertexData) GeomVertexData::
 replace_column(InternalName *name, int num_components,
                GeomVertexData::NumericType numeric_type,
                GeomVertexData::Contents contents) const {
-  PT(GeomVertexFormat) new_format = new GeomVertexFormat(*_format);
+  CDReader cdata(_cycler);
+  PT(GeomVertexFormat) new_format = new GeomVertexFormat(*cdata->_format);
 
   // Remove the old description of the type from the format.
   bool removed_type_array = false;
-  int old_type_array = _format->get_array_with(name);
+  int old_type_array = cdata->_format->get_array_with(name);
   if (old_type_array != -1) {
     GeomVertexArrayFormat *array_format = new_format->modify_array(old_type_array);
     if (array_format->get_num_columns() == 1) {
@@ -1103,7 +1026,7 @@ replace_column(InternalName *name, int num_components,
     gobj_cat.debug()
       << "Replacing data type " << *name << "; converting "
       << get_num_rows() << " rows from " 
-      << *_format << " to " << *format << "\n";
+      << *cdata->_format << " to " << *format << "\n";
   }
   
   PT(GeomVertexData) new_data = new GeomVertexData(*this, format);
@@ -1163,7 +1086,7 @@ write(ostream &out, int indent_level) const {
   if (!get_name().empty()) {
     indent(out, indent_level) << get_name() << "\n";
   }
-  _format->write_with_data(out, indent_level + 2, this);
+  get_format()->write_with_data(out, indent_level + 2, this);
   if (get_transform_blend_table() != (TransformBlendTable *)NULL) {
     indent(out, indent_level)
       << "Transform blend table:\n";
@@ -1215,118 +1138,6 @@ clear_cache_stage() {
   }
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::get_array_info
-//       Access: Public
-//  Description: A convenience function to collect together the
-//               important parts of the array data for rendering.
-//               Given the name of a data type, fills in the start of
-//               the array, the number of numeric values for each
-//               vertex, the starting bytes number, and the number of
-//               bytes to increment for each consecutive vertex.
-//
-//               The return value is true if the named array data
-//               exists in this record, or false if it does not (in
-//               which case none of the output parameters are valid).
-////////////////////////////////////////////////////////////////////
-bool GeomVertexData::
-get_array_info(const InternalName *name, 
-               const GeomVertexArrayData *&array_data,
-               int &num_values, 
-               GeomVertexData::NumericType &numeric_type, 
-               int &start, int &stride) const {
-  int array_index;
-  const GeomVertexColumn *column;
-  if (_format->get_array_info(name, array_index, column)) {
-    CDReader cdata(_cycler);
-    array_data = cdata->_arrays[array_index];
-    num_values = column->get_num_values();
-    numeric_type = column->get_numeric_type();
-    start = column->get_start();
-    stride = _format->get_array(array_index)->get_stride();
-    return true;
-  }
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::get_vertex_info
-//       Access: Public
-//  Description: A shortcut to get_array_info() for the "vertex"
-//               column.
-////////////////////////////////////////////////////////////////////
-bool GeomVertexData::
-get_vertex_info(const GeomVertexArrayData *&array_data,
-                int &num_values, 
-                GeomVertexData::NumericType &numeric_type, 
-                int &start, int &stride) const {
-  int array_index = _format->get_vertex_array_index();
-  if (array_index >= 0) {
-    const GeomVertexColumn *column = _format->get_vertex_column();
-
-    CDReader cdata(_cycler);
-    array_data = cdata->_arrays[array_index];
-    num_values = column->get_num_values();
-    numeric_type = column->get_numeric_type();
-    start = column->get_start();
-    stride = _format->get_array(array_index)->get_stride();
-    return true;
-  }
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::get_normal_info
-//       Access: Public
-//  Description: A shortcut to get_array_info() for the "normal"
-//               column.  Note that there is no num_values return,
-//               since normals should always have three values.
-////////////////////////////////////////////////////////////////////
-bool GeomVertexData::
-get_normal_info(const GeomVertexArrayData *&array_data,
-                GeomVertexData::NumericType &numeric_type, 
-                int &start, int &stride) const {
-  int array_index = _format->get_normal_array_index();
-  if (array_index >= 0) {
-    const GeomVertexColumn *column = _format->get_normal_column();
-    nassertr(column->get_num_values() == 3, false);
-
-    CDReader cdata(_cycler);
-    array_data = cdata->_arrays[array_index];
-    numeric_type = column->get_numeric_type();
-    start = column->get_start();
-    stride = _format->get_array(array_index)->get_stride();
-    return true;
-  }
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::get_color_info
-//       Access: Public
-//  Description: A shortcut to get_array_info() for the "color"
-//               column.
-////////////////////////////////////////////////////////////////////
-bool GeomVertexData::
-get_color_info(const GeomVertexArrayData *&array_data,
-                int &num_values, 
-                GeomVertexData::NumericType &numeric_type, 
-                int &start, int &stride) const {
-  int array_index = _format->get_color_array_index();
-  if (array_index >= 0) {
-    const GeomVertexColumn *column = _format->get_color_column();
-
-    CDReader cdata(_cycler);
-    array_data = cdata->_arrays[array_index];
-    num_values = column->get_num_values();
-    numeric_type = column->get_numeric_type();
-    start = column->get_start();
-    stride = _format->get_array(array_index)->get_stride();
-    return true;
-  }
-  return false;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexData::packed_argb_to_uint8_rgba
 //       Access: Private, Static
@@ -1383,81 +1194,6 @@ uint8_rgba_to_packed_argb(unsigned char *to, int to_stride,
   }
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexData::do_set_num_rows
-//       Access: Private
-//  Description: The private implementation of set_num_rows().
-////////////////////////////////////////////////////////////////////
-bool GeomVertexData::
-do_set_num_rows(int n, GeomVertexData::CData *cdata) {
-  nassertr(_format->get_num_arrays() == (int)cdata->_arrays.size(), false);
-
-  bool any_changed = false;
-
-  int color_array = -1;
-  int orig_color_rows = -1;
-
-  for (size_t i = 0; i < cdata->_arrays.size(); i++) {
-    if (cdata->_arrays[i]->get_num_rows() != n) {
-      // Copy-on-write.
-      if (cdata->_arrays[i]->get_ref_count() > 1) {
-        cdata->_arrays[i] = new GeomVertexArrayData(*cdata->_arrays[i]);
-      }
-      if (cdata->_arrays[i]->has_column(InternalName::get_color())) {
-        color_array = i;
-        orig_color_rows = cdata->_arrays[i]->get_num_rows();
-      }
-      cdata->_arrays[i]->set_num_rows(n);
-      any_changed = true;
-    }
-  }
-
-  if (color_array >= 0 && orig_color_rows < n) {
-    // We have just added some rows, fill the "color" column with
-    // (1, 1, 1, 1), for the programmer's convenience.
-    GeomVertexArrayData *array_data = cdata->_arrays[color_array];
-    const GeomVertexColumn *column = 
-      array_data->get_array_format()->get_column(InternalName::get_color());
-    int stride = array_data->get_array_format()->get_stride();
-    unsigned char *start = 
-      array_data->modify_data() + column->get_start();
-    unsigned char *stop = start + array_data->get_data_size_bytes();
-    unsigned char *pointer = start + stride * orig_color_rows;
-    int num_values = column->get_num_values();
-
-    switch (column->get_numeric_type()) {
-    case NT_packed_dcba:
-    case NT_packed_dabc:
-    case NT_uint8:
-    case NT_uint16:
-    case NT_uint32:
-      while (pointer < stop) {
-        memset(pointer, 0xff, column->get_total_bytes());
-        pointer += stride;
-      }
-      break;
-
-    case NT_float32:
-      while (pointer < stop) {
-        PN_float32 *pi = (PN_float32 *)pointer;
-        for (int i = 0; i < num_values; i++) {
-          pi[i] = 1.0f;
-        }
-        pointer += stride;
-      }
-      break;
-    }          
-  }
-
-  if (any_changed) {
-    clear_cache_stage();
-    cdata->_modified = Geom::get_next_modified();
-    cdata->_animated_vertices.clear();
-  }
-
-  return any_changed;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexData::update_animated_vertices
 //       Access: Private
@@ -1477,8 +1213,10 @@ update_animated_vertices(GeomVertexData::CData *cdata) {
 
   PStatTimer timer(_char_pcollector);
 
+  const GeomVertexFormat *orig_format = cdata->_format;
+
   if (cdata->_animated_vertices == (GeomVertexData *)NULL) {
-    CPT(GeomVertexFormat) new_format = _format->get_post_animated_format();
+    CPT(GeomVertexFormat) new_format = orig_format->get_post_animated_format();
     cdata->_animated_vertices = 
       new GeomVertexData(get_name(), new_format,
                            min(get_usage_hint(), UH_dynamic));
@@ -1496,15 +1234,15 @@ update_animated_vertices(GeomVertexData::CData *cdata) {
   CPT(SliderTable) slider_table = cdata->_slider_table;
   if (slider_table != (SliderTable *)NULL) {
     PStatTimer timer2(_morphs_pcollector);
-    int num_morphs = _format->get_num_morphs();
+    int num_morphs = orig_format->get_num_morphs();
     for (int mi = 0; mi < num_morphs; mi++) {
-      CPT(InternalName) slider_name = _format->get_morph_slider(mi);
+      CPT(InternalName) slider_name = orig_format->get_morph_slider(mi);
       const VertexSlider *slider = slider_table->find_slider(slider_name);
       if (slider != (VertexSlider *)NULL) {
         float slider_value = slider->get_slider();
         if (slider_value != 0.0f) {
-          CPT(InternalName) base_name = _format->get_morph_base(mi);
-          CPT(InternalName) delta_name = _format->get_morph_delta(mi);
+          CPT(InternalName) base_name = orig_format->get_morph_base(mi);
+          CPT(InternalName) delta_name = orig_format->get_morph_delta(mi);
           
           GeomVertexRewriter data(new_data, base_name);
           GeomVertexReader delta(this, delta_name);
@@ -1566,8 +1304,8 @@ update_animated_vertices(GeomVertexData::CData *cdata) {
     }
 
     int ci;
-    for (ci = 0; ci < _format->get_num_points(); ci++) {
-      GeomVertexRewriter data(new_data, _format->get_point(ci));
+    for (ci = 0; ci < orig_format->get_num_points(); ci++) {
+      GeomVertexRewriter data(new_data, orig_format->get_point(ci));
       blendi.set_row(0);
       
       if (data.get_column()->get_num_values() == 4) {
@@ -1586,8 +1324,8 @@ update_animated_vertices(GeomVertexData::CData *cdata) {
         }
       }
     }
-    for (ci = 0; ci < _format->get_num_vectors(); ci++) {
-      GeomVertexRewriter data(new_data, _format->get_vector(ci));
+    for (ci = 0; ci < orig_format->get_num_vectors(); ci++) {
+      GeomVertexRewriter data(new_data, orig_format->get_vector(ci));
       blendi.set_row(0);
       
       for (int i = 0; i < num_rows; i++) {
@@ -1622,8 +1360,6 @@ write_datagram(BamWriter *manager, Datagram &dg) {
   TypedWritableReferenceCount::write_datagram(manager, dg);
 
   dg.add_string(_name);
-  manager->write_pointer(dg, _format);
-
   manager->write_cdata(dg, _cycler);
 }
 
@@ -1658,9 +1394,6 @@ make_from_bam(const FactoryParams &params) {
 int GeomVertexData::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = TypedWritableReferenceCount::complete_pointers(p_list, manager);
-
-  _format = DCAST(GeomVertexFormat, p_list[pi++]);
-
   return pi;
 }
 
@@ -1689,10 +1422,12 @@ finalize(BamReader *manager) {
 
   CDWriter cdata(_cycler, true);
 
-  CPT(GeomVertexFormat) new_format = 
-    GeomVertexFormat::register_format(_format);
-
   for (size_t i = 0; i < cdata->_arrays.size(); ++i) {
+    CPT(GeomVertexFormat) new_format = 
+      GeomVertexFormat::register_format(cdata->_format);
+    manager->change_pointer(cdata->_format, new_format);
+    cdata->_format = new_format;
+
     CPT(GeomVertexArrayFormat) new_array_format = new_format->get_array(i);
     nassertv(cdata->_arrays[i]->_array_format->compare_to(*new_array_format) == 0);
 
@@ -1700,9 +1435,6 @@ finalize(BamReader *manager) {
     cdata->_arrays[i]->_array_format = new_array_format;
   }
 
-  manager->change_pointer(_format, new_format);
-  _format = new_format;
-
   if (cdata->_transform_table != (TransformTable *)NULL) {
     CPT(TransformTable) new_transform_table = 
       TransformTable::register_table(cdata->_transform_table);
@@ -1730,8 +1462,6 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   TypedWritableReferenceCount::fillin(scan, manager);
 
   set_name(scan.get_string());
-  manager->read_pointer(scan);
-
   manager->read_cdata(scan, _cycler);
 }
 
@@ -1788,6 +1518,7 @@ make_copy() const {
 ////////////////////////////////////////////////////////////////////
 void GeomVertexData::CData::
 write_datagram(BamWriter *manager, Datagram &dg) const {
+  manager->write_pointer(dg, _format);
   dg.add_uint8(_usage_hint);
 
   dg.add_uint16(_arrays.size());
@@ -1812,6 +1543,8 @@ int GeomVertexData::CData::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = CycleData::complete_pointers(p_list, manager);
 
+  _format = DCAST(GeomVertexFormat, p_list[pi++]);
+
   Arrays::iterator ai;
   for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
     (*ai) = DCAST(GeomVertexArrayData, p_list[pi++]);    
@@ -1835,6 +1568,7 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
 ////////////////////////////////////////////////////////////////////
 void GeomVertexData::CData::
 fillin(DatagramIterator &scan, BamReader *manager) {
+  manager->read_pointer(scan);
   _usage_hint = (UsageHint)scan.get_uint8();
 
   size_t num_arrays = scan.get_uint16();
@@ -1848,3 +1582,355 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   manager->read_pointer(scan);
   manager->read_pointer(scan);
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineBase::get_num_bytes
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int GeomVertexDataPipelineBase::
+get_num_bytes() const {
+  int num_bytes = sizeof(GeomVertexData);
+
+  GeomVertexData::Arrays::const_iterator ai;
+  for (ai = _cdata->_arrays.begin(); ai != _cdata->_arrays.end(); ++ai) {
+    num_bytes += (*ai)->get_data_size_bytes();
+  }
+
+  return num_bytes;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineReader::get_num_rows
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int GeomVertexDataPipelineReader::
+get_num_rows() const {
+  nassertr(_cdata->_format->get_num_arrays() == (int)_cdata->_arrays.size(), 0);
+  nassertr(_got_array_readers, 0);
+
+  if (_cdata->_format->get_num_arrays() == 0) {
+    // No arrays means no rows.  Weird but legal.
+    return 0;
+  }
+
+  // Look up the answer on the first array (since any array will do).
+  int stride = _cdata->_format->get_array(0)->get_stride();
+  return _array_readers[0]->get_data_size_bytes() / stride;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineReader::get_array_info
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool GeomVertexDataPipelineReader::
+get_array_info(const InternalName *name, 
+               const GeomVertexArrayDataPipelineReader *&array_reader,
+               int &num_values, 
+               GeomVertexDataPipelineReader::NumericType &numeric_type, 
+               int &start, int &stride) const {
+  nassertr(_got_array_readers, false);
+  int array_index;
+  const GeomVertexColumn *column;
+  if (_cdata->_format->get_array_info(name, array_index, column)) {
+    array_reader = _array_readers[array_index];
+    num_values = column->get_num_values();
+    numeric_type = column->get_numeric_type();
+    start = column->get_start();
+    stride = _cdata->_format->get_array(array_index)->get_stride();
+    return true;
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineReader::get_vertex_info
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool GeomVertexDataPipelineReader::
+get_vertex_info(const GeomVertexArrayDataPipelineReader *&array_reader,
+                int &num_values, 
+                GeomVertexDataPipelineReader::NumericType &numeric_type, 
+                int &start, int &stride) const {
+  nassertr(_got_array_readers, false);
+  int array_index = _cdata->_format->get_vertex_array_index();
+  if (array_index >= 0) {
+    const GeomVertexColumn *column = _cdata->_format->get_vertex_column();
+
+    array_reader = _array_readers[array_index];
+    num_values = column->get_num_values();
+    numeric_type = column->get_numeric_type();
+    start = column->get_start();
+    stride = _cdata->_format->get_array(array_index)->get_stride();
+    return true;
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineReader::get_normal_info
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool GeomVertexDataPipelineReader::
+get_normal_info(const GeomVertexArrayDataPipelineReader *&array_reader,
+                GeomVertexDataPipelineReader::NumericType &numeric_type, 
+                int &start, int &stride) const {
+  nassertr(_got_array_readers, false);
+  int array_index = _cdata->_format->get_normal_array_index();
+  if (array_index >= 0) {
+    const GeomVertexColumn *column = _cdata->_format->get_normal_column();
+    nassertr(column->get_num_values() == 3, false);
+
+    array_reader = _array_readers[array_index];
+    numeric_type = column->get_numeric_type();
+    start = column->get_start();
+    stride = _cdata->_format->get_array(array_index)->get_stride();
+    return true;
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineReader::get_color_info
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool GeomVertexDataPipelineReader::
+get_color_info(const GeomVertexArrayDataPipelineReader *&array_reader,
+               int &num_values, 
+               GeomVertexDataPipelineReader::NumericType &numeric_type, 
+               int &start, int &stride) const {
+  nassertr(_got_array_readers, false);
+  int array_index = _cdata->_format->get_color_array_index();
+  if (array_index >= 0) {
+    const GeomVertexColumn *column = _cdata->_format->get_color_column();
+
+    array_reader = _array_readers[array_index];
+    num_values = column->get_num_values();
+    numeric_type = column->get_numeric_type();
+    start = column->get_start();
+    stride = _cdata->_format->get_array(array_index)->get_stride();
+    return true;
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineReader::make_array_readers
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void GeomVertexDataPipelineReader::
+make_array_readers() {
+  nassertv(!_got_array_readers);
+
+  _array_readers.reserve(_cdata->_arrays.size());
+  GeomVertexData::Arrays::const_iterator ai;
+  for (ai = _cdata->_arrays.begin(); ai != _cdata->_arrays.end(); ++ai) {
+    _array_readers.push_back(new GeomVertexArrayDataPipelineReader(*ai, _pipeline_stage));
+  }
+
+  _got_array_readers = true;
+  nassertv(get_array_reader(0)->get_data_size_bytes() == get_array(0)->get_data_size_bytes());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineReader::delete_array_readers
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void GeomVertexDataPipelineReader::
+delete_array_readers() {
+  nassertv(_got_array_readers);
+
+  ArrayReaders::iterator ri;
+  for (ri = _array_readers.begin(); ri != _array_readers.end(); ++ri) {
+    delete (*ri);
+  }
+  _array_readers.clear();
+  _got_array_readers = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineWriter::get_num_rows
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+int GeomVertexDataPipelineWriter::
+get_num_rows() const {
+  nassertr(_cdata->_format->get_num_arrays() == (int)_cdata->_arrays.size(), 0);
+  nassertr(_got_array_writers, 0);
+
+  if (_cdata->_format->get_num_arrays() == 0) {
+    // No arrays means no rows.  Weird but legal.
+    return 0;
+  }
+
+  // Look up the answer on the first array (since any array will do).
+  int stride = _cdata->_format->get_array(0)->get_stride();
+  return _array_writers[0]->get_data_size_bytes() / stride;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineWriter::set_num_rows
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool GeomVertexDataPipelineWriter::
+set_num_rows(int n) {
+  nassertr(_got_array_writers, false);
+  nassertr(_cdata->_format->get_num_arrays() == (int)_cdata->_arrays.size(), false);
+
+  bool any_changed = false;
+
+  int color_array = -1;
+  int orig_color_rows = -1;
+
+  for (size_t i = 0; i < _cdata->_arrays.size(); i++) {
+    if (_array_writers[i]->get_num_rows() != n) {
+      // Copy-on-write.
+      if (_cdata->_arrays[i]->get_ref_count() > 1) {
+        delete _array_writers[i];
+        _cdata->_arrays[i] = new GeomVertexArrayData(*_cdata->_arrays[i]);
+        _array_writers[i] = new GeomVertexArrayDataPipelineWriter(_cdata->_arrays[i], _pipeline_stage, _force_to_0);
+      }
+      if (_cdata->_arrays[i]->has_column(InternalName::get_color())) {
+        color_array = i;
+        orig_color_rows = _array_writers[i]->get_num_rows();
+      }
+      _array_writers[i]->set_num_rows(n);
+      any_changed = true;
+    }
+  }
+
+  if (color_array >= 0 && orig_color_rows < n) {
+    // We have just added some rows, fill the "color" column with
+    // (1, 1, 1, 1), for the programmer's convenience.
+    GeomVertexArrayData *array_data = _cdata->_arrays[color_array];
+    const GeomVertexColumn *column = 
+      array_data->get_array_format()->get_column(InternalName::get_color());
+    int stride = array_data->get_array_format()->get_stride();
+    unsigned char *start = 
+      array_data->modify_data() + column->get_start();
+    unsigned char *stop = start + array_data->get_data_size_bytes();
+    unsigned char *pointer = start + stride * orig_color_rows;
+    int num_values = column->get_num_values();
+
+    switch (column->get_numeric_type()) {
+    case NT_packed_dcba:
+    case NT_packed_dabc:
+    case NT_uint8:
+    case NT_uint16:
+    case NT_uint32:
+      while (pointer < stop) {
+        memset(pointer, 0xff, column->get_total_bytes());
+        pointer += stride;
+      }
+      break;
+
+    case NT_float32:
+      while (pointer < stop) {
+        PN_float32 *pi = (PN_float32 *)pointer;
+        for (int i = 0; i < num_values; i++) {
+          pi[i] = 1.0f;
+        }
+        pointer += stride;
+      }
+      break;
+    }          
+  }
+
+  if (any_changed) {
+    _object->clear_cache_stage();
+    _cdata->_modified = Geom::get_next_modified();
+    _cdata->_animated_vertices.clear();
+  }
+
+  return any_changed;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineWriter::modify_array
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+GeomVertexArrayData *GeomVertexDataPipelineWriter::
+modify_array(int i) {
+  // Perform copy-on-write: if the reference count on the vertex data
+  // is greater than 1, assume some other GeomVertexData has the same
+  // pointer, so make a copy of it first.
+  nassertr(i >= 0 && i < (int)_cdata->_arrays.size(), NULL);
+
+  if (_cdata->_arrays[i]->get_ref_count() > 1) {
+    _cdata->_arrays[i] = new GeomVertexArrayData(*_cdata->_arrays[i]);
+  }
+  _object->clear_cache_stage();
+  _cdata->_modified = Geom::get_next_modified();
+  _cdata->_animated_vertices_modified = UpdateSeq();
+
+  if (_got_array_writers) {
+    delete _array_writers[i];
+    _array_writers[i] = new GeomVertexArrayDataPipelineWriter(_cdata->_arrays[i], _pipeline_stage, _force_to_0);
+  }
+
+  return _cdata->_arrays[i];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineWriter::set_array
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void GeomVertexDataPipelineWriter::
+set_array(int i, const GeomVertexArrayData *array) {
+  nassertv(i >= 0 && i < (int)_cdata->_arrays.size());
+  _cdata->_arrays[i] = (GeomVertexArrayData *)array;
+  _object->clear_cache_stage();
+  _cdata->_modified = Geom::get_next_modified();
+  _cdata->_animated_vertices_modified = UpdateSeq();
+
+  if (_got_array_writers) {
+    delete _array_writers[i];
+    _array_writers[i] = new GeomVertexArrayDataPipelineWriter(_cdata->_arrays[i], _pipeline_stage, _force_to_0);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineWriter::make_array_writers
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void GeomVertexDataPipelineWriter::
+make_array_writers() {
+  nassertv(!_got_array_writers);
+
+  _array_writers.reserve(_cdata->_arrays.size());
+  GeomVertexData::Arrays::const_iterator ai;
+  for (ai = _cdata->_arrays.begin(); ai != _cdata->_arrays.end(); ++ai) {
+    _array_writers.push_back(new GeomVertexArrayDataPipelineWriter(*ai, _pipeline_stage, _force_to_0));
+  }
+
+  _got_array_writers = true;
+  nassertv(get_array_writer(0)->get_data_size_bytes() == get_array(0)->get_data_size_bytes());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexDataPipelineWriter::delete_array_writers
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void GeomVertexDataPipelineWriter::
+delete_array_writers() {
+  nassertv(_got_array_writers);
+
+  ArrayWriters::iterator ri;
+  for (ri = _array_writers.begin(); ri != _array_writers.end(); ++ri) {
+    delete (*ri);
+  }
+  _array_writers.clear();
+  _got_array_writers = false;
+}

+ 134 - 30
panda/src/gobj/geomVertexData.h

@@ -91,22 +91,22 @@ PUBLISHED:
   INLINE const string &get_name() const;
   void set_name(const string &name);
 
-  INLINE const GeomVertexFormat *get_format() const;
-  void set_format(const GeomVertexFormat *format);
-
   INLINE UsageHint get_usage_hint() const;
   void set_usage_hint(UsageHint usage_hint);
 
+  INLINE const GeomVertexFormat *get_format() const;
+  void set_format(const GeomVertexFormat *format);
+
   INLINE bool has_column(const InternalName *name) const;
 
-  int get_num_rows() const;
+  INLINE int get_num_rows() const;
   INLINE bool set_num_rows(int n);
   void clear_rows();
 
   INLINE int get_num_arrays() const;
   INLINE const GeomVertexArrayData *get_array(int i) const;
-  GeomVertexArrayData *modify_array(int i);
-  void set_array(int i, const GeomVertexArrayData *array);
+  INLINE GeomVertexArrayData *modify_array(int i);
+  INLINE void set_array(int i, const GeomVertexArrayData *array);
 
   INLINE const TransformTable *get_transform_table() const;
   void set_transform_table(const TransformTable *table);
@@ -121,7 +121,7 @@ PUBLISHED:
   void set_slider_table(const SliderTable *table);
   INLINE void clear_slider_table();
 
-  int get_num_bytes() const;
+  INLINE int get_num_bytes() const;
   INLINE UpdateSeq get_modified() const;
 
   void copy_from(const GeomVertexData *source, bool keep_data_objects);
@@ -152,27 +152,6 @@ PUBLISHED:
   void clear_cache_stage();
 
 public:
-  bool get_array_info(const InternalName *name, 
-                      const GeomVertexArrayData *&array_data,
-                      int &num_values, NumericType &numeric_type, 
-                      int &start, int &stride) const;
-
-  INLINE bool has_vertex() const;
-  INLINE bool is_vertex_transformed() const;
-  bool get_vertex_info(const GeomVertexArrayData *&array_data,
-                       int &num_values, NumericType &numeric_type, 
-                       int &start, int &stride) const;
-
-  INLINE bool has_normal() const;
-  bool get_normal_info(const GeomVertexArrayData *&array_data,
-                       NumericType &numeric_type,
-                       int &start, int &stride) const;
-
-  INLINE bool has_color() const;
-  bool get_color_info(const GeomVertexArrayData *&array_data,
-                      int &num_values, NumericType &numeric_type, 
-                      int &start, int &stride) const;
-
   static INLINE PN_uint32 pack_abcd(unsigned int a, unsigned int b,
                                     unsigned int c, unsigned int d);
   static INLINE unsigned int unpack_abcd_a(PN_uint32 data);
@@ -201,7 +180,6 @@ private:
 
 private:
   string _name;
-  CPT(GeomVertexFormat) _format;
 
   typedef pvector< PT(GeomVertexArrayData) > Arrays;
 
@@ -256,6 +234,7 @@ private:
     }
 
     UsageHint _usage_hint;
+    CPT(GeomVertexFormat) _format;
     Arrays _arrays;
     CPT(TransformTable) _transform_table;
     PT(TransformBlendTable) _transform_blend_table;
@@ -274,7 +253,6 @@ private:
   Cache _cache;
 
 private:
-  bool do_set_num_rows(int n, CData *cdata);
   void update_animated_vertices(CData *cdata);
 
   static PStatCollector _convert_pcollector;
@@ -315,6 +293,132 @@ private:
   static TypeHandle _type_handle;
 
   friend class CacheEntry;
+  friend class GeomVertexDataPipelineBase;
+  friend class GeomVertexDataPipelineReader;
+  friend class GeomVertexDataPipelineWriter;
+};
+
+////////////////////////////////////////////////////////////////////
+//       Class : GeomVertexDataPipelineBase
+// Description : The common code from
+//               GeomVertexDataPipelineReader and
+//               GeomVertexDataPipelineWriter.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA GeomVertexDataPipelineBase : public GeomEnums {
+protected:
+  INLINE GeomVertexDataPipelineBase(GeomVertexData *object, 
+                                    int pipeline_stage,
+                                    GeomVertexData::CData *cdata);
+
+public:
+  INLINE int get_pipeline_stage() const;
+
+  INLINE const GeomVertexFormat *get_format() const;
+  INLINE bool has_column(const InternalName *name) const;
+
+  INLINE UsageHint get_usage_hint() const;
+  INLINE int get_num_arrays() const;
+  INLINE const GeomVertexArrayData *get_array(int i) const;
+  INLINE const TransformTable *get_transform_table() const;
+  INLINE const TransformBlendTable *get_transform_blend_table() const;
+  INLINE const SliderTable *get_slider_table() const;
+  int get_num_bytes() const;
+  INLINE UpdateSeq get_modified() const;
+
+protected:
+  GeomVertexData *_object;
+  int _pipeline_stage;
+  GeomVertexData::CData *_cdata;
+};
+
+////////////////////////////////////////////////////////////////////
+//       Class : GeomVertexDataPipelineReader
+// Description : Encapsulates the data from a GeomVertexData,
+//               pre-fetched for one stage of the pipeline.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA GeomVertexDataPipelineReader : public GeomVertexDataPipelineBase {
+public:
+  INLINE GeomVertexDataPipelineReader(const GeomVertexData *object, int pipeline_stage);
+private:
+  INLINE GeomVertexDataPipelineReader(const GeomVertexDataPipelineReader &copy);
+  INLINE void operator = (const GeomVertexDataPipelineReader &copy);
+
+public:
+  INLINE ~GeomVertexDataPipelineReader();
+  ALLOC_DELETED_CHAIN(GeomVertexDataPipelineReader);
+
+  INLINE const GeomVertexData *get_object() const;
+
+  INLINE void check_array_readers() const;
+  INLINE const GeomVertexArrayDataPipelineReader *get_array_reader(int i) const;
+  int get_num_rows() const;
+
+  bool get_array_info(const InternalName *name, 
+                      const GeomVertexArrayDataPipelineReader *&array_reader,
+                      int &num_values, NumericType &numeric_type, 
+                      int &start, int &stride) const;
+
+  INLINE bool has_vertex() const;
+  INLINE bool is_vertex_transformed() const;
+  bool get_vertex_info(const GeomVertexArrayDataPipelineReader *&array_reader,
+                       int &num_values, NumericType &numeric_type, 
+                       int &start, int &stride) const;
+
+  INLINE bool has_normal() const;
+  bool get_normal_info(const GeomVertexArrayDataPipelineReader *&array_reader,
+                       NumericType &numeric_type,
+                       int &start, int &stride) const;
+
+  INLINE bool has_color() const;
+  bool get_color_info(const GeomVertexArrayDataPipelineReader *&array_reader,
+                      int &num_values, NumericType &numeric_type, 
+                      int &start, int &stride) const;
+
+private:
+  void make_array_readers();
+  void delete_array_readers();
+
+  bool _got_array_readers;
+  typedef pvector<GeomVertexArrayDataPipelineReader *> ArrayReaders;
+  ArrayReaders _array_readers;
+};
+
+////////////////////////////////////////////////////////////////////
+//       Class : GeomVertexDataPipelineWriter
+// Description : Encapsulates the data from a GeomVertexData,
+//               pre-fetched for one stage of the pipeline.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA GeomVertexDataPipelineWriter : public GeomVertexDataPipelineBase {
+public:
+  INLINE GeomVertexDataPipelineWriter(GeomVertexData *object, int pipeline_stage,
+                                      bool force_to_0);
+private:
+  INLINE GeomVertexDataPipelineWriter(const GeomVertexDataPipelineWriter &copy);
+  INLINE void operator = (const GeomVertexDataPipelineWriter &copy);
+
+public:
+  INLINE ~GeomVertexDataPipelineWriter();
+  ALLOC_DELETED_CHAIN(GeomVertexDataPipelineWriter);
+
+  INLINE GeomVertexData *get_object() const;
+
+  INLINE void check_array_writers() const;
+  INLINE GeomVertexArrayDataPipelineWriter *get_array_writer(int i) const;
+
+  GeomVertexArrayData *modify_array(int i);
+  void set_array(int i, const GeomVertexArrayData *array);
+
+  int get_num_rows() const;
+  bool set_num_rows(int n);
+
+private:
+  void make_array_writers();
+  void delete_array_writers();
+
+  bool _force_to_0;
+  bool _got_array_writers;
+  typedef pvector<GeomVertexArrayDataPipelineWriter *> ArrayWriters;
+  ArrayWriters _array_writers;
 };
 
 INLINE ostream &operator << (ostream &out, const GeomVertexData &obj);

+ 86 - 39
panda/src/gobj/geomVertexReader.I

@@ -27,7 +27,9 @@
 ////////////////////////////////////////////////////////////////////
 INLINE GeomVertexReader::
 GeomVertexReader() :
-  _vertex_data(NULL)
+  _data_reader(NULL),
+  _array_reader(NULL),
+  _owns_reader(false)
 {
   initialize();
 }
@@ -40,7 +42,9 @@ GeomVertexReader() :
 ////////////////////////////////////////////////////////////////////
 INLINE GeomVertexReader::
 GeomVertexReader(const GeomVertexData *vertex_data) :
-  _vertex_data(vertex_data)
+  _data_reader(new GeomVertexDataPipelineReader(vertex_data, Thread::get_current_pipeline_stage())),
+  _array_reader(NULL),
+  _owns_reader(true)
 {
   initialize();
 }
@@ -54,7 +58,9 @@ GeomVertexReader(const GeomVertexData *vertex_data) :
 ////////////////////////////////////////////////////////////////////
 INLINE GeomVertexReader::
 GeomVertexReader(const GeomVertexData *vertex_data, const string &name) :
-  _vertex_data(vertex_data)
+  _data_reader(new GeomVertexDataPipelineReader(vertex_data, Thread::get_current_pipeline_stage())),
+  _array_reader(NULL),
+  _owns_reader(true)
 {
   initialize();
   set_column(name);
@@ -69,8 +75,10 @@ GeomVertexReader(const GeomVertexData *vertex_data, const string &name) :
 ////////////////////////////////////////////////////////////////////
 INLINE GeomVertexReader::
 GeomVertexReader(const GeomVertexData *vertex_data, 
-                   const InternalName *name) :
-  _vertex_data(vertex_data)
+                 const InternalName *name) :
+  _data_reader(new GeomVertexDataPipelineReader(vertex_data, Thread::get_current_pipeline_stage())),
+  _array_reader(NULL),
+  _owns_reader(true)
 {
   initialize();
   set_column(name);
@@ -84,7 +92,9 @@ GeomVertexReader(const GeomVertexData *vertex_data,
 ////////////////////////////////////////////////////////////////////
 INLINE GeomVertexReader::
 GeomVertexReader(const GeomVertexArrayData *array_data) :
-  _array_data(array_data)
+  _data_reader(NULL),
+  _array_reader(new GeomVertexArrayDataPipelineReader(array_data, Thread::get_current_pipeline_stage())),
+  _owns_reader(true)
 {
   initialize();
 }
@@ -97,22 +107,44 @@ GeomVertexReader(const GeomVertexArrayData *array_data) :
 ////////////////////////////////////////////////////////////////////
 INLINE GeomVertexReader::
 GeomVertexReader(const GeomVertexArrayData *array_data, int column) :
-  _array_data(array_data)
+  _data_reader(NULL),
+  _array_reader(new GeomVertexArrayDataPipelineReader(array_data, Thread::get_current_pipeline_stage())),
+  _owns_reader(true)
 {
   initialize();
   set_column(column);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexReader::Constructor
+//       Access: Public
+//  Description: Constructs a new reader to process the vertices of
+//               the indicated data object.  This flavor creates the
+//               reader specifically to process the named data type.
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexReader::
+GeomVertexReader(const GeomVertexDataPipelineReader *data_reader, 
+                 const InternalName *name) :
+  _data_reader(data_reader),
+  _array_reader(NULL),
+  _owns_reader(false)
+{
+  initialize();
+  set_column(name);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexReader::Copy Constructor
 //       Access: Published
-//  Description: 
+//  Description: The copy constructor steals ownership of the reader
+//               pointer.
 ////////////////////////////////////////////////////////////////////
 INLINE GeomVertexReader::
 GeomVertexReader(const GeomVertexReader &copy) :
-  _vertex_data(copy._vertex_data),
+  _data_reader(copy._data_reader),
   _array(copy._array),
-  _array_data(copy._array_data),
+  _array_reader(copy._array_reader),
+  _owns_reader(copy._owns_reader),
   _packer(copy._packer),
   _stride(copy._stride),
   _pointer_begin(copy._pointer_begin),
@@ -120,24 +152,33 @@ GeomVertexReader(const GeomVertexReader &copy) :
   _pointer(copy._pointer),
   _start_row(copy._start_row)
 {
+  ((GeomVertexReader &)copy)._owns_reader = false;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexReader::Copy Assignment Operator
 //       Access: Published
-//  Description: 
+//  Description: The copy constructor steals ownership of the reader
+//               pointer.
 ////////////////////////////////////////////////////////////////////
 INLINE void GeomVertexReader::
 operator = (const GeomVertexReader &copy) {
-  _vertex_data = copy._vertex_data;
+  if (_owns_reader) {
+    clear_reader();
+  }
+
+  _data_reader = copy._data_reader;
   _array = copy._array;
-  _array_data = copy._array_data;
+  _array_reader = copy._array_reader;
+  _owns_reader = copy._owns_reader;
   _packer = copy._packer;
   _stride = copy._stride;
   _pointer_begin = copy._pointer_begin;
   _pointer_end = copy._pointer_end;
   _pointer = copy._pointer;
   _start_row = copy._start_row;
+
+  ((GeomVertexReader &)copy)._owns_reader = false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -147,6 +188,9 @@ operator = (const GeomVertexReader &copy) {
 ////////////////////////////////////////////////////////////////////
 INLINE GeomVertexReader::
 ~GeomVertexReader() {
+  if (_owns_reader) {
+    clear_reader();
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -158,7 +202,10 @@ INLINE GeomVertexReader::
 ////////////////////////////////////////////////////////////////////
 INLINE const GeomVertexData *GeomVertexReader::
 get_vertex_data() const {
-  return _vertex_data;
+  if (_data_reader != (GeomVertexDataPipelineReader *)NULL) {
+    return _data_reader->get_object();
+  }
+  return NULL;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -169,7 +216,7 @@ get_vertex_data() const {
 ////////////////////////////////////////////////////////////////////
 INLINE const GeomVertexArrayData *GeomVertexReader::
 get_array_data() const {
-  return _array_data;
+  return _array_reader->get_object();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -187,12 +234,12 @@ get_array_data() const {
 ////////////////////////////////////////////////////////////////////
 INLINE bool GeomVertexReader::
 set_column(int column) {
-  if (_vertex_data != (const GeomVertexData *)NULL) {
-    return set_column(_vertex_data->get_format()->get_array_with(column),
-                      _vertex_data->get_format()->get_column(column));
+  if (_data_reader != (const GeomVertexDataPipelineReader *)NULL) {
+    return set_column(_data_reader->get_format()->get_array_with(column),
+                      _data_reader->get_format()->get_column(column));
   }
-  if (_array_data != (const GeomVertexArrayData *)NULL) {
-    return set_column(0, _array_data->get_array_format()->get_column(column));
+  if (_array_reader != (const GeomVertexArrayDataPipelineReader *)NULL) {
+    return set_column(0, _array_reader->get_array_format()->get_column(column));
   }
   return false;
 }
@@ -230,12 +277,12 @@ set_column(const string &name) {
 ////////////////////////////////////////////////////////////////////
 INLINE bool GeomVertexReader::
 set_column(const InternalName *name) {
-  if (_vertex_data != (const GeomVertexData *)NULL) {
-    return set_column(_vertex_data->get_format()->get_array_with(name),
-                      _vertex_data->get_format()->get_column(name));
+  if (_data_reader != (const GeomVertexDataPipelineReader *)NULL) {
+    return set_column(_data_reader->get_format()->get_array_with(name),
+                      _data_reader->get_format()->get_column(name));
   }
-  if (_array_data != (const GeomVertexArrayData *)NULL) {
-    return set_column(0, _array_data->get_array_format()->get_column(name));
+  if (_array_reader != (const GeomVertexArrayDataPipelineReader *)NULL) {
+    return set_column(0, _array_reader->get_array_format()->get_column(name));
   }
 
   return false;
@@ -454,14 +501,14 @@ get_packer() const {
 ////////////////////////////////////////////////////////////////////
 INLINE void GeomVertexReader::
 set_pointer(int row) {
-  if (_vertex_data != (const GeomVertexData *)NULL) {
-    const GeomVertexArrayData *array_data = _vertex_data->get_array(_array);
-    _pointer_begin = array_data->get_data();
-    _pointer_end = _pointer_begin + array_data->get_data_size_bytes();
+  if (_data_reader != (const GeomVertexDataPipelineReader *)NULL) {
+    const GeomVertexArrayDataPipelineReader *array_reader = _data_reader->get_array_reader(_array);
+    _pointer_begin = array_reader->get_data();
+    _pointer_end = _pointer_begin + array_reader->get_data_size_bytes();
 
   } else {
-    _pointer_begin = _array_data->get_data();
-    _pointer_end = _pointer_begin + _array_data->get_data_size_bytes();
+    _pointer_begin = _array_reader->get_data();
+    _pointer_end = _pointer_begin + _array_reader->get_data_size_bytes();
   }
   quick_set_pointer(row);
 }
@@ -478,11 +525,11 @@ quick_set_pointer(int row) {
   nassertv(has_column());
 
 #ifdef _DEBUG
-  if (_vertex_data != (const GeomVertexData *)NULL) {
-    const GeomVertexArrayData *array_data = _vertex_data->get_array(_array);
-    nassertv(_pointer_begin == array_data->get_data());
+  if (_data_reader != (const GeomVertexDataPipelineReader *)NULL) {
+    const GeomVertexArrayDataPipelineReader *array_reader = _data_reader->get_array_reader(_array);
+    nassertv(_pointer_begin == array_reader->get_data());
   } else {
-    nassertv(_pointer_begin == _array_data->get_data());
+    nassertv(_pointer_begin == _array_reader->get_data());
   }
 #endif
 
@@ -499,11 +546,11 @@ INLINE const unsigned char *GeomVertexReader::
 inc_pointer() {
 #ifdef _DEBUG
   nassertr(_pointer < _pointer_end, empty_buffer);
-  if (_vertex_data != (const GeomVertexData *)NULL){ 
-    const GeomVertexArrayData *array_data = _vertex_data->get_array(_array);
-    nassertr(_pointer >= array_data->get_data().p() && _pointer < array_data->get_data().p() + array_data->get_data_size_bytes(), empty_buffer);
+  if (_data_reader != (const GeomVertexDataPipelineReader *)NULL){ 
+    const GeomVertexArrayDataPipelineReader *array_reader = _data_reader->get_array_reader(_array);
+    nassertr(_pointer >= array_reader->get_data().p() && _pointer < array_reader->get_data().p() + array_reader->get_data_size_bytes(), empty_buffer);
   } else {
-    nassertr(_pointer >= _array_data->get_data().p() && _pointer < _array_data->get_data().p() + _array_data->get_data_size_bytes(), empty_buffer);
+    nassertr(_pointer >= _array_reader->get_data().p() && _pointer < _array_reader->get_data().p() + _array_reader->get_data_size_bytes(), empty_buffer);
   }
 #endif
 

+ 33 - 7
panda/src/gobj/geomVertexReader.cxx

@@ -40,8 +40,8 @@ const unsigned char GeomVertexReader::empty_buffer[100] = { 0 };
 ////////////////////////////////////////////////////////////////////
 bool GeomVertexReader::
 set_column(int array, const GeomVertexColumn *column) {
-  if (_vertex_data == (const GeomVertexData *)NULL &&
-      _array_data == (const GeomVertexArrayData *)NULL) {
+  if (_data_reader == (const GeomVertexDataPipelineReader *)NULL &&
+      _array_reader == (const GeomVertexArrayDataPipelineReader *)NULL) {
     return false;
   }
 
@@ -56,18 +56,18 @@ set_column(int array, const GeomVertexColumn *column) {
     return false;
   }
 
-  if (_vertex_data != (const GeomVertexData *)NULL) {
+  if (_data_reader != (const GeomVertexDataPipelineReader *)NULL) {
 #ifndef NDEBUG
     _array = -1;
     _packer = NULL;
-    nassertr(array >= 0 && array < _vertex_data->get_num_arrays(), false);
+    nassertr(array >= 0 && array < _data_reader->get_num_arrays(), false);
 #endif
     _array = array;
-    const GeomVertexArrayData *array_data =_vertex_data->get_array(_array);
-    _stride = array_data->get_array_format()->get_stride();
+    const GeomVertexArrayDataPipelineReader *array_reader = _data_reader->get_array_reader(_array);
+    _stride = array_reader->get_array_format()->get_stride();
 
   } else {
-    _stride = _array_data->get_array_format()->get_stride();
+    _stride = _array_reader->get_array_format()->get_stride();
   }
 
   _packer = column->_packer;
@@ -103,6 +103,10 @@ output(ostream &out) const {
 ////////////////////////////////////////////////////////////////////
 void GeomVertexReader::
 initialize() {
+  if (_data_reader != (const GeomVertexDataPipelineReader *)NULL) {
+    _data_reader->check_array_readers();
+  }
+
   _array = 0;
   _packer = NULL;
   _pointer_begin = NULL;
@@ -110,3 +114,25 @@ initialize() {
   _pointer = NULL;
   _start_row = 0;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexReader::clear_reader
+//       Access: Private
+//  Description: Destructs the GeomVertexDataPipelineReader, when
+//               necessary, for instance when this object destructs.
+////////////////////////////////////////////////////////////////////
+void GeomVertexReader::
+clear_reader() {
+  nassertv(_owns_reader);
+
+  if (_data_reader != (const GeomVertexDataPipelineReader *)NULL) {
+    delete (GeomVertexDataPipelineReader *)_data_reader;
+    _data_reader = NULL;
+  } else {
+    nassertv(_array_reader != (const GeomVertexArrayDataPipelineReader *)NULL);
+    delete (GeomVertexArrayDataPipelineReader *)_array_reader;
+    _array_reader = NULL;
+  }
+
+  _owns_reader = false;
+}

+ 10 - 2
panda/src/gobj/geomVertexReader.h

@@ -68,6 +68,12 @@ PUBLISHED:
   INLINE GeomVertexReader(const GeomVertexArrayData *array_data);
   INLINE GeomVertexReader(const GeomVertexArrayData *array_data, 
                           int column);
+
+public:
+  INLINE GeomVertexReader(const GeomVertexDataPipelineReader *data_reader,
+                          const InternalName *name);
+
+PUBLISHED:
   INLINE GeomVertexReader(const GeomVertexReader &copy);
   INLINE void operator = (const GeomVertexReader &copy);
   INLINE ~GeomVertexReader();
@@ -107,6 +113,7 @@ protected:
 
 private:
   void initialize();
+  void clear_reader();
 
   INLINE void set_pointer(int row);
   INLINE void quick_set_pointer(int row);
@@ -117,9 +124,10 @@ private:
   // must not keep a pointer to the particular ArrayData we are
   // working on (if we do, it may result in an extra copy of the data
   // due to holding the reference count).
-  CPT(GeomVertexData) _vertex_data;
+  const GeomVertexDataPipelineReader *_data_reader;
   int _array;
-  CPT(GeomVertexArrayData) _array_data;
+  const GeomVertexArrayDataPipelineReader *_array_reader;
+  bool _owns_reader;
 
   GeomVertexColumn::Packer *_packer;
   int _stride;

+ 83 - 39
panda/src/gobj/geomVertexWriter.I

@@ -27,7 +27,9 @@
 ////////////////////////////////////////////////////////////////////
 INLINE GeomVertexWriter::
 GeomVertexWriter() :
-  _vertex_data(NULL)
+  _data_writer(NULL),
+  _array_writer(NULL),
+  _owns_writer(false)
 {
   initialize();
 }
@@ -40,7 +42,9 @@ GeomVertexWriter() :
 ////////////////////////////////////////////////////////////////////
 INLINE GeomVertexWriter::
 GeomVertexWriter(GeomVertexData *vertex_data) :
-  _vertex_data(vertex_data)
+  _data_writer(new GeomVertexDataPipelineWriter(vertex_data, Thread::get_current_pipeline_stage(), true)),
+  _array_writer(NULL),
+  _owns_writer(true)
 {
   initialize();
 }
@@ -54,7 +58,9 @@ GeomVertexWriter(GeomVertexData *vertex_data) :
 ////////////////////////////////////////////////////////////////////
 INLINE GeomVertexWriter::
 GeomVertexWriter(GeomVertexData *vertex_data, const string &name) :
-  _vertex_data(vertex_data)
+  _data_writer(new GeomVertexDataPipelineWriter(vertex_data, Thread::get_current_pipeline_stage(), true)),
+  _array_writer(NULL),
+  _owns_writer(true)
 {
   initialize();
   set_column(name);
@@ -69,7 +75,9 @@ GeomVertexWriter(GeomVertexData *vertex_data, const string &name) :
 ////////////////////////////////////////////////////////////////////
 INLINE GeomVertexWriter::
 GeomVertexWriter(GeomVertexData *vertex_data, const InternalName *name) :
-  _vertex_data(vertex_data)
+  _data_writer(new GeomVertexDataPipelineWriter(vertex_data, Thread::get_current_pipeline_stage(), true)),
+  _array_writer(NULL),
+  _owns_writer(true)
 {
   initialize();
   set_column(name);
@@ -83,7 +91,9 @@ GeomVertexWriter(GeomVertexData *vertex_data, const InternalName *name) :
 ////////////////////////////////////////////////////////////////////
 INLINE GeomVertexWriter::
 GeomVertexWriter(GeomVertexArrayData *array_data) :
-  _array_data(array_data)
+  _data_writer(NULL),
+  _array_writer(new GeomVertexArrayDataPipelineWriter(array_data, Thread::get_current_pipeline_stage(), true)),
+  _owns_writer(true)
 {
   initialize();
 }
@@ -96,22 +106,44 @@ GeomVertexWriter(GeomVertexArrayData *array_data) :
 ////////////////////////////////////////////////////////////////////
 INLINE GeomVertexWriter::
 GeomVertexWriter(GeomVertexArrayData *array_data, int column) :
-  _array_data(array_data)
+  _data_writer(NULL),
+  _array_writer(new GeomVertexArrayDataPipelineWriter(array_data, Thread::get_current_pipeline_stage(), true)),
+  _owns_writer(true)
 {
   initialize();
   set_column(column);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexWriter::Constructor
+//       Access: Public
+//  Description: Constructs a new writer to process the vertices of
+//               the indicated data object.  This flavor creates the
+//               writer specifically to process the named data type.
+////////////////////////////////////////////////////////////////////
+INLINE GeomVertexWriter::
+GeomVertexWriter(GeomVertexDataPipelineWriter *data_writer, 
+                 const InternalName *name) :
+  _data_writer(data_writer),
+  _array_writer(NULL),
+  _owns_writer(false)
+{
+  initialize();
+  set_column(name);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexWriter::Copy Constructor
 //       Access: Published
-//  Description: 
+//  Description: The copy constructor steals ownership of the writer
+//               pointer.
 ////////////////////////////////////////////////////////////////////
 INLINE GeomVertexWriter::
 GeomVertexWriter(const GeomVertexWriter &copy) :
-  _vertex_data(copy._vertex_data),
+  _data_writer(copy._data_writer),
   _array(copy._array),
-  _array_data(copy._array_data),
+  _array_writer(copy._array_writer),
+  _owns_writer(copy._owns_writer),
   _packer(copy._packer),
   _stride(copy._stride),
   _pointer_begin(copy._pointer_begin),
@@ -119,24 +151,29 @@ GeomVertexWriter(const GeomVertexWriter &copy) :
   _pointer(copy._pointer),
   _start_row(copy._start_row)
 {
+  ((GeomVertexWriter &)copy)._owns_writer = false;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexWriter::Copy Assignment Operator
 //       Access: Published
-//  Description: 
+//  Description: The copy constructor steals ownership of the writer
+//               pointer.
 ////////////////////////////////////////////////////////////////////
 INLINE void GeomVertexWriter::
 operator = (const GeomVertexWriter &copy) {
-  _vertex_data = copy._vertex_data;
+  _data_writer = copy._data_writer;
   _array = copy._array;
-  _array_data = copy._array_data;
+  _array_writer = copy._array_writer;
+  _owns_writer = copy._owns_writer;
   _packer = copy._packer;
   _stride = copy._stride;
   _pointer_begin = copy._pointer_begin;
   _pointer_end = copy._pointer_end;
   _pointer = copy._pointer;
   _start_row = copy._start_row;
+
+  ((GeomVertexWriter &)copy)._owns_writer = false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -146,6 +183,9 @@ operator = (const GeomVertexWriter &copy) {
 ////////////////////////////////////////////////////////////////////
 INLINE GeomVertexWriter::
 ~GeomVertexWriter() {
+  if (_owns_writer) {
+    clear_writer();
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -157,7 +197,10 @@ INLINE GeomVertexWriter::
 ////////////////////////////////////////////////////////////////////
 INLINE GeomVertexData *GeomVertexWriter::
 get_vertex_data() const {
-  return _vertex_data;
+  if (_data_writer != (GeomVertexDataPipelineWriter *)NULL) {
+    return _data_writer->get_object();
+  }
+  return NULL;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -168,7 +211,7 @@ get_vertex_data() const {
 ////////////////////////////////////////////////////////////////////
 INLINE GeomVertexArrayData *GeomVertexWriter::
 get_array_data() const {
-  return _array_data;
+  return _array_writer->get_object();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -186,12 +229,12 @@ get_array_data() const {
 ////////////////////////////////////////////////////////////////////
 INLINE bool GeomVertexWriter::
 set_column(int column) {
-  if (_vertex_data != (GeomVertexData *)NULL) {
-    return set_column(_vertex_data->get_format()->get_array_with(column),
-                      _vertex_data->get_format()->get_column(column));
+  if (_data_writer != (GeomVertexDataPipelineWriter *)NULL) {
+    return set_column(_data_writer->get_format()->get_array_with(column),
+                      _data_writer->get_format()->get_column(column));
   }
-  if (_array_data != (GeomVertexArrayData *)NULL) {
-    return set_column(0, _array_data->get_array_format()->get_column(column));
+  if (_array_writer != (GeomVertexArrayDataPipelineWriter *)NULL) {
+    return set_column(0, _array_writer->get_array_format()->get_column(column));
   }
   return false;
 }
@@ -229,12 +272,12 @@ set_column(const string &name) {
 ////////////////////////////////////////////////////////////////////
 INLINE bool GeomVertexWriter::
 set_column(const InternalName *name) {
-  if (_vertex_data != (GeomVertexData *)NULL) {
-    return set_column(_vertex_data->get_format()->get_array_with(name),
-                      _vertex_data->get_format()->get_column(name));
+  if (_data_writer != (GeomVertexDataPipelineWriter *)NULL) {
+    return set_column(_data_writer->get_format()->get_array_with(name),
+                      _data_writer->get_format()->get_column(name));
   }
-  if (_array_data != (GeomVertexArrayData *)NULL) {
-    return set_column(0, _array_data->get_array_format()->get_column(name));
+  if (_array_writer != (GeomVertexArrayDataPipelineWriter *)NULL) {
+    return set_column(0, _array_writer->get_array_format()->get_column(name));
   }
 
   return false;
@@ -759,14 +802,15 @@ get_packer() const {
 ////////////////////////////////////////////////////////////////////
 INLINE void GeomVertexWriter::
 set_pointer(int row) {
-  if (_vertex_data != (const GeomVertexData *)NULL) {
-    GeomVertexArrayData *array_data = _vertex_data->modify_array(_array);
+  if (_data_writer != (const GeomVertexDataPipelineWriter *)NULL) {
+    _data_writer->modify_array(_array);
+    GeomVertexArrayDataPipelineWriter *array_data = _data_writer->get_array_writer(_array);
     _pointer_begin = array_data->modify_data();
     _pointer_end = _pointer_begin + array_data->get_data_size_bytes();
 
   } else {
-    _pointer_begin = _array_data->modify_data();
-    _pointer_end = _pointer_begin + _array_data->get_data_size_bytes();
+    _pointer_begin = _array_writer->modify_data();
+    _pointer_end = _pointer_begin + _array_writer->get_data_size_bytes();
   }
   quick_set_pointer(row);
 }
@@ -783,11 +827,11 @@ quick_set_pointer(int row) {
   nassertv(has_column());
 
 #ifdef _DEBUG
-  if (_vertex_data != (const GeomVertexData *)NULL) {
-    const GeomVertexArrayData *array_data = _vertex_data->get_array(_array);
-    nassertv(_pointer_begin == array_data->get_data());
+  if (_data_writer != (const GeomVertexDataPipelineWriter *)NULL) {
+    GeomVertexArrayDataPipelineWriter *array_writer = _data_writer->get_array_writer(_array);
+    nassertv(_pointer_begin == array_writer->get_data());
   } else {
-    nassertv(_pointer_begin == _array_data->get_data());
+    nassertv(_pointer_begin == _array_writer->get_data());
   }
 #endif
 
@@ -804,11 +848,11 @@ INLINE unsigned char *GeomVertexWriter::
 inc_pointer() {
 #ifdef _DEBUG
   nassertr(_pointer < _pointer_end, empty_buffer);
-  if (_vertex_data != (GeomVertexData *)NULL){ 
-    const GeomVertexArrayData *array_data = _vertex_data->get_array(_array);
-    nassertr(_pointer >= array_data->get_data().p() && _pointer < array_data->get_data().p() + array_data->get_data_size_bytes(), empty_buffer);
+  if (_data_writer != (GeomVertexDataPipelineWriter *)NULL){ 
+    GeomVertexArrayDataPipelineWriter *array_writer = _data_writer->get_array_writer(_array);
+    nassertr(_pointer >= array_writer->get_data().p() && _pointer < array_writer->get_data().p() + array_writer->get_data_size_bytes(), empty_buffer);
   } else {
-    nassertr(_pointer >= _array_data->get_data().p() && _pointer < _array_data->get_data().p() + _array_data->get_data_size_bytes(), empty_buffer);
+    nassertr(_pointer >= _array_writer->get_data().p() && _pointer < _array_writer->get_data().p() + _array_writer->get_data_size_bytes(), empty_buffer);
   }
 #endif
 
@@ -830,10 +874,10 @@ inc_add_pointer() {
   if (_pointer >= _pointer_end) {
     // Reset the data pointer.
     int write_row = get_write_row();
-    if (_vertex_data != (GeomVertexData *)NULL) {
-      _vertex_data->set_num_rows(max(write_row + 1, _vertex_data->get_num_rows()));
+    if (_data_writer != (GeomVertexDataPipelineWriter *)NULL) {
+      _data_writer->set_num_rows(max(write_row + 1, _data_writer->get_num_rows()));
     } else {
-      _array_data->set_num_rows(max(write_row + 1, _array_data->get_num_rows()));
+      _array_writer->set_num_rows(max(write_row + 1, _array_writer->get_num_rows()));
     }
     set_pointer(write_row);
   }

+ 33 - 7
panda/src/gobj/geomVertexWriter.cxx

@@ -40,8 +40,8 @@ unsigned char GeomVertexWriter::empty_buffer[100] = { 0 };
 ////////////////////////////////////////////////////////////////////
 bool GeomVertexWriter::
 set_column(int array, const GeomVertexColumn *column) {
-  if (_vertex_data == (GeomVertexData *)NULL &&
-      _array_data == (GeomVertexArrayData *)NULL) {
+  if (_data_writer == (GeomVertexDataPipelineWriter *)NULL &&
+      _array_writer == (GeomVertexArrayDataPipelineWriter *)NULL) {
     return false;
   }
 
@@ -56,18 +56,18 @@ set_column(int array, const GeomVertexColumn *column) {
     return false;
   }
 
-  if (_vertex_data != (GeomVertexData *)NULL) {
+  if (_data_writer != (GeomVertexDataPipelineWriter *)NULL) {
 #ifndef NDEBUG
     _array = -1;
     _packer = NULL;
-    nassertr(array >= 0 && array < _vertex_data->get_num_arrays(), false);
+    nassertr(array >= 0 && array < _data_writer->get_num_arrays(), false);
 #endif
     _array = array;
-    const GeomVertexArrayData *array_data =_vertex_data->get_array(_array);
-    _stride = array_data->get_array_format()->get_stride();
+    GeomVertexArrayDataPipelineWriter *array_writer =_data_writer->get_array_writer(_array);
+    _stride = array_writer->get_array_format()->get_stride();
 
   } else {
-    _stride = _array_data->get_array_format()->get_stride();
+    _stride = _array_writer->get_array_format()->get_stride();
   }
 
   _packer = column->_packer;
@@ -103,6 +103,10 @@ output(ostream &out) const {
 ////////////////////////////////////////////////////////////////////
 void GeomVertexWriter::
 initialize() {
+  if (_data_writer != (const GeomVertexDataPipelineWriter *)NULL) {
+    _data_writer->check_array_writers();
+  }
+
   _array = 0;
   _packer = NULL;
   _pointer_begin = NULL;
@@ -110,3 +114,25 @@ initialize() {
   _pointer = NULL;
   _start_row = 0;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexWriter::clear_writer
+//       Access: Private
+//  Description: Destructs the GeomVertexDataPipelineWriter, when
+//               necessary, for instance when this object destructs.
+////////////////////////////////////////////////////////////////////
+void GeomVertexWriter::
+clear_writer() {
+  nassertv(_owns_writer);
+
+  if (_data_writer != (GeomVertexDataPipelineWriter *)NULL) {
+    delete _data_writer;
+    _data_writer = NULL;
+  } else {
+    nassertv(_array_writer != (GeomVertexArrayDataPipelineWriter *)NULL);
+    delete _array_writer;
+    _array_writer = NULL;
+  }
+
+  _owns_writer = false;
+}

+ 10 - 2
panda/src/gobj/geomVertexWriter.h

@@ -81,6 +81,12 @@ PUBLISHED:
   INLINE GeomVertexWriter(GeomVertexArrayData *array_data);
   INLINE GeomVertexWriter(GeomVertexArrayData *array_data, 
                           int column);
+
+public:
+  INLINE GeomVertexWriter(GeomVertexDataPipelineWriter *data_writer,
+                          const InternalName *name);
+
+PUBLISHED:
   INLINE GeomVertexWriter(const GeomVertexWriter &copy);
   INLINE void operator = (const GeomVertexWriter &copy);
   INLINE ~GeomVertexWriter();
@@ -144,6 +150,7 @@ private:
   class Writer;
 
   void initialize();
+  void clear_writer();
 
   INLINE void set_pointer(int row);
   INLINE void quick_set_pointer(int row);
@@ -155,9 +162,10 @@ private:
   // must not keep a pointer to the particular ArrayData we are
   // working on (if we do, it may result in an extra copy of the data
   // due to holding the reference count).
-  PT(GeomVertexData) _vertex_data;
+  GeomVertexDataPipelineWriter *_data_writer;
   int _array;
-  PT(GeomVertexArrayData) _array_data;
+  GeomVertexArrayDataPipelineWriter *_array_writer;
+  bool _owns_writer;
 
   GeomVertexColumn::Packer *_packer;
   int _stride;

+ 5 - 3
panda/src/gobj/shaderContext.cxx

@@ -40,7 +40,7 @@ cp_report_error(ShaderArgInfo &p, const string &msg)
   if (p._direction == SAD_out)   dstr = "out ";
   if (p._direction == SAD_inout) dstr = "inout ";
 
-  string tstr = "unknown ";
+  string tstr = "invalid ";
   switch (p._type) {
   case SAT_float1: tstr = "float1 "; break;
   case SAT_float2: tstr = "float2 "; break;
@@ -51,6 +51,7 @@ cp_report_error(ShaderArgInfo &p, const string &msg)
   case SAT_sampler2d: tstr = "sampler2d "; break;
   case SAT_sampler3d: tstr = "sampler3d "; break;
   case SAT_samplercube: tstr = "samplercube "; break;
+  case SAT_unknown: tstr = "unknown "; break;
   }
 
   string fn = _shader_expansion->get_name();
@@ -70,7 +71,7 @@ cp_errchk_parameter_words(ShaderArgInfo &p, int len)
 {
   vector_string words;
   tokenize(p._name, words, "_");
-  if (words.size() != len) {
+  if ((int)words.size() != len) {
     cp_report_error(p, "parameter name has wrong number of words");
     return false;
   }
@@ -138,13 +139,14 @@ cp_errchk_parameter_uniform(ShaderArgInfo &p)
 bool ShaderContext::
 cp_errchk_parameter_float(ShaderArgInfo &p, int lo, int hi)
 {
-  int nfloat = 0;
+  int nfloat;
   switch (p._type) {
   case SAT_float1: nfloat = 1; break;
   case SAT_float2: nfloat = 2; break;
   case SAT_float3: nfloat = 3; break;
   case SAT_float4: nfloat = 4; break;
   case SAT_float4x4: nfloat = 16; break;
+  default: nfloat = 0; break;
   }
   if ((nfloat < lo)||(nfloat > hi)) {
     string msg = "wrong type for parameter: should be float";

+ 11 - 30
panda/src/gsgbase/graphicsStateGuardianBase.h

@@ -36,20 +36,12 @@ class OcclusionQueryContext;
 class GeomContext;
 class GeomNode;
 class Geom;
-class GeomPoint;
-class GeomLine;
-class GeomLinestrip;
-class GeomSprite;
-class GeomPolygon;
-class GeomQuad;
-class GeomTri;
-class GeomTristrip;
-class GeomTrifan;
-class GeomSphere;
-class Geom;
+class GeomPipelineReader;
 class GeomVertexData;
+class GeomVertexDataPipelineReader;
 class GeomVertexArrayData;
 class GeomPrimitive;
+class GeomPrimitivePipelineReader;
 class GeomTriangles;
 class GeomTristrips;
 class GeomTrifans;
@@ -177,26 +169,15 @@ public:
   // inconvenient to declare each of those types to be friends of this
   // class.
 
-  virtual void draw_point(GeomPoint *geom, GeomContext *gc) { }
-  virtual void draw_line(GeomLine *geom, GeomContext *gc) { }
-  virtual void draw_linestrip(GeomLinestrip *geom, GeomContext *gc) { }
-  virtual void draw_sprite(GeomSprite *geom, GeomContext *gc) { }
-  virtual void draw_polygon(GeomPolygon *geom, GeomContext *gc) { }
-  virtual void draw_quad(GeomQuad *geom, GeomContext *gc) { }
-  virtual void draw_tri(GeomTri *geom, GeomContext *gc) { }
-  virtual void draw_tristrip(GeomTristrip *geom, GeomContext *gc) { }
-  virtual void draw_trifan(GeomTrifan *geom, GeomContext *gc) { }
-  virtual void draw_sphere(GeomSphere *geom, GeomContext *gc) { }
-
-  virtual bool begin_draw_primitives(const Geom *geom, 
+  virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader, 
                                      const GeomMunger *munger,
-                                     const GeomVertexData *vertex_data)=0;
-  virtual void draw_triangles(const GeomTriangles *primitive)=0;
-  virtual void draw_tristrips(const GeomTristrips *primitive)=0;
-  virtual void draw_trifans(const GeomTrifans *primitive)=0;
-  virtual void draw_lines(const GeomLines *primitive)=0;
-  virtual void draw_linestrips(const GeomLinestrips *primitive)=0;
-  virtual void draw_points(const GeomPoints *primitive)=0;
+                                     const GeomVertexDataPipelineReader *data_reader)=0;
+  virtual void draw_triangles(const GeomPrimitivePipelineReader *reader)=0;
+  virtual void draw_tristrips(const GeomPrimitivePipelineReader *reader)=0;
+  virtual void draw_trifans(const GeomPrimitivePipelineReader *reader)=0;
+  virtual void draw_lines(const GeomPrimitivePipelineReader *reader)=0;
+  virtual void draw_linestrips(const GeomPrimitivePipelineReader *reader)=0;
+  virtual void draw_points(const GeomPrimitivePipelineReader *reader)=0;
   virtual void end_draw_primitives()=0;
 
   virtual void framebuffer_copy_to_texture

+ 4 - 0
panda/src/mathutil/mersenne.h

@@ -69,6 +69,10 @@ PUBLISHED:
   Mersenne(unsigned long seed);
   unsigned long get_uint31();
 
+  enum { 
+    max_value = 0x7fffffff
+  };
+
 private:
   enum {
     // Period parameters

+ 2 - 1
panda/src/mathutil/perlinNoise.h

@@ -21,6 +21,7 @@
 
 #include "pandabase.h"
 #include "pvector.h"
+#include "vector_int.h"
 #include "luse.h"
 #include "mersenne.h"
 
@@ -57,7 +58,7 @@ protected:
   static Mersenne _next_seed;
   static bool _got_first_seed;
 
-  typedef pvector<int> Index;
+  typedef vector_int Index;
   Index _index;
 };
 

+ 2 - 1
panda/src/parametrics/nurbsCurveDrawer.h

@@ -23,6 +23,7 @@
 
 #include "parametricCurveDrawer.h"
 #include "lineSegs.h"
+#include "vector_int.h"
 
 
 ////////////////////////////////////////////////////////////////////
@@ -57,7 +58,7 @@ protected:
   LVecBase3f _cv_color, _hull_color, _knot_color;
   int _num_cvs, _num_hull, _num_knots;
   LineSegs _hull, _knots, _cvs;
-  pvector<int> _knotnums;
+  vector_int _knotnums;
 
   bool _show_cvs, _show_hull, _show_knots;
 

+ 1 - 1
panda/src/particlesystem/spriteParticleRenderer.cxx

@@ -575,7 +575,7 @@ render(pvector< PT(PhysicsObject) >& po_vector, int ttl_particles) {
   // First, since this is the only time we have access to the actual particles, do some delayed initialization.
   if (_animate_frames || anim_count) {
     if (!_birth_list.empty()) {
-      for (pvector<int>::iterator vIter = _birth_list.begin(); vIter != _birth_list.end(); ++vIter) {
+      for (vector_int::iterator vIter = _birth_list.begin(); vIter != _birth_list.end(); ++vIter) {
         cur_particle = (BaseParticle*)po_vector[*vIter].p();
         i = int(NORMALIZED_RAND()*anim_count);
         

+ 3 - 2
panda/src/particlesystem/spriteParticleRenderer.h

@@ -32,6 +32,7 @@
 #include "geomVertexWriter.h"
 #include "textureCollection.h"
 #include "nodePathCollection.h"
+#include "vector_int.h"
 
 class NodePath;
 
@@ -273,9 +274,9 @@ private:
   virtual void resize_pool(int new_size);
   int extract_textures_from_node(const NodePath &node_path, NodePathCollection &np_col, TextureCollection &tex_col);
 
-  pvector<int> _anim_size;   // Holds the number of frames in each animation.
+  vector_int _anim_size;   // Holds the number of frames in each animation.
   pvector<int*> _ttl_count;  // _ttl_count[i][j] holds the number of particles attached to animation 'i' at frame 'j'.
-  pvector<int> _birth_list;  // Holds the list of particles that need a new random animation to start on.
+  vector_int _birth_list;  // Holds the list of particles that need a new random animation to start on.
 };
 
 #include "spriteParticleRenderer.I"

+ 3 - 1
panda/src/pgraph/Sources.pp

@@ -4,10 +4,12 @@
     lerp event gsgbase gobj putil linmath \
     downloader express pandabase pstatclient 
  
-
 #begin lib_target
   #define TARGET pgraph
 
+  // This directory is too big to combine into a single composite file.
+  #define DONT_COMBINE 1
+
   #define SOURCES \
     accumulatedAttribs.I accumulatedAttribs.h \
     alphaTestAttrib.I alphaTestAttrib.h \  

+ 2 - 1
panda/src/pgraph/geomTransformer.cxx

@@ -27,6 +27,7 @@
 #include "sliderTable.h"
 #include "pStatCollector.h"
 #include "pStatTimer.h"
+#include "vector_int.h"
 
 static PStatCollector apply_vertex_collector("*:Flatten:apply:vertex");
 static PStatCollector apply_texcoord_collector("*:Flatten:apply:texcoord");
@@ -483,7 +484,7 @@ collect_vertex_data(Geom *geom, int collect_bits) {
   // remapping transform indices in the vertices.  Each of these has a
   // slightly different way to handle the remapping, because they have
   // slightly different kinds of data.
-  typedef pvector<int> IndexMap;
+  typedef vector_int IndexMap;
 
   if (vdata->get_transform_table() != (TransformTable *)NULL) {
     // The TransformTable.

+ 0 - 3
panda/src/pgraph/shaderPool.cxx

@@ -25,9 +25,6 @@
 
 ShaderPool *ShaderPool::_global_ptr = (ShaderPool *)NULL;
 
-// ??? Is this needed ???
-static Loader model_loader;
-
 ////////////////////////////////////////////////////////////////////
 //     Function: ShaderPool::write
 //       Access: Published, Static

+ 22 - 0
panda/src/pipeline/Sources.pp

@@ -176,3 +176,25 @@
 
 #end test_bin_target
 
+
+#begin test_bin_target
+  #define TARGET test_delete
+  #define LOCAL_LIBS $[LOCAL_LIBS] pipeline
+  #define OTHER_LIBS dtoolutil:c dtool:m dtoolconfig:m pystub
+
+  #define SOURCES \
+    test_delete.cxx
+
+#end test_bin_target
+
+
+#begin test_bin_target
+  #define TARGET test_atomic
+  #define LOCAL_LIBS $[LOCAL_LIBS] pipeline
+  #define OTHER_LIBS dtoolutil:c dtool:m dtoolconfig:m pystub
+
+  #define SOURCES \
+    test_atomic.cxx
+
+#end test_bin_target
+

+ 4 - 4
panda/src/pipeline/config_pipeline.cxx

@@ -33,10 +33,10 @@ ConfigureFn(config_pipeline) {
 }
 
 ConfigVariableInt thread_stack_size
-("thread-stack-size", 16384,
- PRC_DESC("Specifies the size, in bytes, of the stack that will be created "
-          "for each newly-created thread.  Not all thread implementations "
-          "respect this value."));
+("thread-stack-size", 4194304,
+ PRC_DESC("Specifies the minimum size, in bytes, of the stack that will be "
+          "created for each newly-created thread.  Not all thread "
+          "implementations respect this value."));
 
 ConfigVariableBool threads_always_global
 ("threads-always-global", false,

+ 62 - 0
panda/src/pipeline/cycleDataStageWriter.I

@@ -35,6 +35,22 @@ CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int stage) :
   nassertv(_pointer != (CycleDataType *)NULL);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageWriter::Constructor (full)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataStageWriter<CycleDataType>::
+CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int stage,
+                     bool force_to_0) :
+  _cycler(&cycler),
+  _stage(stage)
+{
+  _pointer = _cycler->write_stage_upstream(_stage, force_to_0);
+  nassertv(_pointer != (CycleDataType *)NULL);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CycleDataStageWriter::Copy Constructor (full)
 //       Access: Public
@@ -86,6 +102,25 @@ CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int stage,
   _pointer = _cycler->elevate_read_stage(_stage, take_from.take_pointer());
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageWriter::Constructor (full)
+//       Access: Public
+//  Description: This flavor of the constructor elevates the pointer
+//               from the CycleDataStageReader from a read to a write
+//               pointer (and invalidates the reader).
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataStageWriter<CycleDataType>::
+CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int stage,
+                     CycleDataStageReader<CycleDataType> &take_from,
+                     bool force_to_0) :
+  _cycler(&cycler),
+  _stage(stage)
+{
+  _pointer = _cycler->elevate_read_stage_upstream(_stage, take_from.take_pointer(),
+                                                  force_to_0);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CycleDataStageWriter::Destructor (full)
 //       Access: Public
@@ -152,6 +187,17 @@ CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int) {
   _pointer = cycler.write();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageWriter::Constructor (trivial)
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataStageWriter<CycleDataType>::
+CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int, bool) {
+  _pointer = cycler.write();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CycleDataStageWriter::Copy Constructor (trivial)
 //       Access: Public
@@ -190,6 +236,22 @@ CycleDataStageWriter(PipelineCycler<CycleDataType> &, int,
 {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CycleDataStageWriter::Constructor (trivial)
+//       Access: Public
+//  Description: This flavor of the constructor elevates the pointer
+//               from the CycleDataStageReader from a read to a write
+//               pointer (and invalidates the reader).
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataStageWriter<CycleDataType>::
+CycleDataStageWriter(PipelineCycler<CycleDataType> &, int,
+                     CycleDataStageReader<CycleDataType> &take_from,
+                     bool) :
+  _pointer((CycleDataType *)take_from.take_pointer())
+{
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CycleDataStageWriter::Destructor (trivial)
 //       Access: Public

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

@@ -38,11 +38,16 @@ template<class CycleDataType>
 class CycleDataStageWriter {
 public:
   INLINE CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int stage);
+  INLINE CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int stage,
+                              bool force_to_0);
   INLINE CycleDataStageWriter(const CycleDataStageWriter<CycleDataType> &copy);
   INLINE void operator = (const CycleDataStageWriter<CycleDataType> &copy);
 
   INLINE CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int stage,
                               CycleDataStageReader<CycleDataType> &take_from);
+  INLINE CycleDataStageWriter(PipelineCycler<CycleDataType> &cycler, int stage,
+                              CycleDataStageReader<CycleDataType> &take_from,
+                              bool force_to_0);
 
   INLINE ~CycleDataStageWriter();
 

+ 1 - 3
panda/src/pipeline/mutexDebug.cxx

@@ -140,13 +140,11 @@ do_lock() {
     }
     while (_locking_thread != (Thread *)NULL) {
       _cvar.wait();
-      thread_cat.spam()
-        << *this_thread << " wakeup\n";
     }
     
     if (thread_cat.is_spam()) {
       thread_cat.spam()
-        << *this_thread << " awake\n";
+        << *this_thread << " awake on " << *this << "\n";
     }
 
     this_thread->_blocked_on_mutex = NULL;

+ 66 - 0
panda/src/pipeline/pipelineCycler.I

@@ -122,6 +122,17 @@ elevate_read_stage(int n, const CycleDataType *pointer) {
   return (CycleDataType *)PipelineCyclerBase::elevate_read_stage(n, pointer);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::elevate_read_stage_upstream (dummy or true)
+//       Access: Public
+//  Description: See PipelineCyclerBase::elevate_read_stage_upstream().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+elevate_read_stage_upstream(int n, const CycleDataType *pointer, bool force_to_0) {
+  return (CycleDataType *)PipelineCyclerBase::elevate_read_stage_upstream(n, pointer, force_to_0);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCycler::write_upstream (dummy or true)
 //       Access: Public
@@ -144,6 +155,17 @@ write_stage(int n) {
   return (CycleDataType *)PipelineCyclerBase::write_stage(n);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::write_stage_upstream (dummy or true)
+//       Access: Public
+//  Description: See PipelineCyclerBase::write_stage_upstream().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+write_stage_upstream(int pipeline_stage, bool force_to_0) {
+  return (CycleDataType *)PipelineCyclerBase::write_stage_upstream(pipeline_stage, force_to_0);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCycler::cheat (dummy or true)
 //       Access: Public
@@ -212,6 +234,17 @@ read() const {
   return &_typed_data;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::read_stage (trivial)
+//       Access: Public
+//  Description: See PipelineCyclerBase::read_stage().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE const CycleDataType *PipelineCycler<CycleDataType>::
+read_stage(int) const {
+  return &_typed_data;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCycler::write (trivial)
 //       Access: Public
@@ -245,6 +278,28 @@ elevate_read_upstream(const CycleDataType *, bool) {
   return &_typed_data;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::elevate_read_stage (trivial)
+//       Access: Public
+//  Description: See PipelineCyclerBase::elevate_read_stage().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+elevate_read_stage(int, const CycleDataType *) {
+  return &_typed_data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::elevate_read_stage_upstream (trivial)
+//       Access: Public
+//  Description: See PipelineCyclerBase::elevate_read_stage_upstream().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+elevate_read_stage_upstream(int, const CycleDataType *, bool) {
+  return &_typed_data;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCycler::write_upstream (trivial)
 //       Access: Public
@@ -267,6 +322,17 @@ write_stage(int) {
   return &_typed_data;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCycler::write_stage_upstream (trivial)
+//       Access: Public
+//  Description: See PipelineCyclerBase::write_stage_upstream().
+////////////////////////////////////////////////////////////////////
+template<class CycleDataType>
+INLINE CycleDataType *PipelineCycler<CycleDataType>::
+write_stage_upstream(int, bool) {
+  return &_typed_data;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCycler::cheat (trivial)
 //       Access: Public

+ 3 - 0
panda/src/pipeline/pipelineCycler.h

@@ -22,6 +22,7 @@
 #include "pandabase.h"
 #include "pipelineCyclerBase.h"
 #include "cyclerHolder.h"
+#include "thread.h"     // for convenience of code that uses PipelineReader classes
 
 ////////////////////////////////////////////////////////////////////
 //       Class : PipelineCycler
@@ -68,7 +69,9 @@ public:
   INLINE CycleDataType *elevate_read(const CycleDataType *pointer);
   INLINE CycleDataType *elevate_read_upstream(const CycleDataType *pointer, bool force_to_0);
   INLINE CycleDataType *elevate_read_stage(int n, const CycleDataType *pointer);
+  INLINE CycleDataType *elevate_read_stage_upstream(int n, const CycleDataType *pointer, bool force_to_0);
   INLINE CycleDataType *write_upstream(bool force_to_0);
+  INLINE CycleDataType *write_stage_upstream(int pipeline_stage, bool force_to_0);
   INLINE CycleDataType *write_stage(int n);
 
   INLINE CycleDataType *cheat() const;

+ 35 - 0
panda/src/pipeline/pipelineCyclerDummyImpl.I

@@ -353,6 +353,24 @@ write_stage(int n) {
   return _data;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::write_stage_upstream
+//       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_upstream(int n, bool) {
+  TAU_PROFILE("CycleData *PipelineCyclerDummyImpl::write_stage_upstream(int)", " ", TAU_USER);
+  nassertr(n == 0, (CycleData *)NULL);
+  _write_count++;
+  return _data;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCyclerDummyImpl::elevate_read_stage
 //       Access: Public
@@ -370,6 +388,23 @@ elevate_read_stage(int n, const CycleData *pointer) {
   return write();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerDummyImpl::elevate_read_stage_upstream
+//       Access: Public
+//  Description: Elevates a currently-held read pointer into a write
+//               pointer.  This may or may not change the value of the
+//               pointer.  It is only valid to do this if this is the
+//               only currently-outstanding read pointer on the
+//               current stage.
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerDummyImpl::
+elevate_read_stage_upstream(int n, const CycleData *pointer, bool) {
+  TAU_PROFILE("CycleData *PipelineCyclerDummyImpl::elevate_read_stage(int, CycleData *)", " ", TAU_USER);
+  nassertr(n == 0, NULL);
+  release_read(pointer);
+  return write();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCyclerDummyImpl::release_write_stage
 //       Access: Public

+ 3 - 0
panda/src/pipeline/pipelineCyclerDummyImpl.h

@@ -70,7 +70,10 @@ public:
   INLINE void release_read_stage(int n, const CycleData *pointer) const;
   INLINE CycleData *write_upstream(bool force_to_0);
   INLINE CycleData *write_stage(int n);
+  INLINE CycleData *write_stage_upstream(int n, bool force_to_0);
   INLINE CycleData *elevate_read_stage(int n, const CycleData *pointer);
+  INLINE CycleData *elevate_read_stage_upstream(int n, const CycleData *pointer,
+                                                bool force_to_0);
   INLINE void release_write_stage(int n, CycleData *pointer);
 
   INLINE TypeHandle get_parent_type() const;

+ 37 - 0
panda/src/pipeline/pipelineCyclerTrivialImpl.I

@@ -311,6 +311,25 @@ write_stage(int) {
 #endif  // SIMPLE_STRUCT_POINTERS
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::write_stage_upstream
+//       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_upstream(int, bool) {
+#ifdef SIMPLE_STRUCT_POINTERS
+  return (CycleData *)this;
+#else
+  return _data;
+#endif  // SIMPLE_STRUCT_POINTERS
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCyclerTrivialImpl::elevate_read_stage
 //       Access: Public
@@ -329,6 +348,24 @@ elevate_read_stage(int, const CycleData *) {
 #endif  // SIMPLE_STRUCT_POINTERS
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrivialImpl::elevate_read_stage_upstream
+//       Access: Public
+//  Description: Elevates a currently-held read pointer into a write
+//               pointer.  This may or may not change the value of the
+//               pointer.  It is only valid to do this if this is the
+//               only currently-outstanding read pointer on the
+//               current stage.
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerTrivialImpl::
+elevate_read_stage_upstream(int, const CycleData *, bool) {
+#ifdef SIMPLE_STRUCT_POINTERS
+  return (CycleData *)this;
+#else
+  return _data;
+#endif  // SIMPLE_STRUCT_POINTERS
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCyclerTrivialImpl::release_write_stage
 //       Access: Public

+ 3 - 0
panda/src/pipeline/pipelineCyclerTrivialImpl.h

@@ -74,7 +74,10 @@ public:
   INLINE void release_read_stage(int n, const CycleData *pointer) const;
   INLINE CycleData *write_upstream(bool force_to_0);
   INLINE CycleData *write_stage(int n);
+  INLINE CycleData *write_stage_upstream(int n, bool force_to_0);
   INLINE CycleData *elevate_read_stage(int n, const CycleData *pointer);
+  INLINE CycleData *elevate_read_stage_upstream(int n, const CycleData *pointer,
+                                                bool force_to_0);
   INLINE void release_write_stage(int n, CycleData *pointer);
 
   INLINE TypeHandle get_parent_type() const;

+ 56 - 0
panda/src/pipeline/pipelineCyclerTrueImpl.I

@@ -243,6 +243,41 @@ release_read_stage(int n, const CycleData *pointer) const {
   _lock.release();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::write_upstream
+//       Access: Public
+//  Description: This special variant on write() will automatically
+//               propagate changes back to upstream pipeline stages.
+//               If force_to_0 is false, then it propagates back only
+//               as long as the CycleData pointers are equivalent,
+//               guaranteeing that it does not modify upstream data
+//               (other than the modification that will be performed
+//               by the code that returns this pointer).  This is
+//               particularly appropriate for minor updates, where it
+//               doesn't matter much if the update is lost, such as
+//               storing a cached value.
+//
+//               If force_to_0 is true, then the CycleData pointer for
+//               the current pipeline stage is propagated all the way
+//               back up to stage 0; after this call, there will be
+//               only one CycleData pointer that is duplicated in all
+//               stages between stage 0 and the current stage.  This
+//               may undo some recent changes that were made
+//               independently at pipeline stage 0 (or any other
+//               upstream stage).  However, it guarantees that the
+//               change that is to be applied at this pipeline stage
+//               will stick.  This is slightly dangerous because of
+//               the risk of losing upstream changes; generally, this
+//               should only be done when you are confident that there
+//               are no upstream changes to be lost (for instance, for
+//               an object that has been recently created).
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerTrueImpl::
+write_upstream(bool force_to_0) {
+  int pipeline_stage = Thread::get_current_pipeline_stage();
+  return write_stage_upstream(pipeline_stage, force_to_0);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCyclerTrueImpl::elevate_read_stage
 //       Access: Public
@@ -264,6 +299,27 @@ elevate_read_stage(int n, const CycleData *pointer) {
   return new_pointer;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::elevate_read_stage_upstream
+//       Access: Public
+//  Description: Elevates a currently-held read pointer into a write
+//               pointer.  This may or may not change the value of the
+//               pointer.  It is only valid to do this if this is the
+//               only currently-outstanding read pointer on the
+//               indicated stage.
+////////////////////////////////////////////////////////////////////
+INLINE CycleData *PipelineCyclerTrueImpl::
+elevate_read_stage_upstream(int n, const CycleData *pointer, bool force_to_0) {
+  TAU_PROFILE("CycleData *PipelineCyclerTrueImpl::elevate_read_stage(int, const CycleData *)", " ", TAU_USER);
+#ifdef _DEBUG
+  nassertr(n >= 0 && n < _num_stages, NULL);
+  nassertr(_data[n] == pointer, NULL);
+#endif
+  CycleData *new_pointer = write_stage_upstream(n, force_to_0);
+  _lock.release();
+  return new_pointer;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCyclerTrueImpl::release_write_stage
 //       Access: Public

+ 42 - 66
panda/src/pipeline/pipelineCyclerTrueImpl.cxx

@@ -130,39 +130,52 @@ PipelineCyclerTrueImpl::
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerTrueImpl::write_upstream
+//     Function: PipelineCyclerTrueImpl::write_stage
 //       Access: Public
-//  Description: This special variant on write() will automatically
-//               propagate changes back to upstream pipeline stages.
-//               If force_to_0 is false, then it propagates back only
-//               as long as the CycleData pointers are equivalent,
-//               guaranteeing that it does not modify upstream data
-//               (other than the modification that will be performed
-//               by the code that returns this pointer).  This is
-//               particularly appropriate for minor updates, where it
-//               doesn't matter much if the update is lost, such as
-//               storing a cached value.
-//
-//               If force_to_0 is true, then the CycleData pointer for
-//               the current pipeline stage is propagated all the way
-//               back up to stage 0; after this call, there will be
-//               only one CycleData pointer that is duplicated in all
-//               stages between stage 0 and the current stage.  This
-//               may undo some recent changes that were made
-//               independently at pipeline stage 0 (or any other
-//               upstream stage).  However, it guarantees that the
-//               change that is to be applied at this pipeline stage
-//               will stick.  This is slightly dangerous because of
-//               the risk of losing upstream changes; generally, this
-//               should only be done when you are confident that there
-//               are no upstream changes to be lost (for instance, for
-//               an object that has been recently created).
+//  Description: Returns a pointer suitable for writing to the nth
+//               stage of the pipeline.  This is for special
+//               applications that need to update the entire pipeline
+//               at once (for instance, to remove an invalid pointer).
+//               This pointer should later be released with
+//               release_write_stage().
 ////////////////////////////////////////////////////////////////////
 CycleData *PipelineCyclerTrueImpl::
-write_upstream(bool force_to_0) {
+write_stage(int pipeline_stage) {
+  _lock.lock();
+
+#ifndef NDEBUG
+  nassertd(pipeline_stage >= 0 && pipeline_stage < _num_stages) {
+    _lock.release();
+    return NULL;
+  }
+#endif  // NDEBUG
+
+  CycleData *old_data = _data[pipeline_stage];
+
+  if (old_data->get_ref_count() != 1) {
+    // Copy-on-write.
+    _data[pipeline_stage] = old_data->make_copy();
+
+    // Now we have differences between some of the data pointers, so
+    // we're "dirty".  Mark it so.
+    if (!_dirty) {
+      _pipeline->add_dirty_cycler(this);
+    }
+  }
+
+  return _data[pipeline_stage];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PipelineCyclerTrueImpl::write_stage_upstream
+//       Access: Public
+//  Description: This special variant on write_stage() will
+//               automatically propagate changes back to upstream
+//               pipeline stages.  See write_upstream().
+////////////////////////////////////////////////////////////////////
+CycleData *PipelineCyclerTrueImpl::
+write_stage_upstream(int pipeline_stage, bool force_to_0) {
   _lock.lock();
-  
-  int pipeline_stage = Thread::get_current_pipeline_stage();
 
 #ifndef NDEBUG
   nassertd(pipeline_stage >= 0 && pipeline_stage < _num_stages) {
@@ -218,43 +231,6 @@ write_upstream(bool force_to_0) {
   return _data[pipeline_stage];
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: PipelineCyclerTrueImpl::write_stage
-//       Access: Public
-//  Description: Returns a pointer suitable for writing to the nth
-//               stage of the pipeline.  This is for special
-//               applications that need to update the entire pipeline
-//               at once (for instance, to remove an invalid pointer).
-//               This pointer should later be released with
-//               release_write_stage().
-////////////////////////////////////////////////////////////////////
-CycleData *PipelineCyclerTrueImpl::
-write_stage(int pipeline_stage) {
-  _lock.lock();
-
-#ifndef NDEBUG
-  nassertd(pipeline_stage >= 0 && pipeline_stage < _num_stages) {
-    _lock.release();
-    return NULL;
-  }
-#endif  // NDEBUG
-
-  CycleData *old_data = _data[pipeline_stage];
-
-  if (old_data->get_ref_count() != 1) {
-    // Copy-on-write.
-    _data[pipeline_stage] = old_data->make_copy();
-
-    // Now we have differences between some of the data pointers, so
-    // we're "dirty".  Mark it so.
-    if (!_dirty) {
-      _pipeline->add_dirty_cycler(this);
-    }
-  }
-
-  return _data[pipeline_stage];
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: PipelineCyclerTrueImpl::cycle
 //       Access: Private

+ 3 - 1
panda/src/pipeline/pipelineCyclerTrueImpl.h

@@ -73,9 +73,11 @@ public:
   INLINE int get_num_stages();
   INLINE const CycleData *read_stage(int n) const;
   INLINE void release_read_stage(int n, const CycleData *pointer) const;
-  CycleData *write_upstream(bool force_to_0);
+  INLINE CycleData *write_upstream(bool force_to_0);
   CycleData *write_stage(int pipeline_stage);
+  CycleData *write_stage_upstream(int pipeline_stage, bool force_to_0);
   INLINE CycleData *elevate_read_stage(int n, const CycleData *pointer);
+  INLINE CycleData *elevate_read_stage_upstream(int n, const CycleData *pointer, bool force_to_0);
   INLINE void release_write_stage(int n, CycleData *pointer);
 
   INLINE TypeHandle get_parent_type() const;

+ 1 - 0
panda/src/pipeline/select.tau

@@ -1,3 +1,4 @@
 BEGIN_EXCLUDE_LIST
 void *ThreadPosixImpl::root_func#
+void *ThreadWin32Impl::root_func#
 END_EXCLUDE_LIST

+ 142 - 0
panda/src/pipeline/test_delete.cxx

@@ -0,0 +1,142 @@
+// Filename: test_delete.cxx
+// Created by:  drose (18Apr06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "pandabase.h"
+#include "thread.h"
+#include "pmutex.h"
+#include "mutexHolder.h"
+#include "deletedChain.h"
+
+// This is the number of passes to make per thread.
+static const int num_passes = 100;
+
+// This is the number of doobers to allocate in each pass.
+static const int num_doobers_per_pass = 1000000;
+
+// This is the max number to allocate in each chunk.
+static const int max_doobers_per_chunk = 1000;
+
+// The number of threads to spawn.
+static const int number_of_threads = 4;
+
+#ifdef WIN32_VC
+static int last_rand = 0;
+#endif /* __WIN32__ */
+
+Mutex rand_mutex;
+
+static double
+random_f(double max) {
+  MutexHolder l(rand_mutex);
+  int i = rand();
+#ifdef WIN32_VC
+  last_rand = i;
+#endif /* __WIN32__ */
+  return max * (double)i / (double)RAND_MAX;
+}
+
+#define OUTPUT(stuff) { \
+  MutexHolder holder(Mutex::_notify_mutex); \
+  stuff; \
+}
+
+class Doober {
+public:
+  Doober(int counter) : _counter(counter) {
+  }
+  ~Doober() {
+    nassertv(_counter != -1);
+    _counter = -1;
+  }
+  ALLOC_DELETED_CHAIN(Doober);
+
+  int _counter;
+};
+typedef pvector<Doober *> Doobers;
+
+
+class MyThread : public Thread {
+public:
+  MyThread(const string &name) : Thread(name, name)
+  {
+  }
+    
+  virtual void
+  thread_main() {
+    OUTPUT(nout << *this << " beginning.\n");
+
+#ifdef WIN32_VC
+    rand_mutex.lock();
+    srand(last_rand);
+    rand_mutex.release();
+    random_f(1.0);
+#endif /* __WIN32__ */
+
+    Doobers doobers;
+
+    for (int i = 0; i < num_passes; ++i) {
+      int counter = 0;
+
+      while (counter < num_doobers_per_pass) {
+        int num_alloc = (int)random_f(max_doobers_per_chunk);
+        for (int i = 0; i < num_alloc; ++i) {
+          doobers.push_back(new Doober(++counter));
+        }
+        int num_del = (int)random_f(max_doobers_per_chunk);
+        num_del = min(num_del, (int)doobers.size());
+        
+        for (int j = 0; j < num_del; ++j) {
+          assert(!doobers.empty());
+          delete doobers.back();
+          doobers.pop_back();
+        }
+      }
+      OUTPUT(nout << get_name() << " has " << doobers.size() << " doobers.\n");
+    }
+
+    OUTPUT(nout << *this << " ending.\n");
+  }
+
+};
+
+int
+main(int argc, char *argv[]) {
+  OUTPUT(nout << "Making " << number_of_threads << " threads.\n");
+
+  typedef pvector< PT(MyThread) > Threads;
+  Threads threads;
+
+  PT(MyThread) thread = new MyThread("a");
+  threads.push_back(thread);
+  thread->start(TP_normal, true, true);
+
+  for (int i = 1; i < number_of_threads; ++i) {
+    char name = 'a' + i;
+    PT(MyThread) thread = new MyThread(string(1, name));
+    threads.push_back(thread);
+    thread->start(TP_normal, true, true);
+  }
+
+  // Now join all the threads.
+  Threads::iterator ti;
+  for (ti = threads.begin(); ti != threads.end(); ++ti) {
+    (*ti)->join();
+  }
+
+  exit(0);
+}

+ 2 - 2
panda/src/pipeline/test_diners.cxx

@@ -35,8 +35,6 @@
 static int last_rand = 0;
 #endif /* __WIN32__ */
 
-#define PRINTMSG(x) { MutexHolder l(Mutex::_notify_mutex); x << flush; }
-
 Mutex rand_mutex;
 
 static double random_f(double max)
@@ -49,6 +47,8 @@ static double random_f(double max)
   return max * (double)i / (double)RAND_MAX;
 }
 
+#define PRINTMSG(x) { MutexHolder l(Mutex::_notify_mutex); x << flush; }
+
 // n philosophers sharing n chopsticks.  Philosophers are poor folk and can't
 // afford luxuries like 2 chopsticks per person.
 #define N_DINERS 5

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

@@ -84,9 +84,15 @@ start(ThreadPriority priority, bool global, bool joinable) {
     _detached = true;
   }
 
+  int result = pthread_attr_setstacksize(&attr, thread_stack_size);
+  if (result != 0) {
+    thread_cat.warning()
+      << "Unable to set stack size.\n";
+  }
+
   // Ensure the thread has "system" scope, which should ensure it can
   // run in parallel with other threads.
-  int result = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
+  result = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
   if (result != 0) {
     thread_cat.warning()
       << "Unable to set system scope.\n";

+ 37 - 32
panda/src/pipeline/threadWin32Impl.cxx

@@ -146,45 +146,50 @@ join() {
 ////////////////////////////////////////////////////////////////////
 DWORD ThreadWin32Impl::
 root_func(LPVOID data) {
-  ThreadWin32Impl *self = (ThreadWin32Impl *)data;
-  BOOL result = TlsSetValue(_pt_ptr_index, self->_parent_obj);
-  nassertr(result, 1);
-
+  TAU_REGISTER_THREAD();
   {
-    self->_mutex.lock();
-    nassertd(self->_status == S_start_called) {
+    TAU_PROFILE("void ThreadPosixImpl::root_func()", " ", TAU_USER);
+
+    ThreadWin32Impl *self = (ThreadWin32Impl *)data;
+    BOOL result = TlsSetValue(_pt_ptr_index, self->_parent_obj);
+    nassertr(result, 1);
+    
+    {
+      self->_mutex.lock();
+      nassertd(self->_status == S_start_called) {
+        self->_mutex.release();
+        return 1;
+      }
+      self->_status = S_running;
+      self->_cv.signal();
       self->_mutex.release();
-      return 1;
     }
-    self->_status = S_running;
-    self->_cv.signal();
-    self->_mutex.release();
-  }
-
-  self->_parent_obj->thread_main();
-
-  if (thread_cat.is_debug()) {
-    thread_cat.debug()
-      << "Terminating thread " << self->_parent_obj->get_name() 
-      << ", count = " << self->_parent_obj->get_ref_count() << "\n";
-  }
-
-  {
-    self->_mutex.lock();
-    nassertd(self->_status == S_running) {
+    
+    self->_parent_obj->thread_main();
+    
+    if (thread_cat.is_debug()) {
+      thread_cat.debug()
+        << "Terminating thread " << self->_parent_obj->get_name() 
+        << ", count = " << self->_parent_obj->get_ref_count() << "\n";
+    }
+    
+    {
+      self->_mutex.lock();
+      nassertd(self->_status == S_running) {
+        self->_mutex.release();
+        return 1;
+      }
+      self->_status = S_finished;
+      self->_cv.signal();
       self->_mutex.release();
-      return 1;
     }
-    self->_status = S_finished;
-    self->_cv.signal();
-    self->_mutex.release();
+    
+    // Now drop the parent object reference that we grabbed in start().
+    // This might delete the parent object, and in turn, delete the
+    // ThreadWin32Impl object.
+    unref_delete(self->_parent_obj);
   }
 
-  // Now drop the parent object reference that we grabbed in start().
-  // This might delete the parent object, and in turn, delete the
-  // ThreadWin32Impl object.
-  unref_delete(self->_parent_obj);
-
   return 0;
 }
 

+ 3 - 3
panda/src/pstatclient/pStatClient.cxx

@@ -409,7 +409,7 @@ make_collector_with_name(int parent_index, const string &name) {
   // initialize_collector_def(this, collector->_def);
 
   // We need one PerThreadData for each thread.
-  while (collector->_per_thread.size() < _num_threads) {
+  while ((int)collector->_per_thread.size() < _num_threads) {
     collector->_per_thread.push_back(PerThreadData());
   }
   add_collector(collector);
@@ -787,8 +787,8 @@ add_level(int collector_index, int thread_index, float increment) {
 float PStatClient::
 get_level(int collector_index, int thread_index) const {
 #ifdef _DEBUG
-  nassertv(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors));
-  nassertv(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads));
+  nassertr(collector_index >= 0 && collector_index < AtomicAdjust::get(_num_collectors), 0.0f);
+  nassertr(thread_index >= 0 && thread_index < AtomicAdjust::get(_num_threads), 0.0f);
 #endif
 
   Collector *collector = get_collector_ptr(collector_index);

+ 1 - 2
panda/src/putil/datagramInputFile.cxx

@@ -16,7 +16,7 @@
 //
 ////////////////////////////////////////////////////////////////////
 
-
+#include "datagramInputFile.h"
 #include "numeric_types.h"
 #include "datagramIterator.h"
 #include "profileTimer.h"
@@ -24,7 +24,6 @@
 #include "config_express.h"
 #include "virtualFileSystem.h"
 #include "streamReader.h"
-#include "datagramInputFile.h"
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DatagramInputFile::open

+ 1 - 1
panda/src/windisplay/Sources.pp

@@ -22,7 +22,7 @@
     config_windisplay.cxx winGraphicsPipe.cxx
 
   #define SOURCES \
-    winGraphicsWindow.cxx $[INCLUDED_SOURCES] $[INSTALL_HEADERS]
+    winGraphicsWindow.cxx $[INSTALL_HEADERS]
 
   #define WIN_SYS_LIBS Imm32.lib winmm.lib kernel32.lib oldnames.lib user32.lib gdi32.lib
 

+ 2 - 2
pandatool/src/bam/bamToEgg.cxx

@@ -336,7 +336,7 @@ convert_triangles(const GeomVertexData *vertex_data,
       Vertexf vertex = reader.get_data3f();
       egg_vert.set_pos(LCAST(double, vertex * net_mat));
 
-      if (vertex_data->has_normal()) {
+      if (vertex_data->has_column(InternalName::get_normal())) {
         reader.set_column(InternalName::get_normal());
         Normalf normal = reader.get_data3f();
         egg_vert.set_normal(LCAST(double, normal * net_mat));
@@ -346,7 +346,7 @@ convert_triangles(const GeomVertexData *vertex_data,
 
       } else if (!has_color_off) {
         Colorf color(1.0f, 1.0f, 1.0f, 1.0f);
-        if (vertex_data->has_color()) {
+        if (vertex_data->has_column(InternalName::get_color())) {
           reader.set_column(InternalName::get_color());
           color = reader.get_data4f();
         }