Bläddra i källkod

clean up caching a bit, multitex in gl

David Rose 21 år sedan
förälder
incheckning
92a93e8af0

+ 58 - 14
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -433,6 +433,8 @@ reset() {
 
     _glActiveTexture = (PFNGLACTIVETEXTUREPROC)
       get_extension_func(GLPREFIX_QUOTED, "ActiveTexture");
+    _glClientActiveTexture = (PFNGLACTIVETEXTUREPROC)
+      get_extension_func(GLPREFIX_QUOTED, "ClientActiveTexture");
     _glMultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVPROC)
       get_extension_func(GLPREFIX_QUOTED, "MultiTexCoord2fv");
 
@@ -441,12 +443,15 @@ reset() {
 
     _glActiveTexture = (PFNGLACTIVETEXTUREPROC)
       get_extension_func(GLPREFIX_QUOTED, "ActiveTextureARB");
+    _glClientActiveTexture = (PFNGLACTIVETEXTUREPROC)
+      get_extension_func(GLPREFIX_QUOTED, "ClientActiveTextureARB");
     _glMultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVPROC)
       get_extension_func(GLPREFIX_QUOTED, "MultiTexCoord2fvARB");
   }
 
   if (_supports_multitexture) {
-    if (_glActiveTexture == NULL || _glMultiTexCoord2fv == NULL) {
+    if (_glActiveTexture == NULL || _glClientActiveTexture == NULL ||
+        _glMultiTexCoord2fv == NULL) {
       GLCAT.warning()
         << "Multitexture advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
       _supports_multitexture = false;
@@ -454,6 +459,7 @@ reset() {
   }
   if (!_supports_multitexture) {
     _glActiveTexture = null_glActiveTexture;
+    _glClientActiveTexture = null_glActiveTexture;
   }
 
   _supports_buffers = false;
@@ -707,6 +713,7 @@ reset() {
   _current_tex_gen = DCAST(TexGenAttrib, TexGenAttrib::make());
   _needs_tex_gen = false;
   _tex_gen_modifies_mat = false;
+  _last_max_stage_index = 0;
   _auto_antialias_mode = false;
   _render_mode = RenderModeAttrib::M_filled;
 
@@ -2041,12 +2048,13 @@ begin_draw_primitives(const qpGeom *geom, const qpGeomVertexData *vertex_data) {
   if (geom->get_usage_hint() == qpGeomUsageHint::UH_static && 
       _vertex_data->get_usage_hint() == qpGeomUsageHint::UH_static &&
       display_lists) {
-    // If the geom appears to be totally static, try to build it into
+    // If the geom claims to be totally static, try to build it into
     // a display list.
     GeomContext *gc = ((qpGeom *)geom)->prepare_now(get_prepared_objects(), this);
     nassertr(gc != (GeomContext *)NULL, false);
     CLP(GeomContext) *ggc = DCAST(CLP(GeomContext), gc);
-    if (ggc->_modified == geom->get_modified()) {
+    UpdateSeq modified = max(geom->get_modified(), _vertex_data->get_modified());
+    if (ggc->_modified == modified) {
       // If it hasn't been modified, just play the display list again.
       GLP(CallList)(ggc->_index);
 #ifdef DO_PSTATS
@@ -2064,7 +2072,7 @@ begin_draw_primitives(const qpGeom *geom, const qpGeomVertexData *vertex_data) {
     } else {
       GLP(NewList)(ggc->_index, GL_COMPILE);
     }      
-    ggc->_modified = geom->get_modified();
+    ggc->_modified = modified;
 
 #ifdef DO_PSTATS
     // Count up the number of vertices used by primitives in the Geom,
@@ -2115,16 +2123,52 @@ begin_draw_primitives(const qpGeom *geom, const qpGeomVertexData *vertex_data) {
     GLP(DisableClientState)(GL_COLOR_ARRAY);
   }
 
-  if (_vertex_data->get_array_info(InternalName::get_texcoord(),
-                                   array_data, num_components, numeric_type, 
-                                   start, stride)) {
-    const unsigned char *client_pointer = setup_array_data(array_data);
-    GLP(TexCoordPointer)(num_components, get_numeric_type(numeric_type), 
-                         stride, client_pointer + start);
-    GLP(EnableClientState)(GL_TEXTURE_COORD_ARRAY);
-  } else {
+  // Now set up each of the active texture coordinate stages--or at
+  // least those for which we're not generating texture coordinates
+  // automatically.
+  const Geom::ActiveTextureStages &active_stages = 
+    _current_texture->get_on_stages();
+  const Geom::NoTexCoordStages &no_texcoords = 
+    _current_tex_gen->get_no_texcoords();
+
+  int max_stage_index = (int)active_stages.size();
+  int stage_index = 0;
+  while (stage_index < max_stage_index) {
+    _glClientActiveTexture(GL_TEXTURE0 + stage_index);
+    TextureStage *stage = active_stages[stage_index];
+    if (no_texcoords.find(stage) == no_texcoords.end()) {
+      // This stage is not one of the stages that doesn't need
+      // texcoords issued for it.
+      const InternalName *name = stage->get_texcoord_name();
+
+      if (_vertex_data->get_array_info(name, array_data, num_components, 
+                                       numeric_type, start, stride)) {
+        // The vertex data does have texcoords for this stage.
+        const unsigned char *client_pointer = setup_array_data(array_data);
+        GLP(TexCoordPointer)(num_components, get_numeric_type(numeric_type), 
+                             stride, client_pointer + start);
+        GLP(EnableClientState)(GL_TEXTURE_COORD_ARRAY);
+
+      } else {
+        // The vertex data doesn't have texcoords for this stage (even
+        // though they're needed).
+        GLP(DisableClientState)(GL_TEXTURE_COORD_ARRAY);
+      }
+    } else {
+      // No texcoords are needed for this stage.
+      GLP(DisableClientState)(GL_TEXTURE_COORD_ARRAY);
+    }
+
+    ++stage_index;
+  }
+
+  // Be sure also to disable any texture stages we had enabled before.
+  while (stage_index < _last_max_stage_index) {
+    _glClientActiveTexture(GL_TEXTURE0 + stage_index);
     GLP(DisableClientState)(GL_TEXTURE_COORD_ARRAY);
+    ++stage_index;
   }
+  _last_max_stage_index = max_stage_index;
 
   return true;
 }
@@ -2528,7 +2572,7 @@ setup_array_data(const qpGeomVertexArrayData *data) {
   nassertr(vbc != (VertexBufferContext *)NULL, data->get_data());
   apply_vertex_buffer(vbc);
 
-  // NULL is the OpenGL convention for the first byte of the buffer.
+  // NULL is the OpenGL convention for the first byte of the buffer object.
   return NULL;
 }
 
@@ -2659,7 +2703,7 @@ setup_primitive(const qpGeomPrimitive *data) {
   nassertr(ibc != (IndexBufferContext *)NULL, data->get_flat_last_vertices());
   apply_index_buffer(ibc);
 
-  // NULL is the OpenGL convention for the first byte of the buffer.
+  // NULL is the OpenGL convention for the first byte of the buffer object.
   return NULL;
 }
 

+ 2 - 1
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -318,7 +318,7 @@ protected:
   int _pass_number;
   bool _auto_rescale_normal;
   CLP(GeomContext) *_geom_display_list;
-  float _num_display_list_verts_before;
+  int _last_max_stage_index;
   
   int _error_count;
 
@@ -340,6 +340,7 @@ public:
 
   bool _supports_multitexture;
   PFNGLACTIVETEXTUREPROC _glActiveTexture;
+  PFNGLCLIENTACTIVETEXTUREPROC _glClientActiveTexture;
   PFNGLMULTITEXCOORD2FVPROC _glMultiTexCoord2fv;
 
   bool _supports_buffers;

+ 20 - 1
panda/src/gobj/qpgeom.I

@@ -89,6 +89,7 @@ modify_primitive(int i) {
   CDWriter cdata(_cycler);
   nassertr(i >= 0 && i < (int)cdata->_primitives.size(), NULL);
   cdata->_got_usage_hint = false;
+  cdata->_modified = qpGeom::get_next_modified();
   return cdata->_primitives[i];
 }
 
@@ -117,6 +118,23 @@ set_primitive(int i, const qpGeomPrimitive *primitive) {
     }
   }
   cdata->_primitives[i] = (qpGeomPrimitive *)primitive;
+  cdata->_modified = qpGeom::get_next_modified();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeom::get_modified
+//       Access: Published
+//  Description: Returns a sequence number which is guaranteed to
+//               change at least every time any of the primitives in
+//               the Geom is modified, or the set of primitives is
+//               modified.  However, this does not include
+//               modifications to the vertex data, which should be
+//               tested separately.
+////////////////////////////////////////////////////////////////////
+INLINE UpdateSeq qpGeom::
+get_modified() const {
+  CDReader cdata(_cycler);
+  return cdata->_modified;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -141,7 +159,8 @@ CData(const qpGeom::CData &copy) :
   _data(copy._data),
   _primitives(copy._primitives),
   _usage_hint(copy._usage_hint),
-  _got_usage_hint(copy._got_usage_hint)
+  _got_usage_hint(copy._got_usage_hint),
+  _modified(copy._modified)
 {
 }
 

+ 2 - 34
panda/src/gobj/qpgeom.cxx

@@ -157,6 +157,7 @@ add_primitive(const qpGeomPrimitive *primitive) {
   if (cdata->_got_usage_hint) {
     cdata->_usage_hint = min(cdata->_usage_hint, primitive->get_usage_hint());
   }
+  cdata->_modified = qpGeom::get_next_modified();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -176,6 +177,7 @@ remove_primitive(int i) {
     cdata->_got_usage_hint = false;
   }
   cdata->_primitives.erase(cdata->_primitives.begin() + i);
+  cdata->_modified = qpGeom::get_next_modified();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -214,40 +216,6 @@ get_num_bytes() const {
   return num_bytes;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeom::get_modified
-//       Access: Published
-//  Description: Returns the maximum UpdateSeq of all the Geom's
-//               individual primitives and vertex arrays.  This,
-//               therefore, will change only when any part of the Geom
-//               changes.
-////////////////////////////////////////////////////////////////////
-UpdateSeq qpGeom::
-get_modified() const {
-  CDReader cdata(_cycler);
-  UpdateSeq seq;
-
-  Primitives::const_iterator pi;
-  for (pi = cdata->_primitives.begin(); 
-       pi != cdata->_primitives.end();
-       ++pi) {
-    UpdateSeq pseq = (*pi)->get_modified();
-    if (seq < pseq) {
-      seq = pseq;
-    }
-  }
-
-  int num_arrays = cdata->_data->get_num_arrays();
-  for (int i = 0; i < num_arrays; ++i) {
-    UpdateSeq aseq = cdata->_data->get_array(i)->get_modified();
-    if (seq < aseq) {
-      seq = aseq;
-    }
-  }
-
-  return seq;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeom::munge_geom
 //       Access: Published

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

@@ -78,7 +78,7 @@ PUBLISHED:
   void clear_primitives();
 
   int get_num_bytes() const;
-  UpdateSeq get_modified() const;
+  INLINE UpdateSeq get_modified() const;
 
   void munge_geom(const qpGeomMunger *munger,
                   CPT(qpGeom) &result, CPT(qpGeomVertexData) &data) const;
@@ -129,6 +129,7 @@ private:
     Primitives _primitives;
     qpGeomUsageHint::UsageHint _usage_hint;
     bool _got_usage_hint;
+    UpdateSeq _modified;
     MungedCache _munged_cache;
   };
 

+ 22 - 14
panda/src/gobj/qpgeomPrimitive.I

@@ -197,6 +197,11 @@ get_ends() const {
 INLINE CPTA_ushort qpGeomPrimitive::
 get_mins() const {
   CDReader cdata(_cycler);
+  if (!cdata->_got_minmax) {
+    CDWriter cdataw(((qpGeomPrimitive *)this)->_cycler, cdata);
+    ((qpGeomPrimitive *)this)->recompute_minmax(cdataw);
+    return cdataw->_mins;
+  }
   return cdata->_mins;
 }
 
@@ -214,6 +219,11 @@ get_mins() const {
 INLINE CPTA_ushort qpGeomPrimitive::
 get_maxs() const {
   CDReader cdata(_cycler);
+  if (!cdata->_got_minmax) {
+    CDWriter cdataw(((qpGeomPrimitive *)this)->_cycler, cdata);
+    ((qpGeomPrimitive *)this)->recompute_minmax(cdataw);
+    return cdataw->_maxs;
+  }
   return cdata->_maxs;
 }
 
@@ -252,7 +262,9 @@ INLINE int qpGeomPrimitive::
 get_min_vertex() const {
   CDReader cdata(_cycler);
   if (!cdata->_got_minmax) {
-    ((qpGeomPrimitive *)this)->recompute_minmax();
+    CDWriter cdataw(((qpGeomPrimitive *)this)->_cycler, cdata);
+    ((qpGeomPrimitive *)this)->recompute_minmax(cdataw);
+    return cdataw->_min_vertex;
   }
   return cdata->_min_vertex;
 }
@@ -265,12 +277,9 @@ get_min_vertex() const {
 ////////////////////////////////////////////////////////////////////
 INLINE int qpGeomPrimitive::
 get_min_vertex(int i) const {
-  CDReader cdata(_cycler);
-  if (!cdata->_got_minmax) {
-    ((qpGeomPrimitive *)this)->recompute_minmax();
-  }
-  nassertr(i >= 0 && i < (int)cdata->_mins.size(), -1);
-  return cdata->_mins[i];
+  CPTA_ushort mins = get_mins();
+  nassertr(i >= 0 && i < (int)mins.size(), -1);
+  return mins[i];
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -283,7 +292,9 @@ INLINE int qpGeomPrimitive::
 get_max_vertex() const {
   CDReader cdata(_cycler);
   if (!cdata->_got_minmax) {
-    ((qpGeomPrimitive *)this)->recompute_minmax();
+    CDWriter cdataw(((qpGeomPrimitive *)this)->_cycler, cdata);
+    ((qpGeomPrimitive *)this)->recompute_minmax(cdataw);
+    return cdataw->_max_vertex;
   }
   return cdata->_max_vertex;
 }
@@ -296,12 +307,9 @@ get_max_vertex() const {
 ////////////////////////////////////////////////////////////////////
 INLINE int qpGeomPrimitive::
 get_max_vertex(int i) const {
-  CDReader cdata(_cycler);
-  if (!cdata->_got_minmax) {
-    ((qpGeomPrimitive *)this)->recompute_minmax();
-  }
-  nassertr(i >= 0 && i < (int)cdata->_maxs.size(), -1);
-  return cdata->_maxs[i];
+  CPTA_ushort maxs = get_maxs();
+  nassertr(i >= 0 && i < (int)maxs.size(), -1);
+  return maxs[i];
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 3
panda/src/gobj/qpgeomPrimitive.cxx

@@ -804,9 +804,7 @@ remove_cache_entry() const {
 //               necessary.
 ////////////////////////////////////////////////////////////////////
 void qpGeomPrimitive::
-recompute_minmax() {
-  CDWriter cdata(_cycler);
-  
+recompute_minmax(qpGeomPrimitive::CDWriter &cdata) {
   if (cdata->_vertices.empty()) {
     cdata->_min_vertex = 0;
     cdata->_max_vertex = 0;

+ 3 - 3
panda/src/gobj/qpgeomPrimitive.h

@@ -77,8 +77,8 @@ PUBLISHED:
     SM_smooth,  
 
     // SM_uniform: all vertices across all faces have the same colors
-    // and normals.  It doesn't matter which ShadeModelAttrib mode is
-    // used to render this primitive.
+    // and normals.  It doesn't really matter which ShadeModelAttrib
+    // mode is used to render this primitive.
     SM_uniform, 
 
     // SM_flat_(first,last)_vertex: each face within the primitive
@@ -208,7 +208,7 @@ private:
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataWriter<CData> CDWriter;
 
-  void recompute_minmax();
+  void recompute_minmax(CDWriter &cdata);
 
 public:
   virtual void write_datagram(BamWriter *manager, Datagram &dg);

+ 46 - 46
panda/src/gobj/qpgeomVertexCacheManager.I

@@ -102,62 +102,61 @@ remove_primitive(const qpGeomPrimitive *primitive) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexCacheManager::record_data
+//     Function: qpGeomVertexCacheManager::record_geom
 //       Access: Private
 //  Description: Records a new entry in the cache, or marks a cache
 //               hit for a previous entry in the cache.  This should
-//               only be called by GeomVertexData.
+//               only be called by Geom.
 //
 //               The cache manager will not hold a reference on the
-//               GeomVertexData pointer; if it destructs, it should
-//               call remove_data() to remove itself from the cache.
+//               Geom pointer; if it destructs, it should call
+//               remove_geom() to remove itself from the cache.
 ////////////////////////////////////////////////////////////////////
 INLINE void qpGeomVertexCacheManager::
-record_data(const qpGeomVertexData *source, const qpGeomVertexFormat *modifier,
+record_geom(const qpGeom *source, const qpGeomMunger *modifier,
             int result_size) {
   record_entry(Entry(source, modifier, result_size));
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexCacheManager::remove_data
+//     Function: qpGeomVertexCacheManager::remove_geom
 //       Access: Private
 //  Description: Removes an entry from the cache, if it is there.
 //               Quietly ignores it if it is not.  This should only be
 //               called by GeomVertexData.
 ////////////////////////////////////////////////////////////////////
 INLINE void qpGeomVertexCacheManager::
-remove_data(const qpGeomVertexData *source,
-            const qpGeomVertexFormat *modifier) {
+remove_geom(const qpGeom *source, const qpGeomMunger *modifier) {
   remove_entry(Entry(source, modifier, 0));
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexCacheManager::record_geom
+//     Function: qpGeomVertexCacheManager::dequeue_entry
 //       Access: Private
-//  Description: Records a new entry in the cache, or marks a cache
-//               hit for a previous entry in the cache.  This should
-//               only be called by Geom.
-//
-//               The cache manager will not hold a reference on the
-//               Geom pointer; if it destructs, it should
-//               call remove_geom() to remove itself from the cache.
+//  Description: Removes an Entry record from the doubly-linked list.
 ////////////////////////////////////////////////////////////////////
 INLINE void qpGeomVertexCacheManager::
-record_geom(const qpGeom *source, const qpGeomMunger *modifier,
-            int result_size) {
-  record_entry(Entry(source, modifier, result_size));
+dequeue_entry(qpGeomVertexCacheManager::Entry *entry) {
+  nassertv(entry->_prev->_next == entry &&
+           entry->_next->_prev == entry);
+  entry->_prev->_next = entry->_next;
+  entry->_next->_prev = entry->_prev;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexCacheManager::remove_geom
+//     Function: qpGeomVertexCacheManager::enqueue_entry
 //       Access: Private
-//  Description: Removes an entry from the cache, if it is there.
-//               Quietly ignores it if it is not.  This should only be
-//               called by Geom.
+//  Description: Adds an Entry record to the tail of the doubly-linked
+//               list.
 ////////////////////////////////////////////////////////////////////
 INLINE void qpGeomVertexCacheManager::
-remove_geom(const qpGeom *source, const qpGeomMunger *modifier) {
-  remove_entry(Entry(source, modifier, 0));
+enqueue_entry(qpGeomVertexCacheManager::Entry *entry) {
+  nassertv(_list->_prev->_next == _list &&
+           _list->_next->_prev == _list);
+  entry->_prev = _list->_prev;
+  entry->_next = _list;
+  entry->_prev->_next = entry;
+  _list->_prev = entry;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -166,12 +165,10 @@ remove_geom(const qpGeom *source, const qpGeomMunger *modifier) {
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 INLINE qpGeomVertexCacheManager::Entry::
-Entry(const qpGeomMunger *munger, int result_size) :
-  _cache_type(CT_munger),
-  _result_size(result_size)
+Entry() :
+  _cache_type(CT_none),
+  _result_size(0)
 {
-  _u._munger = munger;
-  _u._munger->ref();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -180,11 +177,12 @@ Entry(const qpGeomMunger *munger, int result_size) :
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 INLINE qpGeomVertexCacheManager::Entry::
-Entry(const qpGeomPrimitive *primitive, int result_size) :
-  _cache_type(CT_primitive),
+Entry(const qpGeomMunger *munger, int result_size) :
+  _cache_type(CT_munger),
   _result_size(result_size)
 {
-  _u._primitive = primitive;
+  _u._munger = munger;
+  _u._munger->ref();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -193,13 +191,11 @@ Entry(const qpGeomPrimitive *primitive, int result_size) :
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 INLINE qpGeomVertexCacheManager::Entry::
-Entry(const qpGeomVertexData *source, const qpGeomVertexFormat *modifier,
-      int result_size) :
-  _cache_type(CT_data),
+Entry(const qpGeomPrimitive *primitive, int result_size) :
+  _cache_type(CT_primitive),
   _result_size(result_size)
 {
-  _u._data._source = source;
-  _u._data._modifier = modifier;
+  _u._primitive = primitive;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -208,7 +204,8 @@ Entry(const qpGeomVertexData *source, const qpGeomVertexFormat *modifier,
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 INLINE qpGeomVertexCacheManager::Entry::
-Entry(const qpGeom *source, const qpGeomMunger *modifier, int result_size) :
+Entry(const qpGeom *source, const qpGeomMunger *modifier,
+      int result_size) :
   _cache_type(CT_geom),
   _result_size(result_size)
 {
@@ -269,19 +266,22 @@ INLINE qpGeomVertexCacheManager::Entry::
 ////////////////////////////////////////////////////////////////////
 INLINE bool qpGeomVertexCacheManager::Entry::
 operator < (const qpGeomVertexCacheManager::Entry &other) const {
+  if (_cache_type != other._cache_type) {
+    return (int)_cache_type < (int)other._cache_type;
+  }
+
   switch (_cache_type) {
+  case CT_none:
+    // We shouldn't be adding the end-of-list token to the index.
+    nassertr(false, false);
+    return false;
+
   case CT_munger:
     return _u._munger < other._u._munger;
 
   case CT_primitive:
     return _u._primitive < other._u._primitive;
 
-  case CT_data:
-    if (_u._data._source != other._u._data._source) {
-      return _u._data._source < other._u._data._source;
-    }
-    return _u._data._modifier < other._u._data._modifier;
-
   case CT_geom:
     if (_u._geom._source != other._u._geom._source) {
       return _u._geom._source < other._u._geom._source;
@@ -289,7 +289,7 @@ operator < (const qpGeomVertexCacheManager::Entry &other) const {
     return _u._geom._modifier < other._u._geom._modifier;
   }
 
-  return 0;
+  return false;
 }
 
 INLINE ostream &

+ 53 - 51
panda/src/gobj/qpgeomVertexCacheManager.cxx

@@ -30,6 +30,9 @@ qpGeomVertexCacheManager::
 qpGeomVertexCacheManager() :
   _total_size(0)
 {
+  _list = new Entry;
+  _list->_next = _list;
+  _list->_prev = _list;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -39,6 +42,8 @@ qpGeomVertexCacheManager() :
 ////////////////////////////////////////////////////////////////////
 qpGeomVertexCacheManager::
 ~qpGeomVertexCacheManager() {
+  // Shouldn't be deleting this global object.
+  nassertv(false);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -61,80 +66,77 @@ get_global_ptr() {
 //               cache hit for a previous entry in the cache.
 ////////////////////////////////////////////////////////////////////
 void qpGeomVertexCacheManager::
-record_entry(const qpGeomVertexCacheManager::Entry &entry) {
+record_entry(const qpGeomVertexCacheManager::Entry &const_entry) {
   MutexHolder holder(_lock);
-  EntriesIndex::iterator ii = _entries_index.find(&entry);
-  if (ii != _entries_index.end()) {
-    // Remove the previous cache entry.
-    Entries::iterator ei = (*ii).second;
-    _total_size -= (*ei)._result_size;
-    _entries_index.erase(ii);
-    _entries.erase(ei);
 
+  EntriesIndex::iterator ii = _entries_index.find((Entry *)&const_entry);
+  if (ii != _entries_index.end()) {
+    // We already had this entry in the cache.  Refresh it.
     if (gobj_cat.is_spam()) {
       gobj_cat.spam()
-        << "refreshing cache entry: " << entry << "\n";
+        << "refreshing cache entry: " << const_entry << "\n";
     }
+
+    // Move the previous cache entry to the tail of the list.
+    Entry *entry = (*ii);
+    dequeue_entry(entry);
+    enqueue_entry(entry);
+
+    _total_size += (const_entry._result_size - entry->_result_size);
+    entry->_result_size = const_entry._result_size;
+
   } else {
+    // There was no such entry already in the cache.  Add it.
     if (gobj_cat.is_debug()) {
       gobj_cat.debug()
-        << "recording cache entry: " << entry << ", total_size = "
-        << _total_size + entry._result_size << "\n";
+        << "recording cache entry: " << const_entry << ", total_size = "
+        << _total_size + const_entry._result_size << "\n";
     }
+    
+    Entry *entry = new Entry(const_entry);
+    enqueue_entry(entry);
+    _total_size += entry->_result_size;
+
+    // Also record an index entry.
+    bool inserted = _entries_index.insert(entry).second;
+    nassertv(inserted);
   }
 
-  // Add the new entry at the end of the list, so it will be the last
-  // to be removed.
-  Entries::iterator ei = 
-    _entries.insert(_entries.end(), entry);
-  Entry *entry_pointer = &(*ei);
-
-  // Also record an index entry.
-  bool inserted = _entries_index.insert
-    (EntriesIndex::value_type(entry_pointer, ei)).second;
-  nassertv(inserted);
-
-  _total_size += entry_pointer->_result_size;
-
   // Now remove any old entries if our cache is over the limit.  This may
   // also remove the entry we just added, especially if our cache size
   // is set to 0.
   int max_size = get_max_size();
   while (_total_size > max_size) {
-    nassertv(!_entries.empty());
-    ei = _entries.begin();
-    entry_pointer = &(*ei);
+    Entry *entry = _list->_next;
+    nassertv(entry != _list);
 
     if (gobj_cat.is_debug()) {
       gobj_cat.debug()
         << "cache total_size = " << _total_size << ", max_size = "
-        << max_size << ", removing " << *entry_pointer << "\n";
+        << max_size << ", removing " << *entry << "\n";
     }
 
-    ii = _entries_index.find(entry_pointer);
+    ii = _entries_index.find(entry);
     nassertv(ii != _entries_index.end());
 
-    switch (entry_pointer->_cache_type) {
-    case CT_data:
-      entry_pointer->_u._data._source->remove_cache_entry
-        (entry_pointer->_u._data._modifier);
-      break;
-
+    switch (entry->_cache_type) {
     case CT_primitive:
-      entry_pointer->_u._primitive->remove_cache_entry();
+      entry->_u._primitive->remove_cache_entry();
       break;
 
     case CT_geom:
-      entry_pointer->_u._geom._source->remove_cache_entry
-        (entry_pointer->_u._geom._modifier);
+      entry->_u._geom._source->remove_cache_entry
+        (entry->_u._geom._modifier);
       break;
 
     default:
       break;
     }
-    _total_size -= entry_pointer->_result_size;
+    _total_size -= entry->_result_size;
     _entries_index.erase(ii);
-    _entries.erase(ei);
+
+    dequeue_entry(entry);
+    delete entry;
   }
 }
 
@@ -145,19 +147,20 @@ record_entry(const qpGeomVertexCacheManager::Entry &entry) {
 //               Quietly ignores it if it is not.
 ////////////////////////////////////////////////////////////////////
 void qpGeomVertexCacheManager::
-remove_entry(const qpGeomVertexCacheManager::Entry &entry) {
+remove_entry(const qpGeomVertexCacheManager::Entry &const_entry) {
   if (gobj_cat.is_debug()) {
     gobj_cat.debug()
-      << "remove_entry(" << entry << ")\n";
+      << "remove_entry(" << const_entry << ")\n";
   }
   MutexHolder holder(_lock);
 
-  EntriesIndex::iterator ii = _entries_index.find(&entry);
+  EntriesIndex::iterator ii = _entries_index.find((Entry *)&const_entry);
   if (ii != _entries_index.end()) {
-    Entries::iterator ei = (*ii).second;
-    _total_size -= (*ei)._result_size;
+    Entry *entry = (*ii);
+    _total_size -= entry->_result_size;
     _entries_index.erase(ii);
-    _entries.erase(ei);
+    dequeue_entry(entry);
+    delete entry;
   }
 }
 
@@ -170,6 +173,10 @@ void qpGeomVertexCacheManager::Entry::
 output(ostream &out) const {
   out << "[ ";
   switch (_cache_type) {
+  case CT_none:
+    out << "end-of-list token";
+    break;
+
   case CT_munger:
     out << "munger " << (void *)_u._munger << ":" 
         << _u._munger->get_ref_count();
@@ -179,11 +186,6 @@ output(ostream &out) const {
     out << "primitive " << (void *)_u._primitive;
     break;
 
-  case CT_data:
-    out << "data " << (void *)_u._data._source << ", " 
-        << (void *)_u._data._modifier;
-    break;
-
   case CT_geom:
     out << "geom " << (void *)_u._geom._source << ", " 
         << (void *)_u._geom._modifier;

+ 25 - 21
panda/src/gobj/qpgeomVertexCacheManager.h

@@ -74,18 +74,17 @@ private:
   INLINE void record_primitive(const qpGeomPrimitive *primitive,
                                int result_size);
   INLINE void remove_primitive(const qpGeomPrimitive *primitive);
-  INLINE void record_data(const qpGeomVertexData *source,
-                          const qpGeomVertexFormat *modifier,
-                          int result_size);
-  INLINE void remove_data(const qpGeomVertexData *source,
-                          const qpGeomVertexFormat *modifier);
   INLINE void record_geom(const qpGeom *source,
                           const qpGeomMunger *modifier,
                           int result_size);
-  INLINE void remove_geom(const qpGeom *geom, const qpGeomMunger *modifier);
+  INLINE void remove_geom(const qpGeom *source,
+                          const qpGeomMunger *modifier);
+
+  void record_entry(const Entry &const_entry);
+  void remove_entry(const Entry &const_entry);
 
-  void record_entry(const Entry &entry);
-  void remove_entry(const Entry &entry);
+  INLINE void dequeue_entry(Entry *entry);
+  INLINE void enqueue_entry(Entry *entry);
 
 private:
   // This mutex protects all operations on this object.
@@ -94,9 +93,9 @@ private:
   int _total_size;
 
   enum CacheType {
+    CT_none,
     CT_munger,
     CT_primitive,
-    CT_data,
     CT_geom,
   };
 
@@ -109,10 +108,9 @@ public:
   // this C-style polymorphism.
   class Entry {
   public:
+    INLINE Entry();
     INLINE Entry(const qpGeomMunger *munger, int result_size);
     INLINE Entry(const qpGeomPrimitive *primitive, int result_size);
-    INLINE Entry(const qpGeomVertexData *source,
-                 const qpGeomVertexFormat *modifier, int result_size);
     INLINE Entry(const qpGeom *source, const qpGeomMunger *modifier, 
                  int result_size);
     INLINE Entry(const Entry &copy);
@@ -127,32 +125,38 @@ public:
     union {
       const qpGeomMunger *_munger;
       const qpGeomPrimitive *_primitive;
-      struct {
-        const qpGeomVertexData *_source;
-        const qpGeomVertexFormat *_modifier;
-      } _data;
       struct {
         const qpGeom *_source;
         const qpGeomMunger *_modifier;
       } _geom;
     } _u;
+
+    Entry *_prev, *_next;
   };
 
 private:
-  // This list keeps the cache entries in least-recently-used order:
-  // the items at the head of the list are ready to be flushed.
-  typedef plist<Entry> Entries;
-  Entries _entries;
+  // We maintain a doubly-linked list to keep the cache entries in
+  // least-recently-used order: the items at the head of the list are
+  // ready to be flushed.  We use our own doubly-linked list instead
+  // of an STL list, just so we can avoid a tiny bit of overhead,
+  // especially with managing the pointer to the entry in
+  // _entries_index.
+
+  // The tail and the head of the list are both kept by the _prev and
+  // _next pointers, respectively, within the following object, which
+  // always exists solely to keep a handle to the list.  Keeping a
+  // token of the list this way avoids special cases for an empty
+  // list.
+  Entry *_list;
 
   // And this indexes into the above list, for fast lookup.
-  typedef pmap<const Entry *, Entries::iterator, IndirectLess<Entry> > EntriesIndex;
+  typedef pset<Entry *, IndirectLess<Entry> > EntriesIndex;
   EntriesIndex _entries_index;
 
   static qpGeomVertexCacheManager *_global_ptr;
 
   friend class qpGeomMunger;
   friend class qpGeomPrimitive;
-  friend class qpGeomVertexData;
   friend class qpGeom;
 };
 

+ 15 - 1
panda/src/gobj/qpgeomVertexData.I

@@ -91,6 +91,19 @@ get_array(int i) const {
   return cdata->_arrays[i];
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexData::get_modified
+//       Access: Published
+//  Description: Returns a sequence number which is guaranteed to
+//               change at least every time the vertex data is
+//               modified.
+////////////////////////////////////////////////////////////////////
+INLINE UpdateSeq qpGeomVertexData::
+get_modified() const {
+  CDReader cdata(_cycler);
+  return cdata->_modified;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexData::CData::Constructor
 //       Access: Public
@@ -107,7 +120,8 @@ CData() {
 ////////////////////////////////////////////////////////////////////
 INLINE qpGeomVertexData::CData::
 CData(const qpGeomVertexData::CData &copy) :
-  _arrays(copy._arrays)
+  _arrays(copy._arrays),
+  _modified(copy._modified)
 {
 }
 

+ 10 - 103
panda/src/gobj/qpgeomVertexData.cxx

@@ -17,7 +17,6 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "qpgeomVertexData.h"
-#include "qpgeomVertexCacheManager.h"
 #include "pStatTimer.h"
 #include "bamReader.h"
 #include "bamWriter.h"
@@ -93,25 +92,6 @@ operator = (const qpGeomVertexData &copy) {
 ////////////////////////////////////////////////////////////////////
 qpGeomVertexData::
 ~qpGeomVertexData() {
-  // When we destruct, we should ensure that all of our cached
-  // entries, across all pipeline stages, are properly removed from
-  // the cache manager.
-  qpGeomVertexCacheManager *cache_mgr = 
-    qpGeomVertexCacheManager::get_global_ptr();
-
-  int num_stages = _cycler.get_num_stages();
-  for (int i = 0; i < num_stages; i++) {
-    if (_cycler.is_stage_unique(i)) {
-      CData *cdata = _cycler.write_stage(i);
-      for (ConvertedCache::iterator ci = cdata->_converted_cache.begin();
-           ci != cdata->_converted_cache.end();
-           ++ci) {
-        cache_mgr->remove_data(this, (*ci).first);
-      }
-      cdata->_converted_cache.clear();
-      _cycler.release_write_stage(i, cdata);
-    }
-  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -144,7 +124,6 @@ get_num_vertices() const {
 ////////////////////////////////////////////////////////////////////
 void qpGeomVertexData::
 clear_vertices() {
-  clear_cache();
   CDWriter cdata(_cycler);
   nassertv(_format->get_num_arrays() == (int)cdata->_arrays.size());
 
@@ -154,6 +133,7 @@ clear_vertices() {
        ++ai) {
     (*ai)->clear_vertices();
   }
+  cdata->_modified = qpGeom::get_next_modified();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -170,13 +150,13 @@ modify_array(int i) {
   // Perform copy-on-write: if the reference count on the vertex data
   // is greater than 1, assume some other GeomVertexData has the same
   // pointer, so make a copy of it first.
-  clear_cache();
   CDWriter cdata(_cycler);
   nassertr(i >= 0 && i < (int)cdata->_arrays.size(), NULL);
 
   if (cdata->_arrays[i]->get_ref_count() > 1) {
     cdata->_arrays[i] = new qpGeomVertexArrayData(*cdata->_arrays[i]);
   }
+  cdata->_modified = qpGeom::get_next_modified();
 
   return cdata->_arrays[i];
 }
@@ -191,10 +171,10 @@ modify_array(int i) {
 ////////////////////////////////////////////////////////////////////
 void qpGeomVertexData::
 set_array(int i, const qpGeomVertexArrayData *array) {
-  clear_cache();
   CDWriter cdata(_cycler);
   nassertv(i >= 0 && i < (int)cdata->_arrays.size());
   cdata->_arrays[i] = (qpGeomVertexArrayData *)array;
+  cdata->_modified = qpGeom::get_next_modified();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -232,28 +212,6 @@ convert_to(const qpGeomVertexFormat *new_format) const {
     return this;
   }
 
-  // Look up the format in our cache--maybe we've recently converted
-  // to the requested format.
-  {
-    // Use read() and release_read() instead of CDReader, because the
-    // call to record_data() might recursively call back into this
-    // object, and require a write.
-    const CData *cdata = _cycler.read();
-    ConvertedCache::const_iterator ci = 
-      cdata->_converted_cache.find(new_format);
-    if (ci != cdata->_converted_cache.end()) {
-      _cycler.release_read(cdata);
-      // Record a cache hit, so this element will stay in the cache a
-      // while longer.
-      qpGeomVertexCacheManager *cache_mgr = 
-        qpGeomVertexCacheManager::get_global_ptr();
-      cache_mgr->record_data(this, new_format, (*ci).second->get_num_bytes());
-
-      return (*ci).second;
-    }
-    _cycler.release_read(cdata);
-  }
-
   // Okay, convert the data to the new format.
   int num_vertices = get_num_vertices();
 
@@ -326,19 +284,6 @@ convert_to(const qpGeomVertexFormat *new_format) const {
     }
   }
 
-  // Record the new result in the cache.
-  {
-    CDWriter cdata(((qpGeomVertexData *)this)->_cycler);
-    cdata->_converted_cache[new_format] = new_data;
-  }
-
-  // And tell the cache manager about the new entry.  (It might
-  // immediately request a delete from the cache of the thing we just
-  // added.)
-  qpGeomVertexCacheManager *cache_mgr = 
-    qpGeomVertexCacheManager::get_global_ptr();
-  cache_mgr->record_data(this, new_format, new_data->get_num_bytes());
-
   return new_data;
 }
 
@@ -362,28 +307,6 @@ write(ostream &out, int indent_level) const {
   _format->write_with_data(out, indent_level, this);
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexData::clear_cache
-//       Access: Published
-//  Description: Removes all of the previously-cached results of
-//               convert_to().
-////////////////////////////////////////////////////////////////////
-void qpGeomVertexData::
-clear_cache() {
-  // Probably we shouldn't do anything at all here unless we are
-  // running in pipeline stage 0.
-  qpGeomVertexCacheManager *cache_mgr = 
-    qpGeomVertexCacheManager::get_global_ptr();
-
-  CData *cdata = CDWriter(_cycler);
-  for (ConvertedCache::iterator ci = cdata->_converted_cache.begin();
-       ci != cdata->_converted_cache.end();
-       ++ci) {
-    cache_mgr->remove_data(this, (*ci).first);
-  }
-  cdata->_converted_cache.clear();
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexData::set_data
 //       Access: Public
@@ -695,27 +618,6 @@ unpack_argb(float data[4], unsigned int packed_argb) {
   data[3] = (float)((packed_argb >> 24) & 0xff) / 255.0f;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexData::remove_cache_entry
-//       Access: Private
-//  Description: Removes a particular entry from the local cache; it
-//               has already been removed from the cache manager.
-//               This is only called from GeomVertexCacheManager.
-////////////////////////////////////////////////////////////////////
-void qpGeomVertexData::
-remove_cache_entry(const qpGeomVertexFormat *modifier) const {
-  // We have to operate on stage 0 of the pipeline, since that's where
-  // the cache really counts.  Because of the multistage pipeline, we
-  // might not actually have a cache entry there (it might have been
-  // added to stage 1 instead).  No big deal if we don't.
-  CData *cdata = ((qpGeomVertexData *)this)->_cycler.write_stage(0);
-  ConvertedCache::iterator ci = cdata->_converted_cache.find(modifier);
-  if (ci != cdata->_converted_cache.end()) {
-    cdata->_converted_cache.erase(ci);
-  }
-  ((qpGeomVertexData *)this)->_cycler.release_write_stage(0, cdata);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexData::do_set_num_vertices
 //       Access: Private
@@ -728,13 +630,18 @@ do_set_num_vertices(int n, qpGeomVertexData::CDWriter &cdata) {
   bool any_changed = false;
 
   for (size_t i = 0; i < cdata->_arrays.size(); i++) {
-    if (cdata->_arrays[i]->set_num_vertices(n)) {
+    if (cdata->_arrays[i]->get_num_vertices() != n) {
+      // Copy-on-write.
+      if (cdata->_arrays[i]->get_ref_count() > 1) {
+        cdata->_arrays[i] = new qpGeomVertexArrayData(*cdata->_arrays[i]);
+      }
+      cdata->_arrays[i]->set_num_vertices(n);
       any_changed = true;
     }
   }
 
   if (any_changed) {
-    clear_cache();
+    cdata->_modified = qpGeom::get_next_modified();
   }
 
   return any_changed;

+ 2 - 15
panda/src/gobj/qpgeomVertexData.h

@@ -82,14 +82,13 @@ PUBLISHED:
   void set_array(int i, const qpGeomVertexArrayData *array);
 
   int get_num_bytes() const;
+  INLINE UpdateSeq get_modified() const;
 
   CPT(qpGeomVertexData) convert_to(const qpGeomVertexFormat *new_format) const;
 
   void output(ostream &out) const;
   void write(ostream &out, int indent_level = 0) const;
 
-  void clear_cache();
-
 public:
   void set_data(int array, const qpGeomVertexDataType *data_type,
                 int vertex, const float *data, int num_values);
@@ -109,22 +108,12 @@ public:
   static unsigned int pack_argb(const float data[4]);
   static void unpack_argb(float data[4], unsigned int packed_argb);
 
-private:
-  void remove_cache_entry(const qpGeomVertexFormat *modifier) const;
-
 private:
   CPT(qpGeomVertexFormat) _format;
   qpGeomUsageHint::UsageHint _usage_hint;
 
   typedef pvector< PT(qpGeomVertexArrayData) > Arrays;
 
-  // We have to use reference-counting pointers here instead of having
-  // explicit cleanup in the GeomVertexFormat destructor, because the
-  // cache needs to be stored in the CycleData, which makes accurate
-  // cleanup more difficult.  We use the GeomVertexCacheManager class
-  // to avoid cache bloat.
-  typedef pmap<CPT(qpGeomVertexFormat), CPT(qpGeomVertexData) > ConvertedCache;
-
   // This is the data that must be cycled between pipeline stages.
   class EXPCL_PANDA CData : public CycleData {
   public:
@@ -136,7 +125,7 @@ private:
     virtual void fillin(DatagramIterator &scan, BamReader *manager);
 
     Arrays _arrays;
-    ConvertedCache _converted_cache;
+    UpdateSeq _modified;
   };
 
   PipelineCycler<CData> _cycler;
@@ -172,8 +161,6 @@ public:
 
 private:
   static TypeHandle _type_handle;
-
-  friend class qpGeomVertexCacheManager;
 };
 
 INLINE ostream &operator << (ostream &out, const qpGeomVertexData &obj);

+ 69 - 11
panda/src/pgraph/renderState.I

@@ -155,21 +155,79 @@ get_bin_index() const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: RenderState::is_flat_shaded
+//     Function: RenderState::get_color
 //       Access: Published
-//  Description: Returns true if this RenderState specifies a flat
-//               shading model (that is, per-vertex color applies to
-//               entire triangles), or false if it specifies a smooth
-//               shading model or does not specify.
+//  Description: This function is provided as an optimization, to
+//               speed up the render-time checking for the existance
+//               of a ColorAttrib on this state.  It returns a
+//               pointer to the ColorAttrib, if there is one, or
+//               NULL if there is not.
 ////////////////////////////////////////////////////////////////////
-INLINE bool RenderState::
-is_flat_shaded() const {
-  if ((_flags & F_checked_flat_shaded) == 0) {
+INLINE const ColorAttrib *RenderState::
+get_color() const {
+  if ((_flags & F_checked_color) == 0) {
+    // We pretend this function is const, even though it transparently
+    // modifies the internal color cache.
+    ((RenderState *)this)->determine_color();
+  }
+  return _color;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::get_color_scale
+//       Access: Published
+//  Description: This function is provided as an optimization, to
+//               speed up the render-time checking for the existance
+//               of a ColorScaleAttrib on this state.  It returns a
+//               pointer to the ColorScaleAttrib, if there is one, or
+//               NULL if there is not.
+////////////////////////////////////////////////////////////////////
+INLINE const ColorScaleAttrib *RenderState::
+get_color_scale() const {
+  if ((_flags & F_checked_color_scale) == 0) {
+    // We pretend this function is const, even though it transparently
+    // modifies the internal color_scale cache.
+    ((RenderState *)this)->determine_color_scale();
+  }
+  return _color_scale;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::get_texture
+//       Access: Published
+//  Description: This function is provided as an optimization, to
+//               speed up the render-time checking for the existance
+//               of a TextureAttrib on this state.  It returns a
+//               pointer to the TextureAttrib, if there is one, or
+//               NULL if there is not.
+////////////////////////////////////////////////////////////////////
+INLINE const TextureAttrib *RenderState::
+get_texture() const {
+  if ((_flags & F_checked_texture) == 0) {
+    // We pretend this function is const, even though it transparently
+    // modifies the internal texture cache.
+    ((RenderState *)this)->determine_texture();
+  }
+  return _texture;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::get_tex_gen
+//       Access: Published
+//  Description: This function is provided as an optimization, to
+//               speed up the render-time checking for the existance
+//               of a TexGenAttrib on this state.  It returns a
+//               pointer to the TexGenAttrib, if there is one, or
+//               NULL if there is not.
+////////////////////////////////////////////////////////////////////
+INLINE const TexGenAttrib *RenderState::
+get_tex_gen() const {
+  if ((_flags & F_checked_tex_gen) == 0) {
     // We pretend this function is const, even though it transparently
-    // modifies the internal flat_shaded cache.
-    ((RenderState *)this)->determine_flat_shaded();
+    // modifies the internal tex_gen cache.
+    ((RenderState *)this)->determine_tex_gen();
   }
-  return ((_flags & F_flat_shaded) != 0);
+  return _tex_gen;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 55 - 12
panda/src/pgraph/renderState.cxx

@@ -22,7 +22,10 @@
 #include "cullBinManager.h"
 #include "fogAttrib.h"
 #include "transparencyAttrib.h"
-#include "shadeModelAttrib.h"
+#include "colorAttrib.h"
+#include "colorScaleAttrib.h"
+#include "textureAttrib.h"
+#include "texGenAttrib.h"
 #include "pStatTimer.h"
 #include "config_pgraph.h"
 #include "bamReader.h"
@@ -1513,23 +1516,63 @@ determine_transparency() {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: RenderState::determine_flat_shaded
+//     Function: RenderState::determine_color
 //       Access: Private
-//  Description: This is the private implementation of
-//               is_flat_shaded().
+//  Description: This is the private implementation of get_color().
 ////////////////////////////////////////////////////////////////////
 void RenderState::
-determine_flat_shaded() {
-  const RenderAttrib *attrib = 
-    get_attrib(ShadeModelAttrib::get_class_type());
+determine_color() {
+  const RenderAttrib *attrib = get_attrib(ColorAttrib::get_class_type());
+  _color = (const ColorAttrib *)NULL;
   if (attrib != (const RenderAttrib *)NULL) {
-    const ShadeModelAttrib *sma = DCAST(ShadeModelAttrib, attrib);
-    if (sma->get_mode() == ShadeModelAttrib::M_flat) {
-      _flags |= F_flat_shaded;
-    }
+    _color = DCAST(ColorAttrib, attrib);
+  }
+  _flags |= F_checked_color;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::determine_color_scale
+//       Access: Private
+//  Description: This is the private implementation of get_color_scale().
+////////////////////////////////////////////////////////////////////
+void RenderState::
+determine_color_scale() {
+  const RenderAttrib *attrib = get_attrib(ColorScaleAttrib::get_class_type());
+  _color_scale = (const ColorScaleAttrib *)NULL;
+  if (attrib != (const RenderAttrib *)NULL) {
+    _color_scale = DCAST(ColorScaleAttrib, attrib);
+  }
+  _flags |= F_checked_color_scale;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::determine_texture
+//       Access: Private
+//  Description: This is the private implementation of get_texture().
+////////////////////////////////////////////////////////////////////
+void RenderState::
+determine_texture() {
+  const RenderAttrib *attrib = get_attrib(TextureAttrib::get_class_type());
+  _texture = (const TextureAttrib *)NULL;
+  if (attrib != (const RenderAttrib *)NULL) {
+    _texture = DCAST(TextureAttrib, attrib);
   }
+  _flags |= F_checked_texture;
+}
 
-  _flags |= F_checked_flat_shaded;
+////////////////////////////////////////////////////////////////////
+//     Function: RenderState::determine_tex_gen
+//       Access: Private
+//  Description: This is the private implementation of get_tex_gen().
+////////////////////////////////////////////////////////////////////
+void RenderState::
+determine_tex_gen() {
+  const RenderAttrib *attrib = get_attrib(TexGenAttrib::get_class_type());
+  _tex_gen = (const TexGenAttrib *)NULL;
+  if (attrib != (const RenderAttrib *)NULL) {
+    _tex_gen = DCAST(TexGenAttrib, attrib);
+  }
+  _flags |= F_checked_tex_gen;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 20 - 4
panda/src/pgraph/renderState.h

@@ -32,6 +32,10 @@ class GraphicsStateGuardianBase;
 class FogAttrib;
 class CullBinAttrib;
 class TransparencyAttrib;
+class ColorAttrib;
+class ColorScaleAttrib;
+class TextureAttrib;
+class TexGenAttrib;
 class FactoryParams;
 
 ////////////////////////////////////////////////////////////////////
@@ -114,7 +118,10 @@ PUBLISHED:
   INLINE const CullBinAttrib *get_bin() const;
   INLINE const TransparencyAttrib *get_transparency() const;
   INLINE int get_bin_index() const;
-  INLINE bool is_flat_shaded() const;
+  INLINE const ColorAttrib *get_color() const;
+  INLINE const ColorScaleAttrib *get_color_scale() const;
+  INLINE const TextureAttrib *get_texture() const;
+  INLINE const TexGenAttrib *get_tex_gen() const;
 
 public:
   CPT(RenderState) issue_delta_modify(const RenderState *other, 
@@ -151,7 +158,10 @@ private:
   void determine_fog();
   void determine_bin();
   void determine_transparency();
-  void determine_flat_shaded();
+  void determine_color();
+  void determine_color_scale();
+  void determine_texture();
+  void determine_tex_gen();
 
   INLINE void set_destructing();
   INLINE bool is_destructing() const;
@@ -225,14 +235,20 @@ private:
   const FogAttrib *_fog;
   const CullBinAttrib *_bin;
   const TransparencyAttrib *_transparency;
+  const ColorAttrib *_color;
+  const ColorScaleAttrib *_color_scale;
+  const TextureAttrib *_texture;
+  const TexGenAttrib *_tex_gen;
 
   enum Flags {
     F_checked_bin_index    = 0x0001,
     F_checked_fog          = 0x0002,
     F_checked_bin          = 0x0004,
     F_checked_transparency = 0x0008,
-    F_checked_flat_shaded  = 0x0010,
-    F_flat_shaded          = 0x0020,
+    F_checked_color        = 0x0010,
+    F_checked_color_scale  = 0x0020,
+    F_checked_texture      = 0x0040,
+    F_checked_tex_gen      = 0x0080,
     F_is_destructing       = 0x8000,
   };
   unsigned short _flags;

+ 25 - 11
panda/src/putil/updateSeq.I

@@ -67,8 +67,9 @@ fresh() {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE UpdateSeq::
-UpdateSeq(const UpdateSeq &copy) {
-  _seq = copy._seq;
+UpdateSeq(const UpdateSeq &copy) :
+  _seq(copy._seq)
+{
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -132,15 +133,8 @@ is_fresh() const {
 ////////////////////////////////////////////////////////////////////
 INLINE bool UpdateSeq::
 is_special() const {
-  switch (_seq) {
-  case (unsigned int)SC_initial:
-  case (unsigned int)SC_old:
-  case (unsigned int)SC_fresh:
-    return true;
-
-  default:
-    return false;
-  }
+  // This relies on the assumption that (~0 + 1) == 0.
+  return ((_seq + 1) <= 2);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -190,6 +184,26 @@ operator <= (const UpdateSeq &other) const {
   return (*this) == other || (*this) < other;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: UpdateSeq::Comparison operator
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool UpdateSeq::
+operator > (const UpdateSeq &other) const {
+  return (other < (*this));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UpdateSeq::Comparison operator
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool UpdateSeq::
+operator >= (const UpdateSeq &other) const {
+  return (other <= (*this));
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: UpdateSeq::Preincrement operator
 //       Access: Published

+ 3 - 1
panda/src/putil/updateSeq.h

@@ -61,6 +61,8 @@ PUBLISHED:
   INLINE bool operator != (const UpdateSeq &other) const;
   INLINE bool operator < (const UpdateSeq &other) const;
   INLINE bool operator <= (const UpdateSeq &other) const;
+  INLINE bool operator > (const UpdateSeq &other) const;
+  INLINE bool operator >= (const UpdateSeq &other) const;
 
   INLINE UpdateSeq operator ++ ();
   INLINE UpdateSeq operator ++ (int);
@@ -71,7 +73,7 @@ private:
   enum SpecialCases {
     SC_initial = 0,
     SC_old = 1,
-    SC_fresh = ~0,
+    SC_fresh = ~(unsigned int)0,
   };
 
   unsigned int _seq;