Parcourir la source

gl display lists with new geom

David Rose il y a 21 ans
Parent
commit
3f7956f4e9

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

@@ -670,7 +670,7 @@ finish_decal() {
 //               are ok, false to abort this group of primitives.
 ////////////////////////////////////////////////////////////////////
 bool GraphicsStateGuardian::
-begin_draw_primitives(const qpGeomVertexData *data) {
+begin_draw_primitives(const qpGeom *, const qpGeomVertexData *data) {
   _vertex_data = data;
   return true;
 }
@@ -719,6 +719,7 @@ draw_trifans(const qpGeomTrifans *primitive) {
 ////////////////////////////////////////////////////////////////////
 void GraphicsStateGuardian::
 end_draw_primitives() {
+  _vertex_data = NULL;
 }
 
 ////////////////////////////////////////////////////////////////////

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

@@ -148,7 +148,7 @@ public:
   virtual CPT(RenderState) begin_decal_base_second();
   virtual void finish_decal();
 
-  virtual bool begin_draw_primitives(const qpGeomVertexData *vertex_data);
+  virtual bool begin_draw_primitives(const qpGeom *geom, const qpGeomVertexData *vertex_data);
   virtual void draw_triangles(const qpGeomTriangles *primitive);
   virtual void draw_tristrips(const qpGeomTristrips *primitive);
   virtual void draw_trifans(const qpGeomTrifans *primitive);

+ 3 - 2
panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx

@@ -2606,10 +2606,10 @@ draw_sphere(GeomSphere *geom, GeomContext *gc) {
 //               are ok, false to abort this group of primitives.
 ////////////////////////////////////////////////////////////////////
 bool DXGraphicsStateGuardian8::
-begin_draw_primitives(const qpGeomVertexData *vertex_data) {
+begin_draw_primitives(const qpGeom *geom, const qpGeomVertexData *vertex_data) {
   DO_PSTATS_STUFF(_draw_primitive_pcollector.start());
 
-  if (!GraphicsStateGuardian::begin_draw_primitives(vertex_data)) {
+  if (!GraphicsStateGuardian::begin_draw_primitives(geom, vertex_data)) {
     return false;
   }
   nassertr(_vertex_data != (qpGeomVertexData *)NULL, false);
@@ -2694,6 +2694,7 @@ draw_tristrips(const qpGeomTristrips *primitive) {
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian8::
 end_draw_primitives() {
+  GraphicsStateGuardian::end_draw_primitives();
   DO_PSTATS_STUFF(_draw_primitive_pcollector.stop());
 }
 

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

@@ -90,7 +90,7 @@ public:
   virtual void draw_trifan(GeomTrifan *geom, GeomContext *gc);
   virtual void draw_sphere(GeomSphere *geom, GeomContext *gc);
 
-  virtual bool begin_draw_primitives(const qpGeomVertexData *vertex_data);
+  virtual bool begin_draw_primitives(const qpGeom *geom, const qpGeomVertexData *vertex_data);
   virtual void draw_triangles(const qpGeomTriangles *primitive);
   virtual void draw_tristrips(const qpGeomTristrips *primitive);
   virtual void end_draw_primitives();

+ 2 - 0
panda/src/glstuff/glGeomContext_src.h

@@ -30,6 +30,8 @@ public:
   // This is the GL display list index.
   GLuint _index;
 
+  UpdateSeq _modified;
+
   // The number of vertices encoded in the display list, for stats
   // reporting.
 #ifdef DO_PSTATS

+ 143 - 74
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -2030,14 +2030,53 @@ draw_sphere(GeomSphere *geom, GeomContext *gc) {
 //               are ok, false to abort this group of primitives.
 ////////////////////////////////////////////////////////////////////
 bool CLP(GraphicsStateGuardian)::
-begin_draw_primitives(const qpGeomVertexData *vertex_data) {
+begin_draw_primitives(const qpGeom *geom, const qpGeomVertexData *vertex_data) {
   DO_PSTATS_STUFF(_draw_primitive_pcollector.start());
 
-  if (!GraphicsStateGuardian::begin_draw_primitives(vertex_data)) {
+  if (!GraphicsStateGuardian::begin_draw_primitives(geom, vertex_data)) {
     return false;
   }
   nassertr(_vertex_data != (qpGeomVertexData *)NULL, false);
 
+  _geom_display_list = NULL;
+
+  if (geom->get_usage_hint() == qpGeomUsageHint::UH_static && 
+      _vertex_data->get_usage_hint() == qpGeomUsageHint::UH_static &&
+      display_lists) {
+    // If the geom appears to be totally static, try to build it into
+    // a display list.
+    GeomContext *gc = ((qpGeom *)geom)->prepare_now(get_prepared_objects(), this);
+    nassertr(gc != (GeomContext *)NULL, false);
+    CLP(GeomContext) *ggc = DCAST(CLP(GeomContext), gc);
+    if (ggc->_modified == geom->get_modified()) {
+      // If it hasn't been modified, just play the display list again.
+      GLP(CallList)(ggc->_index);
+#ifdef DO_PSTATS
+      _vertices_display_list_pcollector.add_level(ggc->_num_verts);
+#endif
+      
+      // And now we don't need to do anything else for this geom.
+      return false;
+    }
+
+    // If it has been modified, or this is the first time, then we
+    // need to build the display list up.
+    GLP(NewList)(ggc->_index, GL_COMPILE_AND_EXECUTE);
+    ggc->_modified = geom->get_modified();
+    _geom_display_list = ggc;
+
+#ifdef DO_PSTATS
+    // Count up the number of vertices we're about to render, by
+    // checking the PStats vertex counters now, and at the end.  This is
+    // kind of hacky, but this is debug code.
+    _num_display_list_verts_before =
+      _vertices_tristrip_pcollector.get_level() +
+      _vertices_trifan_pcollector.get_level() +
+      _vertices_tri_pcollector.get_level() +
+      _vertices_other_pcollector.get_level();
+#endif
+  }
+
   const qpGeomVertexArrayData *array_data;
   int num_components;
   qpGeomVertexDataType::NumericType numeric_type;
@@ -2051,9 +2090,6 @@ begin_draw_primitives(const qpGeomVertexData *vertex_data) {
     GLP(VertexPointer)(num_components, get_numeric_type(numeric_type), 
                        stride, client_pointer + start);
     GLP(EnableClientState)(GL_VERTEX_ARRAY);
-  } else {
-    // No vertex data?  No primitives!
-    return false;
   }
 
   if (wants_normals() && 
@@ -2073,14 +2109,8 @@ begin_draw_primitives(const qpGeomVertexData *vertex_data) {
                                      array_data, num_components, numeric_type, 
                                      start, stride)) {
       const unsigned char *client_pointer = setup_array_data(array_data);
-      if (numeric_type == qpGeomVertexDataType::NT_packed_argb) {
-        // Temporary hack--this will probably reverse r and b.
-        GLP(ColorPointer)(4, GL_UNSIGNED_BYTE, stride, client_pointer + start);
-        
-      } else {
-        GLP(ColorPointer)(num_components, get_numeric_type(numeric_type), 
-                          stride, client_pointer + start);
-      }
+      GLP(ColorPointer)(num_components, get_numeric_type(numeric_type), 
+                        stride, client_pointer + start);
       GLP(EnableClientState)(GL_COLOR_ARRAY);
 
     } else {
@@ -2105,8 +2135,6 @@ begin_draw_primitives(const qpGeomVertexData *vertex_data) {
     GLP(DisableClientState)(GL_TEXTURE_COORD_ARRAY);
   }
 
-  issue_scene_graph_color();
-
   return true;
 }
 
@@ -2168,7 +2196,23 @@ draw_tristrips(const qpGeomTristrips *primitive) {
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsStateGuardian)::
 end_draw_primitives() {
-  GLP(DisableClientState)(GL_VERTEX_ARRAY);
+  if (_geom_display_list != NULL) {
+    // If we were building a display list, close it now.
+    GLP(EndList)();
+
+#ifdef DO_PSTATS
+    float num_verts_after =
+      _vertices_tristrip_pcollector.get_level() +
+      _vertices_trifan_pcollector.get_level() +
+      _vertices_tri_pcollector.get_level() +
+      _vertices_other_pcollector.get_level();
+    float num_verts = num_verts_after - _num_display_list_verts_before;
+    _geom_display_list->_num_verts = (int)(num_verts + 0.5);
+#endif
+  }
+  _geom_display_list = NULL;
+
+  GraphicsStateGuardian::end_draw_primitives();
 
   DO_PSTATS_STUFF(_draw_primitive_pcollector.stop());
 }
@@ -2262,69 +2306,94 @@ release_texture(TextureContext *tc) {
 ////////////////////////////////////////////////////////////////////
 GeomContext *CLP(GraphicsStateGuardian)::
 prepare_geom(Geom *geom) {
-  if (!_vertex_colors_enabled) {
-    // We can't build a display list (or play back a display list) if
-    // its color is overridden with a scene graph color.  Maybe if we
-    // take advantage of the OpenGL color matrix we can do this, but
-    // for now we'll just ignore it.
-    return NULL;
-  }
+  // Temporary test until the experimental Geom rewrite becomes the
+  // actual Geom implementation.
+  if (geom->is_exact_type(qpGeom::get_class_type())) {
+    CLP(GeomContext) *ggc = new CLP(GeomContext)(geom);
+    ggc->_index = GLP(GenLists)(1);
+    if (GLCAT.is_debug()) {
+      GLCAT.debug()
+        << "preparing " << *geom << ", index " << ggc->_index << "\n";
+    }
+    if (ggc->_index == 0) {
+      GLCAT.error()
+        << "Ran out of display list indices.\n";
+      delete ggc;
+      return NULL;
+    }
 
-  if (geom->is_dynamic()) {
-    // If the Geom is dynamic in some way, we shouldn't try to
-    // display-list it.
-    return NULL;
-  }
+    report_my_gl_errors();
+    return ggc;
 
-  CLP(GeomContext) *ggc = new CLP(GeomContext)(geom);
-  ggc->_index = GLP(GenLists)(1);
-  if (GLCAT.is_debug()) {
-    GLCAT.debug()
-      << "preparing " << *geom << ", index " << ggc->_index << "\n";
-  }
-  if (ggc->_index == 0) {
-    GLCAT.error()
-      << "Ran out of display list indices.\n";
-    delete ggc;
-    return NULL;
-  }
+  } else {
+    // Original Geom display list implementation.  Slightly broken,
+    // since it doesn't work well with scene graph color
+    // manipulations.
+
+    if (!_vertex_colors_enabled) {
+      // We can't build a display list (or play back a display list) if
+      // its color is overridden with a scene graph color.  Maybe if we
+      // take advantage of the OpenGL color matrix we can do this, but
+      // for now we'll just ignore it.
+      return NULL;
+    }
+
+    if (geom->is_dynamic()) {
+      // If the Geom is dynamic in some way, we shouldn't try to
+      // display-list it.
+      return NULL;
+    }
+
+    CLP(GeomContext) *ggc = new CLP(GeomContext)(geom);
+    ggc->_index = GLP(GenLists)(1);
+    if (GLCAT.is_debug()) {
+      GLCAT.debug()
+        << "preparing " << *geom << ", index " << ggc->_index << "\n";
+    }
+    if (ggc->_index == 0) {
+      GLCAT.error()
+        << "Ran out of display list indices.\n";
+      delete ggc;
+      return NULL;
+    }
 
-  // We need to temporarily force normals and UV's on, so the display
-  // list will have them built in.
-  //force_texcoords(); 
-  force_normals();
+    // We need to temporarily force normals and UV's on, so the display
+    // list will have them built in.
+    //force_texcoords(); 
+    force_normals();
 
 #ifdef DO_PSTATS
-  // Count up the number of vertices we're about to render, by
-  // checking the PStats vertex counters now, and at the end.  This is
-  // kind of hacky, but this is debug code.
-  float num_verts_before =
-    _vertices_tristrip_pcollector.get_level() +
-    _vertices_trifan_pcollector.get_level() +
-    _vertices_tri_pcollector.get_level() +
-    _vertices_other_pcollector.get_level();
+    // Count up the number of vertices we're about to render, by
+    // checking the PStats vertex counters now, and at the end.  This is
+    // kind of hacky, but this is debug code.
+    float num_verts_before =
+      _vertices_tristrip_pcollector.get_level() +
+      _vertices_trifan_pcollector.get_level() +
+      _vertices_tri_pcollector.get_level() +
+      _vertices_other_pcollector.get_level();
 #endif
 
-  // Now define the display list.
-  GLP(NewList)(ggc->_index, GL_COMPILE);
-  geom->draw_immediate(this, NULL);
-  GLP(EndList)();
+    // Now define the display list.
+    GLP(NewList)(ggc->_index, GL_COMPILE);
+    geom->draw_immediate(this, NULL);
+    GLP(EndList)();
 
 #ifdef DO_PSTATS
-  float num_verts_after =
-    _vertices_tristrip_pcollector.get_level() +
-    _vertices_trifan_pcollector.get_level() +
-    _vertices_tri_pcollector.get_level() +
-    _vertices_other_pcollector.get_level();
-  float num_verts = num_verts_after - num_verts_before;
-  ggc->_num_verts = (int)(num_verts + 0.5);
+    float num_verts_after =
+      _vertices_tristrip_pcollector.get_level() +
+      _vertices_trifan_pcollector.get_level() +
+      _vertices_tri_pcollector.get_level() +
+      _vertices_other_pcollector.get_level();
+    float num_verts = num_verts_after - num_verts_before;
+    ggc->_num_verts = (int)(num_verts + 0.5);
 #endif
 
-  undo_force_normals();
-  //undo_force_texcoords();
+    undo_force_normals();
+    //undo_force_texcoords();
 
-  report_my_gl_errors();
-  return ggc;
+    report_my_gl_errors();
+    return ggc;
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -2464,7 +2533,7 @@ setup_array_data(const qpGeomVertexArrayData *data) {
     // No support for buffer objects; always render from client.
     return data->get_data();
   }
-  if (!vertex_buffers ||
+  if (!vertex_buffers || _geom_display_list != NULL ||
       data->get_usage_hint() == qpGeomUsageHint::UH_client) {
     // The array specifies client rendering only, or buffer objects
     // are configured off.
@@ -2534,12 +2603,12 @@ apply_index_buffer(IndexBufferContext *ibc) {
     }
     if (gibc->changed_size()) {
       _glBufferData(GL_ELEMENT_ARRAY_BUFFER, gibc->get_data()->get_data_size_bytes(),
-                    gibc->get_data()->get_vertices(), 
+                    gibc->get_data()->get_flat_last_vertices(), 
                     get_usage(gibc->get_data()->get_usage_hint()));
 
     } else {
       _glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, gibc->get_data_size_bytes(),
-                       gibc->get_data()->get_vertices());
+                       gibc->get_data()->get_flat_last_vertices());
     }
 
     gibc->mark_loaded();
@@ -2593,19 +2662,19 @@ const unsigned short *CLP(GraphicsStateGuardian)::
 setup_primitive(const qpGeomPrimitive *data) {
   if (!_supports_buffers) {
     // No support for buffer objects; always render from client.
-    return data->get_vertices();
+    return data->get_flat_last_vertices();
   }
-  if (!vertex_buffers ||
+  if (!vertex_buffers || _geom_display_list != NULL ||
       data->get_usage_hint() == qpGeomUsageHint::UH_client) {
     // The array specifies client rendering only, or buffer objects
     // are configured off.
     _glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
-    return data->get_vertices();
+    return data->get_flat_last_vertices();
   }
 
   // Prepare the buffer object and bind it.
   IndexBufferContext *ibc = ((qpGeomPrimitive *)data)->prepare_now(get_prepared_objects(), this);
-  nassertr(ibc != (IndexBufferContext *)NULL, data->get_vertices());
+  nassertr(ibc != (IndexBufferContext *)NULL, data->get_flat_last_vertices());
   apply_index_buffer(ibc);
 
   // NULL is the OpenGL convention for the first byte of the buffer.

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

@@ -58,6 +58,8 @@ typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVPROC) (GLenum target, const GLfloat
 typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode);
 typedef void (APIENTRYP PFNGLBLENDCOLORPROC) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
 
+class CLP(GeomContext);
+
 ////////////////////////////////////////////////////////////////////
 //       Class : GLGraphicsStateGuardian
 // Description : A GraphicsStateGuardian specialized for rendering
@@ -90,7 +92,7 @@ public:
   virtual void draw_trifan(GeomTrifan *geom, GeomContext *gc);
   virtual void draw_sphere(GeomSphere *geom, GeomContext *gc);
 
-  virtual bool begin_draw_primitives(const qpGeomVertexData *vertex_data);
+  virtual bool begin_draw_primitives(const qpGeom *geom, const qpGeomVertexData *vertex_data);
   virtual void draw_triangles(const qpGeomTriangles *primitive);
   virtual void draw_tristrips(const qpGeomTristrips *primitive);
   virtual void end_draw_primitives();
@@ -315,6 +317,8 @@ protected:
 
   int _pass_number;
   bool _auto_rescale_normal;
+  CLP(GeomContext) *_geom_display_list;
+  float _num_display_list_verts_before;
   
   int _error_count;
 

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

@@ -84,7 +84,9 @@ ConfigVariableBool retained_mode
           "creates specific cache information (like display lists or vertex "
           "buffers) with the GSG for static geometry, when supported by the "
           "GSG.  Set it false to use only immediate mode, which sends the "
-          "vertices to the GSG every frame."));
+          "vertices to the GSG every frame.  This is used only in the "
+          "original Geom implementation; it is replaced by display-lists "
+          "in the experimental Geom rewrite."));
 
 ConfigVariableBool vertex_buffers
 ("vertex-buffers", false,
@@ -95,6 +97,16 @@ ConfigVariableBool vertex_buffers
           "graphics memory (which might otherwise be used for textures "
           "or offscreen buffers)."));
 
+ConfigVariableBool display_lists
+("display-lists", false,
+ PRC_DESC("Set this true to allow the use of OpenGL display lists for "
+          "rendering static geometry.  On some systems, this can result "
+          "in a performance improvement over vertex buffers alone; on "
+          "other systems (particularly low-end systems) it makes little to "
+          "no difference.  This has no effect on DirectX rendering.  If "
+          "vertex-buffers is also enabled, then OpenGL buffer objects "
+          "will also be created for dynamic geometry."));
+
 ConfigVariableBool use_qpgeom
 ("use-qpgeom", false,
  PRC_DESC("A temporary variable while the experimental Geom rewrite is "

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

@@ -52,6 +52,7 @@ extern EXPCL_PANDA ConfigVariableBool keep_texture_ram;
 extern EXPCL_PANDA ConfigVariableBool keep_geom_ram;
 extern EXPCL_PANDA ConfigVariableBool retained_mode;
 extern EXPCL_PANDA ConfigVariableBool vertex_buffers;
+extern EXPCL_PANDA ConfigVariableBool display_lists;
 
 extern EXPCL_PANDA ConfigVariableBool use_qpgeom;
 

+ 38 - 2
panda/src/gobj/qpgeom.I

@@ -17,6 +17,23 @@
 ////////////////////////////////////////////////////////////////////
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeom::get_usage_hint
+//       Access: Published
+//  Description: Returns the minimum (i.e. most dynamic) usage_hint
+//               among all of the individual GeomPrimitives that have
+//               been added to the geom.
+////////////////////////////////////////////////////////////////////
+INLINE qpGeomUsageHint::UsageHint qpGeom::
+get_usage_hint() const {
+  CDReader cdata(_cycler);
+  if (!cdata->_got_usage_hint) {
+    CDWriter cdataw(((qpGeom *)this)->_cycler, cdata);
+    ((qpGeom *)this)->reset_usage_hint(cdataw);
+  }
+  return cdata->_usage_hint;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeom::get_vertex_data
 //       Access: Published
@@ -71,6 +88,7 @@ modify_primitive(int i) {
   clear_cache();
   CDWriter cdata(_cycler);
   nassertr(i >= 0 && i < (int)cdata->_primitives.size(), NULL);
+  cdata->_got_usage_hint = false;
   return cdata->_primitives[i];
 }
 
@@ -85,6 +103,19 @@ set_primitive(int i, const qpGeomPrimitive *primitive) {
   clear_cache();
   CDWriter cdata(_cycler);
   nassertv(i >= 0 && i < (int)cdata->_primitives.size());
+  if (cdata->_got_usage_hint &&
+      cdata->_primitives[i]->get_usage_hint() != primitive->get_usage_hint()) {
+    if (cdata->_primitives[i]->get_usage_hint() < primitive->get_usage_hint()) {
+      // If we're reducing the usage hint, we might also be reducing
+      // the minimum usage hit.
+      cdata->_usage_hint = min(cdata->_usage_hint, primitive->get_usage_hint());
+    } else { // (cdata->_primitives[i]->get_usage_hint() > primitive->get_usage_hint())
+      // If we're increasing it, we might have to rederive the minimum.
+      if (cdata->_usage_hint == cdata->_primitives[i]->get_usage_hint()) {
+        cdata->_got_usage_hint = false;
+      }
+    }
+  }
   cdata->_primitives[i] = (qpGeomPrimitive *)primitive;
 }
 
@@ -94,7 +125,10 @@ set_primitive(int i, const qpGeomPrimitive *primitive) {
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE qpGeom::CData::
-CData() {
+CData() :
+  _usage_hint(qpGeomUsageHint::UH_static),
+  _got_usage_hint(false)
+{
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -105,7 +139,9 @@ CData() {
 INLINE qpGeom::CData::
 CData(const qpGeom::CData &copy) :
   _data(copy._data),
-  _primitives(copy._primitives)
+  _primitives(copy._primitives),
+  _usage_hint(copy._usage_hint),
+  _got_usage_hint(copy._got_usage_hint)
 {
 }
 

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

@@ -22,6 +22,7 @@
 #include "bamReader.h"
 #include "bamWriter.h"
 
+UpdateSeq qpGeom::_next_modified;
 TypeHandle qpGeom::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
@@ -152,6 +153,10 @@ add_primitive(const qpGeomPrimitive *primitive) {
   clear_cache();
   CDWriter cdata(_cycler);
   cdata->_primitives.push_back((qpGeomPrimitive *)primitive);
+
+  if (cdata->_got_usage_hint) {
+    cdata->_usage_hint = min(cdata->_usage_hint, primitive->get_usage_hint());
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -164,6 +169,12 @@ remove_primitive(int i) {
   clear_cache();
   CDWriter cdata(_cycler);
   nassertv(i >= 0 && i < (int)cdata->_primitives.size());
+  if (cdata->_got_usage_hint &&
+      cdata->_usage_hint == cdata->_primitives[i]->get_usage_hint()) {
+    // Maybe we're raising the minimum usage_hint; we have to rederive
+    // the usage_hint later.
+    cdata->_got_usage_hint = false;
+  }
   cdata->_primitives.erase(cdata->_primitives.begin() + i);
 }
 
@@ -203,6 +214,34 @@ get_num_bytes() const {
   return num_bytes;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeom::get_modified
+//       Access: Published
+//  Description: Returns the maximum UpdateSeq of all the Geom's
+//               individual primitives and vertex arrays.  This,
+//               therefore, will change only when any part of the Geom
+//               changes.
+////////////////////////////////////////////////////////////////////
+UpdateSeq qpGeom::
+get_modified() const {
+  CDReader cdata(_cycler);
+  UpdateSeq seq;
+
+  Primitives::const_iterator pi;
+  for (pi = cdata->_primitives.begin(); 
+       pi != cdata->_primitives.end();
+       ++pi) {
+    seq = max(seq, (*pi)->get_modified());
+  }
+
+  int num_arrays = cdata->_data->get_num_arrays();
+  for (int i = 0; i < num_arrays; ++i) {
+    seq = max(seq, cdata->_data->get_array(i)->get_modified());
+  }
+
+  return seq;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeom::munge_geom
 //       Access: Published
@@ -342,9 +381,25 @@ clear_cache() {
 ////////////////////////////////////////////////////////////////////
 void qpGeom::
 draw(GraphicsStateGuardianBase *gsg, const qpGeomVertexData *vertex_data) const {
-  CDReader cdata(_cycler);
+#ifdef DO_PIPELINING
+  // Make sure the usage_hint is already updated before we start to
+  // draw, so we don't end up with a circular lock if the GSG asks us
+  // to update this while we're holding the read lock.
+  {
+    CDReader cdata(_cycler);
+    if (!cdata->_got_usage_hint) {
+      CDWriter cdataw(((qpGeom *)this)->_cycler, cdata);
+      ((qpGeom *)this)->reset_usage_hint(cdataw);
+    }
+  }
+  // TODO: fix up the race condition between this line and the next.
+  // Maybe CDWriter's elevate-to-write should return the read lock to
+  // its original locked state when it's done.
+#endif  // DO_PIPELINING
 
-  if (gsg->begin_draw_primitives(vertex_data)) {
+  CDReader cdata(_cycler);
+  
+  if (gsg->begin_draw_primitives(this, vertex_data)) {
     Primitives::const_iterator pi;
     for (pi = cdata->_primitives.begin(); 
          pi != cdata->_primitives.end();
@@ -355,6 +410,25 @@ draw(GraphicsStateGuardianBase *gsg, const qpGeomVertexData *vertex_data) const
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeom::get_next_modified
+//       Access: Public
+//  Description: Returns a monotonically increasing sequence.  Each
+//               time this is called, a new sequence number is
+//               returned, higher than the previous value.
+//
+//               This is used to ensure that
+//               GeomVertexArrayData::get_modified() and
+//               GeomPrimitive::get_modified() update from the same
+//               space, so that Geom::get_modified() returns a
+//               meaningful value.
+////////////////////////////////////////////////////////////////////
+UpdateSeq qpGeom::
+get_next_modified() {
+  ++_next_modified;
+  return _next_modified;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeom::recompute_bound
 //       Access: Protected, Virtual
@@ -445,6 +519,23 @@ remove_cache_entry(const qpGeomMunger *modifier) const {
   ((qpGeom *)this)->_cycler.release_write_stage(0, cdata);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeom::reset_usage_hint
+//       Access: Private
+//  Description: Recomputes the minimum usage_hint.
+////////////////////////////////////////////////////////////////////
+void qpGeom::
+reset_usage_hint(qpGeom::CDWriter &cdata) {
+  cdata->_usage_hint = qpGeomUsageHint::UH_static;
+  Primitives::const_iterator pi;
+  for (pi = cdata->_primitives.begin(); 
+       pi != cdata->_primitives.end();
+       ++pi) {
+    cdata->_usage_hint = min(cdata->_usage_hint, (*pi)->get_usage_hint());
+  }
+  cdata->_got_usage_hint = true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeom::register_with_read_factory
 //       Access: Public, Static

+ 13 - 0
panda/src/gobj/qpgeom.h

@@ -29,6 +29,8 @@
 #include "qpgeomVertexData.h"
 #include "qpgeomPrimitive.h"
 #include "qpgeomMunger.h"
+#include "qpgeomUsageHint.h"
+#include "updateSeq.h"
 #include "pointerTo.h"
 #include "geom.h"
 
@@ -61,6 +63,8 @@ PUBLISHED:
   // Temporary.
   virtual Geom *make_copy() const;
 
+  INLINE qpGeomUsageHint::UsageHint get_usage_hint() const;
+
   INLINE CPT(qpGeomVertexData) get_vertex_data() const;
   PT(qpGeomVertexData) modify_vertex_data();
   void set_vertex_data(const qpGeomVertexData *data);
@@ -74,6 +78,7 @@ PUBLISHED:
   void clear_primitives();
 
   int get_num_bytes() const;
+  UpdateSeq get_modified() const;
 
   void munge_geom(const qpGeomMunger *munger,
                   CPT(qpGeom) &result, CPT(qpGeomVertexData) &data) const;
@@ -87,6 +92,8 @@ public:
   void draw(GraphicsStateGuardianBase *gsg, 
             const qpGeomVertexData *vertex_data) const;
 
+  static UpdateSeq get_next_modified();
+
 protected:
   virtual BoundingVolume *recompute_bound();
 
@@ -120,6 +127,8 @@ private:
 
     PT(qpGeomVertexData) _data;
     Primitives _primitives;
+    qpGeomUsageHint::UsageHint _usage_hint;
+    bool _got_usage_hint;
     MungedCache _munged_cache;
   };
 
@@ -127,6 +136,10 @@ private:
   typedef CycleDataReader<CData> CDReader;
   typedef CycleDataWriter<CData> CDWriter;
 
+  void reset_usage_hint(CDWriter &cdata);
+
+  static UpdateSeq _next_modified;
+
 public:
   static void register_with_read_factory();
   virtual void write_datagram(BamWriter *manager, Datagram &dg);

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

@@ -17,6 +17,7 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "qpgeomPrimitive.h"
+#include "qpgeom.h"
 #include "qpgeomVertexData.h"
 #include "qpgeomVertexArrayFormat.h"
 #include "qpgeomVertexDataType.h"
@@ -536,7 +537,7 @@ clear_cache() {
 
   // This, on the other hand, should be applied to the current
   // pipeline stage.
-  ++(cdata->_modified);
+  cdata->_modified = qpGeom::get_next_modified();
 }
 
 

+ 10 - 6
panda/src/gobj/qpgeomUsageHint.h

@@ -50,26 +50,30 @@
 class EXPCL_PANDA qpGeomUsageHint {
 PUBLISHED:
   enum UsageHint {
+    // The following are intentionally ordered from most dynamic to
+    // most static.  In general, if usage_a < usage_b, then usage_a is
+    // more dynamic than usage_b.
+
     // UH_client: don't attempt to upload the data; always keep it on
     // the client.
     UH_client,
 
     // UH_stream: the data will be created once, used to render a few
     // times, and then discarded.  This should be used for short-lived
-    // temporary arrays.
+    // temporary objects.
     UH_stream,
 
+    // UH_dynamic: the data will be repeatedly modified and
+    // re-rendered.  This is for data that will be modified at
+    // runtime, such as animated or soft-skinned vertices.
+    UH_dynamic,
+
     // UH_static: the data will be created once, and used to render
     // many times, without modification.  This is the most common
     // case, since typically vertex data is not directly animated
     // (this is not related to scene graph animation, e.g. from
     // adjusting transforms on a node).
     UH_static,
-
-    // UH_dynamic: the data will be repeatedly modified and
-    // re-rendered.  This is for data that will be modified at
-    // runtime, such as animated or soft-skinned vertices.
-    UH_dynamic,
   };
 };
 

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

@@ -52,43 +52,6 @@ get_data() const {
   return cdata->_data;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexArrayData::modify_data
-//       Access: Published
-//  Description: Returns a modifiable pointer to the actual vertex
-//               array, so that application code may directly
-//               manipulate the vertices.
-////////////////////////////////////////////////////////////////////
-INLINE PTA_uchar qpGeomVertexArrayData::
-modify_data() {
-  // Perform copy-on-write: if the reference count on the vertex data
-  // is greater than 1, assume some other GeomVertexData has the same
-  // pointer, so make a copy of it first.
-  CDWriter cdata(_cycler);
-
-  if (cdata->_data.get_ref_count() > 1) {
-    PTA_uchar orig_data = cdata->_data;
-    cdata->_data = PTA_uchar();
-    cdata->_data.v() = orig_data.v();
-  }
-  ++(cdata->_modified);
-
-  return cdata->_data;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexArrayData::set_data
-//       Access: Published
-//  Description: Replaces the vertex data array with a completely new
-//               array.
-////////////////////////////////////////////////////////////////////
-INLINE void qpGeomVertexArrayData::
-set_data(CPTA_uchar array) {
-  CDWriter cdata(_cycler);
-  cdata->_data = (PTA_uchar &)array;
-  ++(cdata->_modified);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexArrayData::get_num_vertices
 //       Access: Published

+ 39 - 1
panda/src/gobj/qpgeomVertexArrayData.cxx

@@ -17,6 +17,7 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "qpgeomVertexArrayData.h"
+#include "qpgeom.h"
 #include "preparedGraphicsObjects.h"
 #include "bamReader.h"
 #include "bamWriter.h"
@@ -82,6 +83,43 @@ qpGeomVertexArrayData::
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::modify_data
+//       Access: Published
+//  Description: Returns a modifiable pointer to the actual vertex
+//               array, so that application code may directly
+//               manipulate the vertices.
+////////////////////////////////////////////////////////////////////
+PTA_uchar qpGeomVertexArrayData::
+modify_data() {
+  // Perform copy-on-write: if the reference count on the vertex data
+  // is greater than 1, assume some other GeomVertexData has the same
+  // pointer, so make a copy of it first.
+  CDWriter cdata(_cycler);
+
+  if (cdata->_data.get_ref_count() > 1) {
+    PTA_uchar orig_data = cdata->_data;
+    cdata->_data = PTA_uchar();
+    cdata->_data.v() = orig_data.v();
+  }
+  cdata->_modified = qpGeom::get_next_modified();
+
+  return cdata->_data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::set_data
+//       Access: Published
+//  Description: Replaces the vertex data array with a completely new
+//               array.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexArrayData::
+set_data(CPTA_uchar array) {
+  CDWriter cdata(_cycler);
+  cdata->_data = (PTA_uchar &)array;
+  cdata->_modified = qpGeom::get_next_modified();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: qpGeomVertexArrayData::set_num_vertices
 //       Access: Published
@@ -125,7 +163,7 @@ set_num_vertices(int n) {
       }
     }
 
-    ++(cdata->_modified);
+    cdata->_modified = qpGeom::get_next_modified();
 
     return true;
   }

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

@@ -66,8 +66,8 @@ PUBLISHED:
   INLINE qpGeomUsageHint::UsageHint get_usage_hint() const;
 
   INLINE CPTA_uchar get_data() const;
-  INLINE PTA_uchar modify_data();
-  INLINE void set_data(CPTA_uchar data);
+  PTA_uchar modify_data();
+  void set_data(CPTA_uchar data);
 
   INLINE int get_num_vertices() const;
   bool set_num_vertices(int n);

+ 2 - 1
panda/src/gsgbase/graphicsStateGuardianBase.h

@@ -45,6 +45,7 @@ class GeomTri;
 class GeomTristrip;
 class GeomTrifan;
 class GeomSphere;
+class qpGeom;
 class qpGeomVertexData;
 class qpGeomVertexArrayData;
 class qpGeomPrimitive;
@@ -183,7 +184,7 @@ public:
   virtual void draw_trifan(GeomTrifan *geom, GeomContext *gc)=0;
   virtual void draw_sphere(GeomSphere *geom, GeomContext *gc)=0;
 
-  virtual bool begin_draw_primitives(const qpGeomVertexData *vertex_data)=0;
+  virtual bool begin_draw_primitives(const qpGeom *geom, const qpGeomVertexData *vertex_data)=0;
   virtual void draw_triangles(const qpGeomTriangles *primitive)=0;
   virtual void draw_tristrips(const qpGeomTristrips *primitive)=0;
   virtual void draw_trifans(const qpGeomTrifans *primitive)=0;