2
0
Эх сурвалжийг харах

Big patch full of performance improvements
This particularly benefits applications with a lot of nodes.

rdb 8 жил өмнө
parent
commit
6f8b379bda
100 өөрчлөгдсөн 1381 нэмэгдсэн , 982 устгасан
  1. 4 0
      dtool/src/dtoolbase/typeHandle.cxx
  2. 3 4
      dtool/src/prc/configVariableBool.I
  3. 9 0
      dtool/src/prc/configVariableBool.cxx
  4. 6 4
      dtool/src/prc/configVariableBool.h
  5. 10 0
      dtool/src/prc/notifyCategory.I
  6. 10 0
      dtool/src/prc/notifyCategoryProxy.I
  7. 13 6
      dtool/src/prc/pnotify.h
  8. 5 3
      panda/src/char/characterJointEffect.cxx
  9. 1 1
      panda/src/char/characterJointEffect.h
  10. 7 5
      panda/src/collide/collisionLevelState.I
  11. 1 1
      panda/src/collide/collisionLevelStateBase.h
  12. 1 1
      panda/src/collide/collisionNode.cxx
  13. 2 0
      panda/src/collide/collisionNode.h
  14. 36 22
      panda/src/collide/collisionTraverser.cxx
  15. 1 4
      panda/src/collide/collisionVisualizer.cxx
  16. 74 69
      panda/src/display/graphicsStateGuardian.cxx
  17. 1 1
      panda/src/display/graphicsStateGuardian.h
  18. 1 1
      panda/src/distort/projectionScreen.cxx
  19. 7 4
      panda/src/express/memoryUsage.I
  20. 10 0
      panda/src/express/memoryUsage.cxx
  21. 2 0
      panda/src/express/memoryUsage.h
  22. 32 22
      panda/src/express/pointerToBase.I
  23. 1 1
      panda/src/express/pointerToBase.h
  24. 5 0
      panda/src/glstuff/glCgShaderContext_src.cxx
  25. 6 4
      panda/src/glstuff/glCgShaderContext_src.h
  26. 1 1
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  27. 66 17
      panda/src/glstuff/glShaderContext_src.cxx
  28. 11 9
      panda/src/glstuff/glShaderContext_src.h
  29. 9 5
      panda/src/gobj/adaptiveLru.cxx
  30. 63 15
      panda/src/gobj/geom.cxx
  31. 13 2
      panda/src/gobj/geomPrimitive.I
  32. 95 31
      panda/src/gobj/geomPrimitive.cxx
  33. 1 0
      panda/src/gobj/geomPrimitive.h
  34. 2 2
      panda/src/gobj/geomVertexData.I
  35. 6 2
      panda/src/gobj/geomVertexData.h
  36. 6 0
      panda/src/gobj/internalName.h
  37. 6 4
      panda/src/gobj/shader.cxx
  38. 2 1
      panda/src/gobj/shader.h
  39. 4 1
      panda/src/gobj/shaderContext.h
  40. 13 22
      panda/src/grutil/pipeOcclusionCullTraverser.cxx
  41. 5 5
      panda/src/grutil/shaderTerrainMesh.cxx
  42. 3 0
      panda/src/mathutil/geometricBoundingVolume.I
  43. 6 0
      panda/src/mathutil/geometricBoundingVolume.h
  44. 2 2
      panda/src/parametrics/ropeNode.cxx
  45. 1 1
      panda/src/parametrics/sheetNode.cxx
  46. 1 1
      panda/src/pgraph/billboardEffect.cxx
  47. 1 1
      panda/src/pgraph/billboardEffect.h
  48. 1 1
      panda/src/pgraph/compassEffect.cxx
  49. 1 1
      panda/src/pgraph/compassEffect.h
  50. 0 3
      panda/src/pgraph/config_pgraph.cxx
  51. 2 3
      panda/src/pgraph/cullPlanes.cxx
  52. 6 0
      panda/src/pgraph/cullPlanes.h
  53. 2 8
      panda/src/pgraph/cullTraverser.I
  54. 52 61
      panda/src/pgraph/cullTraverser.cxx
  55. 19 41
      panda/src/pgraph/cullTraverserData.I
  56. 62 31
      panda/src/pgraph/cullTraverserData.cxx
  57. 12 10
      panda/src/pgraph/cullTraverserData.h
  58. 3 8
      panda/src/pgraph/geomTransformer.cxx
  59. 26 26
      panda/src/pgraph/nodePath.I
  60. 38 21
      panda/src/pgraph/nodePath.cxx
  61. 3 2
      panda/src/pgraph/nodePath.h
  62. 9 0
      panda/src/pgraph/nodePathComponent.I
  63. 0 11
      panda/src/pgraph/nodePathComponent.cxx
  64. 8 2
      panda/src/pgraph/nodePathComponent.h
  65. 36 36
      panda/src/pgraph/nodePath_ext.cxx
  66. 2 2
      panda/src/pgraph/pandaNode.I
  67. 9 2
      panda/src/pgraph/pandaNode.h
  68. 6 6
      panda/src/pgraph/polylightEffect.cxx
  69. 1 1
      panda/src/pgraph/portalClipper.h
  70. 2 2
      panda/src/pgraph/portalNode.cxx
  71. 15 12
      panda/src/pgraph/renderAttrib.cxx
  72. 1 1
      panda/src/pgraph/renderAttrib.h
  73. 1 1
      panda/src/pgraph/renderEffect.cxx
  74. 1 1
      panda/src/pgraph/renderEffect.h
  75. 1 1
      panda/src/pgraph/renderEffects.cxx
  76. 1 1
      panda/src/pgraph/renderEffects.h
  77. 2 1
      panda/src/pgraph/renderState.I
  78. 38 24
      panda/src/pgraph/renderState.cxx
  79. 7 1
      panda/src/pgraph/renderState.h
  80. 15 15
      panda/src/pgraph/shaderAttrib.I
  81. 90 76
      panda/src/pgraph/shaderAttrib.cxx
  82. 6 4
      panda/src/pgraph/shaderAttrib.h
  83. 83 7
      panda/src/pgraph/shaderInput.I
  84. 26 7
      panda/src/pgraph/shaderInput.cxx
  85. 11 24
      panda/src/pgraph/shaderInput.h
  86. 151 207
      panda/src/pgraph/transformState.cxx
  87. 9 5
      panda/src/pgraph/transformState.h
  88. 1 1
      panda/src/pgraphnodes/ambientLight.h
  89. 1 1
      panda/src/pgraphnodes/fadeLodNode.cxx
  90. 1 1
      panda/src/pgraphnodes/lightLensNode.I
  91. 1 1
      panda/src/pgraphnodes/lightLensNode.h
  92. 5 11
      panda/src/pgraphnodes/lodNode.cxx
  93. 1 1
      panda/src/pgraphnodes/nodeCullCallbackData.cxx
  94. 2 2
      panda/src/pgraphnodes/shaderGenerator.cxx
  95. 10 0
      panda/src/pipeline/pipelineCyclerTrueImpl.I
  96. 1 1
      panda/src/pipeline/thread.I
  97. 6 0
      panda/src/putil/copyOnWriteObject.h
  98. 15 13
      panda/src/putil/simpleHashMap.I
  99. 9 9
      panda/src/putil/simpleHashMap.h
  100. 5 0
      panda/src/putil/typedWritableReferenceCount.h

+ 4 - 0
dtool/src/dtoolbase/typeHandle.cxx

@@ -45,7 +45,9 @@ get_memory_usage(MemoryClass memory_class) const {
 void TypeHandle::
 inc_memory_usage(MemoryClass memory_class, size_t size) {
 #ifdef DO_MEMORY_USAGE
+#ifdef _DEBUG
   assert((int)memory_class >= 0 && (int)memory_class < (int)MC_limit);
+#endif
   if ((*this) != TypeHandle::none()) {
     TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, NULL);
     assert(rnode != (TypeRegistryNode *)NULL);
@@ -67,7 +69,9 @@ inc_memory_usage(MemoryClass memory_class, size_t size) {
 void TypeHandle::
 dec_memory_usage(MemoryClass memory_class, size_t size) {
 #ifdef DO_MEMORY_USAGE
+#ifdef _DEBUG
   assert((int)memory_class >= 0 && (int)memory_class < (int)MC_limit);
+#endif
   if ((*this) != TypeHandle::none()) {
     TypeRegistryNode *rnode = TypeRegistry::ptr()->look_up(*this, NULL);
     assert(rnode != (TypeRegistryNode *)NULL);

+ 3 - 4
dtool/src/prc/configVariableBool.I

@@ -67,7 +67,7 @@ operator = (bool value) {
 /**
  * Returns the variable's value.
  */
-INLINE ConfigVariableBool::
+ALWAYS_INLINE ConfigVariableBool::
 operator bool () const {
   return get_value();
 }
@@ -100,12 +100,11 @@ set_value(bool value) {
 /**
  * Returns the variable's value.
  */
-INLINE bool ConfigVariableBool::
+ALWAYS_INLINE bool ConfigVariableBool::
 get_value() const {
   TAU_PROFILE("bool ConfigVariableBool::get_value() const", " ", TAU_USER);
   if (!is_cache_valid(_local_modified)) {
-    mark_cache_valid(((ConfigVariableBool *)this)->_local_modified);
-    ((ConfigVariableBool *)this)->_cache = get_bool_word(0);
+    reload_value();
   }
   return _cache;
 }

+ 9 - 0
dtool/src/prc/configVariableBool.cxx

@@ -12,3 +12,12 @@
  */
 
 #include "configVariableBool.h"
+
+/**
+ * Refreshes the cached value.
+ */
+void ConfigVariableBool::
+reload_value() const {
+  mark_cache_valid(_local_modified);
+  _cache = get_bool_word(0);
+}

+ 6 - 4
dtool/src/prc/configVariableBool.h

@@ -29,13 +29,13 @@ PUBLISHED:
                             const string &description = string(), int flags = 0);
 
   INLINE void operator = (bool value);
-  INLINE operator bool () const;
+  ALWAYS_INLINE operator bool () const;
 
   INLINE size_t size() const;
   INLINE bool operator [] (size_t n) const;
 
   INLINE void set_value(bool value);
-  INLINE bool get_value() const;
+  ALWAYS_INLINE bool get_value() const;
   INLINE bool get_default_value() const;
   MAKE_PROPERTY(value, get_value, set_value);
   MAKE_PROPERTY(default_value, get_default_value);
@@ -44,8 +44,10 @@ PUBLISHED:
   INLINE void set_word(size_t n, bool value);
 
 private:
-  AtomicAdjust::Integer _local_modified;
-  bool _cache;
+  void reload_value() const;
+
+  mutable AtomicAdjust::Integer _local_modified;
+  mutable bool _cache;
 };
 
 #include "configVariableBool.I"

+ 10 - 0
dtool/src/prc/notifyCategory.I

@@ -70,7 +70,12 @@ is_on(NotifySeverity severity) const {
  */
 INLINE bool NotifyCategory::
 is_spam() const {
+  // Instruct the compiler to optimize for the usual case.
+#ifdef __GNUC__
+  return __builtin_expect(is_on(NS_spam), 0);
+#else
   return is_on(NS_spam);
+#endif
 }
 
 /**
@@ -78,7 +83,12 @@ is_spam() const {
  */
 INLINE bool NotifyCategory::
 is_debug() const {
+  // Instruct the compiler to optimize for the usual case.
+#ifdef __GNUC__
+  return __builtin_expect(is_on(NS_debug), 0);
+#else
   return is_on(NS_debug);
+#endif
 }
 #else
 /**

+ 10 - 0
dtool/src/prc/notifyCategoryProxy.I

@@ -69,7 +69,12 @@ is_on(NotifySeverity severity) {
 template<class GetCategory>
 INLINE bool NotifyCategoryProxy<GetCategory>::
 is_spam() {
+  // Instruct the compiler to optimize for the usual case.
+#ifdef __GNUC__
+  return __builtin_expect(get_unsafe_ptr()->is_spam(), 0);
+#else
   return get_unsafe_ptr()->is_spam();
+#endif
 }
 #else
 template<class GetCategory>
@@ -86,7 +91,12 @@ is_spam() {
 template<class GetCategory>
 INLINE bool NotifyCategoryProxy<GetCategory>::
 is_debug() {
+  // Instruct the compiler to optimize for the usual case.
+#ifdef __GNUC__
+  return __builtin_expect(get_unsafe_ptr()->is_debug(), 0);
+#else
   return get_unsafe_ptr()->is_debug();
+#endif
 }
 #else
 template<class GetCategory>

+ 13 - 6
dtool/src/prc/pnotify.h

@@ -122,6 +122,13 @@ private:
 // constant expressions and compilation will fail if the assertion is not
 // true.
 
+#ifdef __GNUC__
+// Tell the optimizer to optimize for the case where the condition is true.
+#define _nassert_check(condition) (__builtin_expect(!(condition), 0))
+#else
+#define _nassert_check(condition) (!(condition))
+#endif
+
 #ifdef NDEBUG
 
 #define nassertr(condition, return_value)
@@ -131,14 +138,14 @@ private:
 
 #define nassertr_always(condition, return_value) \
   { \
-    if (!(condition)) { \
+    if (_nassert_check(condition)) { \
       return return_value; \
     } \
   }
 
 #define nassertv_always(condition) \
   { \
-    if (!(condition)) { \
+    if (_nassert_check(condition)) { \
       return; \
     } \
   }
@@ -151,7 +158,7 @@ private:
 
 #define nassertr(condition, return_value) \
   { \
-    if (!(condition)) { \
+    if (_nassert_check(condition)) { \
       if (Notify::ptr()->assert_failure(#condition, __LINE__, __FILE__)) { \
         return return_value; \
       } \
@@ -160,7 +167,7 @@ private:
 
 #define nassertv(condition) \
   { \
-    if (!(condition)) { \
+    if (_nassert_check(condition)) { \
       if (Notify::ptr()->assert_failure(#condition, __LINE__, __FILE__)) { \
         return; \
       } \
@@ -168,7 +175,7 @@ private:
   }
 
 #define nassertd(condition) \
-  if (!(condition) && \
+  if (_nassert_check(condition) && \
       Notify::ptr()->assert_failure(#condition, __LINE__, __FILE__))
 
 #define nassertr_always(condition, return_value) nassertr(condition, return_value)
@@ -177,7 +184,7 @@ private:
 #define nassert_raise(message) Notify::ptr()->assert_failure(message, __LINE__, __FILE__)
 
 #define enter_debugger_if(condition) \
-  if (condition) { \
+  if (_nassert_check(condition)) { \
     Notify::ptr()->assert_failure(#condition, __LINE__, __FILE__); \
     __asm { int 3 } \
   }

+ 5 - 3
panda/src/char/characterJointEffect.cxx

@@ -122,8 +122,10 @@ void CharacterJointEffect::
 cull_callback(CullTraverser *trav, CullTraverserData &data,
               CPT(TransformState) &node_transform,
               CPT(RenderState) &) const {
-  CPT(TransformState) dummy_transform = TransformState::make_identity();
-  adjust_transform(dummy_transform, node_transform, data.node());
+  if (_character.is_valid_pointer()) {
+    _character->update();
+  }
+  node_transform = data.node()->get_transform();
 }
 
 /**
@@ -147,7 +149,7 @@ has_adjust_transform() const {
 void CharacterJointEffect::
 adjust_transform(CPT(TransformState) &net_transform,
                  CPT(TransformState) &node_transform,
-                 PandaNode *node) const {
+                 const PandaNode *node) const {
   if (_character.is_valid_pointer()) {
     _character->update();
   }

+ 1 - 1
panda/src/char/characterJointEffect.h

@@ -53,7 +53,7 @@ public:
   virtual bool has_adjust_transform() const;
   virtual void adjust_transform(CPT(TransformState) &net_transform,
                                 CPT(TransformState) &node_transform,
-                                PandaNode *node) const;
+                                const PandaNode *node) const;
 
 protected:
   virtual int compare_to_impl(const RenderEffect *other) const;

+ 7 - 5
panda/src/collide/collisionLevelState.I

@@ -111,10 +111,12 @@ any_in_bounds() {
   }
 #endif  // NDEBUG
 
-  CPT(BoundingVolume) node_bv = node()->get_bounds();
+  PandaNode *pnode = node();
+
+  CPT(BoundingVolume) node_bv = pnode->get_bounds();
   if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
-    const GeometricBoundingVolume *node_gbv;
-    DCAST_INTO_R(node_gbv, node_bv, false);
+    const GeometricBoundingVolume *node_gbv = (const GeometricBoundingVolume *)node_bv.p();
+    CollideMask this_mask = pnode->get_net_collide_mask();
 
     int num_colliders = get_num_colliders();
     for (int c = 0; c < num_colliders; c++) {
@@ -125,10 +127,10 @@ any_in_bounds() {
         // Don't even bother testing the bounding volume if there are no
         // collide bits in common between our collider and this node.
         CollideMask from_mask = cnode->get_from_collide_mask() & _include_mask;
-        if (!(from_mask & node()->get_net_collide_mask()).is_zero()) {
+        if (!(from_mask & this_mask).is_zero()) {
           // Also don't test a node with itself, or with any of its
           // descendants.
-          if (node() == cnode) {
+          if (pnode == cnode) {
 #ifndef NDEBUG
             if (collide_cat.is_spam()) {
               indent(collide_cat.spam(false), indent_level)

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

@@ -51,7 +51,7 @@ public:
 
   INLINE CollisionLevelStateBase(const NodePath &node_path);
   INLINE CollisionLevelStateBase(const CollisionLevelStateBase &parent,
-                             PandaNode *child);
+                                 PandaNode *child);
   INLINE CollisionLevelStateBase(const CollisionLevelStateBase &copy);
   INLINE void operator = (const CollisionLevelStateBase &copy);
 

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

@@ -196,7 +196,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
   if (respect_prev_transform) {
     // Determine the previous frame's position, relative to the current
     // position.
-    NodePath node_path = data._node_path.get_node_path();
+    NodePath node_path = data.get_node_path();
     CPT(TransformState) transform = node_path.get_net_transform()->invert_compose(node_path.get_net_prev_transform());
 
     if (!transform->is_identity()) {

+ 2 - 0
panda/src/collide/collisionNode.h

@@ -93,6 +93,8 @@ private:
   typedef pvector< COWPT(CollisionSolid) > Solids;
   Solids _solids;
 
+  friend class CollisionTraverser;
+
 public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &dg);

+ 36 - 22
panda/src/collide/collisionTraverser.cxx

@@ -57,7 +57,7 @@ public:
   inline bool operator () (int a, int b) const {
     const CollisionTraverser::OrderedColliderDef &ocd_a = _trav._ordered_colliders[a];
     const CollisionTraverser::OrderedColliderDef &ocd_b = _trav._ordered_colliders[b];
-    return DCAST(CollisionNode, ocd_a._node_path.node())->get_collider_sort() < DCAST(CollisionNode, ocd_b._node_path.node())->get_collider_sort();
+    return ((const CollisionNode *)ocd_a._node_path.node())->get_collider_sort() < ((const CollisionNode *)ocd_b._node_path.node())->get_collider_sort();
   }
 
   const CollisionTraverser &_trav;
@@ -1117,31 +1117,45 @@ compare_collider_to_node(CollisionEntry &entry,
   }
 
   if (within_node_bounds) {
+    Thread *current_thread = Thread::get_current_thread();
+
     CollisionNode *cnode;
     DCAST_INTO_V(cnode, entry._into_node);
+
     int num_solids = cnode->get_num_solids();
-    collide_cat.spam()
-      << "Colliding against CollisionNode " << entry._into_node
-      << " which has " << num_solids << " collision solids.\n";
-    for (int s = 0; s < num_solids; ++s) {
-      entry._into = cnode->get_solid(s);
-
-      // We should allow a collision test for solid into itself, because the
-      // solid might be simply instanced into multiple different
-      // CollisionNodes.  We are already filtering out tests for a
-      // CollisionNode into itself.
-      CPT(BoundingVolume) solid_bv = entry._into->get_bounds();
-      const GeometricBoundingVolume *solid_gbv = NULL;
-      if (num_solids > 1 &&
-          solid_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
-        // Only bother to test against each solid's bounding volume if we have
-        // more than one solid in the node, as a slight optimization.  (If the
-        // node contains just one solid, then the node's bounding volume,
-        // which we just tested, is the same as the solid's bounding volume.)
-        DCAST_INTO_V(solid_gbv, solid_bv);
-      }
+    if (collide_cat.is_spam()) {
+      collide_cat.spam()
+        << "Colliding against CollisionNode " << entry._into_node
+        << " which has " << num_solids << " collision solids.\n";
+    }
 
-      compare_collider_to_solid(entry, from_node_gbv, solid_gbv);
+    // Only bother to test against each solid's bounding volume if we have
+    // more than one solid in the node, as a slight optimization.  (If the
+    // node contains just one solid, then the node's bounding volume, which
+    // we just tested, is the same as the solid's bounding volume.)
+    if (num_solids == 1) {
+      entry._into = cnode->_solids[0].get_read_pointer(current_thread);
+      Colliders::const_iterator ci;
+      ci = _colliders.find(entry.get_from_node_path());
+      nassertv(ci != _colliders.end());
+      entry.test_intersection((*ci).second, this);
+    } else {
+      CollisionNode::Solids::const_iterator si;
+      for (si = cnode->_solids.begin(); si != cnode->_solids.end(); ++si) {
+        entry._into = (*si).get_read_pointer(current_thread);
+
+        // We should allow a collision test for solid into itself, because the
+        // solid might be simply instanced into multiple different
+        // CollisionNodes.  We are already filtering out tests for a
+        // CollisionNode into itself.
+        CPT(BoundingVolume) solid_bv = entry._into->get_bounds();
+        const GeometricBoundingVolume *solid_gbv = nullptr;
+        if (solid_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
+          solid_gbv = (const GeometricBoundingVolume *)solid_bv.p();
+        }
+
+        compare_collider_to_solid(entry, from_node_gbv, solid_gbv);
+      }
     }
   }
 }

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

@@ -111,10 +111,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
     // its objects according to their appropriate net transform.
     xform_data._net_transform = TransformState::make_identity();
     xform_data._view_frustum = trav->get_view_frustum();
-    xform_data.apply_transform_and_state(trav, net_transform,
-                                         RenderState::make_empty(),
-                                         RenderEffects::make_empty(),
-                                         ClipPlaneAttrib::make());
+    xform_data.apply_transform(net_transform);
 
     // Draw all the collision solids.
     Solids::const_iterator si;

+ 74 - 69
panda/src/display/graphicsStateGuardian.cxx

@@ -903,16 +903,10 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
   case Shader::SMO_identity: {
     return &LMatrix4::ident_mat();
   }
-  case Shader::SMO_window_size: {
-    t = LMatrix4::translate_mat(_current_display_region->get_pixel_width(),
-                                 _current_display_region->get_pixel_height(),
-                                 0.0);
-    return &t;
-  }
+  case Shader::SMO_window_size:
   case Shader::SMO_pixel_size: {
-    t = LMatrix4::translate_mat(_current_display_region->get_pixel_width(),
-                                 _current_display_region->get_pixel_height(),
-                                 0.0);
+    LVecBase2i pixel_size = _current_display_region->get_pixel_size();
+    t = LMatrix4::translate_mat(pixel_size[0], pixel_size[1], 0);
     return &t;
   }
   case Shader::SMO_frame_time: {
@@ -1049,7 +1043,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     LColor const &c = lt->get_color();
     LColor const &s = lt->get_specular_color();
     t = np.get_net_transform()->get_mat() *
-      get_scene()->get_world_transform()->get_mat();
+      _scene_setup->get_world_transform()->get_mat();
     LVecBase3 d = -(t.xform_vec(lt->get_direction()));
     d.normalize();
     LVecBase3 h = d + LVecBase3(0,-1,0);
@@ -1066,11 +1060,12 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     LColor const &c = lt->get_color();
     LColor const &s = lt->get_specular_color();
     t = np.get_net_transform()->get_mat() *
-      get_scene()->get_world_transform()->get_mat();
+      _scene_setup->get_world_transform()->get_mat();
     LVecBase3 p = (t.xform_point(lt->get_point()));
     LVecBase3 a = lt->get_attenuation();
-    PN_stdfloat lnear = lt->get_lens(0)->get_near();
-    PN_stdfloat lfar = lt->get_lens(0)->get_far();
+    Lens *lens = lt->get_lens(0);
+    PN_stdfloat lnear = lens->get_near();
+    PN_stdfloat lfar = lens->get_far();
     t = LMatrix4(c[0],c[1],c[2],c[3],s[0],s[1],s[2],s[3],p[0],p[1],p[2],lnear,a[0],a[1],a[2],lfar);
     return &t;
   }
@@ -1086,7 +1081,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     LColor const &s = lt->get_specular_color();
     PN_stdfloat cutoff = ccos(deg_2_rad(lens->get_hfov() * 0.5f));
     t = np.get_net_transform()->get_mat() *
-      get_scene()->get_world_transform()->get_mat();
+      _scene_setup->get_world_transform()->get_mat();
     LVecBase3 p = t.xform_point(lens->get_nodal_point());
     LVecBase3 d = -(t.xform_vec(lens->get_view_vector()));
     t = LMatrix4(c[0],c[1],c[2],c[3],s[0],s[1],s[2],s[3],p[0],p[1],p[2],0,d[0],d[1],d[2],cutoff);
@@ -1109,7 +1104,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
         Light *light_obj = light.node()->as_light();
         nassertr(light_obj != (Light *)NULL, &LMatrix4::zeros_mat());
 
-        if (light_obj->get_type() == AmbientLight::get_class_type()) {
+        if (light_obj->is_ambient_light()) {
           cur_ambient_light += light_obj->get_color();
         }
       }
@@ -1155,27 +1150,31 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
   case Shader::SMO_plane_x: {
     const NodePath &np = _target_shader->get_shader_input_nodepath(name);
     nassertr(!np.is_empty(), &LMatrix4::zeros_mat());
-    nassertr(np.node()->is_of_type(PlaneNode::get_class_type()), &LMatrix4::zeros_mat());
-    LPlane p = DCAST(PlaneNode, np.node())->get_plane();
+    const PlaneNode *plane_node;
+    DCAST_INTO_R(plane_node, np.node(), &LMatrix4::zeros_mat());
+    LPlane p = plane_node->get_plane();
     t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,p[0],p[1],p[2],p[3]);
     return &t;
   }
   case Shader::SMO_clipplane_x: {
-    const ClipPlaneAttrib *cpa = DCAST(ClipPlaneAttrib, _target_rs->get_attrib_def(ClipPlaneAttrib::get_class_slot()));
+    const ClipPlaneAttrib *cpa;
+    _target_rs->get_attrib_def(cpa);
     int planenr = atoi(name->get_name().c_str());
     if (planenr >= cpa->get_num_on_planes()) {
       return &LMatrix4::zeros_mat();
     }
     const NodePath &np = cpa->get_on_plane(planenr);
     nassertr(!np.is_empty(), &LMatrix4::zeros_mat());
-    nassertr(np.node()->is_of_type(PlaneNode::get_class_type()), &LMatrix4::zeros_mat());
-    LPlane p (DCAST(PlaneNode, np.node())->get_plane());
+    const PlaneNode *plane_node;
+    DCAST_INTO_R(plane_node, np.node(), &LMatrix4::zeros_mat());
+    LPlane p (plane_node->get_plane());
     p.xform(np.get_net_transform()->get_mat()); // World-space
     t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,p[0],p[1],p[2],p[3]);
     return &t;
   }
   case Shader::SMO_apiview_clipplane_i: {
-    const ClipPlaneAttrib *cpa = DCAST(ClipPlaneAttrib, _target_rs->get_attrib_def(ClipPlaneAttrib::get_class_slot()));
+    const ClipPlaneAttrib *cpa;
+    _target_rs->get_attrib_def(cpa);
     if (index >= cpa->get_num_on_planes()) {
       return &LMatrix4::zeros_mat();
     }
@@ -1186,7 +1185,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     DCAST_INTO_R(plane_node, plane.node(), &LMatrix4::zeros_mat());
 
     CPT(TransformState) transform =
-      get_scene()->get_cs_world_transform()->compose(
+      _scene_setup->get_cs_world_transform()->compose(
         plane.get_transform(_scene_setup->get_scene_root().get_parent()));
 
     LPlane xformed_plane = plane_node->get_plane() * transform->get_mat();
@@ -1206,24 +1205,24 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     return &t;
   }
   case Shader::SMO_world_to_view: {
-    return &(get_scene()->get_world_transform()->get_mat());
+    return &(_scene_setup->get_world_transform()->get_mat());
     break;
   }
   case Shader::SMO_view_to_world: {
-    return &(get_scene()->get_camera_transform()->get_mat());
+    return &(_scene_setup->get_camera_transform()->get_mat());
   }
   case Shader::SMO_model_to_view: {
-    return &(get_external_transform()->get_mat());
+    return &(_inv_cs_transform->compose(_internal_transform)->get_mat());
   }
   case Shader::SMO_model_to_apiview: {
-    return &(get_internal_transform()->get_mat());
+    return &(_internal_transform->get_mat());
   }
   case Shader::SMO_view_to_model: {
-    t = get_external_transform()->get_inverse()->get_mat();
+    t = _internal_transform->invert_compose(_cs_transform)->get_mat();
     return &t;
   }
   case Shader::SMO_apiview_to_model: {
-    t = get_internal_transform()->get_inverse()->get_mat();
+    t = _internal_transform->get_inverse()->get_mat();
     return &t;
   }
   case Shader::SMO_apiview_to_view: {
@@ -1268,13 +1267,13 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     const NodePath &np = _target_shader->get_shader_input_nodepath(name);
     nassertr(!np.is_empty(), &LMatrix4::ident_mat());
     t = np.get_net_transform()->get_mat() *
-      get_scene()->get_world_transform()->get_mat();
+      _scene_setup->get_world_transform()->get_mat();
     return &t;
   }
   case Shader::SMO_view_to_view_x: {
     const NodePath &np = _target_shader->get_shader_input_nodepath(name);
     nassertr(!np.is_empty(), &LMatrix4::ident_mat());
-    t = get_scene()->get_camera_transform()->get_mat() *
+    t = _scene_setup->get_camera_transform()->get_mat() *
       np.get_net_transform()->get_inverse()->get_mat();
     return &t;
   }
@@ -1283,13 +1282,13 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     nassertr(!np.is_empty(), &LMatrix4::ident_mat());
     t = LMatrix4::convert_mat(_internal_coordinate_system, _coordinate_system) *
       np.get_net_transform()->get_mat() *
-      get_scene()->get_world_transform()->get_mat();
+      _scene_setup->get_world_transform()->get_mat();
     return &t;
   }
   case Shader::SMO_view_to_apiview_x: {
     const NodePath &np = _target_shader->get_shader_input_nodepath(name);
     nassertr(!np.is_empty(), &LMatrix4::ident_mat());
-    t = (get_scene()->get_camera_transform()->get_mat() *
+    t = (_scene_setup->get_camera_transform()->get_mat() *
          np.get_net_transform()->get_inverse()->get_mat() *
          LMatrix4::convert_mat(_coordinate_system, _internal_coordinate_system));
     return &t;
@@ -1297,20 +1296,22 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
   case Shader::SMO_clip_x_to_view: {
     const NodePath &np = _target_shader->get_shader_input_nodepath(name);
     nassertr(!np.is_empty(), &LMatrix4::ident_mat());
-    nassertr(np.node()->is_of_type(LensNode::get_class_type()), &LMatrix4::ident_mat());
-    Lens *lens = DCAST(LensNode, np.node())->get_lens();
+    const LensNode *node;
+    DCAST_INTO_R(node, np.node(), &LMatrix4::ident_mat());
+    const Lens *lens = node->get_lens();
     t = lens->get_projection_mat_inv(_current_stereo_channel) *
       LMatrix4::convert_mat(lens->get_coordinate_system(), _coordinate_system) *
       np.get_net_transform()->get_mat() *
-      get_scene()->get_world_transform()->get_mat();
+      _scene_setup->get_world_transform()->get_mat();
     return &t;
   }
   case Shader::SMO_view_to_clip_x: {
     const NodePath &np = _target_shader->get_shader_input_nodepath(name);
     nassertr(!np.is_empty(), &LMatrix4::ident_mat());
-    nassertr(np.node()->is_of_type(LensNode::get_class_type()), &LMatrix4::ident_mat());
-    Lens *lens = DCAST(LensNode, np.node())->get_lens();
-    t = get_scene()->get_camera_transform()->get_mat() *
+    const LensNode *node;
+    DCAST_INTO_R(node, np.node(), &LMatrix4::ident_mat());
+    const Lens *lens = node->get_lens();
+    t = _scene_setup->get_camera_transform()->get_mat() *
       np.get_net_transform()->get_inverse()->get_mat() *
       LMatrix4::convert_mat(_coordinate_system, lens->get_coordinate_system()) *
       lens->get_projection_mat(_current_stereo_channel);
@@ -1319,20 +1320,22 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
   case Shader::SMO_apiclip_x_to_view: {
     const NodePath &np = _target_shader->get_shader_input_nodepath(name);
     nassertr(!np.is_empty(), &LMatrix4::ident_mat());
-    nassertr(np.node()->is_of_type(LensNode::get_class_type()), &LMatrix4::ident_mat());
-    Lens *lens = DCAST(LensNode, np.node())->get_lens();
+    const LensNode *node;
+    DCAST_INTO_R(node, np.node(), &LMatrix4::ident_mat());
+    const Lens *lens = node->get_lens();
     t = calc_projection_mat(lens)->get_inverse()->get_mat() *
       get_cs_transform_for(lens->get_coordinate_system())->get_inverse()->get_mat() *
       np.get_net_transform()->get_mat() *
-      get_scene()->get_world_transform()->get_mat();
+      _scene_setup->get_world_transform()->get_mat();
     return &t;
   }
   case Shader::SMO_view_to_apiclip_x: {
     const NodePath &np = _target_shader->get_shader_input_nodepath(name);
     nassertr(!np.is_empty(), &LMatrix4::ident_mat());
-    nassertr(np.node()->is_of_type(LensNode::get_class_type()), &LMatrix4::ident_mat());
-    Lens *lens = DCAST(LensNode, np.node())->get_lens();
-    t = get_scene()->get_camera_transform()->get_mat() *
+    const LensNode *node;
+    DCAST_INTO_R(node, np.node(), &LMatrix4::ident_mat());
+    const Lens *lens = node->get_lens();
+    t = _scene_setup->get_camera_transform()->get_mat() *
       np.get_net_transform()->get_inverse()->get_mat() *
       get_cs_transform_for(lens->get_coordinate_system())->get_mat() *
       calc_projection_mat(lens)->get_mat();
@@ -1383,7 +1386,7 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
       Light *light_obj = light.node()->as_light();
       nassertr(light_obj != (Light *)NULL, &LMatrix4::ident_mat());
 
-      if (light_obj->get_type() != AmbientLight::get_class_type()) {
+      if (!light_obj->is_ambient_light()) {
         if (i++ == index) {
           return fetch_specified_member(light, name, t);
         }
@@ -1430,7 +1433,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
   static const CPT_InternalName IN_constantAttenuation("constantAttenuation");
   static const CPT_InternalName IN_linearAttenuation("linearAttenuation");
   static const CPT_InternalName IN_quadraticAttenuation("quadraticAttenuation");
-  static const CPT_InternalName IN_shadowMatrix("shadowMatrix");
+  static const CPT_InternalName IN_shadowViewMatrix("shadowViewMatrix");
 
   PandaNode *node = NULL;
   if (!np.is_empty()) {
@@ -1454,7 +1457,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
     }
     Light *light = node->as_light();
     nassertr(light != (Light *)NULL, &LMatrix4::ident_mat());
-    if (node->is_of_type(AmbientLight::get_class_type())) {
+    if (node->is_ambient_light()) {
       LColor c = light->get_color();
       c.componentwise_mult(_light_color_scale);
       t.set_row(3, c);
@@ -1470,7 +1473,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
     }
     Light *light = node->as_light();
     nassertr(light != (Light *)NULL, &LMatrix4::ones_mat());
-    if (node->is_of_type(AmbientLight::get_class_type())) {
+    if (node->is_ambient_light()) {
       // Ambient light has no diffuse color.
       t.set_row(3, LColor(0.0f, 0.0f, 0.0f, 1.0f));
     } else {
@@ -1493,7 +1496,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
     if (np.is_empty()) {
       t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0);
       return &t;
-    } else if (node->is_of_type(AmbientLight::get_class_type())) {
+    } else if (node->is_ambient_light()) {
       // Ambient light has no position.
       t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
       return &t;
@@ -1503,7 +1506,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
 
       CPT(TransformState) transform = np.get_transform(_scene_setup->get_scene_root().get_parent());
       LVector3 dir = -(light->get_direction() * transform->get_mat());
-      dir *= get_scene()->get_cs_world_transform()->get_mat();
+      dir *= _scene_setup->get_cs_world_transform()->get_mat();
       t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,dir[0],dir[1],dir[2],0);
       return &t;
     } else {
@@ -1513,7 +1516,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
       nassertr(lens != (Lens *)NULL, &LMatrix4::ident_mat());
 
       CPT(TransformState) transform =
-        get_scene()->get_cs_world_transform()->compose(
+        _scene_setup->get_cs_world_transform()->compose(
           np.get_transform(_scene_setup->get_scene_root().get_parent()));
 
       const LMatrix4 &light_mat = transform->get_mat();
@@ -1526,7 +1529,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
     if (np.is_empty()) {
       t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0);
       return &t;
-    } else if (node->is_of_type(AmbientLight::get_class_type())) {
+    } else if (node->is_ambient_light()) {
       // Ambient light has no half-vector.
       t = LMatrix4(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
       return &t;
@@ -1536,7 +1539,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
 
       CPT(TransformState) transform = np.get_transform(_scene_setup->get_scene_root().get_parent());
       LVector3 dir = -(light->get_direction() * transform->get_mat());
-      dir *= get_scene()->get_cs_world_transform()->get_mat();
+      dir *= _scene_setup->get_cs_world_transform()->get_mat();
       dir.normalize();
       dir += LVector3(0, 0, 1);
       dir.normalize();
@@ -1549,7 +1552,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
       nassertr(lens != (Lens *)NULL, &LMatrix4::ident_mat());
 
       CPT(TransformState) transform =
-        get_scene()->get_cs_world_transform()->compose(
+        _scene_setup->get_cs_world_transform()->compose(
           np.get_transform(_scene_setup->get_scene_root().get_parent()));
 
       const LMatrix4 &light_mat = transform->get_mat();
@@ -1565,7 +1568,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
     if (node == (PandaNode *)NULL) {
       t.set_row(3, LVector3(0.0f, 0.0f, -1.0f));
       return &t;
-    } else if (node->is_of_type(AmbientLight::get_class_type())) {
+    } else if (node->is_ambient_light()) {
       // Ambient light has no spot direction.
       t.set_row(3, LVector3(0.0f, 0.0f, 0.0f));
       return &t;
@@ -1576,7 +1579,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
       nassertr(lens != (Lens *)NULL, &LMatrix4::ident_mat());
 
       CPT(TransformState) transform =
-        get_scene()->get_cs_world_transform()->compose(
+        _scene_setup->get_cs_world_transform()->compose(
           np.get_transform(_scene_setup->get_scene_root().get_parent()));
 
       const LMatrix4 &light_mat = transform->get_mat();
@@ -1670,7 +1673,7 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
     t.set_row(3, LVecBase4(light->get_attenuation()[2]));
     return &t;
 
-  } else if (attrib == IN_shadowMatrix) {
+  } else if (attrib == IN_shadowViewMatrix) {
     static const LMatrix4 biasmat(0.5f, 0.0f, 0.0f, 0.0f,
                                   0.0f, 0.5f, 0.0f, 0.0f,
                                   0.0f, 0.0f, 0.5f, 0.0f,
@@ -1684,8 +1687,8 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LMatrix4 &t)
     DCAST_INTO_R(lnode, node, &LMatrix4::ident_mat());
     Lens *lens = lnode->get_lens();
 
-    t = get_external_transform()->get_mat() *
-      get_scene()->get_camera_transform()->get_mat() *
+    t = _inv_cs_transform->get_mat() *
+      _scene_setup->get_camera_transform()->get_mat() *
       np.get_net_transform()->get_inverse()->get_mat() *
       LMatrix4::convert_mat(_coordinate_system, lens->get_coordinate_system());
 
@@ -1785,7 +1788,7 @@ fetch_specified_texture(Shader::ShaderTexSpec &spec, SamplerState &sampler,
         Light *light_obj = light.node()->as_light();
         nassertr(light_obj != (Light *)NULL, NULL);
 
-        if (light_obj->get_type() != AmbientLight::get_class_type()) {
+        if (!light_obj->is_ambient_light()) {
           if (i++ == spec._stage) {
             PT(Texture) tex = get_shadow_map(light);
             if (tex != (Texture *)NULL) {
@@ -2663,7 +2666,7 @@ do_issue_light() {
         _lighting_enabled = true;
       }
 
-      if (light_obj->get_type() == AmbientLight::get_class_type()) {
+      if (light_obj->is_ambient_light()) {
         // Ambient lights don't require specific light ids; simply add in the
         // ambient contribution to the current total
         cur_ambient_light += light_obj->get_color();
@@ -3143,11 +3146,12 @@ async_reload_texture(TextureContext *tc) {
  */
 PT(Texture) GraphicsStateGuardian::
 get_shadow_map(const NodePath &light_np, GraphicsOutputBase *host) {
-  nassertr(light_np.node()->is_of_type(DirectionalLight::get_class_type()) ||
-           light_np.node()->is_of_type(PointLight::get_class_type()) ||
-           light_np.node()->is_of_type(Spotlight::get_class_type()), NULL);
+  PandaNode *node = light_np.node();
+  nassertr(node->is_of_type(DirectionalLight::get_class_type()) ||
+           node->is_of_type(PointLight::get_class_type()) ||
+           node->is_of_type(Spotlight::get_class_type()), NULL);
 
-  PT(LightLensNode) light = DCAST(LightLensNode, light_np.node());
+  LightLensNode *light = (LightLensNode *)node;
   if (light == NULL || !light->_shadow_caster) {
     // TODO: return dummy shadow map (all white).
     return NULL;
@@ -3177,11 +3181,12 @@ get_shadow_map(const NodePath &light_np, GraphicsOutputBase *host) {
 PT(Texture) GraphicsStateGuardian::
 make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host) {
   // Make sure everything is valid.
-  nassertr(light_np.node()->is_of_type(DirectionalLight::get_class_type()) ||
-           light_np.node()->is_of_type(PointLight::get_class_type()) ||
-           light_np.node()->is_of_type(Spotlight::get_class_type()), NULL);
+  PandaNode *node = light_np.node();
+  nassertr(node->is_of_type(DirectionalLight::get_class_type()) ||
+           node->is_of_type(PointLight::get_class_type()) ||
+           node->is_of_type(Spotlight::get_class_type()), NULL);
 
-  PT(LightLensNode) light = DCAST(LightLensNode, light_np.node());
+  LightLensNode *light = (LightLensNode *)node;
   if (light == NULL || !light->_shadow_caster) {
     return NULL;
   }

+ 1 - 1
panda/src/display/graphicsStateGuardian.h

@@ -286,7 +286,7 @@ PUBLISHED:
   MAKE_PROPERTY(driver_shader_version_minor, get_driver_shader_version_minor);
 
   bool set_scene(SceneSetup *scene_setup);
-  virtual SceneSetup *get_scene() const;
+  virtual SceneSetup *get_scene() const FINAL;
   MAKE_PROPERTY(scene, get_scene, set_scene);
 
 public:

+ 1 - 1
panda/src/distort/projectionScreen.cxx

@@ -103,7 +103,7 @@ make_copy() const {
 bool ProjectionScreen::
 cull_callback(CullTraverser *, CullTraverserData &data) {
   if (_auto_recompute) {
-    recompute_if_stale(data._node_path.get_node_path());
+    recompute_if_stale(data.get_node_path());
   }
   return true;
 }

+ 7 - 4
panda/src/express/memoryUsage.I

@@ -295,10 +295,13 @@ show_trend_ages() {
  */
 INLINE MemoryUsage *MemoryUsage::
 get_global_ptr() {
-  if (_global_ptr == (MemoryUsage *)NULL) {
-    init_memory_hook();
-    _global_ptr = new MemoryUsage(*memory_hook);
-    memory_hook = _global_ptr;
+#ifdef __GNUC__
+  // Tell the compiler that this is an unlikely branch.
+  if (__builtin_expect(_global_ptr == nullptr, 0)) {
+#else
+  if (_global_ptr == nullptr) {
+#endif
+    init_memory_usage();
   }
 
   return _global_ptr;

+ 10 - 0
panda/src/express/memoryUsage.cxx

@@ -489,6 +489,16 @@ MemoryUsage(const MemoryHook &copy) : MemoryHook(copy) {
   _total_size = 0;
 }
 
+/**
+ * Initializes the global MemoryUsage pointer.
+ */
+void MemoryUsage::
+init_memory_usage() {
+  init_memory_hook();
+  _global_ptr = new MemoryUsage(*memory_hook);
+  memory_hook = _global_ptr;
+}
+
 /**
  * This callback method is called whenever the total allocated heap size
  * exceeds _max_heap_size.  It's mainly intended for reporting memory leaks,

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

@@ -95,6 +95,8 @@ private:
   MemoryUsage(const MemoryHook &copy);
   INLINE static MemoryUsage *get_global_ptr();
 
+  static void init_memory_usage();
+
   void ns_record_pointer(ReferenceCount *ptr);
   void ns_update_type(ReferenceCount *ptr, TypeHandle type);
   void ns_update_type(ReferenceCount *ptr, TypedObject *typed_ptr);

+ 32 - 22
panda/src/express/pointerToBase.I

@@ -21,9 +21,7 @@ PointerToBase(To *ptr) {
   if (ptr != (To *)NULL) {
     ptr->ref();
 #ifdef DO_MEMORY_USAGE
-    if (MemoryUsage::get_track_memory_usage()) {
-      update_type(ptr);
-    }
+    update_type(ptr);
 #endif
   }
 }
@@ -38,11 +36,6 @@ PointerToBase(const PointerToBase<T> &copy) {
   if (_void_ptr != NULL) {
     To *ptr = (To *)_void_ptr;
     ptr->ref();
-#ifdef DO_MEMORY_USAGE
-    if (MemoryUsage::get_track_memory_usage()) {
-      update_type(ptr);
-    }
-#endif
   }
 }
 
@@ -108,17 +101,15 @@ reassign(To *ptr) {
     To *old_ptr = (To *)_void_ptr;
 
     _void_ptr = (void *)ptr;
-    if (ptr != (To *)NULL) {
+    if (ptr != nullptr) {
       ptr->ref();
 #ifdef DO_MEMORY_USAGE
-      if (MemoryUsage::get_track_memory_usage()) {
-        update_type(ptr);
-      }
+      update_type(ptr);
 #endif
     }
 
     // Now delete the old pointer.
-    if (old_ptr != (To *)NULL) {
+    if (old_ptr != nullptr) {
       unref_delete(old_ptr);
     }
   }
@@ -130,7 +121,24 @@ reassign(To *ptr) {
 template<class T>
 INLINE void PointerToBase<T>::
 reassign(const PointerToBase<To> &copy) {
-  reassign((To *)copy._void_ptr);
+  if (copy._void_ptr != _void_ptr) {
+    // First save the old pointer; we won't delete it until we have assigned
+    // the new one.  We do this just in case there are cascading effects from
+    // deleting this pointer that might inadvertently delete the new one.
+    // (Don't laugh--it's happened!)
+    To *old_ptr = (To *)_void_ptr;
+    To *new_ptr = (To *)copy._void_ptr;
+
+    _void_ptr = copy._void_ptr;
+    if (new_ptr != nullptr) {
+      new_ptr->ref();
+    }
+
+    // Now delete the old pointer.
+    if (old_ptr != nullptr) {
+      unref_delete(old_ptr);
+    }
+  }
 }
 
 #ifdef DO_MEMORY_USAGE
@@ -139,15 +147,17 @@ reassign(const PointerToBase<To> &copy) {
  * object, if we know the type ourselves.
  */
 template<class T>
-void PointerToBase<T>::
+INLINE void PointerToBase<T>::
 update_type(To *ptr) {
-  TypeHandle type = get_type_handle(To);
-  if (type == TypeHandle::none()) {
-    do_init_type(To);
-    type = get_type_handle(To);
-  }
-  if (type != TypeHandle::none()) {
-    MemoryUsage::update_type(ptr, type);
+  if (MemoryUsage::get_track_memory_usage()) {
+    TypeHandle type = get_type_handle(To);
+    if (type == TypeHandle::none()) {
+      do_init_type(To);
+      type = get_type_handle(To);
+    }
+    if (type != TypeHandle::none()) {
+      MemoryUsage::update_type(ptr, type);
+    }
   }
 }
 #endif  // DO_MEMORY_USAGE

+ 1 - 1
panda/src/express/pointerToBase.h

@@ -45,7 +45,7 @@ protected:
   INLINE void reassign(const PointerToBase<To> &copy);
 
 #ifdef DO_MEMORY_USAGE
-  void update_type(To *ptr);
+  INLINE void update_type(To *ptr);
 #endif  // DO_MEMORY_USAGE
 
   // No assignment or retrieval functions are declared in PointerToBase,

+ 5 - 0
panda/src/glstuff/glCgShaderContext_src.cxx

@@ -399,6 +399,7 @@ unbind() {
 void CLP(CgShaderContext)::
 set_state_and_transform(const RenderState *target_rs,
                         const TransformState *modelview_transform,
+                        const TransformState *camera_transform,
                         const TransformState *projection_transform) {
 
   if (!valid()) {
@@ -410,6 +411,10 @@ set_state_and_transform(const RenderState *target_rs,
 
   if (_modelview_transform != modelview_transform) {
     _modelview_transform = modelview_transform;
+    altered |= (Shader::SSD_transform & ~Shader::SSD_view_transform);
+  }
+  if (_camera_transform != camera_transform) {
+    _camera_transform = camera_transform;
     altered |= Shader::SSD_transform;
   }
   if (_projection_transform != projection_transform) {

+ 6 - 4
panda/src/glstuff/glCgShaderContext_src.h

@@ -25,7 +25,7 @@ class CLP(GraphicsStateGuardian);
 /**
  * xyz
  */
-class EXPCL_GL CLP(CgShaderContext) : public ShaderContext {
+class EXPCL_GL CLP(CgShaderContext) FINAL : public ShaderContext {
 public:
   friend class CLP(GraphicsStateGuardian);
 
@@ -39,7 +39,8 @@ public:
 
   void set_state_and_transform(const RenderState *state,
                                const TransformState *modelview_transform,
-                               const TransformState *projection_transform);
+                               const TransformState *camera_transform,
+                               const TransformState *projection_transform) OVERRIDE;
 
   void issue_parameters(int altered) OVERRIDE;
   void update_transform_table(const TransformTable *table);
@@ -77,6 +78,7 @@ private:
 
   WCPT(RenderState) _state_rs;
   CPT(TransformState) _modelview_transform;
+  CPT(TransformState) _camera_transform;
   CPT(TransformState) _projection_transform;
   GLint _frame_number;
 
@@ -93,10 +95,10 @@ public:
     register_type(_type_handle, CLASSPREFIX_QUOTED "CgShaderContext",
                   ShaderContext::get_class_type());
   }
-  virtual TypeHandle get_type() const {
+  virtual TypeHandle get_type() const OVERRIDE {
     return get_class_type();
   }
-  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  virtual TypeHandle force_init_type() OVERRIDE {init_type(); return get_class_type();}
 
 private:
   static TypeHandle _type_handle;

+ 1 - 1
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -10350,7 +10350,7 @@ set_state_and_transform(const RenderState *target,
 
   // Update all of the state that is bound to the shader program.
   if (_current_shader_context != NULL) {
-    _current_shader_context->set_state_and_transform(target, transform, _projection_mat);
+    _current_shader_context->set_state_and_transform(target, transform, _scene_setup->get_camera_transform(), _projection_mat);
   }
 #endif
 

+ 66 - 17
panda/src/glstuff/glShaderContext_src.cxx

@@ -874,7 +874,7 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
                  matrix_name.substr(0, 12) == "LightSource[" &&
                  sscanf(matrix_name.c_str(), "LightSource[%d].%s", &bind._index, name_buffer) == 2) {
         // A matrix member of a p3d_LightSource struct.
-        if (strncmp(name_buffer, "shadowMatrix", 127) == 0) {
+        if (strncmp(name_buffer, "shadowViewMatrix", 127) == 0) {
           if (inverse) {
             // Tack inverse back onto the end.
             strcpy(name_buffer + strlen(name_buffer), "Inverse");
@@ -884,7 +884,25 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
           bind._part[0] = Shader::SMO_light_source_i_attrib;
           bind._arg[0] = InternalName::make(name_buffer);
           bind._part[1] = Shader::SMO_identity;
+          bind._arg[1] = NULL;
 
+        } else if (strncmp(name_buffer, "shadowMatrix", 127) == 0) {
+          // Only supported for backward compatibility: includes the model
+          // matrix.  Not very efficient to do this.
+          bind._func = Shader::SMF_compose;
+          bind._part[0] = Shader::SMO_model_to_apiview;
+          bind._arg[0] = NULL;
+          bind._part[1] = Shader::SMO_light_source_i_attrib;
+          bind._arg[1] = InternalName::make("shadowViewMatrix");
+
+          static bool warned = false;
+          if (!warned) {
+            warned = true;
+            GLCAT.warning()
+              << "p3d_LightSource[].shadowMatrix is deprecated; use "
+                "shadowViewMatrix instead, which transforms from view space "
+                "instead of model space.\n";
+          }
         } else {
           GLCAT.error() << "p3d_LightSource struct does not provide a matrix named " << matrix_name << "!\n";
           return;
@@ -1163,11 +1181,16 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
           bind._index = index;
           bind._part[0] = Shader::SMO_light_source_i_attrib;
           bind._arg[0] = InternalName::make(member_name);
-          bind._dep[0] = Shader::SSD_general | Shader::SSD_light | Shader::SSD_frame | Shader::SSD_transform;
+          bind._dep[0] = Shader::SSD_general | Shader::SSD_light | Shader::SSD_frame;
           bind._part[1] = Shader::SMO_identity;
           bind._arg[1] = NULL;
           bind._dep[1] = Shader::SSD_NONE;
 
+          if (member_name == "position" || member_name == "halfVector" ||
+              member_name == "spotDirection") {
+            bind._dep[0] |= Shader::SSD_view_transform;
+          }
+
           switch (param_type) {
           case GL_FLOAT:
             bind._piece = Shader::SMP_row3x1;
@@ -1250,7 +1273,7 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
       bind._func = Shader::SMF_compose;
       bind._part[0] = Shader::SMO_world_to_view;
       bind._part[1] = Shader::SMO_view_to_apiview;
-      bind._dep[0] = Shader::SSD_general | Shader::SSD_transform;
+      bind._dep[0] = Shader::SSD_general | Shader::SSD_view_transform;
       bind._dep[1] = Shader::SSD_general;
       _shader->_mat_spec.push_back(bind);
       _shader->_mat_deps |= bind._dep[0] | bind._dep[1];
@@ -1262,7 +1285,7 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
       bind._part[0] = Shader::SMO_apiview_to_view;
       bind._part[1] = Shader::SMO_view_to_world;
       bind._dep[0] = Shader::SSD_general;
-      bind._dep[1] = Shader::SSD_general | Shader::SSD_transform;
+      bind._dep[1] = Shader::SSD_general | Shader::SSD_view_transform;
       _shader->_mat_spec.push_back(bind);
       _shader->_mat_deps |= bind._dep[0] | bind._dep[1];
       return;
@@ -1383,22 +1406,43 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
         bind._id = arg_id;
         bind._piece = Shader::SMP_whole;
         bind._func = Shader::SMF_first;
+        bind._part[1] = Shader::SMO_identity;
+        bind._arg[1] = NULL;
+        bind._dep[1] = Shader::SSD_NONE;
         PT(InternalName) iname = InternalName::make(param_name);
         if (iname->get_parent() != InternalName::get_root()) {
           // It might be something like an attribute of a shader input, like a
           // light parameter.  It might also just be a custom struct
           // parameter.  We can't know yet, sadly.
-          bind._part[0] = Shader::SMO_mat_constant_x_attrib;
-          bind._arg[0] = InternalName::make(param_name);
-          bind._dep[0] = Shader::SSD_general | Shader::SSD_shaderinputs | Shader::SSD_frame | Shader::SSD_transform;
+          if (iname->get_basename() == "shadowMatrix") {
+            // Special exception for shadowMatrix, which is deprecated,
+            // because it includes the model transformation.  It is far more
+            // efficient to do that in the shader instead.
+            static bool warned = false;
+            if (!warned) {
+              warned = true;
+              GLCAT.warning()
+                << "light.shadowMatrix inputs are deprecated; use "
+                   "shadowViewMatrix instead, which transforms from view "
+                   "space instead of model space.\n";
+            }
+            bind._func = Shader::SMF_compose;
+            bind._part[0] = Shader::SMO_model_to_apiview;
+            bind._arg[0] = NULL;
+            bind._dep[0] = Shader::SSD_general | Shader::SSD_transform;
+            bind._part[1] = Shader::SMO_mat_constant_x_attrib;
+            bind._arg[1] = InternalName::make("shadowViewMatrix");
+            bind._dep[1] = Shader::SSD_general | Shader::SSD_shaderinputs | Shader::SSD_frame | Shader::SSD_view_transform;
+          } else {
+            bind._part[0] = Shader::SMO_mat_constant_x_attrib;
+            bind._arg[0] = InternalName::make(param_name);
+            bind._dep[0] = Shader::SSD_general | Shader::SSD_shaderinputs | Shader::SSD_frame | Shader::SSD_view_transform;
+          }
         } else {
           bind._part[0] = Shader::SMO_mat_constant_x;
           bind._arg[0] = InternalName::make(param_name);
           bind._dep[0] = Shader::SSD_general | Shader::SSD_shaderinputs | Shader::SSD_frame;
         }
-        bind._part[1] = Shader::SMO_identity;
-        bind._arg[1] = NULL;
-        bind._dep[1] = Shader::SSD_NONE;
         _shader->_mat_spec.push_back(bind);
         _shader->_mat_deps |= bind._dep[0];
         return;
@@ -1430,9 +1474,9 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
           bind._func = Shader::SMF_first;
           bind._part[0] = Shader::SMO_vec_constant_x_attrib;
           bind._arg[0] = iname;
-          // We need SSD_transform since some attributes (eg.  light position)
-          // have to be transformed to view space.
-          bind._dep[0] = Shader::SSD_general | Shader::SSD_shaderinputs | Shader::SSD_frame | Shader::SSD_transform;
+          // We need SSD_view_transform since some attributes (eg. light
+          // position) have to be transformed to view space.
+          bind._dep[0] = Shader::SSD_general | Shader::SSD_shaderinputs | Shader::SSD_frame | Shader::SSD_view_transform;
           bind._part[1] = Shader::SMO_identity;
           bind._arg[1] = NULL;
           bind._dep[1] = Shader::SSD_NONE;
@@ -1822,6 +1866,7 @@ unbind() {
 void CLP(ShaderContext)::
 set_state_and_transform(const RenderState *target_rs,
                         const TransformState *modelview_transform,
+                        const TransformState *camera_transform,
                         const TransformState *projection_transform) {
 
   // Find out which state properties have changed.
@@ -1829,6 +1874,10 @@ set_state_and_transform(const RenderState *target_rs,
 
   if (_modelview_transform != modelview_transform) {
     _modelview_transform = modelview_transform;
+    altered |= (Shader::SSD_transform & ~Shader::SSD_view_transform);
+  }
+  if (_camera_transform != camera_transform) {
+    _camera_transform = camera_transform;
     altered |= Shader::SSD_transform;
   }
   if (_projection_transform != projection_transform) {
@@ -2440,17 +2489,17 @@ update_shader_texture_bindings(ShaderContext *prev) {
       const ParamTextureImage *param = NULL;
       Texture *tex;
 
-      const ShaderInput *sinp = _glgsg->_target_shader->get_shader_input(input._name);
-      switch (sinp->get_value_type()) {
+      const ShaderInput &sinp = _glgsg->_target_shader->get_shader_input(input._name);
+      switch (sinp.get_value_type()) {
       case ShaderInput::M_texture_image:
-        param = (const ParamTextureImage *)sinp->get_param();
+        param = (const ParamTextureImage *)sinp.get_param();
         tex = param->get_texture();
         break;
 
       case ShaderInput::M_texture:
         // People find it convenient to be able to pass a texture without
         // further ado.
-        tex = sinp->get_texture();
+        tex = sinp.get_texture();
         break;
 
       case ShaderInput::M_invalid:

+ 11 - 9
panda/src/glstuff/glShaderContext_src.h

@@ -26,7 +26,7 @@ class CLP(GraphicsStateGuardian);
 /**
  * xyz
  */
-class EXPCL_GL CLP(ShaderContext) : public ShaderContext {
+class EXPCL_GL CLP(ShaderContext) FINAL : public ShaderContext {
 public:
   friend class CLP(GraphicsStateGuardian);
 
@@ -41,18 +41,19 @@ public:
   bool get_sampler_texture_type(int &out, GLenum param_type);
 
   INLINE bool valid(void);
-  void bind();
-  void unbind();
+  void bind() OVERRIDE;
+  void unbind() OVERRIDE;
 
   void set_state_and_transform(const RenderState *state,
                                const TransformState *modelview_transform,
-                               const TransformState *projection_transform);
+                               const TransformState *camera_transform,
+                               const TransformState *projection_transform) OVERRIDE;
 
-  void issue_parameters(int altered);
+  void issue_parameters(int altered) OVERRIDE;
   void update_transform_table(const TransformTable *table);
   void update_slider_table(const SliderTable *table);
-  void disable_shader_vertex_arrays();
-  bool update_shader_vertex_arrays(ShaderContext *prev, bool force);
+  void disable_shader_vertex_arrays() OVERRIDE;
+  bool update_shader_vertex_arrays(ShaderContext *prev, bool force) OVERRIDE;
   void disable_shader_texture_bindings() OVERRIDE;
   void update_shader_texture_bindings(ShaderContext *prev) OVERRIDE;
   void update_shader_buffer_bindings(ShaderContext *prev) OVERRIDE;
@@ -68,6 +69,7 @@ private:
 
   WCPT(RenderState) _state_rs;
   CPT(TransformState) _modelview_transform;
+  CPT(TransformState) _camera_transform;
   CPT(TransformState) _projection_transform;
 
 /*
@@ -126,10 +128,10 @@ public:
     register_type(_type_handle, CLASSPREFIX_QUOTED "ShaderContext",
                   ShaderContext::get_class_type());
   }
-  virtual TypeHandle get_type() const {
+  virtual TypeHandle get_type() const OVERRIDE {
     return get_class_type();
   }
-  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  virtual TypeHandle force_init_type() OVERRIDE {init_type(); return get_class_type();}
 
 private:
   static TypeHandle _type_handle;

+ 9 - 5
panda/src/gobj/adaptiveLru.cxx

@@ -114,12 +114,16 @@ update_page(AdaptiveLruPage *page) {
 
       update_frames = (_current_frame_identifier - page->_update_frame_identifier);
       if (update_frames > 0) {
-        PN_stdfloat update_average_frame_utilization =
-          (PN_stdfloat) (page->_update_total_usage) / (PN_stdfloat)update_frames;
+        if (page->_update_total_usage > 0) {
+          PN_stdfloat update_average_frame_utilization =
+            (PN_stdfloat) (page->_update_total_usage) / (PN_stdfloat)update_frames;
 
-        page->_average_frame_utilization =
-          calculate_exponential_moving_average(update_average_frame_utilization,
-                                               page->_average_frame_utilization);
+          page->_average_frame_utilization =
+            calculate_exponential_moving_average(update_average_frame_utilization,
+                                                 page->_average_frame_utilization);
+        } else {
+          page->_average_frame_utilization *= 1.0f - _weight;
+        }
 
         target_priority = page->_priority;
         if (page->_average_frame_utilization >= 1.0f) {

+ 63 - 15
panda/src/gobj/geom.cxx

@@ -657,26 +657,70 @@ unify_in_place(int max_indices, bool preserve_order) {
     if (prim->get_num_vertices() > max_indices) {
       // Copy prim into smaller prims, no one of which has more than
       // max_indices vertices.
-      int i = 0;
+      GeomPrimitivePipelineReader reader(prim, current_thread);
 
-      while (i < prim->get_num_primitives()) {
-        PT(GeomPrimitive) smaller = prim->make_copy();
-        smaller->clear_vertices();
-        while (i < prim->get_num_primitives() &&
-               smaller->get_num_vertices() + prim->get_primitive_num_vertices(i) < max_indices) {
-          int start = prim->get_primitive_start(i);
-          int end = prim->get_primitive_end(i);
-          for (int n = start; n < end; ++n) {
-            smaller->add_vertex(prim->get_vertex(n));
+      // Copy prim into smaller prims, no one of which has more than
+      // max_indices vertices.
+      int i = 0;
+      int num_primitives = reader.get_num_primitives();
+      int num_vertices_per_primitive = prim->get_num_vertices_per_primitive();
+      int num_unused_vertices_per_primitive = prim->get_num_unused_vertices_per_primitive();
+      if (num_vertices_per_primitive != 0) {
+        // This is a simple primitive type like a triangle, where all the
+        // primitives share the same number of vertices.
+        int total_vertices_per_primitive = num_vertices_per_primitive + num_unused_vertices_per_primitive;
+        int max_primitives = max_indices / total_vertices_per_primitive;
+        const unsigned char *ptr = reader.get_read_pointer(true);
+        size_t stride = reader.get_index_stride();
+
+        while (i < num_primitives) {
+          PT(GeomPrimitive) smaller = prim->make_copy();
+          smaller->clear_vertices();
+
+          // Since the number of vertices is consistent, we can calculate how
+          // many primitives will fit, and copy them all in one go.
+          int copy_primitives = min((num_primitives - i), max_primitives);
+          int num_vertices = copy_primitives * total_vertices_per_primitive;
+          nassertv(num_vertices > 0);
+          {
+            GeomVertexArrayDataHandle writer(smaller->modify_vertices(), current_thread);
+            writer.unclean_set_num_rows(num_vertices);
+            memcpy(writer.get_write_pointer(), ptr, stride * (size_t)(num_vertices - num_unused_vertices_per_primitive));
           }
-          smaller->close_primitive();
 
-          ++i;
+          cdata->_primitives.push_back(smaller.p());
+
+          ptr += stride * (size_t)num_vertices;
+          i += copy_primitives;
         }
+      } else {
+        // This is a complex primitive type like a triangle strip.
+        CPTA_int ends = reader.get_ends();
+        int start = 0;
+        int end = ends[0];
+
+        while (i < num_primitives) {
+          PT(GeomPrimitive) smaller = prim->make_copy();
+          smaller->clear_vertices();
+
+          while (smaller->get_num_vertices() + (end - start) < max_indices) {
+            for (int n = start; n < end; ++n) {
+              smaller->add_vertex(reader.get_vertex(n));
+            }
+            smaller->close_primitive();
 
-        cdata->_primitives.push_back(smaller.p());
-      }
+            ++i;
+            if (i >= num_primitives) {
+              break;
+            }
+
+            start = end + num_unused_vertices_per_primitive;
+            end = ends[i];
+          }
 
+          cdata->_primitives.push_back(smaller.p());
+        }
+      }
     } else {
       // The prim has few enough vertices; keep it.
       cdata->_primitives.push_back(prim);
@@ -925,7 +969,8 @@ bool Geom::
 check_valid() const {
   Thread *current_thread = Thread::get_current_thread();
   GeomPipelineReader geom_reader(this, current_thread);
-  GeomVertexDataPipelineReader data_reader(geom_reader.get_vertex_data(), current_thread);
+  CPT(GeomVertexData) vertex_data = geom_reader.get_vertex_data();
+  GeomVertexDataPipelineReader data_reader(vertex_data, current_thread);
   data_reader.check_array_readers();
   return geom_reader.check_valid(&data_reader);
 }
@@ -1212,6 +1257,9 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const {
                        InternalName::get_vertex(),
                        cdata, current_thread);
 
+  nassertv(!pmin.is_nan());
+  nassertv(!pmax.is_nan());
+
   BoundingVolume::BoundsType btype = cdata->_bounds_type;
   if (btype == BoundingVolume::BT_default) {
     btype = bounds_type;

+ 13 - 2
panda/src/gobj/geomPrimitive.I

@@ -124,8 +124,19 @@ get_vertex(int i) const {
  */
 INLINE int GeomPrimitive::
 get_num_primitives() const {
-  GeomPrimitivePipelineReader reader(this, Thread::get_current_thread());
-  return reader.get_num_primitives();
+  int num_vertices_per_primitive = 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.
+    CDReader cdata(_cycler);
+    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);
+  }
 }
 
 /**

+ 95 - 31
panda/src/gobj/geomPrimitive.cxx

@@ -165,16 +165,17 @@ add_vertex(int vertex) {
 
   consider_elevate_index_type(cdata, vertex);
 
-  int num_primitives = get_num_primitives();
-  if (num_primitives > 0 &&
-      requires_unused_vertices() &&
-      get_num_vertices() == get_primitive_end(num_primitives - 1)) {
-    // If we are beginning a new primitive, give the derived class a chance to
-    // insert some degenerate vertices.
-    if (cdata->_vertices.is_null()) {
-      do_make_indexed(cdata);
+  if (requires_unused_vertices()) {
+    int num_primitives = get_num_primitives();
+    if (num_primitives > 0 &&
+        get_num_vertices() == get_primitive_end(num_primitives - 1)) {
+      // If we are beginning a new primitive, give the derived class a chance to
+      // insert some degenerate vertices.
+      if (cdata->_vertices.is_null()) {
+        do_make_indexed(cdata);
+      }
+      append_unused_vertices(cdata->_vertices.get_write_pointer(), vertex);
     }
-    append_unused_vertices(cdata->_vertices.get_write_pointer(), vertex);
   }
 
   if (cdata->_vertices.is_null()) {
@@ -199,11 +200,28 @@ add_vertex(int vertex) {
     do_make_indexed(cdata);
   }
 
-  PT(GeomVertexArrayData) array_obj = cdata->_vertices.get_write_pointer();
-  GeomVertexWriter index(array_obj, 0);
-  index.set_row_unsafe(array_obj->get_num_rows());
+  {
+    GeomVertexArrayDataHandle handle(cdata->_vertices.get_write_pointer(),
+                                     Thread::get_current_thread());
+    int num_rows = handle.get_num_rows();
+    handle.set_num_rows(num_rows + 1);
 
-  index.add_data1i(vertex);
+    unsigned char *ptr = handle.get_write_pointer();
+    switch (cdata->_index_type) {
+    case GeomEnums::NT_uint8:
+      ((uint8_t *)ptr)[num_rows] = vertex;
+      break;
+    case GeomEnums::NT_uint16:
+      ((uint16_t *)ptr)[num_rows] = vertex;
+      break;
+    case GeomEnums::NT_uint32:
+      ((uint32_t *)ptr)[num_rows] = vertex;
+      break;
+    default:
+      nassertv(false);
+      break;
+    }
+  }
 
   cdata->_modified = Geom::get_next_modified();
   cdata->_got_minmax = false;
@@ -888,21 +906,9 @@ make_points() const {
   // First, get a list of all of the vertices referenced by the original
   // primitive.
   BitArray bits;
-  int num_vertices = get_num_vertices();
-  if (is_indexed()) {
-    CPT(GeomVertexArrayData) vertices = get_vertices();
-    int strip_cut_index = get_strip_cut_index();
-    GeomVertexReader index(vertices, 0);
-    for (int vi = 0; vi < num_vertices; ++vi) {
-      nassertr(!index.is_at_end(), NULL);
-      int vertex = index.get_data1i();
-      if (vertex != strip_cut_index) {
-        bits.set_bit(vertex);
-      }
-    }
-  } else {
-    int first_vertex = get_first_vertex();
-    bits.set_range(first_vertex, num_vertices);
+  {
+    GeomPrimitivePipelineReader reader(this, Thread::get_current_thread());
+    reader.get_referenced_vertices(bits);
   }
 
   // Now construct a new index array with just those bits.
@@ -2200,9 +2206,21 @@ get_vertex(int i) const {
     // The indexed case.
     nassertr(i >= 0 && i < get_num_vertices(), -1);
 
-    GeomVertexReader index(_vertices, 0);
-    index.set_row_unsafe(i);
-    return index.get_data1i();
+    const unsigned char *ptr = get_read_pointer(true);
+    switch (_cdata->_index_type) {
+    case GeomEnums::NT_uint8:
+      return ((uint8_t *)ptr)[i];
+      break;
+    case GeomEnums::NT_uint16:
+      return ((uint16_t *)ptr)[i];
+      break;
+    case GeomEnums::NT_uint32:
+      return ((uint32_t *)ptr)[i];
+      break;
+    default:
+      nassertr(false, -1);
+      return -1;
+    }
 
   } else {
     // The nonindexed case.
@@ -2229,6 +2247,52 @@ get_num_primitives() const {
   }
 }
 
+/**
+ * Turns on all the bits corresponding to the vertices that are referenced
+ * by this GeomPrimitive.
+ */
+void GeomPrimitivePipelineReader::
+get_referenced_vertices(BitArray &bits) const {
+  int num_vertices = get_num_vertices();
+
+  if (is_indexed()) {
+    int strip_cut_index = get_strip_cut_index();
+    const unsigned char *ptr = get_read_pointer(true);
+    switch (get_index_type()) {
+    case GeomEnums::NT_uint8:
+      for (int vi = 0; vi < num_vertices; ++vi) {
+        int index = ((const uint8_t *)ptr)[vi];
+        if (index != strip_cut_index) {
+          bits.set_bit(index);
+        }
+      }
+      break;
+    case GeomEnums::NT_uint16:
+      for (int vi = 0; vi < num_vertices; ++vi) {
+        int index = ((const uint16_t *)ptr)[vi];
+        if (index != strip_cut_index) {
+          bits.set_bit(index);
+        }
+      }
+      break;
+    case GeomEnums::NT_uint32:
+      for (int vi = 0; vi < num_vertices; ++vi) {
+        int index = ((const uint32_t *)ptr)[vi];
+        if (index != strip_cut_index) {
+          bits.set_bit(index);
+        }
+      }
+      break;
+    default:
+      nassertv(false);
+      break;
+    }
+  } else {
+    // Nonindexed case.
+    bits.set_range(get_first_vertex(), num_vertices);
+  }
+}
+
 /**
  *
  */

+ 1 - 0
panda/src/gobj/geomPrimitive.h

@@ -371,6 +371,7 @@ public:
   INLINE int get_num_vertices() const;
   int get_vertex(int i) const;
   int get_num_primitives() const;
+  void get_referenced_vertices(BitArray &bits) const;
   INLINE int get_min_vertex() const;
   INLINE int get_max_vertex() const;
   INLINE int get_data_size_bytes() const;

+ 2 - 2
panda/src/gobj/geomVertexData.I

@@ -756,7 +756,7 @@ GeomVertexDataPipelineReader(const GeomVertexData *object,
  *
  */
 INLINE void GeomVertexDataPipelineReader::
-set_object(CPT(GeomVertexData) object) {
+set_object(const GeomVertexData *object) {
 #ifdef DO_PIPELINING
   if (_cdata != NULL) {
     unref_delete((CycleData *)_cdata);
@@ -764,7 +764,7 @@ set_object(CPT(GeomVertexData) object) {
 #endif  // DO_PIPELINING
   _array_readers.clear();
 
-  _object.swap(object);
+  _object = (GeomVertexData *)object;
   _cdata = (GeomVertexData::CData *)_object->_cycler.read_unlocked(_current_thread);
   _got_array_readers = false;
 

+ 6 - 2
panda/src/gobj/geomVertexData.h

@@ -432,7 +432,7 @@ public:
   INLINE UpdateSeq get_modified() const;
 
 protected:
-  PT(GeomVertexData) _object;
+  GeomVertexData *_object;
   Thread *_current_thread;
   GeomVertexData::CData *_cdata;
 };
@@ -440,6 +440,8 @@ protected:
 /**
  * Encapsulates the data from a GeomVertexData, pre-fetched for one stage of
  * the pipeline.
+ * Does not hold a reference to the GeomVertexData, so make sure it does not
+ * go out of scope.
  */
 class EXPCL_PANDA_GOBJ GeomVertexDataPipelineReader : public GeomVertexDataPipelineBase {
 public:
@@ -448,7 +450,7 @@ public:
 
   ALLOC_DELETED_CHAIN(GeomVertexDataPipelineReader);
 
-  INLINE void set_object(CPT(GeomVertexData) object);
+  INLINE void set_object(const GeomVertexData *object);
   INLINE const GeomVertexData *get_object() const;
 
   INLINE void check_array_readers() const;
@@ -504,6 +506,8 @@ private:
 /**
  * Encapsulates the data from a GeomVertexData, pre-fetched for one stage of
  * the pipeline.
+ * Does not hold a reference to the GeomVertexData, so make sure it does not
+ * go out of scope.
  */
 class EXPCL_PANDA_GOBJ GeomVertexDataPipelineWriter : public GeomVertexDataPipelineBase {
 public:

+ 6 - 0
panda/src/gobj/internalName.h

@@ -178,6 +178,12 @@ private:
   static TypeHandle _texcoord_type_handle;
 };
 
+#ifdef DO_MEMORY_USAGE
+// We can safely redefine this as a no-op.
+template<>
+INLINE void PointerToBase<InternalName>::update_type(To *ptr) {}
+#endif
+
 INLINE ostream &operator << (ostream &out, const InternalName &tcn);
 
 /**

+ 6 - 4
panda/src/gobj/shader.cxx

@@ -390,8 +390,10 @@ cp_dependency(ShaderMatInput inp) {
   if ((inp == SMO_model_to_view) ||
       (inp == SMO_view_to_model) ||
       (inp == SMO_model_to_apiview) ||
-      (inp == SMO_apiview_to_model) ||
-      (inp == SMO_view_to_world) ||
+      (inp == SMO_apiview_to_model)) {
+    dep |= SSD_transform;
+  }
+  if ((inp == SMO_view_to_world) ||
       (inp == SMO_world_to_view) ||
       (inp == SMO_view_x_to_view) ||
       (inp == SMO_view_to_view_x) ||
@@ -404,7 +406,7 @@ cp_dependency(ShaderMatInput inp) {
       (inp == SMO_dlight_x) ||
       (inp == SMO_plight_x) ||
       (inp == SMO_slight_x)) {
-    dep |= SSD_transform;
+    dep |= SSD_view_transform;
   }
   if ((inp == SMO_texpad_x) ||
       (inp == SMO_texpix_x) ||
@@ -449,7 +451,7 @@ cp_dependency(ShaderMatInput inp) {
       (inp == SMO_light_source_i_attrib)) {
     dep |= SSD_light;
     if (inp == SMO_light_source_i_attrib) {
-      dep |= SSD_transform;
+      dep |= SSD_view_transform;
     }
   }
   if ((inp == SMO_light_product_i_ambient) ||

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

@@ -287,7 +287,7 @@ public:
   enum ShaderStateDep {
     SSD_NONE          = 0x000,
     SSD_general       = 0x001,
-    SSD_transform     = 0x002,
+    SSD_transform    = 0x2002,
     SSD_color         = 0x004,
     SSD_colorscale    = 0x008,
     SSD_material      = 0x010,
@@ -299,6 +299,7 @@ public:
     SSD_frame         = 0x400,
     SSD_projection    = 0x800,
     SSD_texture      = 0x1000,
+    SSD_view_transform= 0x2000,
   };
 
   enum ShaderBug {

+ 4 - 1
panda/src/gobj/shaderContext.h

@@ -32,7 +32,10 @@ class EXPCL_PANDA_GOBJ ShaderContext: public SavedContext {
 public:
   INLINE ShaderContext(Shader *se);
 
-  INLINE virtual void set_state_and_transform(const RenderState *, const TransformState *, const TransformState*) {};
+  virtual void set_state_and_transform(const RenderState *,
+                                       const TransformState *,
+                                       const TransformState *,
+                                       const TransformState *) {};
 
   INLINE virtual bool valid() { return false; }
   INLINE virtual void bind() {};

+ 13 - 22
panda/src/grutil/pipeOcclusionCullTraverser.cxx

@@ -464,42 +464,33 @@ void PipeOcclusionCullTraverser::
 make_box() {
   PT(GeomVertexData) vdata = new GeomVertexData
     ("occlusion_box", GeomVertexFormat::get_v3(), Geom::UH_static);
-  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
-
-  vertex.add_data3(0.0f, 0.0f, 0.0f);
-  vertex.add_data3(0.0f, 0.0f, 1.0f);
-  vertex.add_data3(0.0f, 1.0f, 0.0f);
-  vertex.add_data3(0.0f, 1.0f, 1.0f);
-  vertex.add_data3(1.0f, 0.0f, 0.0f);
-  vertex.add_data3(1.0f, 0.0f, 1.0f);
-  vertex.add_data3(1.0f, 1.0f, 0.0f);
-  vertex.add_data3(1.0f, 1.0f, 1.0f);
+  vdata->unclean_set_num_rows(8);
+
+  {
+    GeomVertexWriter vertex(vdata, InternalName::get_vertex());
+    vertex.set_data3(0.0f, 0.0f, 0.0f);
+    vertex.set_data3(0.0f, 0.0f, 1.0f);
+    vertex.set_data3(0.0f, 1.0f, 0.0f);
+    vertex.set_data3(0.0f, 1.0f, 1.0f);
+    vertex.set_data3(1.0f, 0.0f, 0.0f);
+    vertex.set_data3(1.0f, 0.0f, 1.0f);
+    vertex.set_data3(1.0f, 1.0f, 0.0f);
+    vertex.set_data3(1.0f, 1.0f, 1.0f);
+  }
 
   PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_static);
   tris->add_vertices(0, 4, 5);
-  tris->close_primitive();
   tris->add_vertices(0, 5, 1);
-  tris->close_primitive();
   tris->add_vertices(4, 6, 7);
-  tris->close_primitive();
   tris->add_vertices(4, 7, 5);
-  tris->close_primitive();
   tris->add_vertices(6, 2, 3);
-  tris->close_primitive();
   tris->add_vertices(6, 3, 7);
-  tris->close_primitive();
   tris->add_vertices(2, 0, 1);
-  tris->close_primitive();
   tris->add_vertices(2, 1, 3);
-  tris->close_primitive();
   tris->add_vertices(1, 5, 7);
-  tris->close_primitive();
   tris->add_vertices(1, 7, 3);
-  tris->close_primitive();
   tris->add_vertices(2, 6, 4);
-  tris->close_primitive();
   tris->add_vertices(2, 4, 0);
-  tris->close_primitive();
 
   _box_geom = new Geom(vdata);
   _box_geom->add_primitive(tris);

+ 5 - 5
panda/src/grutil/shaderTerrainMesh.cxx

@@ -513,15 +513,15 @@ void ShaderTerrainMesh::add_for_draw(CullTraverser *trav, CullTraverserData &dat
   nassertv(current_shader_attrib != NULL);
 
   current_shader_attrib = DCAST(ShaderAttrib, current_shader_attrib)->set_shader_input(
-    new ShaderInput("ShaderTerrainMesh.terrain_size", LVecBase2i(_size)) );
+    ShaderInput("ShaderTerrainMesh.terrain_size", LVecBase2i(_size)));
   current_shader_attrib = DCAST(ShaderAttrib, current_shader_attrib)->set_shader_input(
-    new ShaderInput("ShaderTerrainMesh.chunk_size", LVecBase2i(_chunk_size)));
+    ShaderInput("ShaderTerrainMesh.chunk_size", LVecBase2i(_chunk_size)));
   current_shader_attrib = DCAST(ShaderAttrib, current_shader_attrib)->set_shader_input(
-    new ShaderInput("ShaderTerrainMesh.view_index", LVecBase2i(_current_view_index)));
+    ShaderInput("ShaderTerrainMesh.view_index", LVecBase2i(_current_view_index)));
   current_shader_attrib = DCAST(ShaderAttrib, current_shader_attrib)->set_shader_input(
-    new ShaderInput("ShaderTerrainMesh.data_texture", _data_texture));
+    ShaderInput("ShaderTerrainMesh.data_texture", _data_texture));
   current_shader_attrib = DCAST(ShaderAttrib, current_shader_attrib)->set_shader_input(
-    new ShaderInput("ShaderTerrainMesh.heightfield", _heightfield_tex));
+    ShaderInput("ShaderTerrainMesh.heightfield", _heightfield_tex));
   current_shader_attrib = DCAST(ShaderAttrib, current_shader_attrib)->set_instance_count(
     traversal_data.emitted_chunks);
 

+ 3 - 0
panda/src/mathutil/geometricBoundingVolume.I

@@ -16,6 +16,9 @@
  */
 INLINE_MATHUTIL GeometricBoundingVolume::
 GeometricBoundingVolume() {
+#ifdef DO_MEMORY_USAGE
+  MemoryUsage::update_type(this, this);
+#endif
 }
 
 /**

+ 6 - 0
panda/src/mathutil/geometricBoundingVolume.h

@@ -83,6 +83,12 @@ private:
   static TypeHandle _type_handle;
 };
 
+#ifdef DO_MEMORY_USAGE
+// We can safely redefine this as a no-op.
+template<>
+INLINE void PointerToBase<GeometricBoundingVolume>::update_type(To *ptr) {}
+#endif
+
 #include "geometricBoundingVolume.I"
 
 #endif

+ 2 - 2
panda/src/parametrics/ropeNode.cxx

@@ -132,9 +132,9 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
     if (curve != (NurbsCurveEvaluator *)NULL) {
       PT(NurbsCurveResult) result;
       if (has_matrix()) {
-        result = curve->evaluate(data._node_path.get_node_path(), get_matrix());
+        result = curve->evaluate(data.get_node_path(), get_matrix());
       } else {
-        result = curve->evaluate(data._node_path.get_node_path());
+        result = curve->evaluate(data.get_node_path());
       }
 
       if (result->get_num_segments() > 0) {

+ 1 - 1
panda/src/parametrics/sheetNode.cxx

@@ -129,7 +129,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
   if (get_num_u_subdiv() > 0 && get_num_v_subdiv() > 0) {
     NurbsSurfaceEvaluator *surface = get_surface();
     if (surface != (NurbsSurfaceEvaluator *)NULL) {
-      PT(NurbsSurfaceResult) result = surface->evaluate(data._node_path.get_node_path());
+      PT(NurbsSurfaceResult) result = surface->evaluate(data.get_node_path());
 
       if (result->get_num_u_segments() > 0 && result->get_num_v_segments() > 0) {
         render_sheet(trav, data, result);

+ 1 - 1
panda/src/pgraph/billboardEffect.cxx

@@ -164,7 +164,7 @@ has_adjust_transform() const {
 void BillboardEffect::
 adjust_transform(CPT(TransformState) &net_transform,
                  CPT(TransformState) &node_transform,
-                 PandaNode *) const {
+                 const PandaNode *) const {
   // A BillboardEffect can only affect the net transform when it is to a
   // particular node.  A billboard to a camera is camera-dependent, of course,
   // so it has no effect in the absence of any particular camera viewing it.

+ 1 - 1
panda/src/pgraph/billboardEffect.h

@@ -60,7 +60,7 @@ public:
   virtual bool has_adjust_transform() const;
   virtual void adjust_transform(CPT(TransformState) &net_transform,
                                 CPT(TransformState) &node_transform,
-                                PandaNode *node) const;
+                                const PandaNode *node) const;
 
 protected:
   virtual int compare_to_impl(const RenderEffect *other) const;

+ 1 - 1
panda/src/pgraph/compassEffect.cxx

@@ -157,7 +157,7 @@ has_adjust_transform() const {
 void CompassEffect::
 adjust_transform(CPT(TransformState) &net_transform,
                  CPT(TransformState) &node_transform,
-                 PandaNode *) const {
+                 const PandaNode *) const {
   if (_properties == 0) {
     // Nothing to do.
     return;

+ 1 - 1
panda/src/pgraph/compassEffect.h

@@ -78,7 +78,7 @@ public:
   virtual bool has_adjust_transform() const;
   virtual void adjust_transform(CPT(TransformState) &net_transform,
                                 CPT(TransformState) &node_transform,
-                                PandaNode *node) const;
+                                const PandaNode *node) const;
 
 protected:
   virtual int compare_to_impl(const RenderEffect *other) const;

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

@@ -78,7 +78,6 @@
 #include "scissorAttrib.h"
 #include "scissorEffect.h"
 #include "shadeModelAttrib.h"
-#include "shaderInput.h"
 #include "shaderAttrib.h"
 #include "shader.h"
 #include "showBoundsEffect.h"
@@ -449,7 +448,6 @@ init_libpgraph() {
   ScissorAttrib::init_type();
   ScissorEffect::init_type();
   ShadeModelAttrib::init_type();
-  ShaderInput::init_type();
   ShaderAttrib::init_type();
   ShowBoundsEffect::init_type();
   StateMunger::init_type();
@@ -502,7 +500,6 @@ init_libpgraph() {
   ScissorAttrib::register_with_read_factory();
   ScissorEffect::register_with_read_factory();
   ShadeModelAttrib::register_with_read_factory();
-  ShaderInput::register_with_read_factory();
   ShaderAttrib::register_with_read_factory();
   ShowBoundsEffect::register_with_read_factory();
   TexMatrixAttrib::register_with_read_factory();

+ 2 - 3
panda/src/pgraph/cullPlanes.cxx

@@ -315,11 +315,10 @@ do_cull(int &result, CPT(RenderState) &state,
   result =
     BoundingVolume::IF_all | BoundingVolume::IF_possible | BoundingVolume::IF_some;
 
-  CPT(ClipPlaneAttrib) orig_cpa = DCAST(ClipPlaneAttrib, state->get_attrib(ClipPlaneAttrib::get_class_slot()));
-
   CPT(CullPlanes) new_planes = this;
 
-  if (orig_cpa == (ClipPlaneAttrib *)NULL) {
+  const ClipPlaneAttrib *orig_cpa;
+  if (!state->get_attrib(orig_cpa)) {
     // If there are no clip planes in the state, the node is completely in
     // front of all zero of the clip planes.  (This can happen if someone
     // directly changes the state during the traversal.)

+ 6 - 0
panda/src/pgraph/cullPlanes.h

@@ -74,6 +74,12 @@ private:
   Occluders _occluders;
 };
 
+#ifdef DO_MEMORY_USAGE
+// We can safely redefine this as a no-op.
+template<>
+INLINE void PointerToBase<CullPlanes>::update_type(To *ptr) {}
+#endif
+
 #include "cullPlanes.I"
 
 #endif

+ 2 - 8
panda/src/pgraph/cullTraverser.I

@@ -200,20 +200,14 @@ do_traverse(CullTraverserData &data) {
   if (is_in_view(data)) {
     if (pgraph_cat.is_spam()) {
       pgraph_cat.spam()
-        << "\n" << data._node_path
+        << "\n" << data.get_node_path()
         << " " << data._draw_mask << "\n";
     }
 
     PandaNodePipelineReader *node_reader = data.node_reader();
     int fancy_bits = node_reader->get_fancy_bits();
 
-    if ((fancy_bits & (PandaNode::FB_transform |
-                       PandaNode::FB_state |
-                       PandaNode::FB_effects |
-                       PandaNode::FB_tag |
-                       PandaNode::FB_draw_mask |
-                       PandaNode::FB_cull_callback)) == 0 &&
-        data._cull_planes->is_empty()) {
+    if (fancy_bits == 0 && data._cull_planes->is_empty()) {
       // Nothing interesting in this node; just move on.
 
     } else {

+ 52 - 61
panda/src/pgraph/cullTraverser.cxx

@@ -113,10 +113,8 @@ traverse(const NodePath &root) {
 
     GeometricBoundingVolume *local_frustum = NULL;
     PT(BoundingVolume) bv = _scene_setup->get_lens()->make_bounds();
-    if (bv != (BoundingVolume *)NULL &&
-        bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
-
-      local_frustum = DCAST(GeometricBoundingVolume, bv);
+    if (bv != nullptr) {
+      local_frustum = bv->as_geometric_bounding_volume();
     }
 
     // This local_frustum is in camera space
@@ -199,18 +197,17 @@ traverse_below(CullTraverserData &data) {
   PandaNode::Children children = node_reader->get_children();
   node_reader->release();
   int num_children = children.get_num_children();
-  if (node->has_selective_visibility()) {
-    int i = node->get_first_visible_child();
-    while (i < num_children) {
+  if (!node->has_selective_visibility()) {
+    for (int i = 0; i < num_children; ++i) {
       CullTraverserData next_data(data, children.get_child(i));
       do_traverse(next_data);
-      i = node->get_next_visible_child(i);
     }
-
   } else {
-    for (int i = 0; i < num_children; i++) {
+    int i = node->get_first_visible_child();
+    while (i < num_children) {
       CullTraverserData next_data(data, children.get_child(i));
       do_traverse(next_data);
+      i = node->get_next_visible_child(i);
     }
   }
 }
@@ -235,12 +232,12 @@ draw_bounding_volume(const BoundingVolume *vol,
   if (bounds_viz != (Geom *)NULL) {
     _geoms_pcollector.add_level(2);
     CullableObject *outer_viz =
-      new CullableObject(bounds_viz, get_bounds_outer_viz_state(),
+      new CullableObject(move(bounds_viz), get_bounds_outer_viz_state(),
                          internal_transform);
     _cull_handler->record_object(outer_viz, this);
 
     CullableObject *inner_viz =
-      new CullableObject(bounds_viz, get_bounds_inner_viz_state(),
+      new CullableObject(move(bounds_viz), get_bounds_inner_viz_state(),
                          internal_transform);
     _cull_handler->record_object(inner_viz, this);
   }
@@ -270,7 +267,7 @@ show_bounds(CullTraverserData &data, bool tight) {
     if (bounds_viz != (Geom *)NULL) {
       _geoms_pcollector.add_level(1);
       CullableObject *outer_viz =
-        new CullableObject(bounds_viz, get_bounds_outer_viz_state(),
+        new CullableObject(move(bounds_viz), get_bounds_outer_viz_state(),
                            internal_transform);
       _cull_handler->record_object(outer_viz, this);
     }
@@ -281,7 +278,7 @@ show_bounds(CullTraverserData &data, bool tight) {
     if (node->is_geom_node()) {
       // Also show the bounding volumes of included Geoms.
       internal_transform = internal_transform->compose(node->get_transform());
-      GeomNode *gnode = DCAST(GeomNode, node);
+      GeomNode *gnode = (GeomNode *)node;
       int num_geoms = gnode->get_num_geoms();
       for (int i = 0; i < num_geoms; ++i) {
         draw_bounding_volume(gnode->get_geom(i)->get_bounds(),
@@ -334,29 +331,31 @@ make_bounds_viz(const BoundingVolume *vol) {
     const BoundingHexahedron *fvol = DCAST(BoundingHexahedron, vol);
 
     PT(GeomVertexData) vdata = new GeomVertexData
-      ("bounds", GeomVertexFormat::get_v3(),
-       Geom::UH_stream);
-    GeomVertexWriter vertex(vdata, InternalName::get_vertex());
+      ("bounds", GeomVertexFormat::get_v3(), Geom::UH_stream);
+    vdata->unclean_set_num_rows(8);
 
-    for (int i = 0; i < 8; ++i ) {
-      vertex.add_data3(fvol->get_point(i));
+    {
+      GeomVertexWriter vertex(vdata, InternalName::get_vertex());
+      for (int i = 0; i < 8; ++i) {
+        vertex.set_data3(fvol->get_point(i));
+      }
     }
 
     PT(GeomLines) lines = new GeomLines(Geom::UH_stream);
-    lines->add_vertices(0, 1); lines->close_primitive();
-    lines->add_vertices(1, 2); lines->close_primitive();
-    lines->add_vertices(2, 3); lines->close_primitive();
-    lines->add_vertices(3, 0); lines->close_primitive();
+    lines->add_vertices(0, 1);
+    lines->add_vertices(1, 2);
+    lines->add_vertices(2, 3);
+    lines->add_vertices(3, 0);
 
-    lines->add_vertices(4, 5); lines->close_primitive();
-    lines->add_vertices(5, 6); lines->close_primitive();
-    lines->add_vertices(6, 7); lines->close_primitive();
-    lines->add_vertices(7, 4); lines->close_primitive();
+    lines->add_vertices(4, 5);
+    lines->add_vertices(5, 6);
+    lines->add_vertices(6, 7);
+    lines->add_vertices(7, 4);
 
-    lines->add_vertices(0, 4); lines->close_primitive();
-    lines->add_vertices(1, 5); lines->close_primitive();
-    lines->add_vertices(2, 6); lines->close_primitive();
-    lines->add_vertices(3, 7); lines->close_primitive();
+    lines->add_vertices(0, 4);
+    lines->add_vertices(1, 5);
+    lines->add_vertices(2, 6);
+    lines->add_vertices(3, 7);
 
     geom = new Geom(vdata);
     geom->add_primitive(lines);
@@ -368,39 +367,29 @@ make_bounds_viz(const BoundingVolume *vol) {
     box.local_object();
 
     PT(GeomVertexData) vdata = new GeomVertexData
-      ("bounds", GeomVertexFormat::get_v3(),
-       Geom::UH_stream);
-    GeomVertexWriter vertex(vdata, InternalName::get_vertex());
+      ("bounds", GeomVertexFormat::get_v3(), Geom::UH_stream);
+    vdata->unclean_set_num_rows(8);
 
-    for (int i = 0; i < 8; ++i ) {
-      vertex.add_data3(box.get_point(i));
+    {
+      GeomVertexWriter vertex(vdata, InternalName::get_vertex());
+      for (int i = 0; i < 8; ++i) {
+        vertex.set_data3(box.get_point(i));
+      }
     }
 
     PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_stream);
     tris->add_vertices(0, 4, 5);
-    tris->close_primitive();
     tris->add_vertices(0, 5, 1);
-    tris->close_primitive();
     tris->add_vertices(4, 6, 7);
-    tris->close_primitive();
     tris->add_vertices(4, 7, 5);
-    tris->close_primitive();
     tris->add_vertices(6, 2, 3);
-    tris->close_primitive();
     tris->add_vertices(6, 3, 7);
-    tris->close_primitive();
     tris->add_vertices(2, 0, 1);
-    tris->close_primitive();
     tris->add_vertices(2, 1, 3);
-    tris->close_primitive();
     tris->add_vertices(1, 5, 7);
-    tris->close_primitive();
     tris->add_vertices(1, 7, 3);
-    tris->close_primitive();
     tris->add_vertices(2, 6, 4);
-    tris->close_primitive();
     tris->add_vertices(2, 4, 0);
-    tris->close_primitive();
 
     geom = new Geom(vdata);
     geom->add_primitive(tris);
@@ -430,19 +419,21 @@ make_tight_bounds_viz(PandaNode *node) const {
                           _current_thread);
   if (found_any) {
     PT(GeomVertexData) vdata = new GeomVertexData
-      ("bounds", GeomVertexFormat::get_v3(),
-      Geom::UH_stream);
-    GeomVertexWriter vertex(vdata, InternalName::get_vertex(),
-                            _current_thread);
-
-    vertex.add_data3(n[0], n[1], n[2]);
-    vertex.add_data3(n[0], n[1], x[2]);
-    vertex.add_data3(n[0], x[1], n[2]);
-    vertex.add_data3(n[0], x[1], x[2]);
-    vertex.add_data3(x[0], n[1], n[2]);
-    vertex.add_data3(x[0], n[1], x[2]);
-    vertex.add_data3(x[0], x[1], n[2]);
-    vertex.add_data3(x[0], x[1], x[2]);
+      ("bounds", GeomVertexFormat::get_v3(), Geom::UH_stream);
+    vdata->unclean_set_num_rows(8);
+
+    {
+      GeomVertexWriter vertex(vdata, InternalName::get_vertex(),
+                              _current_thread);
+      vertex.set_data3(n[0], n[1], n[2]);
+      vertex.set_data3(n[0], n[1], x[2]);
+      vertex.set_data3(n[0], x[1], n[2]);
+      vertex.set_data3(n[0], x[1], x[2]);
+      vertex.set_data3(x[0], n[1], n[2]);
+      vertex.set_data3(x[0], n[1], x[2]);
+      vertex.set_data3(x[0], x[1], n[2]);
+      vertex.set_data3(x[0], x[1], x[2]);
+    }
 
     PT(GeomLinestrips) strip = new GeomLinestrips(Geom::UH_stream);
 

+ 19 - 41
panda/src/pgraph/cullTraverserData.I

@@ -20,7 +20,8 @@ CullTraverserData(const NodePath &start,
                   const RenderState *state,
                   GeometricBoundingVolume *view_frustum,
                   Thread *current_thread) :
-  _node_path(start),
+  _next(nullptr),
+  _start(start._head),
   _node_reader(start.node(), current_thread),
   _net_transform(net_transform),
   _state(state),
@@ -34,44 +35,16 @@ CullTraverserData(const NodePath &start,
   _node_reader.check_cached(check_bounds);
 }
 
-/**
- *
- */
-INLINE CullTraverserData::
-CullTraverserData(const CullTraverserData &copy) :
-  _node_path(copy._node_path),
-  _node_reader(copy._node_reader),
-  _net_transform(copy._net_transform),
-  _state(copy._state),
-  _view_frustum(copy._view_frustum),
-  _cull_planes(copy._cull_planes),
-  _draw_mask(copy._draw_mask),
-  _portal_depth(copy._portal_depth)
-{
-}
-
-/**
- *
- */
-INLINE void CullTraverserData::
-operator = (const CullTraverserData &copy) {
-  _node_path = copy._node_path;
-  _node_reader = copy._node_reader;
-  _net_transform = copy._net_transform;
-  _state = copy._state;
-  _view_frustum = copy._view_frustum;
-  _cull_planes = copy._cull_planes;
-  _draw_mask = copy._draw_mask;
-  _portal_depth = copy._portal_depth;
-}
-
 /**
  * This constructor creates a CullTraverserData object that reflects the next
  * node down in the traversal.
  */
 INLINE CullTraverserData::
 CullTraverserData(const CullTraverserData &parent, PandaNode *child) :
-  _node_path(parent._node_path, child),
+  _next(&parent),
+#ifdef _DEBUG
+  _start(nullptr),
+#endif
   _node_reader(child, parent._node_reader.get_current_thread()),
   _net_transform(parent._net_transform),
   _state(parent._state),
@@ -86,19 +59,12 @@ CullTraverserData(const CullTraverserData &parent, PandaNode *child) :
   _node_reader.check_cached(check_bounds);
 }
 
-/**
- *
- */
-INLINE CullTraverserData::
-~CullTraverserData() {
-}
-
 /**
  * Returns the node traversed to so far.
  */
 INLINE PandaNode *CullTraverserData::
 node() const {
-  return _node_path.node();
+  return (PandaNode *)_node_reader.get_node();
 }
 
 /**
@@ -117,6 +83,18 @@ node_reader() const {
   return &_node_reader;
 }
 
+/**
+ * Constructs and returns an actual NodePath that represents the same path we
+ * have just traversed.
+ */
+INLINE NodePath CullTraverserData::
+get_node_path() const {
+  NodePath result;
+  result._head = r_get_node_path();
+  nassertr(result._head != nullptr, NodePath::fail());
+  return result;
+}
+
 /**
  * Returns the modelview transform: the relative transform from the camera to
  * the model.

+ 62 - 31
panda/src/pgraph/cullTraverserData.cxx

@@ -46,25 +46,33 @@ apply_transform_and_state(CullTraverser *trav) {
   }
   _node_reader.compose_draw_mask(_draw_mask);
 
-  apply_transform_and_state(trav, _node_reader.get_transform(),
-                            MOVE(node_state), _node_reader.get_effects(),
-                            _node_reader.get_off_clip_planes());
+  const RenderEffects *node_effects = _node_reader.get_effects();
+  if (!node_effects->has_cull_callback()) {
+    apply_transform(_node_reader.get_transform());
+  } else {
+    // The cull callback may decide to modify the node_transform.
+    CPT(TransformState) node_transform = _node_reader.get_transform();
+    node_effects->cull_callback(trav, *this, node_transform, node_state);
+    apply_transform(node_transform);
+  }
+
+  if (!node_state->is_empty()) {
+    _state = _state->compose(node_state);
+  }
+
+  if (clip_plane_cull) {
+    _cull_planes = _cull_planes->apply_state(trav, this,
+      (const ClipPlaneAttrib *)node_state->get_attrib(ClipPlaneAttrib::get_class_slot()),
+      (const ClipPlaneAttrib *)_node_reader.get_off_clip_planes(),
+      (const OccluderEffect *)node_effects->get_effect(OccluderEffect::get_class_type()));
+  }
 }
 
 /**
- * Applies the indicated transform and state changes (e.g.  as extracted from
- * a node) onto the current data.  This also evaluates billboards, etc.
+ * Applies the indicated transform changes onto the current data.
  */
 void CullTraverserData::
-apply_transform_and_state(CullTraverser *trav,
-                          CPT(TransformState) node_transform,
-                          CPT(RenderState) node_state,
-                          CPT(RenderEffects) node_effects,
-                          const RenderAttrib *off_clip_planes) {
-  if (node_effects->has_cull_callback()) {
-    node_effects->cull_callback(trav, *this, node_transform, node_state);
-  }
-
+apply_transform(const TransformState *node_transform) {
   if (!node_transform->is_identity()) {
     _net_transform = _net_transform->compose(node_transform);
 
@@ -95,15 +103,40 @@ apply_transform_and_state(CullTraverser *trav,
       }
     }
   }
+}
 
-  _state = _state->compose(node_state);
+/**
+ * The private, recursive implementation of get_node_path(), this returns the
+ * NodePathComponent representing the NodePath.
+ */
+PT(NodePathComponent) CullTraverserData::
+r_get_node_path() const {
+  if (_next == nullptr) {
+    nassertr(_start != nullptr, nullptr);
+    return _start;
+  }
 
-  if (clip_plane_cull) {
-    _cull_planes = _cull_planes->apply_state(trav, this,
-                                             (const ClipPlaneAttrib *)node_state->get_attrib(ClipPlaneAttrib::get_class_slot()),
-                                             (const ClipPlaneAttrib *)off_clip_planes,
-                                             (const OccluderEffect *)node_effects->get_effect(OccluderEffect::get_class_type()));
+#ifdef _DEBUG
+  nassertr(_start == nullptr, nullptr);
+#endif
+  nassertr(node() != nullptr, nullptr);
+
+  PT(NodePathComponent) comp = _next->r_get_node_path();
+  nassertr(comp != nullptr, nullptr);
+
+  Thread *current_thread = Thread::get_current_thread();
+  int pipeline_stage = current_thread->get_pipeline_stage();
+  PT(NodePathComponent) result =
+    PandaNode::get_component(comp, node(), pipeline_stage, current_thread);
+  if (result == nullptr) {
+    // This means we found a disconnected chain in the CullTraverserData's
+    // ancestry: the node above this node isn't connected.  In this case,
+    // don't attempt to go higher; just truncate the NodePath at the bottom of
+    // the disconnect.
+    return PandaNode::get_top_component(node(), true, pipeline_stage, current_thread);
   }
+
+  return result;
 }
 
 /**
@@ -121,7 +154,7 @@ is_in_view_impl() {
 
     if (pgraph_cat.is_spam()) {
       pgraph_cat.spam()
-        << _node_path << " cull result = " << hex << result << dec << "\n";
+        << get_node_path() << " cull result = " << hex << result << dec << "\n";
     }
 
     if (result == BoundingVolume::IF_no_intersection) {
@@ -136,8 +169,7 @@ is_in_view_impl() {
       // If we have fake view-frustum culling enabled, instead of actually
       // culling an object we simply force it to be drawn in red wireframe.
       _view_frustum = (GeometricBoundingVolume *)NULL;
-      CPT(RenderState) fake_state = get_fake_view_frustum_cull_state();
-      _state = _state->compose(fake_state);
+      _state = _state->compose(get_fake_view_frustum_cull_state());
 #endif
 
     } else if ((result & BoundingVolume::IF_all) != 0) {
@@ -170,7 +202,7 @@ is_in_view_impl() {
 
     if (pgraph_cat.is_spam()) {
       pgraph_cat.spam()
-        << _node_path << " cull planes cull result = " << hex
+        << get_node_path() << " cull planes cull result = " << hex
         << result << dec << "\n";
       _cull_planes->write(pgraph_cat.spam(false));
     }
@@ -182,7 +214,7 @@ is_in_view_impl() {
 
       if (pgraph_cat.is_spam()) {
         pgraph_cat.spam()
-          << _node_path << " is_final, cull planes disabled, state:\n";
+          << get_node_path() << " is_final, cull planes disabled, state:\n";
         _state->write(pgraph_cat.spam(false), 2);
       }
     }
@@ -196,8 +228,7 @@ is_in_view_impl() {
         return false;
       }
       _cull_planes = CullPlanes::make_empty();
-      CPT(RenderState) fake_state = get_fake_view_frustum_cull_state();
-      _state = _state->compose(fake_state);
+      _state = _state->compose(get_fake_view_frustum_cull_state());
 #endif
 
     } else if ((result & BoundingVolume::IF_all) != 0) {
@@ -215,15 +246,15 @@ is_in_view_impl() {
  * Returns a RenderState for rendering stuff in red wireframe, strictly for
  * the fake_view_frustum_cull effect.
  */
-CPT(RenderState) CullTraverserData::
+const RenderState *CullTraverserData::
 get_fake_view_frustum_cull_state() {
 #ifdef NDEBUG
-  return NULL;
+  return nullptr;
 #else
   // Once someone asks for this pointer, we hold its reference count and never
   // free it.
-  static CPT(RenderState) state = (const RenderState *)NULL;
-  if (state == (const RenderState *)NULL) {
+  static CPT(RenderState) state;
+  if (state == nullptr) {
     state = RenderState::make
       (ColorAttrib::make_flat(LColor(1.0f, 0.0f, 0.0f, 1.0f)),
        TextureAttrib::make_all_off(),

+ 12 - 10
panda/src/pgraph/cullTraverserData.h

@@ -44,11 +44,8 @@ public:
                            const RenderState *state,
                            GeometricBoundingVolume *view_frustum,
                            Thread *current_thread);
-  INLINE CullTraverserData(const CullTraverserData &copy);
-  INLINE void operator = (const CullTraverserData &copy);
   INLINE CullTraverserData(const CullTraverserData &parent,
                            PandaNode *child);
-  INLINE ~CullTraverserData();
 
 PUBLISHED:
   INLINE PandaNode *node() const;
@@ -57,6 +54,8 @@ public:
   INLINE PandaNodePipelineReader *node_reader();
   INLINE const PandaNodePipelineReader *node_reader() const;
 
+  INLINE NodePath get_node_path() const;
+
 PUBLISHED:
   INLINE CPT(TransformState) get_modelview_transform(const CullTraverser *trav) const;
   INLINE CPT(TransformState) get_internal_transform(const CullTraverser *trav) const;
@@ -66,14 +65,15 @@ PUBLISHED:
   INLINE bool is_this_node_hidden(const DrawMask &camera_mask) const;
 
   void apply_transform_and_state(CullTraverser *trav);
-  void apply_transform_and_state(CullTraverser *trav,
-                                 CPT(TransformState) node_transform,
-                                 CPT(RenderState) node_state,
-                                 CPT(RenderEffects) node_effects,
-                                 const RenderAttrib *off_clip_planes);
+  void apply_transform(const TransformState *node_transform);
+
+private:
+  // We store a chain leading all the way to the root, so that we can compose
+  // a NodePath.  We may be able to eliminate this requirement in the future.
+  const CullTraverserData *_next;
+  NodePathComponent *_start;
 
 public:
-  WorkingNodePath _node_path;
   PandaNodePipelineReader _node_reader;
   CPT(TransformState) _net_transform;
   CPT(RenderState) _state;
@@ -83,8 +83,10 @@ public:
   int _portal_depth;
 
 private:
+  PT(NodePathComponent) r_get_node_path() const;
+
   bool is_in_view_impl();
-  static CPT(RenderState) get_fake_view_frustum_cull_state();
+  static const RenderState *get_fake_view_frustum_cull_state();
 };
 
 /* okcircular */

+ 3 - 8
panda/src/pgraph/geomTransformer.cxx

@@ -151,7 +151,7 @@ transform_vertices(GeomNode *node, const LMatrix4 &mat) {
       GeomNode::GeomEntry &entry = (*gi);
       PT(Geom) new_geom = entry._geom.get_read_pointer()->make_copy();
       if (transform_vertices(new_geom, mat)) {
-        entry._geom = new_geom;
+        entry._geom = move(new_geom);
         any_changed = true;
       }
     }
@@ -1439,13 +1439,8 @@ remove_unused_vertices(const GeomVertexData *vdata) {
     any_referenced = true;
     int num_primitives = geom->get_num_primitives();
     for (int i = 0; i < num_primitives; ++i) {
-      CPT(GeomPrimitive) prim = geom->get_primitive(i);
-
-      GeomPrimitivePipelineReader reader(prim, current_thread);
-      int num_vertices = reader.get_num_vertices();
-      for (int vi = 0; vi < num_vertices; ++vi) {
-        referenced_vertices.set_bit(reader.get_vertex(vi));
-      }
+      GeomPrimitivePipelineReader reader(geom->get_primitive(i), current_thread);
+      reader.get_referenced_vertices(referenced_vertices);
     }
   }
 

+ 26 - 26
panda/src/pgraph/nodePath.I

@@ -1080,7 +1080,7 @@ get_sa() const {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, const PTA_float &v, int priority) {
-  set_shader_input(new ShaderInput(id, v, priority));
+  set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -1088,7 +1088,7 @@ set_shader_input(CPT_InternalName id, const PTA_float &v, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, const PTA_double &v, int priority) {
-  set_shader_input(new ShaderInput(id, v, priority));
+  set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -1096,7 +1096,7 @@ set_shader_input(CPT_InternalName id, const PTA_double &v, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, const PTA_int &v, int priority) {
-  set_shader_input(new ShaderInput(id, v, priority));
+  set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -1104,7 +1104,7 @@ set_shader_input(CPT_InternalName id, const PTA_int &v, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, const PTA_LVecBase4 &v, int priority) {
-  set_shader_input(new ShaderInput(id, v, priority));
+  set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -1112,7 +1112,7 @@ set_shader_input(CPT_InternalName id, const PTA_LVecBase4 &v, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, const PTA_LVecBase3 &v, int priority) {
-  set_shader_input(new ShaderInput(id, v, priority));
+  set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 
@@ -1121,7 +1121,7 @@ set_shader_input(CPT_InternalName id, const PTA_LVecBase3 &v, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, const PTA_LVecBase2 &v, int priority) {
-  set_shader_input(new ShaderInput(id, v, priority));
+  set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -1129,7 +1129,7 @@ set_shader_input(CPT_InternalName id, const PTA_LVecBase2 &v, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, const LVecBase4 &v, int priority) {
-  set_shader_input(new ShaderInput(id, v, priority));
+  set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -1137,7 +1137,7 @@ set_shader_input(CPT_InternalName id, const LVecBase4 &v, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, const LVecBase3 &v, int priority) {
-  set_shader_input(new ShaderInput(id, v, priority));
+  set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -1145,7 +1145,7 @@ set_shader_input(CPT_InternalName id, const LVecBase3 &v, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, const LVecBase2 &v, int priority) {
-  set_shader_input(new ShaderInput(id, v, priority));
+  set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -1153,7 +1153,7 @@ set_shader_input(CPT_InternalName id, const LVecBase2 &v, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, const PTA_LVecBase4i &v, int priority) {
-  set_shader_input(new ShaderInput(id, v, priority));
+  set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -1161,7 +1161,7 @@ set_shader_input(CPT_InternalName id, const PTA_LVecBase4i &v, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, const PTA_LVecBase3i &v, int priority) {
-  set_shader_input(new ShaderInput(id, v, priority));
+  set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 
@@ -1170,7 +1170,7 @@ set_shader_input(CPT_InternalName id, const PTA_LVecBase3i &v, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, const PTA_LVecBase2i &v, int priority) {
-  set_shader_input(new ShaderInput(id, v, priority));
+  set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -1178,7 +1178,7 @@ set_shader_input(CPT_InternalName id, const PTA_LVecBase2i &v, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, const LVecBase4i &v, int priority) {
-  set_shader_input(new ShaderInput(id, v, priority));
+  set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -1186,7 +1186,7 @@ set_shader_input(CPT_InternalName id, const LVecBase4i &v, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, const LVecBase3i &v, int priority) {
-  set_shader_input(new ShaderInput(id, v, priority));
+  set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -1194,7 +1194,7 @@ set_shader_input(CPT_InternalName id, const LVecBase3i &v, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, const LVecBase2i &v, int priority) {
-  set_shader_input(new ShaderInput(id, v, priority));
+  set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -1202,7 +1202,7 @@ set_shader_input(CPT_InternalName id, const LVecBase2i &v, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, const PTA_LMatrix4 &v, int priority) {
-  set_shader_input(new ShaderInput(id, v, priority));
+  set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -1210,7 +1210,7 @@ set_shader_input(CPT_InternalName id, const PTA_LMatrix4 &v, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, const PTA_LMatrix3 &v, int priority) {
-  set_shader_input(new ShaderInput(id, v, priority));
+  set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -1218,7 +1218,7 @@ set_shader_input(CPT_InternalName id, const PTA_LMatrix3 &v, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, const LMatrix4 &v, int priority) {
-  set_shader_input(new ShaderInput(id, v, priority));
+  set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -1226,7 +1226,7 @@ set_shader_input(CPT_InternalName id, const LMatrix4 &v, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, const LMatrix3 &v, int priority) {
-  set_shader_input(new ShaderInput(id, v, priority));
+  set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -1234,7 +1234,7 @@ set_shader_input(CPT_InternalName id, const LMatrix3 &v, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, Texture *tex, int priority) {
-  set_shader_input(new ShaderInput(id, tex, priority));
+  set_shader_input(ShaderInput(move(id), tex, priority));
 }
 
 /**
@@ -1242,7 +1242,7 @@ set_shader_input(CPT_InternalName id, Texture *tex, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, Texture *tex, const SamplerState &sampler, int priority) {
-  set_shader_input(new ShaderInput(id, tex, sampler, priority));
+  set_shader_input(ShaderInput(move(id), tex, sampler, priority));
 }
 
 /**
@@ -1250,7 +1250,7 @@ set_shader_input(CPT_InternalName id, Texture *tex, const SamplerState &sampler,
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, Texture *tex, bool read, bool write, int z, int n, int priority) {
-  set_shader_input(new ShaderInput(id, tex, read, write, z, n, priority));
+  set_shader_input(ShaderInput(move(id), tex, read, write, z, n, priority));
 }
 
 /**
@@ -1258,7 +1258,7 @@ set_shader_input(CPT_InternalName id, Texture *tex, bool read, bool write, int z
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, ShaderBuffer *buf, int priority) {
-  set_shader_input(new ShaderInput(id, buf, priority));
+  set_shader_input(ShaderInput(move(id), buf, priority));
 }
 
 /**
@@ -1266,7 +1266,7 @@ set_shader_input(CPT_InternalName id, ShaderBuffer *buf, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, const NodePath &np, int priority) {
-  set_shader_input(new ShaderInput(id, np, priority));
+  set_shader_input(ShaderInput(move(id), np, priority));
 }
 
 /**
@@ -1274,7 +1274,7 @@ set_shader_input(CPT_InternalName id, const NodePath &np, int priority) {
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, int n1, int n2, int n3, int n4, int priority) {
-  set_shader_input(new ShaderInput(id, LVecBase4i(n1, n2, n3, n4), priority));
+  set_shader_input(ShaderInput(move(id), LVecBase4i(n1, n2, n3, n4), priority));
 }
 
 /**
@@ -1282,7 +1282,7 @@ set_shader_input(CPT_InternalName id, int n1, int n2, int n3, int n4, int priori
  */
 INLINE void NodePath::
 set_shader_input(CPT_InternalName id, PN_stdfloat n1, PN_stdfloat n2, PN_stdfloat n3, PN_stdfloat n4, int priority) {
-  set_shader_input(new ShaderInput(id, LVecBase4(n1, n2, n3, n4), priority));
+  set_shader_input(ShaderInput(move(id), LVecBase4(n1, n2, n3, n4), priority));
 }
 
 /**

+ 38 - 21
panda/src/pgraph/nodePath.cxx

@@ -697,8 +697,10 @@ get_state(const NodePath &other, Thread *current_thread) const {
     return other.get_net_state(current_thread)->invert_compose(RenderState::make_empty());
   }
 
+#if defined(_DEBUG) || (defined(HAVE_THREADS) && defined(SIMPLE_THREADS))
   nassertr(verify_complete(current_thread), RenderState::make_empty());
   nassertr(other.verify_complete(current_thread), RenderState::make_empty());
+#endif
 
   int a_count, b_count;
   if (find_common_ancestor(*this, other, a_count, b_count, current_thread) == (NodePathComponent *)NULL) {
@@ -767,8 +769,10 @@ get_transform(const NodePath &other, Thread *current_thread) const {
     return other.get_net_transform(current_thread)->invert_compose(TransformState::make_identity());
   }
 
+#if defined(_DEBUG) || (defined(HAVE_THREADS) && defined(SIMPLE_THREADS))
   nassertr(verify_complete(current_thread), TransformState::make_identity());
   nassertr(other.verify_complete(current_thread), TransformState::make_identity());
+#endif
 
   int a_count, b_count;
   if (find_common_ancestor(*this, other, a_count, b_count, current_thread) == (NodePathComponent *)NULL) {
@@ -852,8 +856,10 @@ get_prev_transform(const NodePath &other, Thread *current_thread) const {
     return other.get_net_prev_transform(current_thread)->invert_compose(TransformState::make_identity());
   }
 
+#if defined(_DEBUG) || (defined(HAVE_THREADS) && defined(SIMPLE_THREADS))
   nassertr(verify_complete(current_thread), TransformState::make_identity());
   nassertr(other.verify_complete(current_thread), TransformState::make_identity());
+#endif
 
   int a_count, b_count;
   if (find_common_ancestor(*this, other, a_count, b_count, current_thread) == (NodePathComponent *)NULL) {
@@ -3255,35 +3261,36 @@ get_shader() const {
  *
  */
 void NodePath::
-set_shader_input(const ShaderInput *inp) {
+set_shader_input(ShaderInput inp) {
   nassertv_always(!is_empty());
 
+  PandaNode *pnode = node();
   const RenderAttrib *attrib =
-    node()->get_attrib(ShaderAttrib::get_class_slot());
-  if (attrib != (const RenderAttrib *)NULL) {
-    const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib);
-    node()->set_attrib(sa->set_shader_input(inp));
+    pnode->get_attrib(ShaderAttrib::get_class_slot());
+  if (attrib != nullptr) {
+    const ShaderAttrib *sa = (const ShaderAttrib *)attrib;
+    pnode->set_attrib(sa->set_shader_input(inp));
   } else {
     // Create a new ShaderAttrib for this node.
     CPT(ShaderAttrib) sa = DCAST(ShaderAttrib, ShaderAttrib::make());
-    node()->set_attrib(sa->set_shader_input(inp));
+    pnode->set_attrib(sa->set_shader_input(inp));
   }
 }
 
 /**
  *
  */
-const ShaderInput *NodePath::
+ShaderInput NodePath::
 get_shader_input(CPT_InternalName id) const {
-  nassertr_always(!is_empty(), NULL);
+  nassertr_always(!is_empty(), ShaderInput::get_blank());
 
   const RenderAttrib *attrib =
     node()->get_attrib(ShaderAttrib::get_class_slot());
-  if (attrib != (const RenderAttrib *)NULL) {
-    const ShaderAttrib *sa = DCAST(ShaderAttrib, attrib);
+  if (attrib != nullptr) {
+    const ShaderAttrib *sa = (const ShaderAttrib *)attrib;
     return sa->get_shader_input(id);
   }
-  return NULL;
+  return ShaderInput::get_blank();
 }
 
 /**
@@ -5786,17 +5793,22 @@ r_get_net_transform(NodePathComponent *comp, Thread *current_thread) const {
   if (comp == (NodePathComponent *)NULL) {
     return TransformState::make_identity();
   } else {
+    PandaNode *node = comp->get_node();
     int pipeline_stage = current_thread->get_pipeline_stage();
     CPT(TransformState) net_transform = r_get_net_transform(comp->get_next(pipeline_stage, current_thread), current_thread);
-    PandaNode *node = comp->get_node();
-    CPT(TransformState) transform = node->get_transform(current_thread);
 
-    CPT(RenderEffects) effects = node->get_effects(current_thread);
-    if (effects->has_adjust_transform()) {
-      effects->adjust_transform(net_transform, transform, node);
+    PandaNode::CDReader node_cdata(node->_cycler, current_thread);
+    if (!node_cdata->_effects->has_adjust_transform()) {
+      if (node_cdata->_transform->is_identity()) {
+        return net_transform;
+      } else {
+        return net_transform->compose(node_cdata->_transform);
+      }
+    } else {
+      CPT(TransformState) transform = node_cdata->_transform.p();
+      node_cdata->_effects->adjust_transform(net_transform, transform, node);
+      return net_transform->compose(transform);
     }
-
-    return net_transform->compose(transform);
   }
 }
 
@@ -5814,16 +5826,21 @@ r_get_partial_transform(NodePathComponent *comp, int n,
   if (n == 0 || comp == (NodePathComponent *)NULL) {
     return TransformState::make_identity();
   } else {
-    if (comp->get_node()->get_effects(current_thread)->has_adjust_transform()) {
+    PandaNode *node = comp->get_node();
+    PandaNode::CDReader node_cdata(node->_cycler, current_thread);
+    if (node_cdata->_effects->has_adjust_transform()) {
       return NULL;
     }
-    CPT(TransformState) transform = comp->get_node()->get_transform(current_thread);
     int pipeline_stage = current_thread->get_pipeline_stage();
     CPT(TransformState) partial = r_get_partial_transform(comp->get_next(pipeline_stage, current_thread), n - 1, current_thread);
     if (partial == (const TransformState *)NULL) {
       return NULL;
     }
-    return partial->compose(transform);
+    if (node_cdata->_transform->is_identity()) {
+      return partial;
+    } else {
+      return partial->compose(node_cdata->_transform);
+    }
   }
 }
 

+ 3 - 2
panda/src/pgraph/nodePath.h

@@ -629,7 +629,7 @@ PUBLISHED:
   void set_shader_auto(BitMask32 shader_switch, int priority=0);
   void clear_shader();
 
-  void set_shader_input(const ShaderInput *inp);
+  void set_shader_input(ShaderInput input);
   INLINE void set_shader_input(CPT_InternalName id, Texture *tex, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, Texture *tex, const SamplerState &sampler, int priority=0);
   INLINE void set_shader_input(CPT_InternalName id, Texture *tex, bool read, bool write, int z=-1, int n=0, int priority=0);
@@ -665,7 +665,7 @@ PUBLISHED:
   void set_instance_count(int instance_count);
 
   const Shader *get_shader() const;
-  const ShaderInput *get_shader_input(CPT_InternalName id) const;
+  ShaderInput get_shader_input(CPT_InternalName id) const;
   int get_instance_count() const;
 
   void set_tex_transform(TextureStage *stage, const TransformState *transform);
@@ -1029,6 +1029,7 @@ private:
   friend class NodePathCollection;
   friend class WorkingNodePath;
   friend class WeakNodePath;
+  friend class CullTraverserData;
 };
 
 INLINE ostream &operator << (ostream &out, const NodePath &node_path);

+ 9 - 0
panda/src/pgraph/nodePathComponent.I

@@ -75,6 +75,15 @@ has_key() const {
   return (_key != 0);
 }
 
+/**
+ * Returns the next component in the path.
+ */
+INLINE NodePathComponent *NodePathComponent::
+get_next(int pipeline_stage, Thread *current_thread) const {
+  CDStageReader cdata(_cycler, pipeline_stage, current_thread);
+  return cdata->_next;
+}
+
 INLINE ostream &operator << (ostream &out, const NodePathComponent &comp) {
   comp.output(out);
   return out;

+ 0 - 11
panda/src/pgraph/nodePathComponent.cxx

@@ -92,17 +92,6 @@ get_length(int pipeline_stage, Thread *current_thread) const {
   return cdata->_length;
 }
 
-/**
- * Returns the next component in the path.
- */
-NodePathComponent *NodePathComponent::
-get_next(int pipeline_stage, Thread *current_thread) const {
-  CDStageReader cdata(_cycler, pipeline_stage, current_thread);
-  NodePathComponent *next = cdata->_next;
-
-  return next;
-}
-
 /**
  * Checks that the length indicated by the component is one more than the
  * length of its predecessor.  If this is broken, fixes it and returns true

+ 8 - 2
panda/src/pgraph/nodePathComponent.h

@@ -39,7 +39,7 @@
  * graph, and the NodePathComponents are stored in the nodes themselves to
  * allow the nodes to keep these up to date as the scene graph is manipulated.
  */
-class EXPCL_PANDA_PGRAPH NodePathComponent : public ReferenceCount {
+class EXPCL_PANDA_PGRAPH NodePathComponent FINAL : public ReferenceCount {
 private:
   NodePathComponent(PandaNode *node, NodePathComponent *next,
                     int pipeline_stage, Thread *current_thread);
@@ -55,7 +55,7 @@ public:
   int get_key() const;
   bool is_top_node(int pipeline_stage, Thread *current_thread) const;
 
-  NodePathComponent *get_next(int pipeline_stage, Thread *current_thread) const;
+  INLINE NodePathComponent *get_next(int pipeline_stage, Thread *current_thread) const;
   int get_length(int pipeline_stage, Thread *current_thread) const;
 
   bool fix_length(int pipeline_stage, Thread *current_thread);
@@ -125,6 +125,12 @@ private:
   friend class NodePath;
 };
 
+#ifdef DO_MEMORY_USAGE
+// We can safely redefine this as a no-op.
+template<>
+INLINE void PointerToBase<NodePathComponent>::update_type(To *ptr) {}
+#endif
+
 INLINE ostream &operator << (ostream &out, const NodePathComponent &comp);
 
 #include "nodePathComponent.I"

+ 36 - 36
panda/src/pgraph/nodePath_ext.cxx

@@ -278,7 +278,7 @@ set_shader_inputs(PyObject *args, PyObject *kwargs) {
     }
 
     CPT_InternalName name(string(buffer, length));
-    ShaderInput *input = nullptr;
+    ShaderInput input(nullptr, 0);
 
     if (PyTuple_CheckExact(value)) {
       // A tuple is interpreted as a vector.
@@ -300,13 +300,13 @@ set_shader_inputs(PyObject *args, PyObject *kwargs) {
         for (Py_ssize_t i = 0; i < size; ++i) {
           vec[i] = (PN_stdfloat)PyFloat_AsDouble(PyTuple_GET_ITEM(value, i));
         }
-        input = new ShaderInput(name, vec);
+        input = ShaderInput(move(name), vec);
       } else {
         LVecBase4i vec(0);
         for (Py_ssize_t i = 0; i < size; ++i) {
           vec[i] = (int)PyLong_AsLong(PyTuple_GET_ITEM(value, i));
         }
-        input = new ShaderInput(name, vec);
+        input = ShaderInput(move(name), vec);
       }
 
     } else if (DtoolCanThisBeAPandaInstance(value)) {
@@ -314,91 +314,91 @@ set_shader_inputs(PyObject *args, PyObject *kwargs) {
       void *ptr;
 
       if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_Texture))) {
-        input = new ShaderInput(name, (Texture *)ptr);
+        input = ShaderInput(move(name), (Texture *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_NodePath))) {
-        input = new ShaderInput(name, *(const NodePath *)ptr);
+        input = ShaderInput(move(name), *(const NodePath *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_float))) {
-        input = new ShaderInput(name, *(const PTA_float *)ptr);
+        input = ShaderInput(move(name), *(const PTA_float *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_double))) {
-        input = new ShaderInput(name, *(const PTA_double *)ptr);
+        input = ShaderInput(move(name), *(const PTA_double *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_int))) {
-        input = new ShaderInput(name, *(const PTA_int *)ptr);
+        input = ShaderInput(move(name), *(const PTA_int *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4f))) {
-        input = new ShaderInput(name, *(const PTA_LVecBase4f *)ptr);
+        input = ShaderInput(move(name), *(const PTA_LVecBase4f *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3f))) {
-        input = new ShaderInput(name, *(const PTA_LVecBase3f *)ptr);
+        input = ShaderInput(move(name), *(const PTA_LVecBase3f *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2f))) {
-        input = new ShaderInput(name, *(const PTA_LVecBase2f *)ptr);
+        input = ShaderInput(move(name), *(const PTA_LVecBase2f *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLMatrix4f))) {
-        input = new ShaderInput(name, *(const PTA_LMatrix4f *)ptr);
+        input = ShaderInput(move(name), *(const PTA_LMatrix4f *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LMatrix3f))) {
-        input = new ShaderInput(name, *(const PTA_LMatrix3f *)ptr);
+        input = ShaderInput(move(name), *(const PTA_LMatrix3f *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4d))) {
-        input = new ShaderInput(name, *(const PTA_LVecBase4d *)ptr);
+        input = ShaderInput(move(name), *(const PTA_LVecBase4d *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3d))) {
-        input = new ShaderInput(name, *(const PTA_LVecBase3d *)ptr);
+        input = ShaderInput(move(name), *(const PTA_LVecBase3d *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2d))) {
-        input = new ShaderInput(name, *(const PTA_LVecBase2d *)ptr);
+        input = ShaderInput(move(name), *(const PTA_LVecBase2d *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLMatrix4d))) {
-        input = new ShaderInput(name, *(const PTA_LMatrix4d *)ptr);
+        input = ShaderInput(move(name), *(const PTA_LMatrix4d *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LMatrix3d))) {
-        input = new ShaderInput(name, *(const PTA_LMatrix3d *)ptr);
+        input = ShaderInput(move(name), *(const PTA_LMatrix3d *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_UnalignedLVecBase4i))) {
-        input = new ShaderInput(name, *(const PTA_LVecBase4i *)ptr);
+        input = ShaderInput(move(name), *(const PTA_LVecBase4i *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase3i))) {
-        input = new ShaderInput(name, *(const PTA_LVecBase3i *)ptr);
+        input = ShaderInput(move(name), *(const PTA_LVecBase3i *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_PointerToArray_LVecBase2i))) {
-        input = new ShaderInput(name, *(const PTA_LVecBase2i *)ptr);
+        input = ShaderInput(move(name), *(const PTA_LVecBase2i *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4f))) {
-        input = new ShaderInput(name, *(const LVecBase4f *)ptr);
+        input = ShaderInput(move(name), *(const LVecBase4f *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3f))) {
-        input = new ShaderInput(name, *(const LVecBase3f *)ptr);
+        input = ShaderInput(move(name), *(const LVecBase3f *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2f))) {
-        input = new ShaderInput(name, *(const LVecBase2f *)ptr);
+        input = ShaderInput(move(name), *(const LVecBase2f *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4d))) {
-        input = new ShaderInput(name, *(const LVecBase4d *)ptr);
+        input = ShaderInput(move(name), *(const LVecBase4d *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3d))) {
-        input = new ShaderInput(name, *(const LVecBase3d *)ptr);
+        input = ShaderInput(move(name), *(const LVecBase3d *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2d))) {
-        input = new ShaderInput(name, *(const LVecBase2d *)ptr);
+        input = ShaderInput(move(name), *(const LVecBase2d *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase4i))) {
-        input = new ShaderInput(name, *(const LVecBase4i *)ptr);
+        input = ShaderInput(move(name), *(const LVecBase4i *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase3i))) {
-        input = new ShaderInput(name, *(const LVecBase3i *)ptr);
+        input = ShaderInput(move(name), *(const LVecBase3i *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_LVecBase2i))) {
-        input = new ShaderInput(name, *(const LVecBase2i *)ptr);
+        input = ShaderInput(move(name), *(const LVecBase2i *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_ShaderBuffer))) {
-        input = new ShaderInput(name, (ShaderBuffer *)ptr);
+        input = ShaderInput(move(name), (ShaderBuffer *)ptr);
 
       } else if ((ptr = inst->_My_Type->_Dtool_UpcastInterface(value, &Dtool_ParamValueBase))) {
-        input = new ShaderInput(name, (ParamValueBase *)ptr);
+        input = ShaderInput(move(name), (ParamValueBase *)ptr);
 
       } else {
         Dtool_Raise_TypeError("unknown type passed to NodePath.set_shader_inputs");
@@ -406,22 +406,22 @@ set_shader_inputs(PyObject *args, PyObject *kwargs) {
       }
 
     } else if (PyFloat_Check(value)) {
-      input = new ShaderInput(name, LVecBase4(PyFloat_AS_DOUBLE(value), 0, 0, 0));
+      input = ShaderInput(move(name), LVecBase4(PyFloat_AS_DOUBLE(value), 0, 0, 0));
 
 #if PY_MAJOR_VERSION < 3
     } else if (PyInt_Check(value)) {
-      input = new ShaderInput(name, LVecBase4i((int)PyInt_AS_LONG(value), 0, 0, 0));
+      input = ShaderInput(move(name), LVecBase4i((int)PyInt_AS_LONG(value), 0, 0, 0));
 #endif
 
     } else if (PyLong_Check(value)) {
-      input = new ShaderInput(name, LVecBase4i((int)PyLong_AsLong(value), 0, 0, 0));
+      input = ShaderInput(move(name), LVecBase4i((int)PyLong_AsLong(value), 0, 0, 0));
 
     } else {
       Dtool_Raise_TypeError("unknown type passed to NodePath.set_shader_inputs");
       return;
     }
 
-    attrib->_inputs[move(name)] = input;
+    attrib->_inputs[input.get_name()] = move(input);
   }
 
   node->set_attrib(ShaderAttrib::return_new(attrib));

+ 2 - 2
panda/src/pgraph/pandaNode.I

@@ -1482,7 +1482,7 @@ get_net_collide_mask() const {
  * Returns a ClipPlaneAttrib which represents the union of all of the clip
  * planes that have been turned *off* at this level and below.
  */
-INLINE CPT(RenderAttrib) PandaNodePipelineReader::
+INLINE const RenderAttrib *PandaNodePipelineReader::
 get_off_clip_planes() const {
   nassertr(_cdata->_last_update == _cdata->_next_update, _cdata->_off_clip_planes);
   return _cdata->_off_clip_planes;
@@ -1493,7 +1493,7 @@ get_off_clip_planes() const {
  * contains the user bounding volume, the internal bounding volume, and all of
  * the children's bounding volumes.
  */
-INLINE CPT(BoundingVolume) PandaNodePipelineReader::
+INLINE const BoundingVolume *PandaNodePipelineReader::
 get_bounds() const {
   nassertr(_cdata->_last_bounds_update == _cdata->_next_update, _cdata->_external_bounds);
   return _cdata->_external_bounds;

+ 9 - 2
panda/src/pgraph/pandaNode.h

@@ -827,6 +827,7 @@ private:
   friend class PandaNodePipelineReader;
   friend class EggLoader;
   friend class Extension<PandaNode>;
+  friend class CullTraverserData;
 };
 
 /**
@@ -877,8 +878,8 @@ public:
   INLINE bool has_tag(const string &key) const;
 
   INLINE CollideMask get_net_collide_mask() const;
-  INLINE CPT(RenderAttrib) get_off_clip_planes() const;
-  INLINE CPT(BoundingVolume) get_bounds() const;
+  INLINE const RenderAttrib *get_off_clip_planes() const;
+  INLINE const BoundingVolume *get_bounds() const;
   INLINE int get_nested_vertices() const;
   INLINE bool is_final() const;
   INLINE int get_fancy_bits() const;
@@ -906,6 +907,12 @@ private:
 
 };
 
+#ifdef DO_MEMORY_USAGE
+// We can safely redefine this as a no-op.
+template<>
+INLINE void PointerToBase<PandaNode>::update_type(To *ptr) {}
+#endif
+
 INLINE ostream &operator << (ostream &out, const PandaNode &node) {
   node.output(out);
   return out;

+ 6 - 6
panda/src/pgraph/polylightEffect.cxx

@@ -136,9 +136,9 @@ do_poly_light(const SceneSetup *scene, const CullTraverserData *data, const Tran
     if (light->is_enabled()) { // if enabled get all the properties
       PN_stdfloat light_radius = light->get_radius();
       // Calculate the distance of the node from the light dist =
-      // light_iter->second->get_distance(data->_node_path.get_node_path());
+      // light_iter->second->get_distance(data->get_node_path());
       const NodePath lightnp = *light_iter;
-      LPoint3 relative_point = data->_node_path.get_node_path().get_relative_point(lightnp, light->get_pos());
+      LPoint3 relative_point = data->get_node_path().get_relative_point(lightnp, light->get_pos());
 
       if (_effect_center[2]) {
         dist = (relative_point - _effect_center).length(); // this counts height difference
@@ -155,7 +155,7 @@ do_poly_light(const SceneSetup *scene, const CullTraverserData *data, const Tran
         // LPoint3 camera_position = camera.get_relative_point(lightnp,
         // light->get_pos());
         LPoint3 camera_position = lightnp.get_relative_point(camera, LPoint3(0,0,0));
-        LPoint3 avatar_position = lightnp.get_relative_point(data->_node_path.get_node_path(), LPoint3(0,0,0));
+        LPoint3 avatar_position = lightnp.get_relative_point(data->get_node_path(), LPoint3(0,0,0));
         LVector3 light_camera = camera_position  - light_position;
         LVector3 light_avatar = avatar_position - light_position;
         light_camera.normalize();
@@ -263,7 +263,7 @@ do_poly_light(const SceneSetup *scene, const CullTraverserData *data, const Tran
 
   if (num_lights) {
     // was_under_polylight = true;
-    // data->_node_path.get_node_path().set_color_scale_off();
+    // data->get_node_path().set_color_scale_off();
     if (polylight_info)
       pgraph_cat.debug() << "num lights = " << num_lights << endl;
 
@@ -320,8 +320,8 @@ do_poly_light(const SceneSetup *scene, const CullTraverserData *data, const Tran
   else {
     if (was_under_polylight) {
       // under no polylight influence...so clear the color scale
-      // data->_node_path.get_node_path().clear_color_scale();
-      // data->_node_path.get_node_path().set_color_scale(scene_color);
+      // data->get_node_path().clear_color_scale();
+      // data->get_node_path().set_color_scale(scene_color);
       was_under_polylight = false;
     }
   }

+ 1 - 1
panda/src/pgraph/portalClipper.h

@@ -119,7 +119,7 @@ private:
   LPoint2 _reduced_viewport_max;
   CPT(RenderState) _clip_state; // each portal node needs to know the clip state of its "parent" portal Node
 
-  PortalNode *_portal_node;  // current working portal for dereference ease
+  const PortalNode *_portal_node;  // current working portal for dereference ease
 
   // int _num_vert; LVertex _coords[4];
 

+ 2 - 2
panda/src/pgraph/portalNode.cxx

@@ -213,7 +213,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
     portal_viewer->get_reduced_viewport(old_reduced_viewport_min, old_reduced_viewport_max);
     PT(BoundingHexahedron) old_bh = portal_viewer->get_reduced_frustum();
 
-    if (portal_viewer->prepare_portal(data._node_path.get_node_path())) {
+    if (portal_viewer->prepare_portal(data.get_node_path())) {
       if ((reduced_frustum = portal_viewer->get_reduced_frustum())) {
         // remember current clip state, we might change it
         CPT(RenderState) old_clip_state = portal_viewer->get_clip_state();
@@ -241,7 +241,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
           // camera space to this portal node's space (because the clip planes
           // are attached to this node)
           PT(BoundingHexahedron) temp_bh = DCAST(BoundingHexahedron, vf->make_copy());
-          CPT(TransformState) temp_frustum_transform = data._node_path.get_node_path().get_net_transform()->invert_compose(portal_viewer->_scene_setup->get_cull_center().get_net_transform());
+          CPT(TransformState) temp_frustum_transform = data.get_node_path().get_net_transform()->invert_compose(portal_viewer->_scene_setup->get_cull_center().get_net_transform());
 
           portal_cat.spam() << "clipping plane frustum transform " << *temp_frustum_transform << endl;
           portal_cat.spam() << "frustum before transform " << *temp_bh << endl;

+ 15 - 12
panda/src/pgraph/renderAttrib.cxx

@@ -22,7 +22,7 @@ LightReMutex *RenderAttrib::_attribs_lock = NULL;
 RenderAttrib::Attribs *RenderAttrib::_attribs = NULL;
 TypeHandle RenderAttrib::_type_handle;
 
-int RenderAttrib::_garbage_index = 0;
+size_t RenderAttrib::_garbage_index = 0;
 
 PStatCollector RenderAttrib::_garbage_collect_pcollector("*:State Cache:Garbage Collect");
 
@@ -195,8 +195,8 @@ list_attribs(ostream &out) {
   LightReMutexHolder holder(*_attribs_lock);
 
   out << _attribs->get_num_entries() << " attribs:\n";
-  int size = _attribs->get_size();
-  for (int si = 0; si < size; ++si) {
+  size_t size = _attribs->get_size();
+  for (size_t si = 0; si < size; ++si) {
     if (!_attribs->has_element(si)) {
       continue;
     }
@@ -219,7 +219,9 @@ garbage_collect() {
   PStatTimer timer(_garbage_collect_pcollector);
   int orig_size = _attribs->get_num_entries();
 
+#ifdef _DEBUG
   nassertr(_attribs->validate(), 0);
+#endif
 
   // How many elements to process this pass?
   int size = _attribs->get_size();
@@ -230,11 +232,9 @@ garbage_collect() {
   num_this_pass = min(num_this_pass, size);
   int stop_at_element = (_garbage_index + num_this_pass) % size;
 
-  int num_elements = 0;
-  int si = _garbage_index;
+  size_t si = _garbage_index;
   do {
     if (_attribs->has_element(si)) {
-      ++num_elements;
       RenderAttrib *attrib = (RenderAttrib *)_attribs->get_key(si);
       if (attrib->get_ref_count() == 1) {
         // This attrib has recently been unreffed to 1 (the one we added when
@@ -250,9 +250,12 @@ garbage_collect() {
     si = (si + 1) % size;
   } while (si != stop_at_element);
   _garbage_index = si;
+
+#ifdef _DEBUG
   nassertr(_attribs->validate(), 0);
+#endif
 
-  int new_size = _attribs->get_num_entries();
+  size_t new_size = _attribs->get_num_entries();
   return orig_size - new_size;
 }
 
@@ -272,8 +275,8 @@ validate_attribs() {
     pgraph_cat.error()
       << "RenderAttrib::_attribs cache is invalid!\n";
 
-    int size = _attribs->get_size();
-    for (int si = 0; si < size; ++si) {
+    size_t size = _attribs->get_size();
+    for (size_t si = 0; si < size; ++si) {
       if (!_attribs->has_element(si)) {
         continue;
       }
@@ -285,14 +288,14 @@ validate_attribs() {
     return false;
   }
 
-  int size = _attribs->get_size();
-  int si = 0;
+  size_t size = _attribs->get_size();
+  size_t si = 0;
   while (si < size && !_attribs->has_element(si)) {
     ++si;
   }
   nassertr(si < size, false);
   nassertr(_attribs->get_key(si)->get_ref_count() >= 0, false);
-  int snext = si;
+  size_t snext = si;
   ++snext;
   while (snext < size && !_attribs->has_element(snext)) {
     ++snext;

+ 1 - 1
panda/src/pgraph/renderAttrib.h

@@ -196,7 +196,7 @@ private:
 
   // This keeps track of our current position through the garbage collection
   // cycle.
-  static int _garbage_index;
+  static size_t _garbage_index;
 
   static PStatCollector _garbage_collect_pcollector;
 

+ 1 - 1
panda/src/pgraph/renderEffect.cxx

@@ -160,7 +160,7 @@ has_adjust_transform() const {
  */
 void RenderEffect::
 adjust_transform(CPT(TransformState) &, CPT(TransformState) &,
-                 PandaNode *) const {
+                 const PandaNode *) const {
 }
 
 /**

+ 1 - 1
panda/src/pgraph/renderEffect.h

@@ -68,7 +68,7 @@ public:
   virtual bool has_adjust_transform() const;
   virtual void adjust_transform(CPT(TransformState) &net_transform,
                                 CPT(TransformState) &node_transform,
-                                PandaNode *node) const;
+                                const PandaNode *node) const;
 
 PUBLISHED:
   INLINE int compare_to(const RenderEffect &other) const;

+ 1 - 1
panda/src/pgraph/renderEffects.cxx

@@ -492,7 +492,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data,
 void RenderEffects::
 adjust_transform(CPT(TransformState) &net_transform,
                  CPT(TransformState) &node_transform,
-                 PandaNode *node) const {
+                 const PandaNode *node) const {
   Effects::const_iterator ei;
   for (ei = _effects.begin(); ei != _effects.end(); ++ei) {
     (*ei)._effect->adjust_transform(net_transform, node_transform, node);

+ 1 - 1
panda/src/pgraph/renderEffects.h

@@ -102,7 +102,7 @@ public:
   INLINE bool has_adjust_transform() const;
   void adjust_transform(CPT(TransformState) &net_transform,
                         CPT(TransformState) &node_transform,
-                        PandaNode *node) const;
+                        const PandaNode *node) const;
 
   static void init_states();
 

+ 2 - 1
panda/src/pgraph/renderState.I

@@ -522,7 +522,8 @@ INLINE void RenderState::
 check_hash() const {
   // This pretends to be a const function, even though it's not, because it
   // only updates a transparent cache value.
-  if ((_flags & F_hash_known) == 0) {
+  if ((_flags & F_hash_known) != 0) {
+  } else {
     ((RenderState *)this)->calc_hash();
   }
 }

+ 38 - 24
panda/src/pgraph/renderState.cxx

@@ -40,7 +40,7 @@ LightReMutex *RenderState::_states_lock = NULL;
 RenderState::States *RenderState::_states = NULL;
 const RenderState *RenderState::_empty_state = NULL;
 UpdateSeq RenderState::_last_cycle_detect;
-int RenderState::_garbage_index = 0;
+size_t RenderState::_garbage_index = 0;
 
 PStatCollector RenderState::_cache_update_pcollector("*:State Cache:Update");
 PStatCollector RenderState::_garbage_collect_pcollector("*:State Cache:Garbage Collect");
@@ -75,6 +75,10 @@ RenderState() :
   _cache_stats.add_num_states(1);
   _read_overrides = NULL;
   _generated_shader = NULL;
+
+#ifdef DO_MEMORY_USAGE
+  MemoryUsage::update_type(this, this);
+#endif
 }
 
 /**
@@ -97,6 +101,10 @@ RenderState(const RenderState &copy) :
   _cache_stats.add_num_states(1);
   _read_overrides = NULL;
   _generated_shader = NULL;
+
+#ifdef DO_MEMORY_USAGE
+  MemoryUsage::update_type(this, this);
+#endif
 }
 
 /**
@@ -617,7 +625,7 @@ adjust_all_priorities(int adjustment) const {
  */
 bool RenderState::
 unref() const {
-  if (!state_cache || garbage_collect_states) {
+  if (garbage_collect_states || !state_cache) {
     // If we're not using the cache at all, or if we're relying on garbage
     // collection, just allow the pointer to unref normally.
     return ReferenceCount::unref();
@@ -774,8 +782,8 @@ get_num_unused_states() {
   typedef pmap<const RenderState *, int> StateCount;
   StateCount state_count;
 
-  int size = _states->get_size();
-  for (int si = 0; si < size; ++si) {
+  size_t size = _states->get_size();
+  for (size_t si = 0; si < size; ++si) {
     if (!_states->has_element(si)) {
       continue;
     }
@@ -871,8 +879,8 @@ clear_cache() {
     TempStates temp_states;
     temp_states.reserve(orig_size);
 
-    int size = _states->get_size();
-    for (int si = 0; si < size; ++si) {
+    size_t size = _states->get_size();
+    for (size_t si = 0; si < size; ++si) {
       if (!_states->has_element(si)) {
         continue;
       }
@@ -938,27 +946,30 @@ garbage_collect() {
   if (_states == (States *)NULL || !garbage_collect_states) {
     return num_attribs;
   }
+
+  bool break_and_uniquify = (auto_break_cycles && uniquify_transforms);
+
   LightReMutexHolder holder(*_states_lock);
 
   PStatTimer timer(_garbage_collect_pcollector);
   int orig_size = _states->get_num_entries();
 
   // How many elements to process this pass?
-  int size = _states->get_size();
-  int num_this_pass = int(size * garbage_collect_states_rate);
-  if (num_this_pass <= 0) {
+  size_t size = _states->get_size();
+  size_t num_this_pass = int((int)size * garbage_collect_states_rate);
+  if (size <= 0 || num_this_pass <= 0) {
     return num_attribs;
   }
+
+  size_t si = _garbage_index;
+
   num_this_pass = min(num_this_pass, size);
-  int stop_at_element = (_garbage_index + num_this_pass) % size;
+  size_t stop_at_element = (si + num_this_pass) % (size - 1);
 
-  int num_elements = 0;
-  int si = _garbage_index;
   do {
     if (_states->has_element(si)) {
-      ++num_elements;
       RenderState *state = (RenderState *)_states->get_key(si);
-      if (auto_break_cycles && uniquify_states) {
+      if (break_and_uniquify) {
         if (state->get_cache_ref_count() > 0 &&
             state->get_ref_count() == state->get_cache_ref_count()) {
           // If we have removed all the references to this state not in the
@@ -982,10 +993,13 @@ garbage_collect() {
       }
     }
 
-    si = (si + 1) % size;
+    si = (si + 1) & (size - 1);
   } while (si != stop_at_element);
   _garbage_index = si;
+
+#ifdef _DEBUG
   nassertr(_states->validate(), 0);
+#endif
 
   int new_size = _states->get_num_entries();
   return orig_size - new_size + num_attribs;
@@ -999,8 +1013,8 @@ void RenderState::
 clear_munger_cache() {
   LightReMutexHolder holder(*_states_lock);
 
-  int size = _states->get_size();
-  for (int si = 0; si < size; ++si) {
+  size_t size = _states->get_size();
+  for (size_t si = 0; si < size; ++si) {
     if (!_states->has_element(si)) {
       continue;
     }
@@ -1034,8 +1048,8 @@ list_cycles(ostream &out) {
   VisitedStates visited;
   CompositionCycleDesc cycle_desc;
 
-  int size = _states->get_size();
-  for (int si = 0; si < size; ++si) {
+  size_t size = _states->get_size();
+  for (size_t si = 0; si < size; ++si) {
     if (!_states->has_element(si)) {
       continue;
     }
@@ -1113,8 +1127,8 @@ list_states(ostream &out) {
 
   out << _states->get_num_entries() << " states:\n";
 
-  int size = _states->get_size();
-  for (int si = 0; si < size; ++si) {
+  size_t size = _states->get_size();
+  for (size_t si = 0; si < size; ++si) {
     if (!_states->has_element(si)) {
       continue;
     }
@@ -1148,14 +1162,14 @@ validate_states() {
     return false;
   }
 
-  int size = _states->get_size();
-  int si = 0;
+  size_t size = _states->get_size();
+  size_t si = 0;
   while (si < size && !_states->has_element(si)) {
     ++si;
   }
   nassertr(si < size, false);
   nassertr(_states->get_key(si)->get_ref_count() >= 0, false);
-  int snext = si;
+  size_t snext = si;
   ++snext;
   while (snext < size && !_states->has_element(snext)) {
     ++snext;

+ 7 - 1
panda/src/pgraph/renderState.h

@@ -278,7 +278,7 @@ private:
 
   // This keeps track of our current position through the garbage collection
   // cycle.
-  static int _garbage_index;
+  static size_t _garbage_index;
 
   static PStatCollector _cache_update_pcollector;
   static PStatCollector _garbage_collect_pcollector;
@@ -368,6 +368,12 @@ private:
   friend class Extension<RenderState>;
 };
 
+#ifdef DO_MEMORY_USAGE
+// We can safely redefine this as a no-op.
+template<>
+INLINE void PointerToBase<RenderState>::update_type(To *ptr) {}
+#endif
+
 INLINE ostream &operator << (ostream &out, const RenderState &state) {
   state.output(out);
   return out;

+ 15 - 15
panda/src/pgraph/shaderAttrib.I

@@ -110,7 +110,7 @@ has_shader_input(CPT_InternalName id) const {
  */
 INLINE CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(CPT_InternalName id, const PTA_float &v, int priority) const {
-  return set_shader_input(new ShaderInput(MOVE(id), v, priority));
+  return set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -118,7 +118,7 @@ set_shader_input(CPT_InternalName id, const PTA_float &v, int priority) const {
  */
 INLINE CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(CPT_InternalName id, const PTA_double &v, int priority) const {
-  return set_shader_input(new ShaderInput(MOVE(id), v, priority));
+  return set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -126,7 +126,7 @@ set_shader_input(CPT_InternalName id, const PTA_double &v, int priority) const {
  */
 INLINE CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(CPT_InternalName id, const PTA_LVecBase4 &v, int priority) const {
-  return set_shader_input(new ShaderInput(MOVE(id), v, priority));
+  return set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -134,7 +134,7 @@ set_shader_input(CPT_InternalName id, const PTA_LVecBase4 &v, int priority) cons
  */
 INLINE CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(CPT_InternalName id, const PTA_LVecBase3 &v, int priority) const {
-  return set_shader_input(new ShaderInput(MOVE(id), v, priority));
+  return set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 
@@ -143,7 +143,7 @@ set_shader_input(CPT_InternalName id, const PTA_LVecBase3 &v, int priority) cons
  */
 INLINE CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(CPT_InternalName id, const PTA_LVecBase2 &v, int priority) const {
-  return set_shader_input(new ShaderInput(MOVE(id), v, priority));
+  return set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -151,7 +151,7 @@ set_shader_input(CPT_InternalName id, const PTA_LVecBase2 &v, int priority) cons
  */
 INLINE CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(CPT_InternalName id, const LVecBase4 &v, int priority) const {
-  return set_shader_input(new ShaderInput(MOVE(id), v, priority));
+  return set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -159,7 +159,7 @@ set_shader_input(CPT_InternalName id, const LVecBase4 &v, int priority) const {
  */
 INLINE CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(CPT_InternalName id, const LVecBase3 &v, int priority) const {
-  return set_shader_input(new ShaderInput(MOVE(id), v, priority));
+  return set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -167,7 +167,7 @@ set_shader_input(CPT_InternalName id, const LVecBase3 &v, int priority) const {
  */
 INLINE CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(CPT_InternalName id, const LVecBase2 &v, int priority) const {
-  return set_shader_input(new ShaderInput(MOVE(id), v, priority));
+  return set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -175,7 +175,7 @@ set_shader_input(CPT_InternalName id, const LVecBase2 &v, int priority) const {
  */
 INLINE CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(CPT_InternalName id, const PTA_LMatrix4 &v, int priority) const {
-  return set_shader_input(new ShaderInput(MOVE(id), v, priority));
+  return set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -183,7 +183,7 @@ set_shader_input(CPT_InternalName id, const PTA_LMatrix4 &v, int priority) const
  */
 INLINE CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(CPT_InternalName id, const PTA_LMatrix3 &v, int priority) const {
-  return set_shader_input(new ShaderInput(MOVE(id), v, priority));
+  return set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -191,7 +191,7 @@ set_shader_input(CPT_InternalName id, const PTA_LMatrix3 &v, int priority) const
  */
 INLINE CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(CPT_InternalName id, const LMatrix4 &v, int priority) const {
-  return set_shader_input(new ShaderInput(MOVE(id), v, priority));
+  return set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -199,7 +199,7 @@ set_shader_input(CPT_InternalName id, const LMatrix4 &v, int priority) const {
  */
 INLINE CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(CPT_InternalName id, const LMatrix3 &v, int priority) const {
-  return set_shader_input(new ShaderInput(MOVE(id), v, priority));
+  return set_shader_input(ShaderInput(move(id), v, priority));
 }
 
 /**
@@ -207,7 +207,7 @@ set_shader_input(CPT_InternalName id, const LMatrix3 &v, int priority) const {
  */
 INLINE CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(CPT_InternalName id, Texture *tex, int priority) const {
-  return set_shader_input(new ShaderInput(MOVE(id), tex, priority));
+  return set_shader_input(ShaderInput(move(id), tex, priority));
 }
 
 /**
@@ -215,7 +215,7 @@ set_shader_input(CPT_InternalName id, Texture *tex, int priority) const {
  */
 INLINE CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(CPT_InternalName id, const NodePath &np, int priority) const {
-  return set_shader_input(new ShaderInput(MOVE(id), np, priority));
+  return set_shader_input(ShaderInput(move(id), np, priority));
 }
 
 /**
@@ -223,7 +223,7 @@ set_shader_input(CPT_InternalName id, const NodePath &np, int priority) const {
  */
 INLINE CPT(RenderAttrib) ShaderAttrib::
 set_shader_input(CPT_InternalName id, double n1, double n2, double n3, double n4, int priority) const {
-  return set_shader_input(new ShaderInput(MOVE(id), LVecBase4((PN_stdfloat)n1, (PN_stdfloat)n2, (PN_stdfloat)n3, (PN_stdfloat)n4), priority));
+  return set_shader_input(ShaderInput(move(id), LVecBase4((PN_stdfloat)n1, (PN_stdfloat)n2, (PN_stdfloat)n3, (PN_stdfloat)n4), priority));
 }
 
 INLINE bool ShaderAttrib::

+ 90 - 76
panda/src/pgraph/shaderAttrib.cxx

@@ -193,13 +193,13 @@ clear_flag(int flag) const {
  *
  */
 CPT(RenderAttrib) ShaderAttrib::
-set_shader_input(const ShaderInput *input) const {
+set_shader_input(ShaderInput input) const {
   ShaderAttrib *result = new ShaderAttrib(*this);
-  Inputs::iterator i = result->_inputs.find(input->get_name());
+  Inputs::iterator i = result->_inputs.find(input.get_name());
   if (i == result->_inputs.end()) {
-    result->_inputs.insert(Inputs::value_type(input->get_name(), input));
+    result->_inputs.insert(Inputs::value_type(input.get_name(), move(input)));
   } else {
-    i->second = input;
+    i->second = move(input);
   }
   return return_new(result);
 }
@@ -248,13 +248,13 @@ clear_all_shader_inputs() const {
  * Returns the ShaderInput of the given name.  If no such name is found, this
  * function does not return NULL --- it returns the "blank" ShaderInput.
  */
-const ShaderInput *ShaderAttrib::
+const ShaderInput &ShaderAttrib::
 get_shader_input(const InternalName *id) const {
   Inputs::const_iterator i = _inputs.find(id);
-  if (i == _inputs.end()) {
-    return ShaderInput::get_blank();
-  } else {
+  if (i != _inputs.end()) {
     return (*i).second;
+  } else {
+    return ShaderInput::get_blank();
   }
 }
 
@@ -262,7 +262,7 @@ get_shader_input(const InternalName *id) const {
  * Returns the ShaderInput of the given name.  If no such name is found, this
  * function does not return NULL --- it returns the "blank" ShaderInput.
  */
-const ShaderInput *ShaderAttrib::
+const ShaderInput &ShaderAttrib::
 get_shader_input(const string &id) const {
   return get_shader_input(InternalName::make(id));
 }
@@ -275,20 +275,21 @@ const NodePath &ShaderAttrib::
 get_shader_input_nodepath(const InternalName *id) const {
   static NodePath resfail;
   Inputs::const_iterator i = _inputs.find(id);
-  if (i == _inputs.end()) {
-    ostringstream strm;
-    strm << "Shader input " << id->get_name() << " is not present.\n";
-    nassert_raise(strm.str());
-    return resfail;
-  } else {
-    const ShaderInput *p = (*i).second;
-    if (p->get_value_type() != ShaderInput::M_nodepath) {
+  if (i != _inputs.end()) {
+    const ShaderInput &p = (*i).second;
+    if (p.get_value_type() == ShaderInput::M_nodepath) {
+      return ((const ParamNodePath *)p.get_value())->get_value();
+    } else {
       ostringstream strm;
       strm << "Shader input " << id->get_name() << " is not a nodepath.\n";
       nassert_raise(strm.str());
       return resfail;
     }
-    return p->get_nodepath();
+  } else {
+    ostringstream strm;
+    strm << "Shader input " << id->get_name() << " is not present.\n";
+    nassert_raise(strm.str());
+    return resfail;
   }
 
   // Satisfy compiler.
@@ -303,19 +304,14 @@ LVecBase4 ShaderAttrib::
 get_shader_input_vector(InternalName *id) const {
   static LVecBase4 resfail(0,0,0,0);
   Inputs::const_iterator i = _inputs.find(id);
-  if (i == _inputs.end()) {
-    ostringstream strm;
-    strm << "Shader input " << id->get_name() << " is not present.\n";
-    nassert_raise(strm.str());
-    return resfail;
-  } else {
-    const ShaderInput *p = (*i).second;
+  if (i != _inputs.end()) {
+    const ShaderInput &p = (*i).second;
 
-    if (p->get_value_type() == ShaderInput::M_vector) {
-      return p->get_vector();
+    if (p.get_value_type() == ShaderInput::M_vector) {
+      return p.get_vector();
 
-    } else if (p->get_value_type() == ShaderInput::M_numeric && p->get_ptr()._size <= 4) {
-      const Shader::ShaderPtrData &ptr = p->get_ptr();
+    } else if (p.get_value_type() == ShaderInput::M_numeric && p.get_ptr()._size <= 4) {
+      const Shader::ShaderPtrData &ptr = p.get_ptr();
 
       switch (ptr._type) {
       case Shader::SPT_float:
@@ -339,19 +335,23 @@ get_shader_input_vector(InternalName *id) const {
         }
       }
 
-    } else if (p->get_value_type() == ShaderInput::M_param) {
+    } else if (p.get_value_type() == ShaderInput::M_param) {
       // Temporary solution until the new param system
-      ParamValueBase *param = p->get_param();
+      TypedWritableReferenceCount *param = p.get_value();
       if (param != NULL && param->is_of_type(ParamVecBase4::get_class_type())) {
-        return ((const ParamVecBase4 *) param)->get_value();
+        return ((const ParamVecBase4 *)param)->get_value();
       }
     }
 
     ostringstream strm;
     strm << "Shader input " << id->get_name() << " is not a vector.\n";
     nassert_raise(strm.str());
-    return resfail;
+  } else {
+    ostringstream strm;
+    strm << "Shader input " << id->get_name() << " is not present.\n";
+    nassert_raise(strm.str());
   }
+  return resfail;
 }
 
 /**
@@ -361,21 +361,21 @@ get_shader_input_vector(InternalName *id) const {
 const Shader::ShaderPtrData *ShaderAttrib::
 get_shader_input_ptr(const InternalName *id) const {
   Inputs::const_iterator i = _inputs.find(id);
-  if (i == _inputs.end()) {
-    ostringstream strm;
-    strm << "Shader input " << id->get_name() << " is not present.\n";
-    nassert_raise(strm.str());
-    return NULL;
-  } else {
-    const ShaderInput *p = (*i).second;
-    if (p->get_value_type() != ShaderInput::M_numeric &&
-        p->get_value_type() != ShaderInput::M_vector) {
+  if (i != _inputs.end()) {
+    const ShaderInput &p = (*i).second;
+    if (p.get_value_type() != ShaderInput::M_numeric &&
+        p.get_value_type() != ShaderInput::M_vector) {
       ostringstream strm;
       strm << "Shader input " << id->get_name() << " is not a PTA(float/double) type.\n";
       nassert_raise(strm.str());
       return NULL;
     }
-    return &(p->get_ptr());
+    return &(p.get_ptr());
+  } else {
+    ostringstream strm;
+    strm << "Shader input " << id->get_name() << " is not present.\n";
+    nassert_raise(strm.str());
+    return NULL;
   }
 }
 
@@ -389,24 +389,39 @@ get_shader_input_ptr(const InternalName *id) const {
 Texture *ShaderAttrib::
 get_shader_input_texture(const InternalName *id, SamplerState *sampler) const {
   Inputs::const_iterator i = _inputs.find(id);
-  if (i == _inputs.end()) {
-    ostringstream strm;
-    strm << "Shader input " << id->get_name() << " is not present.\n";
-    nassert_raise(strm.str());
-    return NULL;
-  } else {
-    const ShaderInput *p = (*i).second;
-    if (p->get_value_type() != ShaderInput::M_texture &&
-        p->get_value_type() != ShaderInput::M_texture_sampler) {
+  if (i != _inputs.end()) {
+    const ShaderInput &p = (*i).second;
+    switch (p.get_value_type()) {
+    case ShaderInput::M_texture:
+      {
+        Texture *tex = (Texture *)p.get_value();
+        if (sampler) {
+          *sampler = tex->get_default_sampler();
+        }
+        return tex;
+      }
+
+    case ShaderInput::M_texture_sampler:
+      {
+        const ParamTextureSampler *param = (const ParamTextureSampler *)p.get_value();
+        if (sampler) {
+          *sampler = param->get_sampler();
+        }
+        return param->get_texture();
+      }
+
+    default:
       ostringstream strm;
       strm <<  "Shader input " << id->get_name() << " is not a texture.\n";
       nassert_raise(strm.str());
       return NULL;
     }
-    if (sampler != NULL) {
-      *sampler = p->get_sampler();
-    }
-    return p->get_texture();
+
+  } else {
+    ostringstream strm;
+    strm << "Shader input " << id->get_name() << " is not present.\n";
+    nassert_raise(strm.str());
+    return NULL;
   }
 }
 
@@ -417,22 +432,17 @@ get_shader_input_texture(const InternalName *id, SamplerState *sampler) const {
 const LMatrix4 &ShaderAttrib::
 get_shader_input_matrix(const InternalName *id, LMatrix4 &matrix) const {
   Inputs::const_iterator i = _inputs.find(id);
-  if (i == _inputs.end()) {
-    ostringstream strm;
-    strm << "Shader input " << id->get_name() << " is not present.\n";
-    nassert_raise(strm.str());
-    return LMatrix4::ident_mat();
-  } else {
-    const ShaderInput *p = (*i).second;
+  if (i != _inputs.end()) {
+    const ShaderInput &p = (*i).second;
 
-    if (p->get_value_type() == ShaderInput::M_nodepath) {
-      const NodePath &np = p->get_nodepath();
+    if (p.get_value_type() == ShaderInput::M_nodepath) {
+      const NodePath &np = p.get_nodepath();
       nassertr(!np.is_empty(), LMatrix4::ident_mat());
       return np.get_transform()->get_mat();
 
-    } else if (p->get_value_type() == ShaderInput::M_numeric &&
-               p->get_ptr()._size >= 16 && (p->get_ptr()._size & 15) == 0) {
-      const Shader::ShaderPtrData &ptr = p->get_ptr();
+    } else if (p.get_value_type() == ShaderInput::M_numeric &&
+               p.get_ptr()._size >= 16 && (p.get_ptr()._size & 15) == 0) {
+      const Shader::ShaderPtrData &ptr = p.get_ptr();
 
       switch (ptr._type) {
         case Shader::SPT_float: {
@@ -460,6 +470,11 @@ get_shader_input_matrix(const InternalName *id, LMatrix4 &matrix) const {
     strm << "Shader input " << id->get_name() << " is not a NodePath, LMatrix4 or PTA_LMatrix4.\n";
     nassert_raise(strm.str());
     return LMatrix4::ident_mat();
+  } else {
+    ostringstream strm;
+    strm << "Shader input " << id->get_name() << " is not present.\n";
+    nassert_raise(strm.str());
+    return LMatrix4::ident_mat();
   }
 }
 
@@ -476,11 +491,11 @@ get_shader_input_buffer(const InternalName *id) const {
     nassert_raise(strm.str());
     return NULL;
   } else {
-    const ShaderInput *p = (*i).second;
+    const ShaderInput &p = (*i).second;
 
-    if (p->get_value_type() == ShaderInput::M_buffer) {
+    if (p.get_value_type() == ShaderInput::M_buffer) {
       ShaderBuffer *value;
-      DCAST_INTO_R(value, p->_value, NULL);
+      DCAST_INTO_R(value, p._value, NULL);
       return value;
     }
 
@@ -615,8 +630,7 @@ get_hash_impl() const {
 
   Inputs::const_iterator ii;
   for (ii = _inputs.begin(); ii != _inputs.end(); ++ii) {
-    hash = pointer_hash::add_hash(hash, (*ii).first);
-    hash = pointer_hash::add_hash(hash, (*ii).second);
+    hash = (*ii).second.add_hash(hash);
   }
 
   return hash;
@@ -649,13 +663,13 @@ compose_impl(const RenderAttrib *other) const {
   Inputs::const_iterator iover;
   for (iover=over->_inputs.begin(); iover!=over->_inputs.end(); ++iover) {
     const InternalName *id = (*iover).first;
-    const ShaderInput *dover = (*iover).second;
+    const ShaderInput &dover = (*iover).second;
     Inputs::iterator iattr = attr->_inputs.find(id);
     if (iattr == attr->_inputs.end()) {
       attr->_inputs.insert(Inputs::value_type(id,dover));
     } else {
-      const ShaderInput *dattr = (*iattr).second;
-      if (dattr->get_priority() <= dover->get_priority()) {
+      const ShaderInput &dattr = (*iattr).second;
+      if (dattr.get_priority() <= dover.get_priority()) {
         iattr->second = iover->second;
       }
     }

+ 6 - 4
panda/src/pgraph/shaderAttrib.h

@@ -71,7 +71,7 @@ PUBLISHED:
 
   CPT(RenderAttrib) clear_shader() const;
   // Shader Inputs
-  CPT(RenderAttrib) set_shader_input(const ShaderInput *inp) const;
+  CPT(RenderAttrib) set_shader_input(ShaderInput input) const;
 
   INLINE CPT(RenderAttrib) set_shader_input(CPT_InternalName id, Texture *tex,       int priority=0) const;
   INLINE CPT(RenderAttrib) set_shader_input(CPT_InternalName id, const NodePath &np, int priority=0) const;
@@ -104,8 +104,8 @@ PUBLISHED:
   INLINE bool has_shader_input(CPT_InternalName id) const;
 
   const Shader *get_shader() const;
-  const ShaderInput *get_shader_input(const InternalName *id) const;
-  const ShaderInput *get_shader_input(const string &id) const;
+  const ShaderInput &get_shader_input(const InternalName *id) const;
+  const ShaderInput &get_shader_input(const string &id) const;
 
   const NodePath &get_shader_input_nodepath(const InternalName *id) const;
   LVecBase4 get_shader_input_vector(InternalName *id) const;
@@ -145,7 +145,9 @@ private:
   bool        _auto_ramp_on;
   bool        _auto_shadow_on;
 
-  typedef pmap<CPT_InternalName, CPT(ShaderInput)> Inputs;
+  // We don't keep a reference to the InternalName, since this is also already
+  // stored on the ShaderInput object.
+  typedef pmap<const InternalName *, ShaderInput> Inputs;
   Inputs _inputs;
 
   friend class Extension<NodePath>;

+ 83 - 7
panda/src/pgraph/shaderInput.I

@@ -13,13 +13,6 @@
  * @date 2010-04-06
  */
 
-/**
- *
- */
-INLINE ShaderInput::
-~ShaderInput() {
-}
-
 /**
  *
  */
@@ -424,6 +417,81 @@ ShaderInput(CPT_InternalName name, const LVecBase2i &vec, int priority) :
 {
 }
 
+/**
+ *
+ */
+INLINE bool ShaderInput::
+operator == (const ShaderInput &other) const {
+  if (_type != other._type || _name != other._name || _priority != other._priority) {
+    return false;
+  }
+  switch (_type) {
+  case M_invalid:
+    return true;
+
+  case M_vector:
+    return _stored_vector == other._stored_vector;
+
+  case M_numeric:
+    return _stored_ptr._ptr == other._stored_ptr._ptr;
+
+  default:
+    return _value == other._value;
+  }
+}
+
+/**
+ *
+ */
+INLINE bool ShaderInput::
+operator != (const ShaderInput &other) const {
+  if (_type != other._type || _name != other._name || _priority != other._priority) {
+    return true;
+  }
+  switch (_type) {
+  case M_invalid:
+    return false;
+
+  case M_vector:
+    return _stored_vector != other._stored_vector;
+
+  case M_numeric:
+    return _stored_ptr._ptr != other._stored_ptr._ptr;
+
+  default:
+    return _value != other._value;
+  }
+}
+
+/**
+ *
+ */
+INLINE bool ShaderInput::
+operator < (const ShaderInput &other) const {
+  if (_type != other._type) {
+    return (_type < other._type);
+  }
+  if (_name != other._name) {
+    return (_name < other._name);
+  }
+  if (_priority != other._priority) {
+    return (_priority < other._priority);
+  }
+  switch (_type) {
+  case M_invalid:
+    return false;
+
+  case M_vector:
+    return _stored_vector < other._stored_vector;
+
+  case M_numeric:
+    return _stored_ptr._ptr < other._stored_ptr._ptr;
+
+  default:
+    return _value < other._value;
+  }
+}
+
 /**
  *
  */
@@ -471,3 +539,11 @@ INLINE ParamValueBase *ShaderInput::
 get_param() const {
   return DCAST(ParamValueBase, _value);
 }
+
+/**
+ *
+ */
+INLINE TypedWritableReferenceCount *ShaderInput::
+get_value() const {
+  return _value.p();
+}

+ 26 - 7
panda/src/pgraph/shaderInput.cxx

@@ -15,18 +15,13 @@
 #include "paramNodePath.h"
 #include "paramTexture.h"
 
-TypeHandle ShaderInput::_type_handle;
-
 /**
  * Returns a static ShaderInput object with name NULL, priority zero, type
  * INVALID, and all value-fields cleared.
  */
-const ShaderInput *ShaderInput::
+const ShaderInput &ShaderInput::
 get_blank() {
-  static CPT(ShaderInput) blank;
-  if (blank == 0) {
-    blank = new ShaderInput(NULL, 0);
-  }
+  static ShaderInput blank(nullptr, 0);
   return blank;
 }
 
@@ -66,6 +61,30 @@ ShaderInput(CPT_InternalName name, Texture *tex, const SamplerState &sampler, in
 {
 }
 
+/**
+ *
+ */
+size_t ShaderInput::
+add_hash(size_t hash) const {
+  hash = int_hash::add_hash(hash, _type);
+  hash = pointer_hash::add_hash(hash, _name);
+  hash = int_hash::add_hash(hash, _priority);
+
+  switch (_type) {
+  case M_invalid:
+    return hash;
+
+  case M_vector:
+    return _stored_vector.add_hash(hash);
+
+  case M_numeric:
+    return pointer_hash::add_hash(hash, _stored_ptr._ptr);
+
+  default:
+    return pointer_hash::add_hash(hash, _value);
+  }
+}
+
 /**
  * Warning: no error checking is done.  This *will* crash if get_value_type()
  * is not M_nodepath.

+ 11 - 24
panda/src/pgraph/shaderInput.h

@@ -17,7 +17,6 @@
 #define SHADERINPUT_H
 
 #include "pandabase.h"
-#include "typedWritableReferenceCount.h"
 #include "pointerTo.h"
 #include "internalName.h"
 #include "paramValue.h"
@@ -37,10 +36,7 @@
  * This is a small container class that can hold any one of the value types
  * that can be passed as input to a shader.
  */
-class EXPCL_PANDA_PGRAPH ShaderInput : public TypedWritableReferenceCount {
-public:
-  INLINE ~ShaderInput();
-
+class EXPCL_PANDA_PGRAPH ShaderInput {
 PUBLISHED:
   // Used when binding texture images.
   enum AccessFlags {
@@ -49,7 +45,7 @@ PUBLISHED:
     A_layered = 0x04,
   };
 
-  static const ShaderInput *get_blank();
+  static const ShaderInput &get_blank();
   INLINE ShaderInput(CPT_InternalName name, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, Texture *tex, int priority=0);
   INLINE ShaderInput(CPT_InternalName name, ParamValueBase *param, int priority=0);
@@ -102,6 +98,12 @@ PUBLISHED:
     M_buffer,
   };
 
+  INLINE bool operator == (const ShaderInput &other) const;
+  INLINE bool operator != (const ShaderInput &other) const;
+  INLINE bool operator < (const ShaderInput &other) const;
+
+  size_t add_hash(size_t hash) const;
+
   INLINE const InternalName *get_name() const;
 
   INLINE int get_value_type() const;
@@ -114,7 +116,10 @@ PUBLISHED:
   const SamplerState &get_sampler() const;
 
 public:
+  ShaderInput() DEFAULT_CTOR;
+
   INLINE ParamValueBase *get_param() const;
+  INLINE TypedWritableReferenceCount *get_value() const;
 
   static void register_with_read_factory();
 
@@ -127,26 +132,8 @@ private:
   int _type;
 
   friend class ShaderAttrib;
-
-public:
-  static TypeHandle get_class_type() {
-    return _type_handle;
-  }
-  static void init_type() {
-    TypedWritableReferenceCount::init_type();
-    register_type(_type_handle, "ShaderInput",
-                  TypedWritableReferenceCount::get_class_type());
-  }
-  virtual TypeHandle get_type() const {
-    return get_class_type();
-  }
-  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
-
-private:
-  static TypeHandle _type_handle;
 };
 
-
 #include "shaderInput.I"
 
 #endif  // SHADERINPUT_H

+ 151 - 207
panda/src/pgraph/transformState.cxx

@@ -29,7 +29,7 @@ TransformState::States *TransformState::_states = NULL;
 CPT(TransformState) TransformState::_identity_state;
 CPT(TransformState) TransformState::_invalid_state;
 UpdateSeq TransformState::_last_cycle_detect;
-int TransformState::_garbage_index = 0;
+size_t TransformState::_garbage_index = 0;
 bool TransformState::_uniquify_matrix = true;
 
 PStatCollector TransformState::_cache_update_pcollector("*:State Cache:Update");
@@ -62,6 +62,10 @@ TransformState() : _lock("TransformState") {
   _flags = F_is_identity | F_singular_known | F_is_2d;
   _inv_mat = (LMatrix4 *)NULL;
   _cache_stats.add_num_states(1);
+
+#ifdef DO_MEMORY_USAGE
+  MemoryUsage::update_type(this, this);
+#endif
 }
 
 /**
@@ -608,33 +612,74 @@ compose(const TransformState *other) const {
     return do_compose(other);
   }
 
+  LightReMutexHolder holder(*_states_lock);
+
   // Is this composition already cached?
-  CPT(TransformState) result;
-  {
-    LightReMutexHolder holder(*_states_lock);
-    int index = _composition_cache.find(other);
-    if (index != -1) {
-      const Composition &comp = _composition_cache.get_data(index);
-      result = comp._result;
-    }
-    if (result != (TransformState *)NULL) {
+  int index = _composition_cache.find(other);
+  if (index != -1) {
+    const Composition &comp = _composition_cache.get_data(index);
+    if (comp._result != nullptr) {
+      // Success!
       _cache_stats.inc_hits();
+      return comp._result;
     }
   }
 
-  if (result != (TransformState *)NULL) {
-    // Success!
-    return result;
-  }
-
   // Not in the cache.  Compute a new result.  It's important that we don't
   // hold the lock while we do this, or we lose the benefit of
   // parallelization.
-  result = do_compose(other);
+  CPT(TransformState) result = do_compose(other);
+
+  if (index != -1) {
+    Composition &comp = _composition_cache.modify_data(index);
+    // Well, it wasn't cached already, but we already had an entry (probably
+    // created for the reverse direction), so use the same entry to store
+    // the new result.
+    comp._result = result;
+
+    if (result != (const TransformState *)this) {
+      // See the comments below about the need to up the reference count
+      // only when the result is not the same as this.
+      result->cache_ref();
+    }
+    // Here's the cache!
+    _cache_stats.inc_hits();
+    return result;
+  }
+  _cache_stats.inc_misses();
 
-  // It's OK to cast away the constness of this pointer, because the cache is
-  // a transparent property of the class.
-  return ((TransformState *)this)->store_compose(other, result);
+  // We need to make a new cache entry, both in this object and in the other
+  // object.  We make both records so the other TransformState object will
+  // know to delete the entry from this object when it destructs, and vice-
+  // versa.
+
+  // The cache entry in this object is the only one that indicates the result;
+  // the other will be NULL for now.
+  _cache_stats.add_total_size(1);
+  _cache_stats.inc_adds(_composition_cache.get_size() == 0);
+
+  _composition_cache[other]._result = result;
+
+  if (other != this) {
+    _cache_stats.add_total_size(1);
+    _cache_stats.inc_adds(other->_composition_cache.get_size() == 0);
+    other->_composition_cache[this]._result = NULL;
+  }
+
+  if (result != (TransformState *)this) {
+    // If the result of do_compose() is something other than this, explicitly
+    // increment the reference count.  We have to be sure to decrement it
+    // again later, when the composition entry is removed from the cache.
+    result->cache_ref();
+
+    // (If the result was just this again, we still store the result, but we
+    // don't increment the reference count, since that would be a self-
+    // referential leak.)
+  }
+
+  _cache_stats.maybe_report("TransformState");
+
+  return result;
 }
 
 /**
@@ -676,32 +721,69 @@ invert_compose(const TransformState *other) const {
 
   LightReMutexHolder holder(*_states_lock);
 
-  CPT(TransformState) result;
-  {
-    LightReMutexHolder holder(*_states_lock);
-    int index = _invert_composition_cache.find(other);
-    if (index != -1) {
-      const Composition &comp = _invert_composition_cache.get_data(index);
-      result = comp._result;
-    }
-    if (result != (TransformState *)NULL) {
+  int index = _invert_composition_cache.find(other);
+  if (index != -1) {
+    const Composition &comp = _invert_composition_cache.get_data(index);
+    if (comp._result != nullptr) {
+      // Success!
       _cache_stats.inc_hits();
+      return comp._result;
     }
   }
 
-  if (result != (TransformState *)NULL) {
-    // Success!
-    return result;
-  }
-
   // Not in the cache.  Compute a new result.  It's important that we don't
   // hold the lock while we do this, or we lose the benefit of
   // parallelization.
-  result = do_invert_compose(other);
+  CPT(TransformState) result = do_invert_compose(other);
+
+  // Is this composition already cached?
+  if (index != -1) {
+    Composition &comp = _invert_composition_cache.modify_data(index);
+    // Well, it wasn't cached already, but we already had an entry (probably
+    // created for the reverse direction), so use the same entry to store
+    // the new result.
+    comp._result = result;
+
+    if (result != (const TransformState *)this) {
+      // See the comments below about the need to up the reference count
+      // only when the result is not the same as this.
+      result->cache_ref();
+    }
+    // Here's the cache!
+    _cache_stats.inc_hits();
+    return result;
+  }
+  _cache_stats.inc_misses();
 
-  // It's OK to cast away the constness of this pointer, because the cache is
-  // a transparent property of the class.
-  return ((TransformState *)this)->store_invert_compose(other, result);
+  // We need to make a new cache entry, both in this object and in the other
+  // object.  We make both records so the other TransformState object will
+  // know to delete the entry from this object when it destructs, and vice-
+  // versa.
+
+  // The cache entry in this object is the only one that indicates the result;
+  // the other will be NULL for now.
+  _cache_stats.add_total_size(1);
+  _cache_stats.inc_adds(_invert_composition_cache.get_size() == 0);
+  _invert_composition_cache[other]._result = result;
+
+  if (other != this) {
+    _cache_stats.add_total_size(1);
+    _cache_stats.inc_adds(other->_invert_composition_cache.get_size() == 0);
+    other->_invert_composition_cache[this]._result = NULL;
+  }
+
+  if (result != (TransformState *)this) {
+    // If the result of compose() is something other than this, explicitly
+    // increment the reference count.  We have to be sure to decrement it
+    // again later, when the composition entry is removed from the cache.
+    result->cache_ref();
+
+    // (If the result was just this again, we still store the result, but we
+    // don't increment the reference count, since that would be a self-
+    // referential leak.)
+  }
+
+  return result;
 }
 
 /**
@@ -712,7 +794,7 @@ invert_compose(const TransformState *other) const {
  */
 bool TransformState::
 unref() const {
-  if (!transform_cache || garbage_collect_states) {
+  if (garbage_collect_states || !transform_cache) {
     // If we're not using the cache at all, or if we're relying on garbage
     // collection, just allow the pointer to unref normally.
     return ReferenceCount::unref();
@@ -760,8 +842,8 @@ bool TransformState::
 validate_composition_cache() const {
   LightReMutexHolder holder(*_states_lock);
 
-  int size = _composition_cache.get_size();
-  for (int i = 0; i < size; ++i) {
+  size_t size = _composition_cache.get_size();
+  for (size_t i = 0; i < size; ++i) {
     if (!_composition_cache.has_element(i)) {
       continue;
     }
@@ -955,15 +1037,15 @@ get_num_unused_states() {
   typedef pmap<const TransformState *, int> StateCount;
   StateCount state_count;
 
-  int size = _states->get_size();
-  for (int si = 0; si < size; ++si) {
+  size_t size = _states->get_size();
+  for (size_t si = 0; si < size; ++si) {
     if (!_states->has_element(si)) {
       continue;
     }
     const TransformState *state = _states->get_key(si);
 
-    int i;
-    int cache_size = state->_composition_cache.get_size();
+    size_t i;
+    size_t cache_size = state->_composition_cache.get_size();
     for (i = 0; i < cache_size; ++i) {
       if (state->_composition_cache.has_element(i)) {
         const TransformState *result = state->_composition_cache.get_data(i)._result;
@@ -1053,8 +1135,8 @@ clear_cache() {
     TempStates temp_states;
     temp_states.reserve(orig_size);
 
-    int size = _states->get_size();
-    for (int si = 0; si < size; ++si) {
+    size_t size = _states->get_size();
+    for (size_t si = 0; si < size; ++si) {
       if (!_states->has_element(si)) {
         continue;
       }
@@ -1116,27 +1198,30 @@ garbage_collect() {
   if (_states == (States *)NULL || !garbage_collect_states) {
     return 0;
   }
+
+  bool break_and_uniquify = (auto_break_cycles && uniquify_transforms);
+
   LightReMutexHolder holder(*_states_lock);
 
   PStatTimer timer(_garbage_collect_pcollector);
-  int orig_size = _states->get_num_entries();
+  size_t orig_size = _states->get_num_entries();
 
   // How many elements to process this pass?
-  int size = _states->get_size();
-  int num_this_pass = int(size * garbage_collect_states_rate);
-  if (num_this_pass <= 0) {
+  size_t size = _states->get_size();
+  size_t num_this_pass = int(size * garbage_collect_states_rate);
+  if (size <= 0 || num_this_pass <= 0) {
     return 0;
   }
+
+  size_t si = _garbage_index;
+
   num_this_pass = min(num_this_pass, size);
-  int stop_at_element = (_garbage_index + num_this_pass) % size;
+  size_t stop_at_element = (si + num_this_pass) & (size - 1);
 
-  int num_elements = 0;
-  int si = _garbage_index;
   do {
     if (_states->has_element(si)) {
-      ++num_elements;
       TransformState *state = (TransformState *)_states->get_key(si);
-      if (auto_break_cycles && uniquify_transforms) {
+      if (break_and_uniquify) {
         if (state->get_cache_ref_count() > 0 &&
             state->get_ref_count() == state->get_cache_ref_count()) {
           // If we have removed all the references to this state not in the
@@ -1160,10 +1245,13 @@ garbage_collect() {
       }
     }
 
-    si = (si + 1) % size;
+    si = (si + 1) & (size - 1);
   } while (si != stop_at_element);
   _garbage_index = si;
+
+#ifdef _DEBUG
   nassertr(_states->validate(), 0);
+#endif
 
   int new_size = _states->get_num_entries();
   return orig_size - new_size;
@@ -1193,8 +1281,8 @@ list_cycles(ostream &out) {
   VisitedStates visited;
   CompositionCycleDesc cycle_desc;
 
-  int size = _states->get_size();
-  for (int si = 0; si < size; ++si) {
+  size_t size = _states->get_size();
+  for (size_t si = 0; si < size; ++si) {
     if (!_states->has_element(si)) {
       continue;
     }
@@ -1272,8 +1360,8 @@ list_states(ostream &out) {
 
   out << _states->get_num_entries() << " states:\n";
 
-  int size = _states->get_size();
-  for (int si = 0; si < size; ++si) {
+  size_t size = _states->get_size();
+  for (size_t si = 0; si < size; ++si) {
     if (!_states->has_element(si)) {
       continue;
     }
@@ -1307,14 +1395,14 @@ validate_states() {
     return false;
   }
 
-  int size = _states->get_size();
-  int si = 0;
+  size_t size = _states->get_size();
+  size_t si = 0;
   while (si < size && !_states->has_element(si)) {
     ++si;
   }
   nassertr(si < size, false);
   nassertr(_states->get_key(si)->get_ref_count() >= 0, false);
-  int snext = si;
+  size_t snext = si;
   ++snext;
   while (snext < size && !_states->has_element(snext)) {
     ++snext;
@@ -1533,150 +1621,6 @@ do_compose(const TransformState *other) const {
   }
 }
 
-/**
- * Stores the result of a composition in the cache.  Returns the stored result
- * (it may be a different object than the one passed in, due to another thread
- * having computed the composition first).
- */
-CPT(TransformState) TransformState::
-store_compose(const TransformState *other, const TransformState *result) {
-  // Identity should have already been screened.
-  nassertr(!is_identity(), other);
-  nassertr(!other->is_identity(), this);
-
-  // So should have validity.
-  nassertr(!is_invalid(), this);
-  nassertr(!other->is_invalid(), other);
-
-  LightReMutexHolder holder(*_states_lock);
-
-  // Is this composition already cached?
-  int index = _composition_cache.find(other);
-  if (index != -1) {
-    Composition &comp = _composition_cache.modify_data(index);
-    if (comp._result == (const TransformState *)NULL) {
-      // Well, it wasn't cached already, but we already had an entry (probably
-      // created for the reverse direction), so use the same entry to store
-      // the new result.
-      comp._result = result;
-
-      if (result != (const TransformState *)this) {
-        // See the comments below about the need to up the reference count
-        // only when the result is not the same as this.
-        result->cache_ref();
-      }
-    }
-    // Here's the cache!
-    _cache_stats.inc_hits();
-    return comp._result;
-  }
-  _cache_stats.inc_misses();
-
-  // We need to make a new cache entry, both in this object and in the other
-  // object.  We make both records so the other TransformState object will
-  // know to delete the entry from this object when it destructs, and vice-
-  // versa.
-
-  // The cache entry in this object is the only one that indicates the result;
-  // the other will be NULL for now.
-  _cache_stats.add_total_size(1);
-  _cache_stats.inc_adds(_composition_cache.get_size() == 0);
-
-  _composition_cache[other]._result = result;
-
-  if (other != this) {
-    _cache_stats.add_total_size(1);
-    _cache_stats.inc_adds(other->_composition_cache.get_size() == 0);
-    ((TransformState *)other)->_composition_cache[this]._result = NULL;
-  }
-
-  if (result != (TransformState *)this) {
-    // If the result of do_compose() is something other than this, explicitly
-    // increment the reference count.  We have to be sure to decrement it
-    // again later, when the composition entry is removed from the cache.
-    result->cache_ref();
-
-    // (If the result was just this again, we still store the result, but we
-    // don't increment the reference count, since that would be a self-
-    // referential leak.)
-  }
-
-  _cache_stats.maybe_report("TransformState");
-
-  return result;
-}
-
-/**
- * Stores the result of a composition in the cache.  Returns the stored result
- * (it may be a different object than the one passed in, due to another thread
- * having computed the composition first).
- */
-CPT(TransformState) TransformState::
-store_invert_compose(const TransformState *other, const TransformState *result) {
-  // Identity should have already been screened.
-  nassertr(!is_identity(), other);
-
-  // So should have validity.
-  nassertr(!is_invalid(), this);
-  nassertr(!other->is_invalid(), other);
-
-  nassertr(other != this, make_identity());
-
-  LightReMutexHolder holder(*_states_lock);
-
-  // Is this composition already cached?
-  int index = _invert_composition_cache.find(other);
-  if (index != -1) {
-    Composition &comp = ((TransformState *)this)->_invert_composition_cache.modify_data(index);
-    if (comp._result == (const TransformState *)NULL) {
-      // Well, it wasn't cached already, but we already had an entry (probably
-      // created for the reverse direction), so use the same entry to store
-      // the new result.
-      comp._result = result;
-
-      if (result != (const TransformState *)this) {
-        // See the comments below about the need to up the reference count
-        // only when the result is not the same as this.
-        result->cache_ref();
-      }
-    }
-    // Here's the cache!
-    _cache_stats.inc_hits();
-    return comp._result;
-  }
-  _cache_stats.inc_misses();
-
-  // We need to make a new cache entry, both in this object and in the other
-  // object.  We make both records so the other TransformState object will
-  // know to delete the entry from this object when it destructs, and vice-
-  // versa.
-
-  // The cache entry in this object is the only one that indicates the result;
-  // the other will be NULL for now.
-  _cache_stats.add_total_size(1);
-  _cache_stats.inc_adds(_invert_composition_cache.get_size() == 0);
-  _invert_composition_cache[other]._result = result;
-
-  if (other != this) {
-    _cache_stats.add_total_size(1);
-    _cache_stats.inc_adds(other->_invert_composition_cache.get_size() == 0);
-    ((TransformState *)other)->_invert_composition_cache[this]._result = NULL;
-  }
-
-  if (result != (TransformState *)this) {
-    // If the result of compose() is something other than this, explicitly
-    // increment the reference count.  We have to be sure to decrement it
-    // again later, when the composition entry is removed from the cache.
-    result->cache_ref();
-
-    // (If the result was just this again, we still store the result, but we
-    // don't increment the reference count, since that would be a self-
-    // referential leak.)
-  }
-
-  return result;
-}
-
 /**
  * The private implemention of invert_compose().
  */

+ 9 - 5
panda/src/pgraph/transformState.h

@@ -234,9 +234,7 @@ private:
   static CPT(TransformState) return_unique(TransformState *state);
 
   CPT(TransformState) do_compose(const TransformState *other) const;
-  CPT(TransformState) store_compose(const TransformState *other, const TransformState *result);
   CPT(TransformState) do_invert_compose(const TransformState *other) const;
-  CPT(TransformState) store_invert_compose(const TransformState *other, const TransformState *result);
   void detect_and_break_cycles();
   static bool r_detect_cycles(const TransformState *start_state,
                               const TransformState *current_state,
@@ -288,8 +286,8 @@ private:
   };
 
   typedef SimpleHashMap<const TransformState *, Composition, pointer_hash> CompositionCache;
-  CompositionCache _composition_cache;
-  CompositionCache _invert_composition_cache;
+  mutable CompositionCache _composition_cache;
+  mutable CompositionCache _invert_composition_cache;
 
   // This is used to mark nodes as we visit them to detect cycles.
   UpdateSeq _cycle_detect;
@@ -297,7 +295,7 @@ private:
 
   // This keeps track of our current position through the garbage collection
   // cycle.
-  static int _garbage_index;
+  static size_t _garbage_index;
 
   static bool _uniquify_matrix;
 
@@ -408,6 +406,12 @@ private:
   friend class Extension<TransformState>;
 };
 
+#ifdef DO_MEMORY_USAGE
+// We can safely redefine this as a no-op.
+template<>
+INLINE void PointerToBase<TransformState>::update_type(To *ptr) {}
+#endif
+
 INLINE ostream &operator << (ostream &out, const TransformState &state) {
   state.output(out);
   return out;

+ 1 - 1
panda/src/pgraphnodes/ambientLight.h

@@ -33,7 +33,7 @@ protected:
 public:
   virtual PandaNode *make_copy() const;
   virtual void write(ostream &out, int indent_level) const;
-  virtual bool is_ambient_light() const;
+  virtual bool is_ambient_light() const FINAL;
 
 PUBLISHED:
   virtual int get_class_priority() const;

+ 1 - 1
panda/src/pgraphnodes/fadeLodNode.cxx

@@ -93,7 +93,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
   consider_verify_lods(trav, data);
 
   Camera *camera = trav->get_scene()->get_camera_node();
-  NodePath this_np = data._node_path.get_node_path();
+  NodePath this_np = data.get_node_path();
   FadeLODNodeData *ldata =
     DCAST(FadeLODNodeData, camera->get_aux_scene_data(this_np));
 

+ 1 - 1
panda/src/pgraphnodes/lightLensNode.I

@@ -15,7 +15,7 @@
  * Returns whether this light is configured to cast shadows or not.
  */
 INLINE bool LightLensNode::
-is_shadow_caster() {
+is_shadow_caster() const {
   return _shadow_caster;
 }
 

+ 1 - 1
panda/src/pgraphnodes/lightLensNode.h

@@ -34,7 +34,7 @@ PUBLISHED:
   LightLensNode(const string &name, Lens *lens = new PerspectiveLens());
   virtual ~LightLensNode();
 
-  INLINE bool is_shadow_caster();
+  INLINE bool is_shadow_caster() const;
   INLINE void set_shadow_caster(bool caster);
   INLINE void set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int sort = -10);
 

+ 5 - 11
panda/src/pgraphnodes/lodNode.cxx

@@ -330,7 +330,7 @@ compute_child(CullTraverser *trav, CullTraverserData &data) {
          * trav->get_scene()->get_camera_node()->get_lod_scale())) {
       if (pgraph_cat.is_debug()) {
         pgraph_cat.debug()
-          << data._node_path << " at distance " << sqrt(dist2)
+          << data.get_node_path() << " at distance " << sqrt(dist2)
           << ", selected child " << index << "\n";
       }
 
@@ -340,7 +340,7 @@ compute_child(CullTraverser *trav, CullTraverserData &data) {
 
   if (pgraph_cat.is_debug()) {
     pgraph_cat.debug()
-      << data._node_path << " at distance " << sqrt(dist2)
+      << data.get_node_path() << " at distance " << sqrt(dist2)
       << ", no children in range.\n";
   }
 
@@ -393,20 +393,14 @@ show_switches_cull_callback(CullTraverser *trav, CullTraverserData &data) {
 
         // And draw the spindle in this color.
         CullTraverserData next_data2(data, sw.get_spindle_viz());
-        next_data2.apply_transform_and_state(trav, viz_transform,
-                                             RenderState::make_empty(),
-                                             RenderEffects::make_empty(),
-                                             ClipPlaneAttrib::make());
+        next_data2.apply_transform(viz_transform);
         trav->traverse(next_data2);
       }
 
       // Draw the rings for this switch level.  We do this after we have drawn
       // the geometry and the spindle.
       CullTraverserData next_data(data, sw.get_ring_viz());
-      next_data.apply_transform_and_state(trav, viz_transform,
-                                          RenderState::make_empty(),
-                                          RenderEffects::make_empty(),
-                                          ClipPlaneAttrib::make());
+      next_data.apply_transform(viz_transform);
       trav->traverse(next_data);
     }
   }
@@ -650,7 +644,7 @@ do_auto_verify_lods(CullTraverser *trav, CullTraverserData &data) {
         const Switch &sw = cdata->_switch_vector[index];
         ostringstream strm;
         strm
-          << "Level " << index << " geometry of " << data._node_path
+          << "Level " << index << " geometry of " << data.get_node_path()
           << " is larger than its switch radius; suggest radius of "
           << suggested_radius << " instead of " << sw.get_in()
           << " (configure verify-lods 0 to ignore this error)";

+ 1 - 1
panda/src/pgraphnodes/nodeCullCallbackData.cxx

@@ -41,7 +41,7 @@ void NodeCullCallbackData::
 upcall() {
   PandaNode *node = _data.node();
   if (node->is_of_type(CallbackNode::get_class_type())) {
-    CallbackNode *cbnode = DCAST(CallbackNode, _data.node());
+    CallbackNode *cbnode = (CallbackNode *)node;
 
     // OK, render this node.  Rendering a CallbackNode means creating a
     // CullableObject for the draw_callback, if any.  We don't need to pass

+ 2 - 2
panda/src/pgraphnodes/shaderGenerator.cxx

@@ -284,7 +284,7 @@ analyze_renderstate(const RenderState *rs) {
     PandaNode *light_obj = light.node();
     nassertv(light_obj != (PandaNode *)NULL);
 
-    if (light_obj->get_type() == AmbientLight::get_class_type()) {
+    if (light_obj->is_ambient_light()) {
       if (_material->has_ambient()) {
         LColor a = _material->get_ambient();
         if ((a[0]!=0.0)||(a[1]!=0.0)||(a[2]!=0.0)) {
@@ -298,7 +298,7 @@ analyze_renderstate(const RenderState *rs) {
     } else if (light_obj->is_of_type(LightLensNode::get_class_type())) {
       _lights_np.push_back(light);
       _lights.push_back((LightLensNode *)light_obj);
-      if (DCAST(LightLensNode, light_obj)->is_shadow_caster()) {
+      if (((const LightLensNode *)light_obj)->is_shadow_caster()) {
         _shadows = true;
       }
       _lighting = true;

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

@@ -231,6 +231,8 @@ read_stage_unlocked(int pipeline_stage) const {
   TAU_PROFILE("const CycleData *PipelineCyclerTrueImpl::read_stage_unlocked(int)", " ", TAU_USER);
 #ifdef _DEBUG
   nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
+#elif defined(__has_builtin) && __has_builtin(__builtin_assume)
+  __builtin_assume(pipeline_stage >= 0);
 #endif
   return _data[pipeline_stage]._cdata;
 }
@@ -248,6 +250,8 @@ read_stage(int pipeline_stage, Thread *current_thread) const {
   TAU_PROFILE("const CycleData *PipelineCyclerTrueImpl::read_stage(int, Thread *)", " ", TAU_USER);
 #ifdef _DEBUG
   nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
+#elif defined(__has_builtin) && __has_builtin(__builtin_assume)
+  __builtin_assume(pipeline_stage >= 0);
 #endif
   _lock.acquire(current_thread);
   return _data[pipeline_stage]._cdata;
@@ -278,6 +282,8 @@ elevate_read_stage(int pipeline_stage, const CycleData *pointer,
 #ifdef _DEBUG
   nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
   nassertr(_data[pipeline_stage]._cdata == pointer, NULL);
+#elif defined(__has_builtin) && __has_builtin(__builtin_assume)
+  __builtin_assume(pipeline_stage >= 0);
 #endif
   CycleData *new_pointer = write_stage(pipeline_stage, current_thread);
   _lock.release();
@@ -296,6 +302,8 @@ elevate_read_stage_upstream(int pipeline_stage, const CycleData *pointer,
 #ifdef _DEBUG
   nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
   nassertr(_data[pipeline_stage]._cdata == pointer, NULL);
+#elif defined(__has_builtin) && __has_builtin(__builtin_assume)
+  __builtin_assume(pipeline_stage >= 0);
 #endif
   CycleData *new_pointer =
     write_stage_upstream(pipeline_stage, force_to_0, current_thread);
@@ -313,6 +321,8 @@ release_write_stage(int pipeline_stage, CycleData *pointer) {
   nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages);
   nassertv(_data[pipeline_stage]._cdata == pointer);
   nassertv(_data[pipeline_stage]._writes_outstanding > 0);
+#elif defined(__has_builtin) && __has_builtin(__builtin_assume)
+  __builtin_assume(pipeline_stage >= 0);
 #endif
   --(_data[pipeline_stage]._writes_outstanding);
   _lock.release();

+ 1 - 1
panda/src/pipeline/thread.I

@@ -78,7 +78,7 @@ get_pipeline_stage() const {
   // However, since we guarantee that this is never less than zero, clang
   // offers a nice way to avoid that.
   int pipeline_stage = _pipeline_stage;
-  __builtin_assume(_pipeline_stage >= 0);
+  __builtin_assume(pipeline_stage >= 0);
   return pipeline_stage;
 #else
   return _pipeline_stage;

+ 6 - 0
panda/src/putil/copyOnWriteObject.h

@@ -161,6 +161,12 @@ private:
   static TypeHandle _type_handle;
 };
 
+#ifdef DO_MEMORY_USAGE
+// We can safely redefine this as a no-op.
+template<>
+INLINE void PointerToBase<CopyOnWriteObject>::update_type(To *ptr) {}
+#endif
+
 #include "copyOnWriteObject.I"
 
 #endif

+ 15 - 13
panda/src/putil/simpleHashMap.I

@@ -235,8 +235,8 @@ get_size() const {
  */
 template<class Key, class Value, class Compare>
 INLINE bool SimpleHashMap<Key, Value, Compare>::
-has_element(int n) const {
-  nassertr(n >= 0 && n < (int)_table_size, false);
+has_element(size_t n) const {
+  nassertr(n < _table_size, false);
   return (get_exists_array()[n] != 0);
 }
 
@@ -249,7 +249,7 @@ has_element(int n) const {
  */
 template<class Key, class Value, class Compare>
 INLINE const Key &SimpleHashMap<Key, Value, Compare>::
-get_key(int n) const {
+get_key(size_t n) const {
   nassertr(has_element(n), _table[n]._key);
   return _table[n]._key;
 }
@@ -263,7 +263,7 @@ get_key(int n) const {
  */
 template<class Key, class Value, class Compare>
 INLINE const Value &SimpleHashMap<Key, Value, Compare>::
-get_data(int n) const {
+get_data(size_t n) const {
   nassertr(has_element(n), _table[n]._data);
   return _table[n]._data;
 }
@@ -277,7 +277,7 @@ get_data(int n) const {
  */
 template<class Key, class Value, class Compare>
 INLINE Value &SimpleHashMap<Key, Value, Compare>::
-modify_data(int n) {
+modify_data(size_t n) {
   nassertr(has_element(n), _table[n]._data);
   return _table[n]._data;
 }
@@ -291,7 +291,7 @@ modify_data(int n) {
  */
 template<class Key, class Value, class Compare>
 INLINE void SimpleHashMap<Key, Value, Compare>::
-set_data(int n, const Value &data) {
+set_data(size_t n, const Value &data) {
   nassertv(has_element(n));
   _table[n]._data = data;
 }
@@ -305,7 +305,7 @@ set_data(int n, const Value &data) {
  */
 template<class Key, class Value, class Compare>
 void SimpleHashMap<Key, Value, Compare>::
-remove_element(int n) {
+remove_element(size_t n) {
   nassertv(has_element(n));
 
   clear_element(n);
@@ -314,7 +314,7 @@ remove_element(int n) {
 
   // Now we have put a hole in the table.  If there was a hash conflict in the
   // slot following this one, we have to move it down to close the hole.
-  size_t i = (size_t)n;
+  size_t i = n;
   i = (i + 1) & (_table_size - 1);
   while (has_element(i)) {
     size_t wants_index = get_hash(_table[i]._key);
@@ -403,12 +403,14 @@ bool SimpleHashMap<Key, Value, Compare>::
 validate() const {
   size_t count = 0;
 
+  const unsigned char *exists_array = get_exists_array();
+
   for (size_t i = 0; i < _table_size; ++i) {
-    if (has_element(i)) {
+    if (exists_array[i] != 0) {
       ++count;
       size_t ideal_index = get_hash(_table[i]._key);
       size_t wants_index = ideal_index;
-      while (wants_index != i && has_element(wants_index)) {
+      while (wants_index != i && exists_array[wants_index] != 0) {
         wants_index = (wants_index + 1) & (_table_size - 1);
       }
       if (wants_index != i) {
@@ -455,7 +457,7 @@ get_hash(const Key &key) const {
  */
 template<class Key, class Value, class Compare>
 INLINE bool SimpleHashMap<Key, Value, Compare>::
-is_element(int n, const Key &key) const {
+is_element(size_t n, const Key &key) const {
   nassertr(has_element(n), false);
   return _comp.is_equal(_table[n]._key, key);
 }
@@ -466,7 +468,7 @@ is_element(int n, const Key &key) const {
  */
 template<class Key, class Value, class Compare>
 INLINE void SimpleHashMap<Key, Value, Compare>::
-store_new_element(int n, const Key &key, const Value &data) {
+store_new_element(size_t n, const Key &key, const Value &data) {
   new(&_table[n]) TableEntry(key, data);
   get_exists_array()[n] = true;
 }
@@ -476,7 +478,7 @@ store_new_element(int n, const Key &key, const Value &data) {
  */
 template<class Key, class Value, class Compare>
 INLINE void SimpleHashMap<Key, Value, Compare>::
-clear_element(int n) {
+clear_element(size_t n) {
   _table[n].~TableEntry();
   get_exists_array()[n] = false;
 }

+ 9 - 9
panda/src/putil/simpleHashMap.h

@@ -42,12 +42,12 @@ public:
   INLINE Value &operator [] (const Key &key);
 
   INLINE size_t get_size() const;
-  INLINE bool has_element(int n) const;
-  INLINE const Key &get_key(int n) const;
-  INLINE const Value &get_data(int n) const;
-  INLINE Value &modify_data(int n);
-  INLINE void set_data(int n, const Value &data);
-  void remove_element(int n);
+  INLINE bool has_element(size_t n) const;
+  INLINE const Key &get_key(size_t n) const;
+  INLINE const Value &get_data(size_t n) const;
+  INLINE Value &modify_data(size_t n);
+  INLINE void set_data(size_t n, const Value &data);
+  void remove_element(size_t n);
 
   INLINE size_t get_num_entries() const;
   INLINE bool is_empty() const;
@@ -61,9 +61,9 @@ private:
 
   INLINE size_t get_hash(const Key &key) const;
 
-  INLINE bool is_element(int n, const Key &key) const;
-  INLINE void store_new_element(int n, const Key &key, const Value &data);
-  INLINE void clear_element(int n);
+  INLINE bool is_element(size_t n, const Key &key) const;
+  INLINE void store_new_element(size_t n, const Key &key, const Value &data);
+  INLINE void clear_element(size_t n);
   INLINE unsigned char *get_exists_array() const;
 
   void new_table();

+ 5 - 0
panda/src/putil/typedWritableReferenceCount.h

@@ -61,6 +61,11 @@ private:
   static TypeHandle _type_handle;
 };
 
+#ifdef DO_MEMORY_USAGE
+template<>
+INLINE void PointerToBase<TypedWritableReferenceCount>::update_type(To *ptr) {}
+#endif
+
 #include "typedWritableReferenceCount.I"
 
 #endif

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно