Browse Source

add allow-incomplete-render

David Rose 18 years ago
parent
commit
39dc3d9c3e
38 changed files with 599 additions and 284 deletions
  1. 10 3
      panda/src/cull/drawCullHandler.cxx
  2. 4 7
      panda/src/display/standardMunger.cxx
  3. 2 2
      panda/src/display/standardMunger.h
  4. 1 1
      panda/src/distort/projectionScreen.cxx
  5. 16 16
      panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx
  6. 16 16
      panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx
  7. 10 10
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  8. 30 1
      panda/src/gobj/geom.cxx
  9. 2 0
      panda/src/gobj/geom.h
  10. 19 9
      panda/src/gobj/geomMunger.cxx
  11. 4 4
      panda/src/gobj/geomMunger.h
  12. 2 2
      panda/src/gobj/geomPrimitive.I
  13. 33 0
      panda/src/gobj/geomPrimitive.cxx
  14. 3 1
      panda/src/gobj/geomPrimitive.h
  15. 21 7
      panda/src/gobj/geomVertexArrayData.I
  16. 10 10
      panda/src/gobj/geomVertexArrayData.cxx
  17. 4 2
      panda/src/gobj/geomVertexArrayData.h
  18. 44 3
      panda/src/gobj/geomVertexData.cxx
  19. 3 1
      panda/src/gobj/geomVertexData.h
  20. 1 1
      panda/src/gobj/geomVertexFormat.cxx
  21. 3 3
      panda/src/gobj/geomVertexReader.I
  22. 1 1
      panda/src/gobj/vertexDataBook.I
  23. 101 23
      panda/src/gobj/vertexDataBook.cxx
  24. 5 0
      panda/src/gobj/vertexDataBook.h
  25. 9 37
      panda/src/gobj/vertexDataBuffer.I
  26. 79 30
      panda/src/gobj/vertexDataBuffer.cxx
  27. 2 2
      panda/src/gobj/vertexDataBuffer.h
  28. 9 8
      panda/src/gobj/vertexDataPage.I
  29. 41 30
      panda/src/gobj/vertexDataPage.cxx
  30. 8 3
      panda/src/gobj/vertexDataPage.h
  31. 1 1
      panda/src/grutil/multitexReducer.cxx
  32. 9 0
      panda/src/pgraph/config_pgraph.cxx
  33. 1 0
      panda/src/pgraph/config_pgraph.h
  34. 16 9
      panda/src/pgraph/cullResult.cxx
  35. 20 0
      panda/src/pgraph/cullableObject.I
  36. 52 36
      panda/src/pgraph/cullableObject.cxx
  37. 6 4
      panda/src/pgraph/cullableObject.h
  38. 1 1
      panda/src/pgraph/geomNode.cxx

+ 10 - 3
panda/src/cull/drawCullHandler.cxx

@@ -22,6 +22,7 @@
 #include "transformState.h"
 #include "renderState.h"
 #include "graphicsStateGuardianBase.h"
+#include "config_pgraph.h"
 
 
 ////////////////////////////////////////////////////////////////////
@@ -35,10 +36,16 @@ void DrawCullHandler::
 record_object(CullableObject *object, const CullTraverser *traverser) {
   // Munge vertices as needed for the GSG's requirements, and the
   // object's current state.
+  bool force = !allow_incomplete_render;
   Thread *current_thread = traverser->get_current_thread();
-  object->munge_geom(_gsg, _gsg->get_geom_munger(object->_state, current_thread), traverser);
 
-  // And draw the object, then dispense with it.
-  draw(object, _gsg, current_thread);
+  if (object->munge_geom(_gsg, _gsg->get_geom_munger(object->_state, current_thread), traverser, force)) {
+    if (force || object->request_resident()) {
+      // Now we can immediately draw the object.
+      draw(object, _gsg, current_thread);
+    }
+  }
+
+  // Dispense with the object.
   delete object;
 }

+ 4 - 7
panda/src/display/standardMunger.cxx

@@ -169,8 +169,9 @@ munge_data_impl(const GeomVertexData *data) {
 //       Access: Protected, Virtual
 //  Description: Converts a Geom and/or its data as necessary.
 ////////////////////////////////////////////////////////////////////
-bool StandardMunger::
-munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data, Thread *) {
+void StandardMunger::
+munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data, 
+                Thread *) {
   int supported_geom_rendering = get_gsg()->get_supported_geom_rendering();
 
   int unsupported_bits = geom->get_geom_rendering() & ~supported_geom_rendering;
@@ -208,8 +209,6 @@ munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data, Thread *) {
       vertex_data = new_geom->get_vertex_data();
     }
   }
-
-  return true;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -217,7 +216,7 @@ munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data, Thread *) {
 //       Access: Protected, Virtual
 //  Description: Converts a Geom and/or its data as necessary.
 ////////////////////////////////////////////////////////////////////
-bool StandardMunger::
+void StandardMunger::
 premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data) {
   int supported_geom_rendering = get_gsg()->get_supported_geom_rendering();
 
@@ -256,8 +255,6 @@ premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data) {
       vertex_data = new_geom->get_vertex_data();
     }
   }
-
-  return true;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 2
panda/src/display/standardMunger.h

@@ -47,9 +47,9 @@ public:
 protected:
   virtual CPT(GeomVertexData) munge_data_impl(const GeomVertexData *data);
   virtual int compare_to_impl(const GeomMunger *other) const;
-  virtual bool munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data,
+  virtual void munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data,
                                Thread *current_thread);
-  virtual bool premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data);
+  virtual void premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data);
   virtual int geom_compare_to_impl(const GeomMunger *other) const;
   virtual CPT(RenderState) munge_state_impl(const RenderState *state);
 

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

@@ -542,7 +542,7 @@ recompute_geom(Geom *geom, const LMatrix4f &rel_mat) {
   PT(GeomVertexData) modify_vdata = geom->modify_vertex_data();
 
   // Maybe the vdata has animation that we should consider.
-  CPT(GeomVertexData) animated_vdata = geom->get_vertex_data(current_thread)->animate_vertices(current_thread);
+  CPT(GeomVertexData) animated_vdata = geom->get_vertex_data(current_thread)->animate_vertices(true, current_thread);
 
   GeomVertexWriter texcoord(modify_vdata, _texcoord_name, current_thread);
   GeomVertexWriter color(modify_vdata, current_thread);

+ 16 - 16
panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx

@@ -930,9 +930,9 @@ draw_triangles(const GeomPrimitivePipelineReader *reader) {
         (D3DPT_TRIANGLELIST,
          min_vertex, max_vertex,
          reader->get_num_primitives(),
-         reader->get_read_pointer(),
+         reader->get_read_pointer(true),
          index_type,
-         _data_reader->get_array_reader(0)->get_read_pointer(),
+         _data_reader->get_array_reader(0)->get_read_pointer(true),
          _data_reader->get_format()->get_array(0)->get_stride());
     }
   } else {
@@ -949,7 +949,7 @@ draw_triangles(const GeomPrimitivePipelineReader *reader) {
       draw_primitive_up(D3DPT_TRIANGLELIST, reader->get_num_primitives(),
                         reader->get_first_vertex(),
                         reader->get_num_vertices(),
-                        _data_reader->get_array_reader(0)->get_read_pointer(),
+                        _data_reader->get_array_reader(0)->get_read_pointer(true),
                         _data_reader->get_format()->get_array(0)->get_stride());
     }
   }
@@ -990,8 +990,8 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader) {
           (D3DPT_TRIANGLESTRIP,
            min_vertex, max_vertex,
            reader->get_num_vertices() - 2,
-           reader->get_read_pointer(), index_type,
-           _data_reader->get_array_reader(0)->get_read_pointer(),
+           reader->get_read_pointer(true), index_type,
+           _data_reader->get_array_reader(0)->get_read_pointer(true),
            _data_reader->get_format()->get_array(0)->get_stride());
       }
     } else {
@@ -1008,7 +1008,7 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader) {
                           reader->get_num_vertices() - 2,
                           reader->get_first_vertex(),
                           reader->get_num_vertices(),
-                          _data_reader->get_array_reader(0)->get_read_pointer(),
+                          _data_reader->get_array_reader(0)->get_read_pointer(true),
                           _data_reader->get_format()->get_array(0)->get_stride());
       }
     }
@@ -1050,9 +1050,9 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader) {
 
       } else {
         // Indexed, client arrays, individual triangle strips.
-        const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer();
+        const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(true);
         int stride = _data_reader->get_format()->get_array(0)->get_stride();
-        const unsigned char *vertices = reader->get_read_pointer();
+        const unsigned char *vertices = reader->get_read_pointer(true);
         D3DFORMAT index_type = get_index_type(reader->get_index_type());
 
         unsigned int start = 0;
@@ -1087,7 +1087,7 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader) {
 
       } else {
         // Nonindexed, client arrays, individual triangle strips.
-        const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer();
+        const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(true);
         int stride = _data_reader->get_format()->get_array(0)->get_stride();
 
         unsigned int start = 0;
@@ -1150,9 +1150,9 @@ draw_trifans(const GeomPrimitivePipelineReader *reader) {
 
     } else {
       // Indexed, client arrays.
-      const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer();
+      const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(true);
       int stride = _data_reader->get_format()->get_array(0)->get_stride();
-      const unsigned char *vertices = reader->get_read_pointer();
+      const unsigned char *vertices = reader->get_read_pointer(true);
       D3DFORMAT index_type = get_index_type(reader->get_index_type());
 
       unsigned int start = 0;
@@ -1187,7 +1187,7 @@ draw_trifans(const GeomPrimitivePipelineReader *reader) {
 
     } else {
       // Nonindexed, client arrays.
-      const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer();
+      const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(true);
       int stride = _data_reader->get_format()->get_array(0)->get_stride();
 
       unsigned int start = 0;
@@ -1238,9 +1238,9 @@ draw_lines(const GeomPrimitivePipelineReader *reader) {
         (D3DPT_LINELIST,
          min_vertex, max_vertex,
          reader->get_num_primitives(),
-         reader->get_read_pointer(),
+         reader->get_read_pointer(true),
          index_type,
-         _data_reader->get_array_reader(0)->get_read_pointer(),
+         _data_reader->get_array_reader(0)->get_read_pointer(true),
          _data_reader->get_format()->get_array(0)->get_stride());
     }
   } else {
@@ -1256,7 +1256,7 @@ draw_lines(const GeomPrimitivePipelineReader *reader) {
       draw_primitive_up(D3DPT_LINELIST, reader->get_num_primitives(),
                         reader->get_first_vertex(),
                         reader->get_num_vertices(),
-                        _data_reader->get_array_reader(0)->get_read_pointer(),
+                        _data_reader->get_array_reader(0)->get_read_pointer(true),
                         _data_reader->get_format()->get_array(0)->get_stride());
     }
   }
@@ -1299,7 +1299,7 @@ draw_points(const GeomPrimitivePipelineReader *reader) {
     draw_primitive_up(D3DPT_POINTLIST, reader->get_num_primitives(),
                       reader->get_first_vertex(),
                       reader->get_num_vertices(),
-                      _data_reader->get_array_reader(0)->get_read_pointer(),
+                      _data_reader->get_array_reader(0)->get_read_pointer(true),
                       _data_reader->get_format()->get_array(0)->get_stride());
   }
 }

+ 16 - 16
panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx

@@ -1493,9 +1493,9 @@ draw_triangles(const GeomPrimitivePipelineReader *reader) {
         (D3DPT_TRIANGLELIST,
          min_vertex, max_vertex,
          reader->get_num_primitives(),
-         reader->get_read_pointer(),
+         reader->get_read_pointer(true),
          index_type,
-         _data_reader->get_array_reader(0)->get_read_pointer(),
+         _data_reader->get_array_reader(0)->get_read_pointer(true),
          _data_reader->get_format()->get_array(0)->get_stride());
     }
   } else {
@@ -1517,7 +1517,7 @@ draw_triangles(const GeomPrimitivePipelineReader *reader) {
       draw_primitive_up(D3DPT_TRIANGLELIST, reader->get_num_primitives(),
       reader->get_first_vertex(),
       reader->get_num_vertices(),
-      _data_reader->get_array_reader(0)->get_read_pointer(),
+      _data_reader->get_array_reader(0)->get_read_pointer(true),
       _data_reader->get_format()->get_array(0)->get_stride());
     }
   }
@@ -1568,8 +1568,8 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader) {
           (D3DPT_TRIANGLESTRIP,
            min_vertex, max_vertex,
            reader->get_num_vertices() - 2,
-           reader->get_read_pointer(), index_type,
-           _data_reader->get_array_reader(0)->get_read_pointer(),
+           reader->get_read_pointer(true), index_type,
+           _data_reader->get_array_reader(0)->get_read_pointer(true),
            _data_reader->get_format()->get_array(0)->get_stride());
       }
     } else {
@@ -1589,7 +1589,7 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader) {
         reader->get_num_vertices() - 2,
         reader->get_first_vertex(),
         reader->get_num_vertices(),
-        _data_reader->get_array_reader(0)->get_read_pointer(),
+        _data_reader->get_array_reader(0)->get_read_pointer(true),
         _data_reader->get_format()->get_array(0)->get_stride());
       }
     }
@@ -1632,9 +1632,9 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader) {
 
       } else {
         // Indexed, client arrays, individual triangle strips.
-        const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer();
+        const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(true);
         int stride = _data_reader->get_format()->get_array(0)->get_stride();
-        const unsigned char *vertices = reader->get_read_pointer();
+        const unsigned char *vertices = reader->get_read_pointer(true);
         D3DFORMAT index_type = get_index_type(reader->get_index_type());
 
         unsigned int start = 0;
@@ -1669,7 +1669,7 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader) {
 
       } else {
         // Nonindexed, client arrays, individual triangle strips.
-        const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer();
+        const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(true);
         int stride = _data_reader->get_format()->get_array(0)->get_stride();
 
         unsigned int start = 0;
@@ -1735,9 +1735,9 @@ draw_trifans(const GeomPrimitivePipelineReader *reader) {
 
     } else {
       // Indexed, client arrays.
-      const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer();
+      const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(true);
       int stride = _data_reader->get_format()->get_array(0)->get_stride();
-      const unsigned char *vertices = reader->get_read_pointer();
+      const unsigned char *vertices = reader->get_read_pointer(true);
       D3DFORMAT index_type = get_index_type(reader->get_index_type());
 
       unsigned int start = 0;
@@ -1772,7 +1772,7 @@ draw_trifans(const GeomPrimitivePipelineReader *reader) {
 
     } else {
       // Nonindexed, client arrays.
-      const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer();
+      const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(true);
       int stride = _data_reader->get_format()->get_array(0)->get_stride();
 
       unsigned int start = 0;
@@ -1824,9 +1824,9 @@ draw_lines(const GeomPrimitivePipelineReader *reader) {
         (D3DPT_LINELIST,
          min_vertex, max_vertex,
          reader->get_num_primitives(),
-         reader->get_read_pointer(),
+         reader->get_read_pointer(true),
          index_type,
-         _data_reader->get_array_reader(0)->get_read_pointer(),
+         _data_reader->get_array_reader(0)->get_read_pointer(true),
          _data_reader->get_format()->get_array(0)->get_stride());
     }
   } else {
@@ -1842,7 +1842,7 @@ draw_lines(const GeomPrimitivePipelineReader *reader) {
       draw_primitive_up(D3DPT_LINELIST, reader->get_num_primitives(),
       reader->get_first_vertex(),
       reader->get_num_vertices(),
-      _data_reader->get_array_reader(0)->get_read_pointer(),
+      _data_reader->get_array_reader(0)->get_read_pointer(true),
       _data_reader->get_format()->get_array(0)->get_stride());
     }
   }
@@ -1885,7 +1885,7 @@ draw_points(const GeomPrimitivePipelineReader *reader) {
     draw_primitive_up(D3DPT_POINTLIST, reader->get_num_primitives(),
                       reader->get_first_vertex(),
                       reader->get_num_vertices(),
-                      _data_reader->get_array_reader(0)->get_read_pointer(),
+                      _data_reader->get_array_reader(0)->get_read_pointer(true),
                       _data_reader->get_format()->get_array(0)->get_stride());
   }
 }

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

@@ -2745,12 +2745,12 @@ apply_vertex_buffer(VertexBufferContext *vbc,
     if (num_bytes != 0) {
       if (gvbc->changed_size(reader) || gvbc->changed_usage_hint(reader)) {
         _glBufferData(GL_ARRAY_BUFFER, num_bytes,
-                      reader->get_read_pointer(),
+                      reader->get_read_pointer(true),
                       get_usage(reader->get_usage_hint()));
 
       } else {
         _glBufferSubData(GL_ARRAY_BUFFER, 0, num_bytes,
-                         reader->get_read_pointer());
+                         reader->get_read_pointer(true));
       }
       _data_transferred_pcollector.add_level(num_bytes);
     }
@@ -2819,7 +2819,7 @@ const unsigned char *CLP(GraphicsStateGuardian)::
 setup_array_data(const GeomVertexArrayDataHandle *array_reader) {
   if (!_supports_buffers) {
     // No support for buffer objects; always render from client.
-    return array_reader->get_read_pointer();
+    return array_reader->get_read_pointer(true);
   }
   if (!vertex_buffers || _geom_display_list != 0 ||
       array_reader->get_usage_hint() == Geom::UH_client) {
@@ -2833,12 +2833,12 @@ setup_array_data(const GeomVertexArrayDataHandle *array_reader) {
       _glBindBuffer(GL_ARRAY_BUFFER, 0);
       _current_vbuffer_index = 0;
     }
-    return array_reader->get_read_pointer();
+    return array_reader->get_read_pointer(true);
   }
 
   // Prepare the buffer object and bind it.
   VertexBufferContext *vbc = ((GeomVertexArrayData *)array_reader->get_object())->prepare_now(get_prepared_objects(), this);
-  nassertr(vbc != (VertexBufferContext *)NULL, array_reader->get_read_pointer());
+  nassertr(vbc != (VertexBufferContext *)NULL, array_reader->get_read_pointer(true));
   apply_vertex_buffer(vbc, array_reader);
 
   // NULL is the OpenGL convention for the first byte of the buffer object.
@@ -2915,12 +2915,12 @@ apply_index_buffer(IndexBufferContext *ibc,
     if (num_bytes != 0) {
       if (gibc->changed_size(reader) || gibc->changed_usage_hint(reader)) {
         _glBufferData(GL_ELEMENT_ARRAY_BUFFER, num_bytes,
-                      reader->get_read_pointer(),
+                      reader->get_read_pointer(true),
                       get_usage(reader->get_usage_hint()));
 
       } else {
         _glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, num_bytes,
-                         reader->get_read_pointer());
+                         reader->get_read_pointer(true));
       }
       _data_transferred_pcollector.add_level(num_bytes);
     }
@@ -2988,7 +2988,7 @@ const unsigned char *CLP(GraphicsStateGuardian)::
 setup_primitive(const GeomPrimitivePipelineReader *reader) {
   if (!_supports_buffers) {
     // No support for buffer objects; always render from client.
-    return reader->get_read_pointer();
+    return reader->get_read_pointer(true);
   }
   if (!vertex_buffers || _geom_display_list != 0 ||
       reader->get_usage_hint() == Geom::UH_client) {
@@ -3002,12 +3002,12 @@ setup_primitive(const GeomPrimitivePipelineReader *reader) {
       _glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
       _current_ibuffer_index = 0;
     }
-    return reader->get_read_pointer();
+    return reader->get_read_pointer(true);
   }
 
   // Prepare the buffer object and bind it.
   IndexBufferContext *ibc = ((GeomPrimitive *)reader->get_object())->prepare_now(get_prepared_objects(), this);
-  nassertr(ibc != (IndexBufferContext *)NULL, reader->get_read_pointer());
+  nassertr(ibc != (IndexBufferContext *)NULL, reader->get_read_pointer(true));
   apply_index_buffer(ibc, reader);
 
   // NULL is the OpenGL convention for the first byte of the buffer object.

+ 30 - 1
panda/src/gobj/geom.cxx

@@ -710,6 +710,35 @@ get_num_bytes() const {
   return num_bytes;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Geom::request_resident
+//       Access: Published
+//  Description: Returns true if all the primitive arrays are
+//               currently resident in memory.  If this returns false,
+//               the data will be brought back into memory shortly;
+//               try again later.
+//
+//               This does not also test the Geom's associated
+//               GeomVertexData.  That must be tested separately.
+////////////////////////////////////////////////////////////////////
+bool Geom::
+request_resident() const {
+  CDReader cdata(_cycler);
+
+  bool resident = true;
+
+  Primitives::const_iterator pi;
+  for (pi = cdata->_primitives.begin(); 
+       pi != cdata->_primitives.end();
+       ++pi) {
+    if (!(*pi).get_read_pointer()->request_resident()) {
+      resident = false;
+    }
+  }
+
+  return resident;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Geom::transform_vertices
 //       Access: Published
@@ -1078,7 +1107,7 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const {
 
   // Get the vertex data, after animation.
   CPT(GeomVertexData) vertex_data = cdata->_data.get_read_pointer();
-  vertex_data = vertex_data->animate_vertices(current_thread);
+  vertex_data = vertex_data->animate_vertices(true, current_thread);
 
   // Now actually compute the bounding volume.  We do this by using
   // calc_tight_bounds to determine our box first.

+ 2 - 0
panda/src/gobj/geom.h

@@ -110,6 +110,8 @@ PUBLISHED:
   int get_num_bytes() const;
   INLINE UpdateSeq get_modified(Thread *current_thread = Thread::get_current_thread()) const;
 
+  bool request_resident() const;
+
   void transform_vertices(const LMatrix4f &mat);
   bool check_valid() const;
   bool check_valid(const GeomVertexData *vertex_data) const;

+ 19 - 9
panda/src/gobj/geomMunger.cxx

@@ -104,10 +104,15 @@ remove_data(const GeomVertexData *data) {
 //               The assumption is that for a particular geom and a
 //               particular munger, the result will always be the
 //               same; so this result may be cached.
+//
+//               If force is false, this may do nothing and return
+//               false if the vertex data is nonresident.  If force is
+//               true, this will always return true, but it may have
+//               to block while the vertex data is paged in.
 ////////////////////////////////////////////////////////////////////
-void GeomMunger::
+bool GeomMunger::
 munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data,
-           Thread *current_thread) {
+           bool force, Thread *current_thread) {
   CPT(GeomVertexData) source_data = data;
 
   // Look up the munger in the geom's cache--maybe we've recently
@@ -123,7 +128,7 @@ munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data,
   } else {
     entry = (*ci).second;
     geom->_cache_lock.release();
-    nassertv(entry->_source == geom);
+    nassertr(entry->_source == geom, false);
     
     // Here's an element in the cache for this computation.  Record a
     // cache hit, so this element will stay in the cache a while
@@ -140,7 +145,7 @@ munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data,
       
       geom = cdata->_geom_result;
       data = cdata->_data_result;
-      return;
+      return true;
     }
     
     // The cache entry is stale, but we'll recompute it below.  Note
@@ -149,6 +154,11 @@ munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data,
     // compute the same result.
   }
 
+  if (!force && (!geom->request_resident() || !data->request_resident())) {
+    // Oh dear, the data isn't resident.  We can't munge it, so give up.
+    return false;
+  }
+
   // Ok, invoke the munger.
   PStatTimer timer(_munge_pcollector, current_thread);
 
@@ -166,7 +176,7 @@ munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data,
       if (!inserted) {
         // Some other thread must have beat us to the punch.  Never
         // mind.
-        return;
+        return true;
       }
     }
   
@@ -180,6 +190,8 @@ munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data,
   Geom::CDCacheWriter cdata(entry->_cycler, true, current_thread);
   cdata->_source = (Geom *)orig_geom.p();
   cdata->set_result(geom, data);
+
+  return true;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -255,11 +267,10 @@ munge_data_impl(const GeomVertexData *data) {
 //       Access: Protected, Virtual
 //  Description: Converts a Geom and/or its data as necessary.
 ////////////////////////////////////////////////////////////////////
-bool GeomMunger::
+void GeomMunger::
 munge_geom_impl(CPT(Geom) &, CPT(GeomVertexData) &, Thread *) {
   // The default implementation does nothing (the work has already
   // been done in munge_format_impl() and munge_data_impl()).
-  return true;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -331,11 +342,10 @@ premunge_data_impl(const GeomVertexData *data) {
 //       Access: Protected, Virtual
 //  Description: Converts a Geom and/or its data as necessary.
 ////////////////////////////////////////////////////////////////////
-bool GeomMunger::
+void GeomMunger::
 premunge_geom_impl(CPT(Geom) &, CPT(GeomVertexData) &) {
   // The default implementation does nothing (the work has already
   // been done in premunge_format_impl() and premunge_data_impl()).
-  return true;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 4 - 4
panda/src/gobj/geomMunger.h

@@ -76,8 +76,8 @@ public:
   INLINE CPT(GeomVertexData) munge_data(const GeomVertexData *data) const;
   void remove_data(const GeomVertexData *data);
 
-  void munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data,
-                  Thread *current_thread);
+  bool munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data,
+                  bool force, Thread *current_thread);
 
   INLINE CPT(GeomVertexFormat) premunge_format(const GeomVertexFormat *format) const;
   INLINE CPT(GeomVertexData) premunge_data(const GeomVertexData *data) const;
@@ -96,14 +96,14 @@ protected:
   virtual CPT(GeomVertexFormat) munge_format_impl(const GeomVertexFormat *orig,
                                                   const GeomVertexAnimationSpec &animation);
   virtual CPT(GeomVertexData) munge_data_impl(const GeomVertexData *data);
-  virtual bool munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data,
+  virtual void munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data,
                                Thread *current_thread);
 
 
   CPT(GeomVertexFormat) do_premunge_format(const GeomVertexFormat *format);
   virtual CPT(GeomVertexFormat) premunge_format_impl(const GeomVertexFormat *orig);
   virtual CPT(GeomVertexData) premunge_data_impl(const GeomVertexData *data);
-  virtual bool premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data);
+  virtual void premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data);
 
   virtual int compare_to_impl(const GeomMunger *other) const;
   virtual int geom_compare_to_impl(const GeomMunger *other) const;

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

@@ -666,8 +666,8 @@ get_vertices_reader() const {
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 INLINE const unsigned char *GeomPrimitivePipelineReader::
-get_read_pointer() const {
-  return _vertices_reader->get_read_pointer();
+get_read_pointer(bool force) const {
+  return _vertices_reader->get_read_pointer(force);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 33 - 0
panda/src/gobj/geomPrimitive.cxx

@@ -798,6 +798,39 @@ get_num_bytes() const {
   return num_bytes;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitive::request_resident
+//       Access: Published
+//  Description: Returns true if the primitive data is currently
+//               resident in memory.  If this returns false, the
+//               primitive data will be brought back into memory
+//               shortly; try again later.
+////////////////////////////////////////////////////////////////////
+bool GeomPrimitive::
+request_resident() const {
+  CDReader cdata(_cycler);
+
+  bool resident = true;
+
+  if (!cdata->_vertices.is_null() &&
+      !cdata->_vertices.get_read_pointer()->request_resident()) {
+    resident = false;
+  }
+
+  if (is_composite() && cdata->_got_minmax) {
+    if (!cdata->_mins.is_null() &&
+        !cdata->_mins.get_read_pointer()->request_resident()) {
+      resident = false;
+    }
+    if (!cdata->_maxs.is_null() &&
+        !cdata->_maxs.get_read_pointer()->request_resident()) {
+      resident = false;
+    }
+  }
+
+  return resident;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitive::output
 //       Access: Published, Virtual

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

@@ -136,6 +136,8 @@ PUBLISHED:
   INLINE int get_data_size_bytes() const;
   INLINE UpdateSeq get_modified() const;
 
+  bool request_resident() const;
+
   INLINE bool check_valid(const GeomVertexData *vertex_data) const;
 
   virtual void output(ostream &out) const;
@@ -346,7 +348,7 @@ public:
   bool check_valid(const GeomVertexDataPipelineReader *data_reader) const;
   INLINE int get_index_stride() const;
   INLINE const GeomVertexArrayDataHandle *get_vertices_reader() const;
-  INLINE const unsigned char *get_read_pointer() const;
+  INLINE const unsigned char *get_read_pointer(bool force) const;
   INLINE CPTA_int get_ends() const;
   INLINE CPT(GeomVertexArrayData) get_mins() const;
   INLINE CPT(GeomVertexArrayData) get_maxs() const;

+ 21 - 7
panda/src/gobj/geomVertexArrayData.I

@@ -145,6 +145,21 @@ get_modified() const {
   return cdata->_modified;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexArrayData::request_resident
+//       Access: Published
+//  Description: Returns true if the vertex data is currently resident
+//               in memory.  If this returns true, the next call to
+//               get_handle()->get_read_pointer() will probably not
+//               block.  If this returns false, the vertex data will
+//               be brought back into memory shortly; try again later.
+////////////////////////////////////////////////////////////////////
+INLINE bool GeomVertexArrayData::
+request_resident() const {
+  CPT(GeomVertexArrayDataHandle) handle = get_handle();
+  return (handle->get_read_pointer(false) != (const unsigned char *)NULL);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexArrayData::get_handle
 //       Access: Published
@@ -379,7 +394,7 @@ get_object() {
 ////////////////////////////////////////////////////////////////////
 INLINE const unsigned char *GeomVertexArrayDataHandle::
 get_read_pointer(bool force) const {
-  check_resident();
+  mark_used();
   return _cdata->_buffer.get_read_pointer(force);
 }
 
@@ -453,7 +468,7 @@ get_modified() const {
 ////////////////////////////////////////////////////////////////////
 INLINE string GeomVertexArrayDataHandle::
 get_data() const {
-  check_resident();
+  mark_used();
   return string((const char *)_cdata->_buffer.get_read_pointer(true), _cdata->_buffer.get_size());
 }
 
@@ -467,20 +482,19 @@ get_data() const {
 ////////////////////////////////////////////////////////////////////
 INLINE string GeomVertexArrayDataHandle::
 get_subdata(size_t start, size_t size) const {
-  check_resident();
+  mark_used();
   start = min(start, _cdata->_buffer.get_size());
   size = min(size, _cdata->_buffer.get_size() - start);
   return string((const char *)_cdata->_buffer.get_read_pointer(true) + start, size);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: GeomVertexArrayDataHandle::check_resident
+//     Function: GeomVertexArrayDataHandle::mark_used
 //       Access: Published
-//  Description: Forces the vertex data into system RAM, if it is not
-//               already there; also, marks it recently-used.
+//  Description: Marks the array data recently-used.
 ////////////////////////////////////////////////////////////////////
 void GeomVertexArrayDataHandle::
-check_resident() const {
+mark_used() const {
   _object->set_lru_size(_object->get_lru_size());
 }
 

+ 10 - 10
panda/src/gobj/geomVertexArrayData.cxx

@@ -642,7 +642,7 @@ fillin(DatagramIterator &scan, BamReader *manager, void *extra_data) {
 unsigned char *GeomVertexArrayDataHandle::
 get_write_pointer() {
   nassertr(_writable, NULL);
-  check_resident();
+  mark_used();
   _cdata->_modified = Geom::get_next_modified();
   return _cdata->_buffer.get_write_pointer();
 }
@@ -655,7 +655,7 @@ get_write_pointer() {
 bool GeomVertexArrayDataHandle::
 set_num_rows(int n) {
   nassertr(_writable, false);
-  check_resident();
+  mark_used();
 
   int stride = _object->_array_format->get_stride();
   size_t new_size = n * stride;
@@ -689,7 +689,7 @@ set_num_rows(int n) {
 bool GeomVertexArrayDataHandle::
 unclean_set_num_rows(int n) {
   nassertr(_writable, false);
-  check_resident();
+  mark_used();
 
   int stride = _object->_array_format->get_stride();
   size_t new_size = n * stride;
@@ -719,8 +719,8 @@ unclean_set_num_rows(int n) {
 void GeomVertexArrayDataHandle::
 copy_data_from(const GeomVertexArrayDataHandle *other) {
   nassertv(_writable);
-  check_resident();
-  other->check_resident();
+  mark_used();
+  other->mark_used();
 
   _cdata->_buffer.unclean_realloc(other->_cdata->_buffer.get_size());
   memcpy(_cdata->_buffer.get_write_pointer(),
@@ -747,8 +747,8 @@ copy_subdata_from(size_t to_start, size_t to_size,
                   const GeomVertexArrayDataHandle *other,
                   size_t from_start, size_t from_size) {
   nassertv(_writable);
-  check_resident();
-  other->check_resident();
+  mark_used();
+  other->mark_used();
 
   VertexDataBuffer &to_buffer = _cdata->_buffer;
   size_t to_buffer_orig_size = to_buffer.get_size();
@@ -779,7 +779,7 @@ copy_subdata_from(size_t to_start, size_t to_size,
 
   // Now copy the data.
   memcpy(to_buffer.get_write_pointer() + to_start, 
-         other->get_read_pointer() + from_start, 
+         other->get_read_pointer(true) + from_start, 
          from_size);
   _cdata->_modified = Geom::get_next_modified();
 
@@ -798,7 +798,7 @@ copy_subdata_from(size_t to_start, size_t to_size,
 void GeomVertexArrayDataHandle::
 set_data(const string &data) {
   nassertv(_writable);
-  check_resident();
+  mark_used();
 
   _cdata->_buffer.unclean_realloc(data.size());
   memcpy(_cdata->_buffer.get_write_pointer(), data.data(), data.size());
@@ -823,7 +823,7 @@ set_data(const string &data) {
 void GeomVertexArrayDataHandle::
 set_subdata(size_t start, size_t size, const string &data) {
   nassertv(_writable);
-  check_resident();
+  mark_used();
 
   VertexDataBuffer &to_buffer = _cdata->_buffer;
   size_t to_buffer_orig_size = to_buffer.get_size();

+ 4 - 2
panda/src/gobj/geomVertexArrayData.h

@@ -96,6 +96,8 @@ PUBLISHED:
   void output(ostream &out) const;
   void write(ostream &out, int indent_level = 0) const;
 
+  INLINE bool request_resident() const;
+
   INLINE CPT(GeomVertexArrayDataHandle) get_handle(Thread *current_thread = Thread::get_current_thread()) const;
   INLINE PT(GeomVertexArrayDataHandle) modify_handle(Thread *current_thread = Thread::get_current_thread());
 
@@ -258,7 +260,7 @@ public:
   INLINE const GeomVertexArrayData *get_object() const;
   INLINE GeomVertexArrayData *get_object();
 
-  INLINE const unsigned char *get_read_pointer(bool force = true) const;
+  INLINE const unsigned char *get_read_pointer(bool force) const;
   unsigned char *get_write_pointer();
 
 PUBLISHED:
@@ -283,7 +285,7 @@ PUBLISHED:
   INLINE string get_subdata(size_t start, size_t size) const;
   void set_subdata(size_t start, size_t size, const string &data);
 
-  INLINE void check_resident() const;
+  INLINE void mark_used() const;
   
 private:
   ReMutexHolder _holder;

+ 44 - 3
panda/src/gobj/geomVertexData.cxx

@@ -415,6 +415,31 @@ set_slider_table(const SliderTable *table) {
   cdata->_animated_vertices_modified = UpdateSeq();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomVertexData::request_resident
+//       Access: Published
+//  Description: Returns true if the vertex data is currently resident
+//               in memory.  If this returns false, the vertex data will
+//               be brought back into memory shortly; try again later.
+////////////////////////////////////////////////////////////////////
+bool GeomVertexData::
+request_resident() const {
+  CDReader cdata(_cycler);
+
+  bool resident = true;
+
+  Arrays::const_iterator ai;
+  for (ai = cdata->_arrays.begin();
+       ai != cdata->_arrays.end();
+       ++ai) {
+    if (!(*ai).get_read_pointer()->request_resident()) {
+      resident = false;
+    }
+  }
+
+  return resident;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomVertexData::copy_from
 //       Access: Published
@@ -489,7 +514,7 @@ copy_from(const GeomVertexData *source, bool keep_data_objects,
   for (source_i = 0; source_i < num_arrays; ++source_i) {
     CPT(GeomVertexArrayData) array_obj = source->get_array(source_i);
     CPT(GeomVertexArrayDataHandle) array_handle = array_obj->get_handle();
-    const unsigned char *array_data = array_handle->get_read_pointer();
+    const unsigned char *array_data = array_handle->get_read_pointer(true);
     const GeomVertexArrayFormat *source_array_format = source_format->get_array(source_i);
     int num_columns = source_array_format->get_num_columns();
     for (int di = 0; di < num_columns; ++di) {
@@ -660,7 +685,7 @@ copy_row_from(int dest_row, const GeomVertexData *source,
 
     CPT(GeomVertexArrayData) source_array_obj = source->get_array(i);
     CPT(GeomVertexArrayDataHandle) source_array_handle = source_array_obj->get_handle();
-    const unsigned char *source_array_data = source_array_handle->get_read_pointer();
+    const unsigned char *source_array_data = source_array_handle->get_read_pointer(true);
 
     const GeomVertexArrayFormat *array_format = source_format->get_array(i);
     int stride = array_format->get_stride();
@@ -915,9 +940,15 @@ set_color(const Colorf &color, int num_components,
 //               still return the same pointer, but with its contents
 //               modified (this is preferred, since it allows the
 //               graphics backend to update vertex buffers optimally).
+//
+//               If force is false, this method may return immediately
+//               with stale data, if the vertex data is not completely
+//               resident.  If force is true, this method will never
+//               return stale data, but may block until the data is
+//               available.
 ////////////////////////////////////////////////////////////////////
 CPT(GeomVertexData) GeomVertexData::
-animate_vertices(Thread *current_thread) const {
+animate_vertices(bool force, Thread *current_thread) const {
   CDLockedReader cdata(_cycler, current_thread);
 
   if (cdata->_format->get_animation().get_animation_type() != AT_panda) {
@@ -948,6 +979,16 @@ animate_vertices(Thread *current_thread) const {
     // No changes.
     return cdata->_animated_vertices;
   }
+
+  if (!force && !request_resident()) {
+    // The vertex data isn't resident.  Return the best information
+    // we've got.
+    if (cdata->_animated_vertices != (GeomVertexData *)NULL) {
+      return cdata->_animated_vertices;
+    }
+    return this;
+  }
+
   CDWriter cdataw(((GeomVertexData *)this)->_cycler, cdata, false);
   cdataw->_animated_vertices_modified = modified;
   ((GeomVertexData *)this)->update_animated_vertices(cdataw, current_thread);

+ 3 - 1
panda/src/gobj/geomVertexData.h

@@ -130,6 +130,8 @@ PUBLISHED:
   INLINE int get_num_bytes() const;
   INLINE UpdateSeq get_modified(Thread *current_thread = Thread::get_current_thread()) const;
 
+  bool request_resident() const;
+
   void copy_from(const GeomVertexData *source, bool keep_data_objects,
                  Thread *current_thread = Thread::get_current_thread());
   void copy_row_from(int dest_row, const GeomVertexData *source, 
@@ -146,7 +148,7 @@ PUBLISHED:
     set_color(const Colorf &color, int num_components,
               NumericType numeric_type, Contents contents) const;
 
-  CPT(GeomVertexData) animate_vertices(Thread *current_thread) const;
+  CPT(GeomVertexData) animate_vertices(bool force, Thread *current_thread) const;
 
   PT(GeomVertexData) 
     replace_column(InternalName *name, int num_components,

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

@@ -576,7 +576,7 @@ write_with_data(ostream &out, int indent_level,
     << data->get_num_rows() << " rows.\n";
   for (size_t i = 0; i < _arrays.size(); i++) {
     CPT(GeomVertexArrayDataHandle) handle = data->get_array(i)->get_handle();
-    const unsigned char *array_data = handle->get_read_pointer();
+    const unsigned char *array_data = handle->get_read_pointer(true);
     indent(out, indent_level)
       << "Array " << i << " (" << (void *)array_data << ", "
       << *_arrays[i] << "):\n";

+ 3 - 3
panda/src/gobj/geomVertexReader.I

@@ -508,7 +508,7 @@ get_packer() const {
 ////////////////////////////////////////////////////////////////////
 INLINE void GeomVertexReader::
 set_pointer(int row) {
-  _pointer_begin = _handle->get_read_pointer();
+  _pointer_begin = _handle->get_read_pointer(true);
   _pointer_end = _pointer_begin + _handle->get_data_size_bytes();
   quick_set_pointer(row);
 }
@@ -526,7 +526,7 @@ quick_set_pointer(int row) {
 
 #if defined(_DEBUG)
   // Make sure we still have the same pointer as stored in the array.
-  nassertv(_pointer_begin == _handle->get_read_pointer());
+  nassertv(_pointer_begin == _handle->get_read_pointer(true));
 #endif
 
   _pointer = _pointer_begin + _packer->_column->get_start() + _stride * row;
@@ -547,7 +547,7 @@ inc_pointer() {
 #if defined(_DEBUG)
   nassertr(_pointer < _pointer_end, empty_buffer);
   // Make sure we still have the same pointer as stored in the array.
-  nassertr(_pointer_begin == _handle->get_read_pointer(), empty_buffer);
+  nassertr(_pointer_begin == _handle->get_read_pointer(true), empty_buffer);
   nassertr(_pointer < _pointer_begin + _handle->get_data_size_bytes(), empty_buffer);
 #endif
 

+ 1 - 1
panda/src/gobj/vertexDataBook.I

@@ -48,5 +48,5 @@ get_page(int n) const {
 INLINE VertexDataPage *VertexDataBook::
 create_new_page(size_t size) {
   size_t page_size = ((size + _block_size - 1) / _block_size) * _block_size;
-  return new VertexDataPage(page_size);
+  return new VertexDataPage(this, page_size);
 }

+ 101 - 23
panda/src/gobj/vertexDataBook.cxx

@@ -49,21 +49,26 @@ alloc(size_t size) {
   MutexHolder holder(_lock);
 
   // First, try to allocate from the last page that worked; then
-  // continue to the end of the list.
+  // continue to the end of the list.  We consider only pages that are
+  // currently resident (or that are empty), to minimize unnecessary
+  // swapping.
   size_t pi = _next_pi;
   while (pi < _pages.size()) {
-    VertexDataBlock *block = _pages[pi]->alloc(size);
-    if (block != (VertexDataBlock *)NULL) {
-      _next_pi = pi;
-      return block;
-    }
-    if (_pages[pi]->is_empty()) {
-      // This page is empty, but must have been too small.  Create a
-      // new page in its place.
-      delete _pages[pi];
-      _pages[pi] = create_new_page(size);
+    if (_pages[pi]->get_ram_class() == VertexDataPage::RC_resident ||
+        _pages[pi]->is_empty()) {
       VertexDataBlock *block = _pages[pi]->alloc(size);
-      return block;
+      if (block != (VertexDataBlock *)NULL) {
+        _next_pi = pi;
+        return block;
+      }
+      if (_pages[pi]->is_empty()) {
+        // This page is empty, but must have been too small.  Create a
+        // new page in its place.
+        delete _pages[pi];
+        _pages[pi] = create_new_page(size);
+        VertexDataBlock *block = _pages[pi]->alloc(size);
+        return block;
+      }
     }
     ++pi;
   }
@@ -72,17 +77,20 @@ alloc(size_t size) {
   pi = 0;
   _next_pi = min(_next_pi, _pages.size());
   while (pi < _next_pi) {
-    VertexDataBlock *block = _pages[pi]->alloc(size);
-    if (block != (VertexDataBlock *)NULL) {
-      _next_pi = pi;
-      return block;
-    }
-    if (_pages[pi]->is_empty()) {
-      // This page is empty, but must have been too small.  Create a
-      // new page in its place.
-      delete _pages[pi];
-      _pages[pi] = create_new_page(size);
-      return _pages[pi]->alloc(size);
+    if (_pages[pi]->get_ram_class() == VertexDataPage::RC_resident ||
+        _pages[pi]->is_empty()) {
+      VertexDataBlock *block = _pages[pi]->alloc(size);
+      if (block != (VertexDataBlock *)NULL) {
+        _next_pi = pi;
+        return block;
+      }
+      if (_pages[pi]->is_empty()) {
+        // This page is empty, but must have been too small.  Create a
+        // new page in its place.
+        delete _pages[pi];
+        _pages[pi] = create_new_page(size);
+        return _pages[pi]->alloc(size);
+      }
     }
     ++pi;
   }
@@ -95,6 +103,76 @@ alloc(size_t size) {
   return block;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: VertexDataBook::count_total_page_size
+//       Access: Published
+//  Description: Returns the total size of all bytes owned by all
+//               pages owned by this book.
+////////////////////////////////////////////////////////////////////
+size_t VertexDataBook::
+count_total_page_size() const {
+  size_t total = 0;
+  Pages::const_iterator pi;
+  for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
+    total += (*pi)->get_max_size();
+  }
+  return total;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexDataBook::count_total_page_size
+//       Access: Published
+//  Description: Returns the total size of all bytes owned by all
+//               pages owned by this book that have the indicated ram
+//               class.
+////////////////////////////////////////////////////////////////////
+size_t VertexDataBook::
+count_total_page_size(VertexDataPage::RamClass ram_class) const {
+  size_t total = 0;
+  Pages::const_iterator pi;
+  for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
+    if ((*pi)->get_ram_class() == ram_class) {
+      total += (*pi)->get_max_size();
+    }
+  }
+  return total;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexDataBook::count_allocated_size
+//       Access: Published
+//  Description: Returns the total size of all bytes allocated within
+//               pages owned by this book.
+////////////////////////////////////////////////////////////////////
+size_t VertexDataBook::
+count_allocated_size() const {
+  size_t total = 0;
+  Pages::const_iterator pi;
+  for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
+    total += (*pi)->get_total_size();
+  }
+  return total;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexDataBook::count_allocated_size
+//       Access: Published
+//  Description: Returns the total size of all bytes allocated within
+//               pages owned by this book that have the indicated ram
+//               class.
+////////////////////////////////////////////////////////////////////
+size_t VertexDataBook::
+count_allocated_size(VertexDataPage::RamClass ram_class) const {
+  size_t total = 0;
+  Pages::const_iterator pi;
+  for (pi = _pages.begin(); pi != _pages.end(); ++pi) {
+    if ((*pi)->get_ram_class() == ram_class) {
+      total += (*pi)->get_total_size();
+    }
+  }
+  return total;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: VertexDataBook::save_to_disk
 //       Access: Published

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

@@ -41,6 +41,11 @@ PUBLISHED:
   INLINE int get_num_pages() const;
   INLINE VertexDataPage *get_page(int n) const;
 
+  size_t count_total_page_size() const;
+  size_t count_total_page_size(VertexDataPage::RamClass ram_class) const;
+  size_t count_allocated_size() const;
+  size_t count_allocated_size(VertexDataPage::RamClass ram_class) const;
+
   void save_to_disk();
 
 private:

+ 9 - 37
panda/src/gobj/vertexDataBuffer.I

@@ -55,19 +55,6 @@ VertexDataBuffer(const VertexDataBuffer &copy) :
   (*this) = copy;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: VertexDataBuffer::Copy Assignment Operator
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-INLINE void VertexDataBuffer::
-operator = (const VertexDataBuffer &copy) {
-  MutexHolder holder(_lock);
-
-  do_unclean_realloc(copy.get_size());
-  memcpy(_resident_data, copy.get_read_pointer(true), _size);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: VertexDataBuffer::Destructor
 //       Access: Public
@@ -93,14 +80,16 @@ INLINE const unsigned char *VertexDataBuffer::
 get_read_pointer(bool force) const {
   MutexHolder holder(_lock);
 
-  if (_block != (VertexDataBlock *)NULL) {
-    // We don't necessarily need to page the buffer all the way into
-    // independent status; it's sufficient just to return the block's
-    // pointer, which will force its page to resident status.
-    return _block->get_pointer(force);
+  if (_resident_data != (unsigned char *)NULL || _size == 0) {
+    return _resident_data;
   }
 
-  return _resident_data;
+  nassertr(_block != (VertexDataBlock *)NULL, NULL);
+
+  // We don't necessarily need to page the buffer all the way into
+  // independent status; it's sufficient just to return the block's
+  // pointer, which will force its page to resident status.
+  return _block->get_pointer(force);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -112,8 +101,7 @@ INLINE unsigned char *VertexDataBuffer::
 get_write_pointer() { 
   MutexHolder holder(_lock);
 
-  if (_block != (VertexDataBlock *)NULL || 
-      _resident_data == (unsigned char *)NULL) {
+  if (_resident_data == (unsigned char *)NULL && _size != 0) {
     do_page_in();
   }
   return _resident_data;
@@ -205,19 +193,3 @@ swap(VertexDataBuffer &other) {
   other._size = size;
   other._block = block;
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: VertexDataBuffer::do_unclean_realloc
-//       Access: Private
-//  Description: Changes the size of the buffer, without regard to
-//               preserving its data.  The buffer may contain random
-//               data after this call.
-//
-//               Assumes the lock is already held.
-////////////////////////////////////////////////////////////////////
-INLINE void VertexDataBuffer::
-do_unclean_realloc(size_t size) {
-  // At the moment, this has no distinct definition, since the system
-  // realloc() call doesn't have an unclean variant.
-  do_clean_realloc(size);
-}

+ 79 - 30
panda/src/gobj/vertexDataBuffer.cxx

@@ -21,6 +21,32 @@
 
 TypeHandle VertexDataBuffer::_type_handle;
 
+////////////////////////////////////////////////////////////////////
+//     Function: VertexDataBuffer::Copy Assignment Operator
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void VertexDataBuffer::
+operator = (const VertexDataBuffer &copy) {
+  MutexHolder holder(_lock);
+  MutexHolder holder2(copy._lock);
+
+  if (_resident_data != (unsigned char *)NULL) {
+    nassertv(_size != 0);
+    get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size);
+    free(_resident_data);
+    _resident_data = NULL;
+  }
+  if (copy._resident_data != (unsigned char *)NULL) {
+    nassertv(copy._size != 0);
+    get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)copy._size);
+    _resident_data = (unsigned char *)malloc(copy._size);
+    memcpy(_resident_data, copy._resident_data, copy._size);
+  }
+  _size = copy._size;
+  _block = copy._block;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: VertexDataBuffer::do_clean_realloc
 //       Access: Private
@@ -35,32 +61,56 @@ void VertexDataBuffer::
 do_clean_realloc(size_t size) {
   if (size != _size) {
     if (size == 0) {
-      // If we're going to size 0, we don't necessarily need to page
-      // in first.  But if we're paged out, discard the page.
-      _block = NULL;
-        
-      if (_resident_data != (unsigned char *)NULL) {
-        free(_resident_data);
-        _resident_data = NULL;
-        get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size);
-      }
-      _block = NULL;
-      
+      do_unclean_realloc(size);
+      return;
+    }      
+
+    // Page in if we're currently paged out.
+    if (_size != 0 && _resident_data == (unsigned char *)NULL) {
+      do_page_in();
+    }
+    
+    get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)size - (int)_size);
+    if (_size == 0) {
+      nassertv(_resident_data == (unsigned char *)NULL);
+      _resident_data = (unsigned char *)malloc(size);
     } else {
-      // Page in if we're currently paged out.
-      if (_block != (VertexDataBlock *)NULL || 
-          _resident_data == (unsigned char *)NULL) {
-        do_page_in();
-      }
-      
-      if (_resident_data == (unsigned char *)NULL) {
-        _resident_data = (unsigned char *)malloc(size);
-        get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)size);
-      } else {
-        _resident_data = (unsigned char *)::realloc(_resident_data, size);
-        get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)size - (int)_size);
-      }
       nassertv(_resident_data != (unsigned char *)NULL);
+      _resident_data = (unsigned char *)realloc(_resident_data, size);
+    }
+    nassertv(_resident_data != (unsigned char *)NULL);
+    _size = size;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: VertexDataBuffer::do_unclean_realloc
+//       Access: Private
+//  Description: Changes the size of the buffer, without regard to
+//               preserving its data.  The buffer may contain random
+//               data after this call.
+//
+//               Assumes the lock is already held.
+////////////////////////////////////////////////////////////////////
+void VertexDataBuffer::
+do_unclean_realloc(size_t size) {
+  if (size != _size || _resident_data == (unsigned char *)NULL) {
+    // If we're paged out, discard the page.
+    _block = NULL;
+        
+    if (_resident_data != (unsigned char *)NULL) {
+      nassertv(_size != 0);
+
+      get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size);
+      free(_resident_data);
+      _resident_data = NULL;
+      _size = 0;
+    }
+
+    if (size != 0) {
+      get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)size);
+      nassertv(_resident_data == (unsigned char *)NULL);
+      _resident_data = (unsigned char *)malloc(size);
     }
 
     _size = size;
@@ -93,9 +143,9 @@ do_page_out(VertexDataBook &book) {
   nassertv(pointer != (unsigned char *)NULL);
   memcpy(pointer, _resident_data, _size);
 
+  get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size);
   free(_resident_data);
   _resident_data = NULL;
-  get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -109,17 +159,16 @@ do_page_out(VertexDataBook &book) {
 ////////////////////////////////////////////////////////////////////
 void VertexDataBuffer::
 do_page_in() {
-  if (_block == (VertexDataBlock *)NULL) {
+  if (_resident_data != (unsigned char *)NULL || _size == 0) {
     // We're already paged in.
     return;
   }
 
-  nassertv(_resident_data == (unsigned char *)NULL);
+  nassertv(_block != (VertexDataBlock *)NULL);
 
+  get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)_size);
   _resident_data = (unsigned char *)malloc(_size);
   nassertv(_resident_data != (unsigned char *)NULL);
-  get_class_type().inc_memory_usage(TypeHandle::MC_array, _size);
-
+  
   memcpy(_resident_data, _block->get_pointer(true), _size);
-  _block = NULL;
 }

+ 2 - 2
panda/src/gobj/vertexDataBuffer.h

@@ -65,7 +65,7 @@ public:
   INLINE VertexDataBuffer();
   INLINE VertexDataBuffer(size_t size);
   INLINE VertexDataBuffer(const VertexDataBuffer &copy);
-  INLINE void operator = (const VertexDataBuffer &copy);
+  void operator = (const VertexDataBuffer &copy);
   INLINE ~VertexDataBuffer();
 
   INLINE const unsigned char *get_read_pointer(bool force) const;
@@ -82,7 +82,7 @@ public:
 
 private:
   void do_clean_realloc(size_t size);
-  INLINE void do_unclean_realloc(size_t size);
+  void do_unclean_realloc(size_t size);
 
   void do_page_out(VertexDataBook &book);
   void do_page_in();

+ 9 - 8
panda/src/gobj/vertexDataPage.I

@@ -72,14 +72,13 @@ get_first_block() const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: VertexDataPage::get_total_page_size
-//       Access: Published, Static
-//  Description: Returns the byte count allocated to all
-//               VertexDataPages currently in existance.
+//     Function: VertexDataPage::get_book
+//       Access: Published
+//  Description: Returns a pointer to the book that owns this page.
 ////////////////////////////////////////////////////////////////////
-INLINE size_t VertexDataPage::
-get_total_page_size() {
-  return _total_page_size;
+INLINE VertexDataBook *VertexDataPage::
+get_book() const {
+  return _book;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -155,7 +154,9 @@ get_page_data(bool force) {
       make_resident_now();
     } else {
       request_ram_class(RC_resident);
-      return NULL;
+      if (_ram_class != RC_resident) {
+        return NULL;
+      }
     }
   }
 

+ 41 - 30
panda/src/gobj/vertexDataPage.cxx

@@ -19,6 +19,7 @@
 #include "vertexDataPage.h"
 #include "configVariableInt.h"
 #include "vertexDataSaveFile.h"
+#include "vertexDataBook.h"
 #include "pStatTimer.h"
 #include "mutexHolder.h"
 
@@ -68,13 +69,13 @@ SimpleLru *VertexDataPage::_global_lru[RC_end_of_list] = {
   &VertexDataPage::_disk_lru,
 };
 
-size_t VertexDataPage::_total_page_size = 0;
 VertexDataSaveFile *VertexDataPage::_save_file;
 
 PStatCollector VertexDataPage::_vdata_compress_pcollector("*:Vertex Data:Compress");
 PStatCollector VertexDataPage::_vdata_decompress_pcollector("*:Vertex Data:Decompress");
 PStatCollector VertexDataPage::_vdata_save_pcollector("*:Vertex Data:Save");
 PStatCollector VertexDataPage::_vdata_restore_pcollector("*:Vertex Data:Restore");
+PStatCollector VertexDataPage::_thread_wait_pcollector("*:Wait:Idle");
 
 TypeHandle VertexDataPage::_type_handle;
 
@@ -84,12 +85,18 @@ TypeHandle VertexDataPage::_type_handle;
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 VertexDataPage::
-VertexDataPage(size_t page_size) : SimpleAllocator(page_size), SimpleLruPage(page_size) {
-  _page_data = new unsigned char[get_max_size()];
+VertexDataPage(VertexDataBook *book, size_t page_size) : 
+  SimpleAllocator(page_size), 
+  SimpleLruPage(page_size),
+  _book(book)
+{
+  nassertv(page_size == get_max_size());
+
+  get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)page_size);
+  _page_data = new unsigned char[page_size];
   _size = page_size;
+
   _uncompressed_size = _size;
-  _total_page_size += _size;
-  get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)_size);
   _pending_ram_class = RC_resident;
   set_ram_class(RC_resident);
 }
@@ -101,11 +108,20 @@ VertexDataPage(size_t page_size) : SimpleAllocator(page_size), SimpleLruPage(pag
 ////////////////////////////////////////////////////////////////////
 VertexDataPage::
 ~VertexDataPage() {
-  _total_page_size -= _size;
-  get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size);
+  MutexHolder holder(_lock);
+
+  {
+    MutexHolder holder2(_tlock);
+    if (_pending_ram_class != _ram_class) {
+      nassertv(_thread != (PageThread *)NULL);
+      _thread->remove_page(this);
+    }
+  }
 
   if (_page_data != NULL) {
+    get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size);
     delete[] _page_data;
+    _size = 0;
   }
 }
 
@@ -121,15 +137,11 @@ VertexDataPage::
 VertexDataBlock *VertexDataPage::
 alloc(size_t size) {
   MutexHolder holder(_lock);
-  if (_ram_class != RC_resident) {
-    make_resident_now();
-  }
-  
   VertexDataBlock *block = (VertexDataBlock *)SimpleAllocator::alloc(size);
 
-  if (block != (VertexDataBlock *)NULL) {
-    // When we allocate a new block within the page, we have to clear
-    // the disk cache (since we have just invalidated it).
+  if (block != (VertexDataBlock *)NULL && _ram_class != RC_disk) {
+    // When we allocate a new block within a resident page, we have to
+    // clear the disk cache (since we have just invalidated it).
     _saved_block.clear();
   }
 
@@ -269,15 +281,11 @@ make_resident() {
     }
     nassertv(dest_len == _uncompressed_size);
 
-    get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size);
-    _total_page_size -= _size;
-
+    get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)_uncompressed_size - (int)_size);
     delete[] _page_data;
     _page_data = new_data;
     _size = _uncompressed_size;
 
-    get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)_size);
-    _total_page_size += _size;
   
 #endif
     set_lru_size(_size);
@@ -329,16 +337,11 @@ make_compressed() {
     unsigned char *new_data = new unsigned char[buffer_size];
     memcpy(new_data, buffer, buffer_size);
 
-    get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size);
-    _total_page_size -= _size;
-
+    get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)buffer_size - (int)_size);
     delete[] _page_data;
     _page_data = new_data;
     _size = buffer_size;
 
-    get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)_size);
-    _total_page_size += _size;
-
     if (gobj_cat.is_debug()) {
       gobj_cat.debug()
         << "Compressed " << *this << " from " << _uncompressed_size
@@ -376,8 +379,6 @@ make_disk() {
     }
 
     get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size);
-    _total_page_size -= _size;
-
     delete[] _page_data;
     _page_data = NULL;
     _size = 0;
@@ -454,12 +455,11 @@ do_restore_from_disk() {
       nassert_raise("read error");
     }
 
+    get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)buffer_size);
+    nassertv(_page_data == (unsigned char *)NULL);
     _page_data = new_data;
     _size = buffer_size;
 
-    get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)_size);
-    _total_page_size += _size;
-
     set_lru_size(_size);
     if (_saved_block->get_compressed()) {
       set_ram_class(RC_compressed);
@@ -501,7 +501,11 @@ request_ram_class(RamClass ram_class) {
     case RC_disk:
       make_disk();
       break;
+
+    case RC_end_of_list:
+      break;
     }
+    _pending_ram_class = ram_class;
     return;
   }
 
@@ -580,12 +584,15 @@ add_page(VertexDataPage *page, RamClass ram_class) {
 ////////////////////////////////////////////////////////////////////
 void VertexDataPage::PageThread::
 remove_page(VertexDataPage *page) {
+  nassertv(page != (VertexDataPage *)NULL);
   if (page == _working_page) {
     // Oops, the thread is currently working on this one.  We'll have
     // to wait for the thread to finish.
+    page->_lock.release();
     while (page == _working_page) {
       _working_cvar.wait();
     }
+    page->_lock.lock();
     return;
   }
 
@@ -621,6 +628,7 @@ thread_main() {
         _tlock.release();
         return;
       }
+      PStatTimer timer(_thread_wait_pcollector);
       _pending_cvar.wait();
     }
 
@@ -650,6 +658,9 @@ thread_main() {
       case RC_disk:
         _working_page->make_disk();
         break;
+
+      case RC_end_of_list:
+        break;
       }
     }
     

+ 8 - 3
panda/src/gobj/vertexDataPage.h

@@ -30,6 +30,7 @@
 #include "mutexHolder.h"
 #include "pdeque.h"
 
+class VertexDataBook;
 class VertexDataBlock;
 
 ////////////////////////////////////////////////////////////////////
@@ -40,8 +41,9 @@ class VertexDataBlock;
 //               cache file, if necessary.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA VertexDataPage : public SimpleAllocator, public SimpleLruPage {
+public:
+  VertexDataPage(VertexDataBook *book, size_t page_size);
 PUBLISHED:
-  VertexDataPage(size_t page_size);
   ~VertexDataPage();
 
   // These are used to indicate the current residency state of the
@@ -62,7 +64,8 @@ PUBLISHED:
   VertexDataBlock *alloc(size_t size);
   INLINE VertexDataBlock *get_first_block() const;
 
-  INLINE static size_t get_total_page_size();
+  INLINE VertexDataBook *get_book() const;
+
   INLINE static SimpleLru *get_global_lru(RamClass rclass);
   INLINE static SimpleLru *get_pending_lru();
   INLINE static VertexDataSaveFile *get_save_file();
@@ -126,19 +129,21 @@ private:
 
   RamClass _pending_ram_class;  // Protected by _tlock.
 
+  VertexDataBook *_book;  // never changes.
+
   static SimpleLru _resident_lru;
   static SimpleLru _compressed_lru;
   static SimpleLru _disk_lru;
   static SimpleLru _pending_lru;
   static SimpleLru *_global_lru[RC_end_of_list];
 
-  static size_t _total_page_size;
   static VertexDataSaveFile *_save_file;
 
   static PStatCollector _vdata_compress_pcollector;
   static PStatCollector _vdata_decompress_pcollector;
   static PStatCollector _vdata_save_pcollector;
   static PStatCollector _vdata_restore_pcollector;
+  static PStatCollector _thread_wait_pcollector;
 
 public:
   static TypeHandle get_class_type() {

+ 1 - 1
panda/src/grutil/multitexReducer.cxx

@@ -894,7 +894,7 @@ transfer_geom(GeomNode *geom_node, const InternalName *texcoord_name,
     PT(Geom) geom = orig_geom->make_copy();
 
     // Ensure that any vertex animation has been applied.
-    geom->set_vertex_data(geom->get_vertex_data(current_thread)->animate_vertices(current_thread));
+    geom->set_vertex_data(geom->get_vertex_data(current_thread)->animate_vertices(true, current_thread));
 
     // Now get a modifiable pointer to the vertex data in the new
     // Geom.  This will actually perform a deep copy of the vertex

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

@@ -319,6 +319,15 @@ ConfigVariableString default_model_extension
           "Panda's loader; new code should probably give the correct name "
           "for each model file they intend to load."));
 
+ConfigVariableBool allow_incomplete_render
+("allow-incomplete-render", false,
+ PRC_DESC("When this is true, the frame may be rendered even if some of the "
+          "geometry in the scene has been paged out.  The nonresident "
+          "geometry will be rendered as soon as it can be paged back in, "
+          "which may be several frames in the future.  When this is false, "
+          "geometry is always paged in when needed, holding up the frame "
+          "render if necessary."));
+
 ////////////////////////////////////////////////////////////////////
 //     Function: init_libpgraph
 //  Description: Initializes the library.  This must be called at

+ 1 - 0
panda/src/pgraph/config_pgraph.h

@@ -69,6 +69,7 @@ extern ConfigVariableBool m_dual_flash;
 
 extern ConfigVariableList load_file_type;
 extern ConfigVariableString default_model_extension;
+extern ConfigVariableBool allow_incomplete_render;
 
 extern EXPCL_PANDA void init_libpgraph();
 

+ 16 - 9
panda/src/pgraph/cullResult.cxx

@@ -98,6 +98,7 @@ add_object(CullableObject *object, const CullTraverser *traverser) {
   static const Colorf flash_multisample_color(0.78f, 0.05f, 0.81f, 1.0f);
   static const Colorf flash_dual_color(0.92f, 0.01f, 0.01f, 1.0f);
 
+  bool force = !allow_incomplete_render;
   Thread *current_thread = traverser->get_current_thread();
 
   // Check to see if there's a special transparency setting.
@@ -167,15 +168,18 @@ add_object(CullableObject *object, const CullTraverser *traverser) {
                 get_dual_transparent_state_decals() : 
                 get_dual_transparent_state();
               transparent_part->_state = state->compose(transparent_state);
-              transparent_part->munge_geom
-                (_gsg, _gsg->get_geom_munger(transparent_part->_state, current_thread),
-                 traverser);
-              CullBin *bin = get_bin(transparent_part->_state->get_bin_index());
-              nassertv(bin != (CullBin *)NULL);
+              if (transparent_part->munge_geom
+                  (_gsg, _gsg->get_geom_munger(transparent_part->_state, current_thread),
+                   traverser, force)) {
+                CullBin *bin = get_bin(transparent_part->_state->get_bin_index());
+                nassertv(bin != (CullBin *)NULL);
 #ifndef NDEBUG
-              check_flash_bin(transparent_part->_state, bin);
+                check_flash_bin(transparent_part->_state, bin);
 #endif
-              bin->add_object(transparent_part, current_thread);
+                if (force || transparent_part->request_resident()) {
+                  bin->add_object(transparent_part, current_thread);
+                }
+              }
             }
           
           // Now we can draw the opaque part, with decals.  This will
@@ -208,8 +212,11 @@ add_object(CullableObject *object, const CullTraverser *traverser) {
 
   // Munge vertices as needed for the GSG's requirements, and the
   // object's current state.
-  object->munge_geom(_gsg, _gsg->get_geom_munger(object->_state, current_thread), traverser);
-  bin->add_object(object, current_thread);
+  if (object->munge_geom(_gsg, _gsg->get_geom_munger(object->_state, current_thread), traverser, force)) {
+    if (force || object->request_resident()) {
+      bin->add_object(object, current_thread);
+    }
+  }
 }
 
 ////////////////////////////////////////////////////////////////////

+ 20 - 0
panda/src/pgraph/cullableObject.I

@@ -130,6 +130,26 @@ draw(GraphicsStateGuardianBase *gsg, Thread *current_thread) {
   _geom->draw(gsg, _munger, _munged_data, current_thread);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CullableObject::request_resident
+//       Access: Published
+//  Description: Returns true if all the data necessary to render this
+//               object is currently resident in memory.  If this
+//               returns false, the data will be brought back into
+//               memory shortly; try again later.
+////////////////////////////////////////////////////////////////////
+INLINE bool CullableObject::
+request_resident() const {
+  bool resident = true;
+  if (!_geom->request_resident()) {
+    resident = false;
+  }
+  if (!_munged_data->request_resident()) {
+    resident = false;
+  }
+  return resident;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: CullableObject::flush_level
 //       Access: Public, Static

+ 52 - 36
panda/src/pgraph/cullableObject.cxx

@@ -50,10 +50,16 @@ TypeHandle CullableObject::_type_handle;
 //       Access: Public
 //  Description: Uses the indicated GeomMunger to transform the geom
 //               and/or its vertices.
+//
+//               If force is false, this may do nothing and return
+//               false if the vertex data is nonresident.  If force is
+//               true, this will always return true, but it may have
+//               to block while the vertex data is paged in.
 ////////////////////////////////////////////////////////////////////
-void CullableObject::
+bool CullableObject::
 munge_geom(GraphicsStateGuardianBase *gsg,
-           GeomMunger *munger, const CullTraverser *traverser) {
+           GeomMunger *munger, const CullTraverser *traverser,
+           bool force) {
   Thread *current_thread = traverser->get_current_thread();
   if (_geom != (Geom *)NULL) {
     _munger = munger;
@@ -65,7 +71,7 @@ munge_geom(GraphicsStateGuardianBase *gsg,
     {
       GeomVertexDataPipelineReader data_reader(_munged_data, current_thread);
       data_reader.check_array_readers();
-      nassertv(geom_reader.check_valid(&data_reader));
+      nassertr(geom_reader.check_valid(&data_reader), false);
     }
 #endif  // _DEBUG
 
@@ -94,7 +100,9 @@ munge_geom(GraphicsStateGuardianBase *gsg,
           << hex << geom_rendering << ", unsupported: "
           << (unsupported_bits & Geom::GR_point_bits) << dec << "\n";
       }
-      munge_points_to_quads(traverser);
+      if (!munge_points_to_quads(traverser, force)) {
+        return false;
+      }
     }
 
     bool cpu_animated = false;
@@ -104,20 +112,24 @@ munge_geom(GraphicsStateGuardianBase *gsg,
       // the vertices in the CPU--and we have to do it before we call
       // munge_geom(), which might lose the tangent and binormal.
       CPT(GeomVertexData) animated_vertices = 
-        _munged_data->animate_vertices(current_thread);
+        _munged_data->animate_vertices(force, current_thread);
       if (animated_vertices != _munged_data) {
         cpu_animated = true;
         _munged_data = animated_vertices;
       }
-      munge_texcoord_light_vector(traverser);
+      if (!munge_texcoord_light_vector(traverser, force)) {
+        return false;
+      }
     }
 
     // Now invoke the munger to ensure the resulting geometry is in
     // a GSG-friendly form.
-    munger->munge_geom(_geom, _munged_data, current_thread);
+    if (!munger->munge_geom(_geom, _munged_data, force, current_thread)) {
+      return false;
+    }
     
     StateMunger *state_munger;
-    DCAST_INTO_V(state_munger, munger);
+    DCAST_INTO_R(state_munger, munger, false);
     _state = state_munger->munge_state(_state);
 
     if (!cpu_animated) {
@@ -126,7 +138,7 @@ munge_geom(GraphicsStateGuardianBase *gsg,
       // animation in hardware--then we have to calculate that
       // animation now.
       CPT(GeomVertexData) animated_vertices = 
-        _munged_data->animate_vertices(current_thread);
+        _munged_data->animate_vertices(force, current_thread);
       if (animated_vertices != _munged_data) {
         cpu_animated = true;
         _munged_data = animated_vertices;
@@ -151,11 +163,13 @@ munge_geom(GraphicsStateGuardianBase *gsg,
   if (_next != (CullableObject *)NULL) {
     if (_next->_state != (RenderState *)NULL) {
       _next->munge_geom(gsg, gsg->get_geom_munger(_next->_state, current_thread),
-                        traverser);
+                        traverser, force);
     } else {
-      _next->munge_geom(gsg, munger, traverser);
+      _next->munge_geom(gsg, munger, traverser, force);
     }
   }
+
+  return true;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -193,8 +207,12 @@ output(ostream &out) const {
 //
 //               This may replace _geom, _munged_data, and _state.
 ////////////////////////////////////////////////////////////////////
-void CullableObject::
-munge_points_to_quads(const CullTraverser *traverser) {
+bool CullableObject::
+munge_points_to_quads(const CullTraverser *traverser, bool force) {
+  if (!force && !_munged_data->request_resident()) {
+    return false;
+  }
+
   Thread *current_thread = traverser->get_current_thread();
   PStatTimer timer(_munge_sprites_pcollector, current_thread);
   _sw_sprites_pcollector.add_level(_munged_data->get_num_rows());
@@ -462,8 +480,8 @@ munge_points_to_quads(const CullTraverser *traverser) {
       ++vi;
     }
 
-    nassertv(vi == orig_verts);
-    nassertv(new_data->get_num_rows() == new_verts);
+    nassertr(vi == orig_verts, false);
+    nassertr(new_data->get_num_rows() == new_verts, false);
   }
 
   PT(Geom) new_geom = new Geom(new_data);
@@ -508,7 +526,7 @@ munge_points_to_quads(const CullTraverser *traverser) {
           GeomVertexReader index(primitive->get_vertices(), 0, current_thread);
           for (unsigned int *vi = vertices; vi != vertices_end; ++vi) {
             unsigned int v = index.get_data1i();
-            nassertv(v < (unsigned int)orig_verts);
+            nassertr(v < (unsigned int)orig_verts, false);
             (*vi) = v;
           }
         } else {
@@ -516,7 +534,7 @@ munge_points_to_quads(const CullTraverser *traverser) {
           unsigned int first_vertex = primitive->get_first_vertex();
           for (int i = 0; i < num_vertices; ++i) {
             unsigned int v = i + first_vertex;
-            nassertv(v < (unsigned int)orig_verts);
+            nassertr(v < (unsigned int)orig_verts, false);
             vertices[i] = v;
           }
         }
@@ -540,7 +558,7 @@ munge_points_to_quads(const CullTraverser *traverser) {
         GeomVertexWriter index(new_index, 0);
         for (unsigned int *vi = vertices; vi != vertices_end; ++vi) {
           int new_vi = (*vi) * 4;
-          nassertv(new_vi + 3 < new_prim_verts);
+          nassertr(new_vi + 3 < new_prim_verts, false);
           index.set_data1i(new_vi);
           index.set_data1i(new_vi + 1);
           index.set_data1i(new_vi + 2);
@@ -561,6 +579,8 @@ munge_points_to_quads(const CullTraverser *traverser) {
 
   _geom = new_geom.p();
   _munged_data = new_data;
+
+  return true;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -571,29 +591,24 @@ munge_points_to_quads(const CullTraverser *traverser) {
 //
 //               This may replace _geom, _munged_data, and _state.
 ////////////////////////////////////////////////////////////////////
-void CullableObject::
-munge_texcoord_light_vector(const CullTraverser *traverser) {
+bool CullableObject::
+munge_texcoord_light_vector(const CullTraverser *traverser, bool force) {
   Thread *current_thread = traverser->get_current_thread();
   PStatTimer timer(_munge_light_vector_pcollector, current_thread);
 
   if (_net_transform->is_singular()) {
     // If we're under a singular transform, never mind.
-    return;
+    return true;
   }
 
-  /*
-  CPT(TransformState) net_transform =
-    traverser->get_camera_transform()->compose(_modelview_transform);
-  */
-
   if (!_munged_data->has_column(InternalName::get_vertex()) || 
       !_munged_data->has_column(InternalName::get_normal())) {
     // No vertex or normal; can't compute light vector.
-    return;
+    return true;
   }
 
   CPT(TexGenAttrib) tex_gen = _state->get_tex_gen();
-  nassertv(tex_gen != (TexGenAttrib *)NULL);
+  nassertr(tex_gen != (TexGenAttrib *)NULL, false);
 
   const TexGenAttrib::LightVectors &light_vectors = tex_gen->get_light_vectors();
   TexGenAttrib::LightVectors::const_iterator lvi;
@@ -609,19 +624,12 @@ munge_texcoord_light_vector(const CullTraverser *traverser) {
       if (attrib != (RenderAttrib *)NULL) {
         CPT(LightAttrib) la = DCAST(LightAttrib, attrib);
         light = la->get_most_important_light();
-        /*
-        if (!light.is_empty()) {
-          // Remove that light, now that we're accounting for it in
-          // the normal map.
-          _state->set_attrib(la->remove_on_light(light));
-        }
-        */
       }
     }
     if (!light.is_empty()) {
       string source_name = tex_gen->get_source_name(stage);
       Light *light_obj = light.node()->as_light();
-      nassertv(light_obj != (Light *)NULL);
+      nassertr(light_obj != (Light *)NULL, false);
       
       // Determine the names of the tangent and binormal columns
       // associated with the stage's texcoord name.
@@ -632,6 +640,12 @@ munge_texcoord_light_vector(const CullTraverser *traverser) {
       
       if (_munged_data->has_column(tangent_name) &&
           _munged_data->has_column(binormal_name)) {
+
+        if (!force && !_munged_data->request_resident()) {
+          // The data isn't resident; give up.
+          return false;
+        }
+
         // Create a new column for the new texcoords.
         PT(GeomVertexData) new_data = _munged_data->replace_column
           (texcoord_name, 3, Geom::NT_float32, Geom::C_texcoord);
@@ -668,6 +682,8 @@ munge_texcoord_light_vector(const CullTraverser *traverser) {
       }
     }
   }
+
+  return true;
 }
 
 ////////////////////////////////////////////////////////////////////

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

@@ -61,11 +61,13 @@ public:
 
   INLINE bool has_decals() const;
 
-  void munge_geom(GraphicsStateGuardianBase *gsg,
-                  GeomMunger *munger, const CullTraverser *traverser);
+  bool munge_geom(GraphicsStateGuardianBase *gsg,
+                  GeomMunger *munger, const CullTraverser *traverser,
+                  bool force);
   INLINE void draw(GraphicsStateGuardianBase *gsg,
                    Thread *current_thread);
 
+  INLINE bool request_resident() const;
   INLINE static void flush_level();
 
 public:
@@ -85,8 +87,8 @@ public:
   CullableObject *_next;
 
 private:
-  void munge_points_to_quads(const CullTraverser *traverser);
-  void munge_texcoord_light_vector(const CullTraverser *traverser);
+  bool munge_points_to_quads(const CullTraverser *traverser, bool force);
+  bool munge_texcoord_light_vector(const CullTraverser *traverser, bool force);
 
   static CPT(RenderState) get_flash_cpu_state();
   static CPT(RenderState) get_flash_hardware_state();

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

@@ -326,7 +326,7 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point, bool &found_any,
   for (gi = geoms->begin(); gi != geoms->end(); ++gi) {
     CPT(Geom) geom = (*gi)._geom.get_read_pointer();
     geom->calc_tight_bounds(min_point, max_point, found_any,
-			    geom->get_vertex_data(current_thread)->animate_vertices(current_thread),
+			    geom->get_vertex_data(current_thread)->animate_vertices(true, current_thread),
 			    !next_transform->is_identity(), mat,
                             current_thread);
   }