Browse Source

add ReferenceCount::local_object(), clarify GeomVertexCache

David Rose 20 years ago
parent
commit
05ba41b4d6

+ 22 - 0
panda/src/display/graphicsStateGuardian.I

@@ -62,6 +62,28 @@ release_all_geoms() {
   return _prepared_objects->release_all_geoms();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::release_all_vertex_buffers
+//       Access: Public
+//  Description: Frees the resources for all vertex buffers associated
+//               with this GSG.
+////////////////////////////////////////////////////////////////////
+INLINE int GraphicsStateGuardian::
+release_all_vertex_buffers() {
+  return _prepared_objects->release_all_vertex_buffers();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::release_all_index_buffers
+//       Access: Public
+//  Description: Frees the resources for all index buffers associated
+//               with this GSG.
+////////////////////////////////////////////////////////////////////
+INLINE int GraphicsStateGuardian::
+release_all_index_buffers() {
+  return _prepared_objects->release_all_index_buffers();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::set_active
 //       Access: Published

+ 2 - 0
panda/src/display/graphicsStateGuardian.cxx

@@ -1511,6 +1511,8 @@ close_gsg() {
   if (_prepared_objects->get_ref_count() == 1) {
     release_all_textures();
     release_all_geoms();
+    release_all_vertex_buffers();
+    release_all_index_buffers();
   }
 }
 

+ 2 - 0
panda/src/display/graphicsStateGuardian.h

@@ -74,6 +74,8 @@ public:
 PUBLISHED:
   INLINE int release_all_textures();
   INLINE int release_all_geoms();
+  INLINE int release_all_vertex_buffers();
+  INLINE int release_all_index_buffers();
 
   INLINE void set_active(bool active);
   INLINE bool is_active() const;

+ 1 - 1
panda/src/display/graphicsThreadingModel.cxx

@@ -59,7 +59,7 @@ GraphicsThreadingModel(const string &model) {
 
   size_t slash = model.find('/', start);
   if (slash == string::npos) {
-    _cull_name = model;
+    _cull_name = model.substr(start);
   } else {
     _cull_name = model.substr(start, slash - start);
     _draw_name = model.substr(slash + 1);

+ 53 - 48
panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx

@@ -3197,44 +3197,51 @@ release_texture(TextureContext *tc) {
 VertexBufferContext *DXGraphicsStateGuardian8::
 prepare_vertex_buffer(qpGeomVertexArrayData *data) {
   DXVertexBufferContext8 *dvbc = new DXVertexBufferContext8(data);
-
-  if (vertex_buffers && data->get_usage_hint() != qpGeom::UH_client) {
-    dvbc->create_vbuffer(*_pScrn);
-
-    if (dxgsg8_cat.is_debug()) {
-      dxgsg8_cat.debug()
-        << "creating vertex buffer " << dvbc->_vbuffer << ": "
-        << data->get_num_rows() << " vertices " 
-        << *data->get_array_format() << "\n";
-    }
-  }
-
   return dvbc;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DXGraphicsStateGuardian8::apply_vertex_buffer
 //       Access: Public
-//  Description: Makes the data the currently available data for
-//               rendering.
+//  Description: Updates the vertex buffer with the current data, and
+//               makes it the current vertex buffer for rendering.
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian8::
 apply_vertex_buffer(VertexBufferContext *vbc) {
   DXVertexBufferContext8 *dvbc = DCAST(DXVertexBufferContext8, vbc);
 
-  if (dvbc->_vbuffer != NULL) {
+  if (dvbc->_vbuffer == NULL) {
+    // Attempt to create a new vertex buffer.
+    if (vertex_buffers && 
+        dvbc->get_data()->get_usage_hint() != qpGeom::UH_client) {
+      dvbc->create_vbuffer(*_pScrn);
+    }
+
+    if (dvbc->_vbuffer != NULL) {
+      dvbc->upload_data();
+      
+      add_to_vertex_buffer_record(dvbc);
+      add_to_total_buffer_record(dvbc);
+      dvbc->mark_loaded();
+
+      _pD3DDevice->SetStreamSource
+        (0, dvbc->_vbuffer, dvbc->get_data()->get_array_format()->get_stride());
+      _vbuffer_active = true;
+    } else {
+      _vbuffer_active = false;
+    }
+
+  } else {
     add_to_vertex_buffer_record(dvbc);
   
     if (dvbc->was_modified()) {
       if (dvbc->changed_size()) {
-        // Here we have to destroy the old vertex buffer and create a
-        // new one.
+        // We have to destroy the old vertex buffer and create a new
+        // one.
         dvbc->create_vbuffer(*_pScrn);
-
-      } else {
-        // Here we just copy the new data to the vertex buffer.
-        dvbc->upload_data();
       }
+
+      dvbc->upload_data();
       
       add_to_total_buffer_record(dvbc);
       dvbc->mark_loaded();
@@ -3243,9 +3250,6 @@ apply_vertex_buffer(VertexBufferContext *vbc) {
     _pD3DDevice->SetStreamSource
       (0, dvbc->_vbuffer, dvbc->get_data()->get_array_format()->get_stride());
     _vbuffer_active = true;
-
-  } else {
-    _vbuffer_active = false;
   }
 
   set_vertex_format(dvbc->_fvf);
@@ -3281,43 +3285,48 @@ release_vertex_buffer(VertexBufferContext *vbc) {
 IndexBufferContext *DXGraphicsStateGuardian8::
 prepare_index_buffer(qpGeomPrimitive *data) {
   DXIndexBufferContext8 *dibc = new DXIndexBufferContext8(data);
-
-  dibc->create_ibuffer(*_pScrn);
-
-  if (dxgsg8_cat.is_debug()) {
-    dxgsg8_cat.debug()
-      << "creating index buffer " << dibc->_ibuffer << ": "
-      << data->get_num_vertices() << " indices (" 
-      << data->get_vertices()->get_array_format()->get_column(0)->get_numeric_type()
-      << ")\n";
-  }
-
   return dibc;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DXGraphicsStateGuardian8::apply_index_buffer
 //       Access: Public
-//  Description: Makes the data the currently available data for
-//               rendering.
+//  Description: Updates the index buffer with the current data, and
+//               makes it the current index buffer for rendering.
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian8::
 apply_index_buffer(IndexBufferContext *ibc) {
   DXIndexBufferContext8 *dibc = DCAST(DXIndexBufferContext8, ibc);
 
-  if (dibc->_ibuffer != NULL) {
+  if (dibc->_ibuffer == NULL) {
+    // Attempt to create a new index buffer.
+    dibc->create_ibuffer(*_pScrn);
+
+    if (dibc->_ibuffer != NULL) {
+      dibc->upload_data();
+      add_to_index_buffer_record(dibc);
+      add_to_total_buffer_record(dibc);
+      dibc->mark_loaded();
+
+      _pD3DDevice->SetIndices(dibc->_ibuffer, 0);
+      _ibuffer_active = true;
+    } else {
+
+      _pD3DDevice->SetIndices(NULL, 0);
+      _ibuffer_active = false;
+    }
+
+  } else {
     add_to_index_buffer_record(dibc);
   
     if (dibc->was_modified()) {
       if (dibc->changed_size()) {
-        // Here we have to destroy the old index buffer and create a
-        // new one.
+        // We have to destroy the old index buffer and create a new
+        // one.
         dibc->create_ibuffer(*_pScrn);
-
-      } else {
-        // Here we just copy the new data to the index buffer.
-        dibc->upload_data();
       }
+
+      dibc->upload_data();
       
       add_to_total_buffer_record(dibc);
       dibc->mark_loaded();
@@ -3325,10 +3334,6 @@ apply_index_buffer(IndexBufferContext *ibc) {
 
     _pD3DDevice->SetIndices(dibc->_ibuffer, 0);
     _ibuffer_active = true;
-
-  } else {
-    _pD3DDevice->SetIndices(NULL, 0);
-    _ibuffer_active = false;
   }
 }
 

+ 9 - 3
panda/src/dxgsg8/dxIndexBufferContext8.cxx

@@ -58,7 +58,8 @@ DXIndexBufferContext8::
 ////////////////////////////////////////////////////////////////////
 //     Function: DXIndexBufferContext8::create_ibuffer
 //       Access: Public
-//  Description: Creates a new index buffer and uploads data to it.
+//  Description: Creates a new index buffer (but does not upload data
+//               to it).
 ////////////////////////////////////////////////////////////////////
 void DXIndexBufferContext8::
 create_ibuffer(DXScreenData &scrn) {
@@ -77,9 +78,14 @@ create_ibuffer(DXScreenData &scrn) {
     dxgsg8_cat.warning()
       << "CreateIndexBuffer failed" << D3DERRORSTRING(hr);
     _ibuffer = NULL;
-    
   } else {
-    upload_data();
+    if (dxgsg8_cat.is_debug()) {
+      dxgsg8_cat.debug()
+        << "creating index buffer " << _ibuffer << ": "
+        << get_data()->get_num_vertices() << " indices (" 
+        << get_data()->get_vertices()->get_array_format()->get_column(0)->get_numeric_type()
+        << ")\n";
+    }
   }
 }
 

+ 8 - 3
panda/src/dxgsg8/dxVertexBufferContext8.cxx

@@ -153,7 +153,8 @@ DXVertexBufferContext8::
 ////////////////////////////////////////////////////////////////////
 //     Function: DXVertexBufferContext8::create_vbuffer
 //       Access: Public
-//  Description: Creates a new vertex buffer and uploads data to it.
+//  Description: Creates a new vertex buffer (but does not upload data
+//               to it).
 ////////////////////////////////////////////////////////////////////
 void DXVertexBufferContext8::
 create_vbuffer(DXScreenData &scrn) {
@@ -169,9 +170,13 @@ create_vbuffer(DXScreenData &scrn) {
     dxgsg8_cat.warning()
       << "CreateVertexBuffer failed" << D3DERRORSTRING(hr);
     _vbuffer = NULL;
-    
   } else {
-    upload_data();
+    if (dxgsg8_cat.is_debug()) {
+      dxgsg8_cat.debug()
+        << "created vertex buffer " << _vbuffer << ": "
+        << get_data()->get_num_rows() << " vertices " 
+        << *get_data()->get_array_format() << "\n";
+    }
   }
 }
 

+ 44 - 14
panda/src/express/referenceCount.I

@@ -87,7 +87,7 @@ operator = (const ReferenceCount &) {
   // instance of a class that derives from ReferenceCount.  Or maybe
   // your headers are out of sync, and you need to make clean in
   // direct or some higher tree.
-  nassertv(_ref_count != -100);
+  nassertv(_ref_count != deleted_ref_count);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -112,7 +112,13 @@ INLINE ReferenceCount::
   // automatic (local variable) instance of a class that derives from
   // ReferenceCount.  Or maybe your headers are out of sync, and you
   // need to make clean in direct or some higher tree.
-  nassertv(_ref_count != -100);
+  nassertv(_ref_count != deleted_ref_count);
+
+  // If this assertion fails, we're trying to delete a static object
+  // that still has an outstanding reference count.  You should make
+  // sure that all references to your static objects are gone by the
+  // time the object itself destructs.
+  nassertv(_ref_count <= local_ref_count);
 
   // If this assertion fails, the reference counts are all screwed
   // up altogether.  Maybe some errant code stomped all over memory
@@ -129,7 +135,7 @@ INLINE ReferenceCount::
   // constructor for a ReferenceCount object, and then bitwise
   // copied a dynamically allocated value--reference count and
   // all--onto a locally allocated one.
-  nassertv(_ref_count == 0);
+  nassertv(_ref_count == 0 || _ref_count == local_ref_count);
 
   // Tell our weak reference holders that we're going away now.
   if (_weak_list != (WeakReferenceList *)NULL) {
@@ -138,10 +144,10 @@ INLINE ReferenceCount::
   }
 
 #ifndef NDEBUG
-  // Ok, all clear to delete.  Now set the reference count to -100,
-  // so we'll have a better chance of noticing if we happen to have
-  // a stray pointer to it still out there.
-  _ref_count = -100;
+  // Ok, all clear to delete.  Now set the reference count to
+  // deleted_ref_count, so we'll have a better chance of noticing if
+  // we happen to have a stray pointer to it still out there.
+  _ref_count = deleted_ref_count;
 #endif
 
 #ifdef DO_MEMORY_USAGE
@@ -151,7 +157,7 @@ INLINE ReferenceCount::
 
 ////////////////////////////////////////////////////////////////////
 //     Function: ReferenceCount::get_ref_count
-//       Access: Public
+//       Access: Published
 //  Description: Returns the current reference count.
 ////////////////////////////////////////////////////////////////////
 INLINE int ReferenceCount::
@@ -164,7 +170,7 @@ get_ref_count() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: ReferenceCount::ref
-//       Access: Public
+//       Access: Published
 //  Description: Explicitly increments the reference count.  User code
 //               should avoid using ref() and unref() directly, which
 //               can result in missed reference counts.  Instead, let
@@ -191,7 +197,7 @@ ref() const {
   // automatic (local variable) instance of a class that derives from
   // ReferenceCount.  Or maybe your headers are out of sync, and you
   // need to make clean in direct or some higher tree.
-  nassertr(_ref_count != -100, 0);
+  nassertr(_ref_count != deleted_ref_count, 0);
 
   // If this assertion fails, the reference counts are all screwed
   // up altogether.  Maybe some errant code stomped all over memory
@@ -203,7 +209,7 @@ ref() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: ReferenceCount::unref
-//       Access: Public
+//       Access: Published
 //  Description: Explicitly decrements the reference count.  Note that
 //               the object will not be implicitly deleted by unref()
 //               simply because the reference count drops to zero.
@@ -237,7 +243,7 @@ unref() const {
   // automatic (local variable) instance of a class that derives from
   // ReferenceCount.  Or maybe your headers are out of sync, and you
   // need to make clean in direct or some higher tree.
-  nassertr(_ref_count != -100, false);
+  nassertr(_ref_count != deleted_ref_count, false);
 
   // If this assertion fails, the reference counts are all screwed
   // up altogether.  Maybe some errant code stomped all over memory
@@ -254,7 +260,7 @@ unref() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: ReferenceCount::test_ref_count_integrity
-//       Access: Public
+//       Access: Published
 //  Description: Does some easy checks to make sure that the reference
 //               count isn't completely bogus.
 ////////////////////////////////////////////////////////////////////
@@ -270,7 +276,7 @@ test_ref_count_integrity() const {
   // automatic (local variable) instance of a class that derives from
   // ReferenceCount.  Or maybe your headers are out of sync, and you
   // need to make clean in direct or some higher tree.
-  nassertv(_ref_count != -100);
+  nassertv(_ref_count != deleted_ref_count);
 
   // If this assertion fails, the reference counts are all screwed
   // up altogether.  Maybe some errant code stomped all over memory
@@ -279,6 +285,30 @@ test_ref_count_integrity() const {
 #endif
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: ReferenceCount::local_object
+//       Access: Public
+//  Description: This function should be called, once, immediately
+//               after creating a new instance of some
+//               ReferenceCount-derived object on the stack.
+//
+//               This allows the object to be passed to functions that
+//               will increment and decrement the object's reference
+//               count temporarily, and it will prevent the object
+//               from being deleted (inappropriately), when the
+//               reference count returns to zero.  It actually
+//               achieves this by setting a large positive value in
+//               the reference count field.
+////////////////////////////////////////////////////////////////////
+INLINE void ReferenceCount::
+local_object() {
+  // If this assertion fails, you didn't call this immediately after
+  // creating a local object.
+  nassertv(_ref_count == 0);
+
+  _ref_count = local_ref_count;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: ReferenceCount::has_weak_list
 //       Access: Public

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

@@ -54,6 +54,7 @@ PUBLISHED:
   INLINE void test_ref_count_integrity() const;
 
 public:
+  INLINE void local_object();
   INLINE bool has_weak_list() const;
   INLINE WeakReferenceList *get_weak_list() const;
 
@@ -61,6 +62,22 @@ public:
   INLINE void weak_unref(WeakPointerToVoid *ptv);
 
 private:
+  enum { 
+    // We use this value as a flag to indicate an object has been
+    // indicated as a local object, and should not be deleted except
+    // by its own destructor.  Really, any nonzero value would do, but
+    // having a large specific number makes the sanity checks easier.
+    local_ref_count = 10000000,
+
+    // This value is used as a flag to indicate that an object has
+    // just been deleted, and you're looking at deallocated memory.
+    // It's not guaranteed to stick around, of course (since the
+    // deleted memory might be repurposed for anything else, including
+    // a new object), but if you ever do encounter this value in a
+    // reference count field, you screwed up.
+    deleted_ref_count = -100,
+  };
+
   int _ref_count;
   WeakReferenceList *_weak_list;
 
@@ -137,8 +154,6 @@ private:
   static TypeHandle _type_handle;
 };
 
-
-
 #include "referenceCount.I"
 
 #endif

+ 13 - 8
panda/src/gobj/config_gobj.cxx

@@ -206,14 +206,19 @@ ConfigVariableString fake_texture_image
           "the same texture file, which will presumably only be loaded "
           "once."));
 
-ConfigVariableInt vertex_convert_cache
-("vertex-convert-cache", 4194304, // 4 MB
- PRC_DESC("This is the amount of memory, in bytes, that should be set "
-          "aside for storing pre-processed data for rendering vertices.  "
-          "This is not a limit on the actual vertex data, which is "
-          "determined by the model; it is also not a limit on the "
-          "amount of memory used by the video driver or the system "
-          "graphics interface, which Panda has no control over."));
+ConfigVariableInt geom_cache_size
+("geom-cache-size", 5000,
+ PRC_DESC("Specifies the maximum number of entries in the cache "
+          "for storing pre-processed data for rendering "
+          "vertices.  This limit is flexible, and may be "
+          "temporarily exceeded if many different Geoms are "
+          "pre-processed during the space of a single frame."));
+
+ConfigVariableInt geom_cache_min_frames
+("geom-cache-min-frames", 1,
+ PRC_DESC("Specifies the minimum number of frames any one particular "
+          "object will remain in the geom cache, even if geom-cache-size "
+          "is exceeded."));
 
 ConfigVariableDouble default_near
 ("default-near", 1.0,

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

@@ -66,7 +66,8 @@ extern EXPCL_PANDA ConfigVariableEnum<AutoTextureScale> textures_power_2;
 extern EXPCL_PANDA ConfigVariableEnum<AutoTextureScale> textures_square;
 extern EXPCL_PANDA ConfigVariableString fake_texture_image;
 
-extern EXPCL_PANDA ConfigVariableInt vertex_convert_cache;
+extern EXPCL_PANDA ConfigVariableInt geom_cache_size;
+extern EXPCL_PANDA ConfigVariableInt geom_cache_min_frames;
 
 extern ConfigVariableDouble default_near;
 extern ConfigVariableDouble default_far;

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

@@ -187,11 +187,34 @@ get_modified() const {
 ////////////////////////////////////////////////////////////////////
 INLINE qpGeom::CacheEntry::
 CacheEntry(const qpGeomVertexData *source_data, const qpGeomMunger *modifier) :
+  _source(NULL),
   _source_data(source_data),
-  _modifier(modifier)
+  _modifier(modifier),
+  _geom_result(NULL),
+  _data_result(NULL)
 {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeom::CacheEntry::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE qpGeom::CacheEntry::
+CacheEntry(qpGeom *source, const qpGeomVertexData *source_data,
+           const qpGeomMunger *modifier, const qpGeom *geom_result, 
+           const qpGeomVertexData *data_result) :
+  _source(source),
+  _source_data(source_data),
+  _modifier(modifier),
+  _geom_result(geom_result),
+  _data_result(data_result)
+{
+  if (_geom_result != _source) {
+    _geom_result->ref();
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeom::CacheEntry::operator <
 //       Access: Public

+ 15 - 18
panda/src/gobj/qpgeom.cxx

@@ -469,7 +469,7 @@ munge_geom(const qpGeomMunger *munger,
   {
     CDReader cdata(_cycler);
     CacheEntry temp_entry(source_data, munger);
-    temp_entry.ref();  // big ugly hack to allow a stack-allocated ReferenceCount object.
+    temp_entry.local_object();
     Cache::const_iterator ci = cdata->_cache.find(&temp_entry);
     if (ci != cdata->_cache.end()) {
       CacheEntry *entry = (*ci);
@@ -483,7 +483,6 @@ munge_geom(const qpGeomMunger *munger,
         entry->refresh();
         result = entry->_geom_result;
         data = entry->_data_result;
-        temp_entry.unref();
         return;
       }
 
@@ -496,7 +495,6 @@ munge_geom(const qpGeomMunger *munger,
       CDWriter cdataw(((qpGeom *)this)->_cycler, cdata);
       cdataw->_cache.erase(entry);
     }
-    temp_entry.unref();
   }
 
   // Ok, invoke the munger.
@@ -513,10 +511,8 @@ munge_geom(const qpGeomMunger *munger,
     CacheEntry *entry;
     {
       CDWriter cdata(((qpGeom *)this)->_cycler);
-      entry = new CacheEntry(source_data, munger);
-      entry->_source = (qpGeom *)this; 
-      entry->_geom_result = result;
-      entry->_data_result = data;
+      entry = new CacheEntry((qpGeom *)this, source_data, munger,
+                             result, data);
       bool inserted = cdata->_cache.insert(entry).second;
       nassertv(inserted);
     }
@@ -880,6 +876,18 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   manager->read_cdata(scan, _cycler);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeom::CacheEntry::Destructor
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+qpGeom::CacheEntry::
+~CacheEntry() {
+  if (_geom_result != _source) {
+    unref_delete(_geom_result);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeom::CacheEntry::evict_callback
 //       Access: Public, Virtual
@@ -900,17 +908,6 @@ evict_callback() {
   _source->_cycler.release_write_stage(0, cdata);
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeom::CacheEntry::get_result_size
-//       Access: Public, Virtual
-//  Description: Returns the approximate number of bytes represented
-//               by the computed result.
-////////////////////////////////////////////////////////////////////
-int qpGeom::CacheEntry::
-get_result_size() const {
-  return _geom_result->get_num_bytes() + _data_result->get_num_bytes();
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeom::CacheEntry::output
 //       Access: Public, Virtual

+ 9 - 4
panda/src/gobj/qpgeom.h

@@ -134,22 +134,27 @@ private:
   // 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.
+  // cleanup more difficult.  We use the GeomCacheManager class to
+  // avoid cache bloat.
   class CacheEntry : public qpGeomCacheEntry {
   public:
     INLINE CacheEntry(const qpGeomVertexData *source_data,
                       const qpGeomMunger *modifier);
+    INLINE CacheEntry(qpGeom *source, 
+                      const qpGeomVertexData *source_data,
+                      const qpGeomMunger *modifier, 
+                      const qpGeom *geom_result, 
+                      const qpGeomVertexData *data_result);
+    virtual ~CacheEntry();
     INLINE bool operator < (const CacheEntry &other) const;
 
     virtual void evict_callback();
-    virtual int get_result_size() const;
     virtual void output(ostream &out) const;
 
     qpGeom *_source;
     CPT(qpGeomVertexData) _source_data;
     CPT(qpGeomMunger) _modifier;
-    CPT(qpGeom) _geom_result;
+    const qpGeom *_geom_result;  // ref-counted if not same as _source
     CPT(qpGeomVertexData) _data_result;
   };
   typedef pset<PT(CacheEntry), IndirectLess<CacheEntry> > Cache;

+ 0 - 21
panda/src/gobj/qpgeomCacheEntry.I

@@ -30,27 +30,6 @@ qpGeomCacheEntry() {
 #endif
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomCacheEntry::refresh
-//       Access: Public
-//  Description: Marks the cache entry recently used, so it will not
-//               be evicted for a while.
-////////////////////////////////////////////////////////////////////
-INLINE void qpGeomCacheEntry::
-refresh() {
-  nassertv(_next != (qpGeomCacheEntry *)NULL && _prev != (qpGeomCacheEntry *)NULL);
-
-  qpGeomCacheManager *cache_mgr = qpGeomCacheManager::get_global_ptr();
-  MutexHolder holder(cache_mgr->_lock);
-
-  remove_from_list();
-  insert_before(cache_mgr->_list);
-
-  int new_size = get_result_size();
-  cache_mgr->_total_size += (new_size - _result_size);
-  _result_size = new_size;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomCacheEntry::remove_from_list
 //       Access: Private

+ 24 - 16
panda/src/gobj/qpgeomCacheEntry.cxx

@@ -20,6 +20,7 @@
 #include "qpgeomCacheManager.h"
 #include "mutexHolder.h"
 #include "config_gobj.h"
+#include "clockObject.h"
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomCacheEntry::Destructor
@@ -41,21 +42,20 @@ record() {
   nassertr(_next == (qpGeomCacheEntry *)NULL && _prev == (qpGeomCacheEntry *)NULL, NULL);
   PT(qpGeomCacheEntry) keepme = this;
 
-  _result_size = get_result_size();
-
   qpGeomCacheManager *cache_mgr = qpGeomCacheManager::get_global_ptr();
   MutexHolder holder(cache_mgr->_lock);
 
   if (gobj_cat.is_debug()) {
     gobj_cat.debug()
       << "recording cache entry: " << *this << ", total_size = "
-      << cache_mgr->_total_size + _result_size << "\n";
+      << cache_mgr->_total_size + 1 << "\n";
   }
 
   insert_before(cache_mgr->_list);
-  cache_mgr->_total_size += _result_size;
+  ++cache_mgr->_total_size;
   cache_mgr->_geom_cache_size_pcollector.set_level(cache_mgr->_total_size);
   cache_mgr->_geom_cache_record_pcollector.add_level(1);
+  _last_frame_used = ClockObject::get_global_clock()->get_frame_count();
 
   // Increment our own reference count while we're in the queue, just
   // so we don't have to play games with it later--this is inner-loop
@@ -70,6 +70,25 @@ record() {
   return this;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomCacheEntry::refresh
+//       Access: Public
+//  Description: Marks the cache entry recently used, so it will not
+//               be evicted for a while.
+////////////////////////////////////////////////////////////////////
+void qpGeomCacheEntry::
+refresh() {
+  nassertv(_next != (qpGeomCacheEntry *)NULL && _prev != (qpGeomCacheEntry *)NULL);
+
+  qpGeomCacheManager *cache_mgr = qpGeomCacheManager::get_global_ptr();
+  MutexHolder holder(cache_mgr->_lock);
+
+  remove_from_list();
+  insert_before(cache_mgr->_list);
+
+  _last_frame_used = ClockObject::get_global_clock()->get_frame_count();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomCacheEntry::erase
 //       Access: Public
@@ -92,7 +111,7 @@ erase() {
   MutexHolder holder(cache_mgr->_lock);
 
   remove_from_list();
-  cache_mgr->_total_size -= _result_size;
+  --cache_mgr->_total_size;
   cache_mgr->_geom_cache_size_pcollector.set_level(cache_mgr->_total_size);
   cache_mgr->_geom_cache_erase_pcollector.add_level(1);
 
@@ -109,17 +128,6 @@ void qpGeomCacheEntry::
 evict_callback() {
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomCacheEntry::get_result_size
-//       Access: Public, Virtual
-//  Description: Returns the approximate number of bytes represented
-//               by the computed result.
-////////////////////////////////////////////////////////////////////
-int qpGeomCacheEntry::
-get_result_size() const {
-  return 0;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomCacheEntry::output
 //       Access: Public, Virtual

+ 2 - 3
panda/src/gobj/qpgeomCacheEntry.h

@@ -43,15 +43,14 @@ public:
   virtual ~qpGeomCacheEntry();
 
   PT(qpGeomCacheEntry) record();
-  INLINE void refresh();
+  void refresh();
   PT(qpGeomCacheEntry) erase();
 
   virtual void evict_callback();
-  virtual int get_result_size() const;
   virtual void output(ostream &out) const;
 
 private:
-  int _result_size;
+  int _last_frame_used;
 
   INLINE void remove_from_list();
   INLINE void insert_before(qpGeomCacheEntry *node);

+ 16 - 14
panda/src/gobj/qpgeomCacheManager.I

@@ -20,37 +20,39 @@
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomCacheManager::set_max_size
 //       Access: Published
-//  Description: Specifies the amount of memory, in bytes, that should
-//               be set aside for storing pre-processed data for
-//               rendering vertices.  This is not a limit on the
-//               actual vertex data, which is what it is; it is also
-//               not a limit on the amount of memory used by the video
-//               driver or the system graphics interface, which Panda
-//               has no control over.
+//  Description: Specifies the maximum number of entries in the cache
+//               for storing pre-processed data for rendering
+//               vertices.  This limit is flexible, and may be
+//               temporarily exceeded if many different Geoms are
+//               pre-processed during the space of a single frame.
+//
+//               This is not a limit on the actual vertex data, which
+//               is what it is; it is also not a limit on the amount
+//               of memory used by the video driver or the system
+//               graphics interface, which Panda has no control over.
 ////////////////////////////////////////////////////////////////////
 INLINE void qpGeomCacheManager::
 set_max_size(int max_size) const {
   // We directly change the config variable.
-  vertex_convert_cache = max_size;
+  geom_cache_size = max_size;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomCacheManager::get_max_size
 //       Access: Published
-//  Description: Returns the amount of memory, in bytes, that should
-//               be set aside for storing pre-processed data for
-//               rendering vertices.  See set_max_size().
+//  Description: Returns the maximum number of entries in the cache
+//               for storing pre-processed data for rendering
+//               vertices.  See set_max_size().
 ////////////////////////////////////////////////////////////////////
 INLINE int qpGeomCacheManager::
 get_max_size() const {
-  return vertex_convert_cache;
+  return geom_cache_size;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomCacheManager::get_total_size
 //       Access: Published
-//  Description: Returns the amount of memory, in bytes, currently
-//               consumed by the cache of pre-processed vertex data.
+//  Description: Returns the number of entries currently in the cache.
 ////////////////////////////////////////////////////////////////////
 INLINE int qpGeomCacheManager::
 get_total_size() const {

+ 17 - 2
panda/src/gobj/qpgeomCacheManager.cxx

@@ -77,21 +77,36 @@ void qpGeomCacheManager::
 evict_old_entries() {
   MutexHolder holder(_lock);
 
+  int current_frame = ClockObject::get_global_clock()->get_frame_count();
+  int min_frames = geom_cache_min_frames;
+
   int max_size = get_max_size();
   while (_total_size > max_size) {
     PT(qpGeomCacheEntry) entry = _list->_next;
     nassertv(entry != _list);
+
+    if (current_frame - entry->_last_frame_used < min_frames) {
+      // Never mind, this one is too new.
+      if (gobj_cat.is_debug()) {
+        gobj_cat.debug()
+          << "Oldest element in cache is "
+          << current_frame - entry->_last_frame_used
+          << " frames; keeping cache at " << _total_size << " entries.\n";
+      }
+      break;
+    }
+
     entry->unref();
 
     if (gobj_cat.is_debug()) {
       gobj_cat.debug()
-        << "cache total_size = " << _total_size << ", max_size = "
+        << "cache total_size = " << _total_size << " entries, max_size = "
         << max_size << ", removing " << *entry << "\n";
     }
 
     entry->evict_callback();
 
-    _total_size -= entry->_result_size;
+    --_total_size;
     entry->remove_from_list();
     _geom_cache_evict_pcollector.add_level(1);
   }

+ 0 - 11
panda/src/gobj/qpgeomMunger.cxx

@@ -252,17 +252,6 @@ do_unregister() {
   _formats_by_animation.clear();
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomMunger::CacheEntry::get_result_size
-//       Access: Public, Virtual
-//  Description: Returns the approximate number of bytes represented
-//               by the computed result.
-////////////////////////////////////////////////////////////////////
-int qpGeomMunger::CacheEntry::
-get_result_size() const {
-  return sizeof(qpGeomMunger);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomMunger::CacheEntry::output
 //       Access: Public, Virtual

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

@@ -118,7 +118,6 @@ private:
 private:
   class CacheEntry : public qpGeomCacheEntry {
   public:
-    virtual int get_result_size() const;
     virtual void output(ostream &out) const;
 
     PT(qpGeomMunger) _munger;

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

@@ -300,12 +300,7 @@ clear_vertices() {
 ////////////////////////////////////////////////////////////////////
 void qpGeomPrimitive::
 offset_vertices(int offset) {
-  CDWriter cdata(_cycler);
-
-  cdata->_got_minmax = false;
-
-  qpGeomVertexRewriter index(cdata->_vertices, 0);
-
+  qpGeomVertexRewriter index(modify_vertices(), 0);
   while (!index.is_at_end()) {
     index.set_data1i(index.get_data1i() + offset);
   }

+ 6 - 0
panda/src/gobj/qpgeomVertexArrayData.I

@@ -136,3 +136,9 @@ CData(const qpGeomVertexArrayData::CData &copy) :
   _modified(copy._modified)
 {
 }
+
+INLINE ostream &
+operator << (ostream &out, const qpGeomVertexArrayData &obj) {
+  obj.output(out);
+  return out;
+}

+ 20 - 0
panda/src/gobj/qpgeomVertexArrayData.cxx

@@ -159,6 +159,26 @@ set_usage_hint(qpGeomVertexArrayData::UsageHint usage_hint) {
   cdata->_modified = qpGeom::get_next_modified();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::output
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexArrayData::
+output(ostream &out) const {
+  out << get_num_rows() << " rows: " << *get_array_format();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::write
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexArrayData::
+write(ostream &out, int indent_level) const {
+  _array_format->write_with_data(out, indent_level, this);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexArrayData::modify_data
 //       Access: Public

+ 5 - 0
panda/src/gobj/qpgeomVertexArrayData.h

@@ -81,6 +81,9 @@ PUBLISHED:
   INLINE int get_data_size_bytes() const;
   INLINE UpdateSeq get_modified() const;
 
+  void output(ostream &out) const;
+  void write(ostream &out, int indent_level = 0) const;
+
 public:
   INLINE CPTA_uchar get_data() const;
   PTA_uchar modify_data();
@@ -158,6 +161,8 @@ private:
   friend class PreparedGraphicsObjects;
 };
 
+INLINE ostream &operator << (ostream &out, const qpGeomVertexArrayData &obj);
+
 #include "qpgeomVertexArrayData.I"
 
 #endif

+ 5 - 5
panda/src/gobj/qpgeomVertexArrayFormat.cxx

@@ -435,13 +435,13 @@ write(ostream &out, int indent_level) const {
 ////////////////////////////////////////////////////////////////////
 void qpGeomVertexArrayFormat::
 write_with_data(ostream &out, int indent_level,
-                const qpGeomVertexData *data, int array_index) const {
+                const qpGeomVertexArrayData *array_data) const {
   consider_sort_columns();
-  int num_vertices = data->get_num_rows();
+  int num_rows = array_data->get_num_rows();
 
-  qpGeomVertexReader reader(data);
+  qpGeomVertexReader reader(array_data);
 
-  for (int i = 0; i < num_vertices; i++) {
+  for (int i = 0; i < num_rows; i++) {
     indent(out, indent_level)
       << "row " << i << ":\n";
     reader.set_row(i);
@@ -449,7 +449,7 @@ write_with_data(ostream &out, int indent_level,
     for (ci = _columns.begin(); ci != _columns.end(); ++ci) {
       const qpGeomVertexColumn *column = (*ci);
       int num_values = min(column->get_num_values(), 4);
-      reader.set_column(array_index, column);
+      reader.set_column(0, column);
       const LVecBase4f &d = reader.get_data4f();
 
       indent(out, indent_level + 2) 

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

@@ -29,6 +29,7 @@
 
 class qpGeomVertexFormat;
 class qpGeomVertexData;
+class qpGeomVertexArrayData;
 class InternalName;
 class FactoryParams;
 class BamWriter;
@@ -109,7 +110,7 @@ PUBLISHED:
   void output(ostream &out) const;
   void write(ostream &out, int indent_level = 0) const;
   void write_with_data(ostream &out, int indent_level, 
-                       const qpGeomVertexData *data, int array_index) const;
+                       const qpGeomVertexArrayData *array_data) const;
 
 public:
   int compare_to(const qpGeomVertexArrayFormat &other) const;

+ 2 - 1
panda/src/gobj/qpgeomVertexData.cxx

@@ -553,7 +553,8 @@ convert_to(const qpGeomVertexFormat *new_format) const {
   // Okay, convert the data to the new format.
   if (gobj_cat.is_debug()) {
     gobj_cat.debug()
-      << "Converting " << get_num_rows() << " rows.\n";
+      << "Converting " << get_num_rows() << " rows from " << *_format
+      << " to " << *new_format << "\n";
   }
   PStatTimer timer(_convert_pcollector);
 

+ 1 - 1
panda/src/gobj/qpgeomVertexFormat.cxx

@@ -418,7 +418,7 @@ write_with_data(ostream &out, int indent_level,
     CPTA_uchar array_data = data->get_array(i)->get_data();
     indent(out, indent_level)
       << "Array " << i << " (" << (void *)array_data.p() << "):\n";
-    _arrays[i]->write_with_data(out, indent_level + 2, data, i);
+    _arrays[i]->write_with_data(out, indent_level + 2, data->get_array(i));
   }
 }
 

+ 2 - 2
panda/src/pstatclient/pStatProperties.cxx

@@ -169,8 +169,8 @@ static LevelCollectorProperties level_properties[] = {
   { 1, "Vertex buffer switch",             { 0.0, 0.6, 0.8 },  "", 500 },
   { 1, "Vertex buffer switch:Vertex",      { 0.8, 0.0, 0.6 } },
   { 1, "Vertex buffer switch:Index",       { 0.8, 0.6, 0.3 } },
-  { 1, "Geom cache size",                  { 1.0, 0.8, 0.6 },  "MB", 12, 1048576 },
-  { 1, "Geom cache operations",            { 1.0, 0.8, 0.6 },  "", 500 },
+  { 1, "Geom cache size",                  { 0.6, 0.8, 0.6 },  "", 500 },
+  { 1, "Geom cache operations",            { 1.0, 0.6, 0.6 },  "", 500 },
   { 1, "Geom cache operations:record",     { 0.2, 0.4, 0.8 } },
   { 1, "Geom cache operations:erase",      { 0.4, 0.8, 0.2 } },
   { 1, "Geom cache operations:evict",      { 0.8, 0.2, 0.4 } },