Quellcode durchsuchen

vertex buffers?

David Rose vor 21 Jahren
Ursprung
Commit
85b019feda
33 geänderte Dateien mit 1473 neuen und 106 gelöschten Zeilen
  1. 43 1
      panda/src/display/graphicsStateGuardian.cxx
  2. 7 0
      panda/src/display/graphicsStateGuardian.h
  3. 2 2
      panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx
  4. 2 1
      panda/src/egg2pg/eggLoader.cxx
  5. 6 2
      panda/src/framework/windowFramework.cxx
  6. 3 0
      panda/src/glstuff/Sources.pp
  7. 30 0
      panda/src/glstuff/glDataContext_src.I
  8. 19 0
      panda/src/glstuff/glDataContext_src.cxx
  9. 52 0
      panda/src/glstuff/glDataContext_src.h
  10. 202 6
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  11. 14 0
      panda/src/glstuff/glGraphicsStateGuardian_src.h
  12. 1 0
      panda/src/glstuff/glmisc_src.cxx
  13. 1 0
      panda/src/glstuff/glstuff_src.cxx
  14. 1 0
      panda/src/glstuff/glstuff_src.h
  15. 10 2
      panda/src/gobj/Sources.pp
  16. 18 0
      panda/src/gobj/config_gobj.cxx
  17. 1 0
      panda/src/gobj/config_gobj.h
  18. 29 0
      panda/src/gobj/dataContext.I
  19. 21 0
      panda/src/gobj/dataContext.cxx
  20. 71 0
      panda/src/gobj/dataContext.h
  21. 2 0
      panda/src/gobj/gobj_composite1.cxx
  22. 140 0
      panda/src/gobj/preparedGraphicsObjects.cxx
  23. 16 0
      panda/src/gobj/preparedGraphicsObjects.h
  24. 1 1
      panda/src/gobj/qpgeom.cxx
  25. 1 1
      panda/src/gobj/qpgeomPrimitive.cxx
  26. 167 0
      panda/src/gobj/qpgeomVertexArrayData.I
  27. 360 0
      panda/src/gobj/qpgeomVertexArrayData.cxx
  28. 164 0
      panda/src/gobj/qpgeomVertexArrayData.h
  29. 11 7
      panda/src/gobj/qpgeomVertexData.I
  30. 61 73
      panda/src/gobj/qpgeomVertexData.cxx
  31. 11 9
      panda/src/gobj/qpgeomVertexData.h
  32. 1 1
      panda/src/gobj/qpgeomVertexFormat.cxx
  33. 5 0
      panda/src/gsgbase/graphicsStateGuardianBase.h

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

@@ -20,6 +20,7 @@
 #include "graphicsStateGuardian.h"
 #include "graphicsStateGuardian.h"
 #include "config_display.h"
 #include "config_display.h"
 #include "textureContext.h"
 #include "textureContext.h"
+#include "dataContext.h"
 #include "renderBuffer.h"
 #include "renderBuffer.h"
 #include "colorAttrib.h"
 #include "colorAttrib.h"
 #include "colorScaleAttrib.h"
 #include "colorScaleAttrib.h"
@@ -46,6 +47,8 @@ PStatCollector GraphicsStateGuardian::_total_texusage_pcollector("Texture usage"
 PStatCollector GraphicsStateGuardian::_active_texusage_pcollector("Texture usage:Active");
 PStatCollector GraphicsStateGuardian::_active_texusage_pcollector("Texture usage:Active");
 PStatCollector GraphicsStateGuardian::_total_geom_pcollector("Prepared Geoms");
 PStatCollector GraphicsStateGuardian::_total_geom_pcollector("Prepared Geoms");
 PStatCollector GraphicsStateGuardian::_active_geom_pcollector("Prepared Geoms:Active");
 PStatCollector GraphicsStateGuardian::_active_geom_pcollector("Prepared Geoms:Active");
+PStatCollector GraphicsStateGuardian::_total_buffers_pcollector("Prepared Bufferss");
+PStatCollector GraphicsStateGuardian::_active_buffers_pcollector("Prepared Bufferss:Active");
 PStatCollector GraphicsStateGuardian::_total_geom_node_pcollector("Prepared GeomNodes");
 PStatCollector GraphicsStateGuardian::_total_geom_node_pcollector("Prepared GeomNodes");
 PStatCollector GraphicsStateGuardian::_active_geom_node_pcollector("Prepared GeomNodes:Active");
 PStatCollector GraphicsStateGuardian::_active_geom_node_pcollector("Prepared GeomNodes:Active");
 PStatCollector GraphicsStateGuardian::_total_texmem_pcollector("Texture memory");
 PStatCollector GraphicsStateGuardian::_total_texmem_pcollector("Texture memory");
@@ -267,6 +270,28 @@ void GraphicsStateGuardian::
 release_geom(GeomContext *) {
 release_geom(GeomContext *) {
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::prepare_data
+//       Access: Public, Virtual
+//  Description: Prepares the indicated data array for retained-mode
+//               rendering.
+////////////////////////////////////////////////////////////////////
+DataContext *GraphicsStateGuardian::
+prepare_data(qpGeomVertexArrayData *) {
+  return (DataContext *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::release_data
+//       Access: Public, Virtual
+//  Description: Frees the resources previously allocated via a call
+//               to prepare_data(), including deleting the DataContext
+//               itself, if necessary.
+////////////////////////////////////////////////////////////////////
+void GraphicsStateGuardian::
+release_data(DataContext *) {
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::get_geom_munger
 //     Function: GraphicsStateGuardian::get_geom_munger
 //       Access: Public, Virtual
 //       Access: Public, Virtual
@@ -1458,7 +1483,7 @@ add_to_texture_record(TextureContext *tc) {
 //       Access: Protected
 //       Access: Protected
 //  Description: Records that the indicated Geom has been drawn this
 //  Description: Records that the indicated Geom has been drawn this
 //               frame.  This function is only used to update the
 //               frame.  This function is only used to update the
-//               PStats current_texmem collector; it gets compiled out
+//               PStats active_geom collector; it gets compiled out
 //               if we aren't using PStats.
 //               if we aren't using PStats.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void GraphicsStateGuardian::
 void GraphicsStateGuardian::
@@ -1469,6 +1494,23 @@ add_to_geom_record(GeomContext *gc) {
     }
     }
   }
   }
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::add_to_data_record
+//       Access: Protected
+//  Description: Records that the indicated data array has been drawn
+//               this frame.  This function is only used to update the
+//               PStats active_buffers collector; it gets compiled out
+//               if we aren't using PStats.
+////////////////////////////////////////////////////////////////////
+void GraphicsStateGuardian::
+add_to_data_record(DataContext *dc) {
+  if (PStatClient::is_connected()) {
+    if (dc != (DataContext *)NULL && _current_datas.insert(dc).second) {
+      _active_buffers_pcollector.add_level(dc->_data->get_num_bytes());
+    }
+  }
+}
 #endif  // DO_PSTATS
 #endif  // DO_PSTATS
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

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

@@ -107,6 +107,9 @@ public:
   virtual GeomContext *prepare_geom(Geom *geom);
   virtual GeomContext *prepare_geom(Geom *geom);
   virtual void release_geom(GeomContext *gc);
   virtual void release_geom(GeomContext *gc);
 
 
+  virtual DataContext *prepare_data(qpGeomVertexArrayData *data);
+  virtual void release_data(DataContext *gc);
+
   virtual CPT(qpGeomMunger) get_geom_munger(const RenderState *state);
   virtual CPT(qpGeomMunger) get_geom_munger(const RenderState *state);
 
 
   virtual void set_state_and_transform(const RenderState *state,
   virtual void set_state_and_transform(const RenderState *state,
@@ -223,9 +226,11 @@ protected:
   void init_frame_pstats();
   void init_frame_pstats();
   void add_to_texture_record(TextureContext *tc);
   void add_to_texture_record(TextureContext *tc);
   void add_to_geom_record(GeomContext *gc);
   void add_to_geom_record(GeomContext *gc);
+  void add_to_data_record(DataContext *dc);
 
 
   pset<TextureContext *> _current_textures;
   pset<TextureContext *> _current_textures;
   pset<GeomContext *> _current_geoms;
   pset<GeomContext *> _current_geoms;
+  pset<DataContext *> _current_datas;
 #else
 #else
   INLINE void init_frame_pstats() { }
   INLINE void init_frame_pstats() { }
   INLINE void add_to_texture_record(TextureContext *) { }
   INLINE void add_to_texture_record(TextureContext *) { }
@@ -308,6 +313,8 @@ public:
   static PStatCollector _active_texusage_pcollector;
   static PStatCollector _active_texusage_pcollector;
   static PStatCollector _total_geom_pcollector;
   static PStatCollector _total_geom_pcollector;
   static PStatCollector _active_geom_pcollector;
   static PStatCollector _active_geom_pcollector;
+  static PStatCollector _total_buffers_pcollector;
+  static PStatCollector _active_buffers_pcollector;
   static PStatCollector _total_geom_node_pcollector;
   static PStatCollector _total_geom_node_pcollector;
   static PStatCollector _active_geom_node_pcollector;
   static PStatCollector _active_geom_node_pcollector;
   static PStatCollector _total_texmem_pcollector;
   static PStatCollector _total_texmem_pcollector;

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

@@ -2684,7 +2684,7 @@ draw_triangles(const qpGeomTriangles *primitive) {
      primitive->get_num_primitives(), 
      primitive->get_num_primitives(), 
      primitive->get_flat_first_vertices(),
      primitive->get_flat_first_vertices(),
      D3DFMT_INDEX16,
      D3DFMT_INDEX16,
-     _vertex_data->get_array_data(0), 
+     _vertex_data->get_array(0)->get_data(),
      _vertex_data->get_format()->get_array(0)->get_stride());
      _vertex_data->get_format()->get_array(0)->get_stride());
 }
 }
 
 
@@ -2703,7 +2703,7 @@ draw_tristrips(const qpGeomTristrips *primitive) {
   CPTA_ushort maxs = primitive->get_maxs();
   CPTA_ushort maxs = primitive->get_maxs();
   nassertv(mins.size() == ends.size() && maxs.size() == ends.size());
   nassertv(mins.size() == ends.size() && maxs.size() == ends.size());
 
 
-  CPTA_uchar array_data = _vertex_data->get_array_data(0);
+  CPTA_uchar array_data = _vertex_data->get_array(0)->get_data();
   int stride = _vertex_data->get_format()->get_array(0)->get_stride();
   int stride = _vertex_data->get_format()->get_array(0)->get_stride();
 
 
   unsigned int start = 0;
   unsigned int start = 0;

+ 2 - 1
panda/src/egg2pg/eggLoader.cxx

@@ -1925,7 +1925,8 @@ make_vertex_data(EggVertexPool *vertex_pool, const LMatrix4d &transform) {
     qpGeomVertexFormat::register_format(new qpGeomVertexFormat(array_format));
     qpGeomVertexFormat::register_format(new qpGeomVertexFormat(array_format));
 
 
   // Now create a new GeomVertexData using the indicated format.
   // Now create a new GeomVertexData using the indicated format.
-  PT(qpGeomVertexData) vertex_data = new qpGeomVertexData(format);
+  PT(qpGeomVertexData) vertex_data = 
+    new qpGeomVertexData(format, qpGeomVertexArrayData::UH_static);
 
 
   // And fill the data from the vertex pool.
   // And fill the data from the vertex pool.
   EggVertexPool::const_iterator vi;
   EggVertexPool::const_iterator vi;

+ 6 - 2
panda/src/framework/windowFramework.cxx

@@ -632,7 +632,9 @@ load_default_model(const NodePath &parent) {
 
 
   if (use_qpgeom) {
   if (use_qpgeom) {
     // New, experimental Geom code.
     // New, experimental Geom code.
-    PT(qpGeomVertexData) vdata = new qpGeomVertexData(qpGeomVertexFormat::get_v3n3cpt2());
+    PT(qpGeomVertexData) vdata = new qpGeomVertexData
+      (qpGeomVertexFormat::get_v3n3cpt2(),
+       qpGeomVertexArrayData::UH_static);
     qpGeomVertexIterator vertex(vdata, InternalName::get_vertex());
     qpGeomVertexIterator vertex(vdata, InternalName::get_vertex());
     qpGeomVertexIterator normal(vdata, InternalName::get_normal());
     qpGeomVertexIterator normal(vdata, InternalName::get_normal());
     qpGeomVertexIterator color(vdata, InternalName::get_color());
     qpGeomVertexIterator color(vdata, InternalName::get_color());
@@ -1057,7 +1059,9 @@ load_image_as_model(const Filename &filename) {
   }
   }
 
 
   if (use_qpgeom) {
   if (use_qpgeom) {
-    PT(qpGeomVertexData) vdata = new qpGeomVertexData(qpGeomVertexFormat::get_v3t2());
+    PT(qpGeomVertexData) vdata = new qpGeomVertexData
+      (qpGeomVertexFormat::get_v3t2(),
+       qpGeomVertexArrayData::UH_static);
     qpGeomVertexIterator vertex(vdata, InternalName::get_vertex());
     qpGeomVertexIterator vertex(vdata, InternalName::get_vertex());
     qpGeomVertexIterator texcoord(vdata, InternalName::get_texcoord());
     qpGeomVertexIterator texcoord(vdata, InternalName::get_texcoord());
 
 

+ 3 - 0
panda/src/glstuff/Sources.pp

@@ -19,6 +19,9 @@
      glstuff_src.cxx \
      glstuff_src.cxx \
      glstuff_src.h \
      glstuff_src.h \
      glstuff_undef_src.h \
      glstuff_undef_src.h \
+     glDataContext_src.cxx \
+     glDataContext_src.I \
+     glDataContext_src.h \
      glGeomContext_src.cxx \
      glGeomContext_src.cxx \
      glGeomContext_src.I \
      glGeomContext_src.I \
      glGeomContext_src.h \
      glGeomContext_src.h \

+ 30 - 0
panda/src/glstuff/glDataContext_src.I

@@ -0,0 +1,30 @@
+// Filename: glDataContext_src.I
+// Created by:  drose (17Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(DataContext)::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE CLP(DataContext)::
+CLP(DataContext)(qpGeomVertexArrayData *data) :
+  DataContext(data)
+{
+  _index = 0;
+}

+ 19 - 0
panda/src/glstuff/glDataContext_src.cxx

@@ -0,0 +1,19 @@
+// Filename: glDataContext_src.cxx
+// Created by:  drose (17Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+TypeHandle CLP(DataContext)::_type_handle;

+ 52 - 0
panda/src/glstuff/glDataContext_src.h

@@ -0,0 +1,52 @@
+// Filename: glDataContext_src.h
+// Created by:  drose (17Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pandabase.h"
+#include "dataContext.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : GLDataContext
+// Description :
+////////////////////////////////////////////////////////////////////
+class EXPCL_GL CLP(DataContext) : public DataContext {
+public:
+  INLINE CLP(DataContext)(qpGeomVertexArrayData *data);
+
+  // This is the GL "name" of the data object.
+  GLuint _index;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    DataContext::init_type();
+    register_type(_type_handle, CLASSPREFIX_QUOTED "DataContext",
+                  DataContext::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "glDataContext_src.I"
+

+ 202 - 6
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -57,6 +57,7 @@
 #include "vector_string.h"
 #include "vector_string.h"
 #include "string_utils.h"
 #include "string_utils.h"
 #include "pnmImage.h"
 #include "pnmImage.h"
+#include "config_gobj.h"
 #ifdef DO_PSTATS
 #ifdef DO_PSTATS
 #include "pStatTimer.h"
 #include "pStatTimer.h"
 #endif
 #endif
@@ -455,6 +456,47 @@ reset() {
     _glActiveTexture = null_glActiveTexture;
     _glActiveTexture = null_glActiveTexture;
   }
   }
 
 
+  _supports_buffers = false;
+    
+  if (is_at_least_version(1, 5)) {
+    _supports_buffers = true;
+
+    _glGenBuffers = (PFNGLGENBUFFERSPROC)
+      get_extension_func(GLPREFIX_QUOTED, "GenBuffers");
+    _glBindBuffer = (PFNGLBINDBUFFERPROC)
+      get_extension_func(GLPREFIX_QUOTED, "BindBuffer");
+    _glBufferData = (PFNGLBUFFERDATAPROC)
+      get_extension_func(GLPREFIX_QUOTED, "BufferData");
+    _glBufferSubData = (PFNGLBUFFERSUBDATAPROC)
+      get_extension_func(GLPREFIX_QUOTED, "BufferSubData");
+    _glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)
+      get_extension_func(GLPREFIX_QUOTED, "DeleteBuffers");
+
+  } else if (has_extension("GL_ARB_vertex_buffer_object")) {
+    _supports_buffers = true;
+
+    _glGenBuffers = (PFNGLGENBUFFERSPROC)
+      get_extension_func(GLPREFIX_QUOTED, "GenBuffersARB");
+    _glBindBuffer = (PFNGLBINDBUFFERPROC)
+      get_extension_func(GLPREFIX_QUOTED, "BindBufferARB");
+    _glBufferData = (PFNGLBUFFERDATAPROC)
+      get_extension_func(GLPREFIX_QUOTED, "BufferDataARB");
+    _glBufferSubData = (PFNGLBUFFERSUBDATAPROC)
+      get_extension_func(GLPREFIX_QUOTED, "BufferSubDataARB");
+    _glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)
+      get_extension_func(GLPREFIX_QUOTED, "DeleteBuffersARB");
+  }
+
+  if (_supports_buffers) {
+    if (_glGenBuffers == NULL || _glBindBuffer == NULL ||
+        _glBufferData == NULL || _glBufferSubData == NULL ||
+        _glDeleteBuffers == NULL) {
+      GLCAT.warning()
+        << "Buffers advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
+      _supports_buffers = false;
+    }
+  }
+
   _glBlendEquation = NULL;
   _glBlendEquation = NULL;
   if (has_extension("GL_EXT_blend_minmax") || is_at_least_version(1, 2)) {
   if (has_extension("GL_EXT_blend_minmax") || is_at_least_version(1, 2)) {
     _glBlendEquation = (PFNGLBLENDEQUATIONPROC)
     _glBlendEquation = (PFNGLBLENDEQUATIONPROC)
@@ -1984,7 +2026,7 @@ begin_draw_primitives(const qpGeomVertexData *vertex_data) {
   }
   }
   nassertr(_vertex_data != (qpGeomVertexData *)NULL, false);
   nassertr(_vertex_data != (qpGeomVertexData *)NULL, false);
 
 
-  CPTA_uchar array_data;
+  const qpGeomVertexArrayData *array_data;
   int num_components;
   int num_components;
   qpGeomVertexDataType::NumericType numeric_type;
   qpGeomVertexDataType::NumericType numeric_type;
   int start;
   int start;
@@ -1993,8 +2035,9 @@ begin_draw_primitives(const qpGeomVertexData *vertex_data) {
   if (_vertex_data->get_array_info(InternalName::get_vertex(),
   if (_vertex_data->get_array_info(InternalName::get_vertex(),
                                    array_data, num_components, numeric_type, 
                                    array_data, num_components, numeric_type, 
                                    start, stride)) {
                                    start, stride)) {
+    const unsigned char *client_pointer = setup_array_data(array_data);
     GLP(VertexPointer)(num_components, get_numeric_type(numeric_type), 
     GLP(VertexPointer)(num_components, get_numeric_type(numeric_type), 
-                       stride, array_data + start);
+                       stride, client_pointer + start);
     GLP(EnableClientState)(GL_VERTEX_ARRAY);
     GLP(EnableClientState)(GL_VERTEX_ARRAY);
   } else {
   } else {
     // No vertex data?  No primitives!
     // No vertex data?  No primitives!
@@ -2005,8 +2048,9 @@ begin_draw_primitives(const qpGeomVertexData *vertex_data) {
       _vertex_data->get_array_info(InternalName::get_normal(),
       _vertex_data->get_array_info(InternalName::get_normal(),
                                    array_data, num_components, numeric_type, 
                                    array_data, num_components, numeric_type, 
                                    start, stride)) {
                                    start, stride)) {
+    const unsigned char *client_pointer = setup_array_data(array_data);
     GLP(NormalPointer)(get_numeric_type(numeric_type), stride, 
     GLP(NormalPointer)(get_numeric_type(numeric_type), stride, 
-                       array_data + start);
+                       client_pointer + start);
     GLP(EnableClientState)(GL_NORMAL_ARRAY);
     GLP(EnableClientState)(GL_NORMAL_ARRAY);
   } else {
   } else {
     GLP(DisableClientState)(GL_NORMAL_ARRAY);
     GLP(DisableClientState)(GL_NORMAL_ARRAY);
@@ -2016,13 +2060,14 @@ begin_draw_primitives(const qpGeomVertexData *vertex_data) {
     if (_vertex_data->get_array_info(InternalName::get_color(),
     if (_vertex_data->get_array_info(InternalName::get_color(),
                                      array_data, num_components, numeric_type, 
                                      array_data, num_components, numeric_type, 
                                      start, stride)) {
                                      start, stride)) {
+      const unsigned char *client_pointer = setup_array_data(array_data);
       if (numeric_type == qpGeomVertexDataType::NT_packed_argb) {
       if (numeric_type == qpGeomVertexDataType::NT_packed_argb) {
         // Temporary hack--this will probably reverse r and b.
         // Temporary hack--this will probably reverse r and b.
-        GLP(ColorPointer)(4, GL_UNSIGNED_BYTE, stride, array_data + start);
+        GLP(ColorPointer)(4, GL_UNSIGNED_BYTE, stride, client_pointer + start);
         
         
       } else {
       } else {
         GLP(ColorPointer)(num_components, get_numeric_type(numeric_type), 
         GLP(ColorPointer)(num_components, get_numeric_type(numeric_type), 
-                          stride, array_data + start);
+                          stride, client_pointer + start);
       }
       }
       GLP(EnableClientState)(GL_COLOR_ARRAY);
       GLP(EnableClientState)(GL_COLOR_ARRAY);
 
 
@@ -2040,8 +2085,9 @@ begin_draw_primitives(const qpGeomVertexData *vertex_data) {
       _vertex_data->get_array_info(InternalName::get_texcoord(),
       _vertex_data->get_array_info(InternalName::get_texcoord(),
                                    array_data, num_components, numeric_type, 
                                    array_data, num_components, numeric_type, 
                                    start, stride)) {
                                    start, stride)) {
+    const unsigned char *client_pointer = setup_array_data(array_data);
     GLP(TexCoordPointer)(num_components, get_numeric_type(numeric_type), 
     GLP(TexCoordPointer)(num_components, get_numeric_type(numeric_type), 
-                         stride, array_data + start);
+                         stride, client_pointer + start);
     GLP(EnableClientState)(GL_TEXTURE_COORD_ARRAY);
     GLP(EnableClientState)(GL_TEXTURE_COORD_ARRAY);
   } else {
   } else {
     GLP(DisableClientState)(GL_TEXTURE_COORD_ARRAY);
     GLP(DisableClientState)(GL_TEXTURE_COORD_ARRAY);
@@ -2287,6 +2333,130 @@ release_geom(GeomContext *gc) {
   delete ggc;
   delete ggc;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(GraphicsStateGuardian)::prepare_data
+//       Access: Public, Virtual
+//  Description: Creates a new retained-mode representation of the
+//               given data, and returns a newly-allocated
+//               DataContext pointer to reference it.  It is the
+//               responsibility of the calling function to later
+//               call release_data() with this same pointer (which
+//               will also delete the pointer).
+//
+//               This function should not be called directly to
+//               prepare a data.  Instead, call Data::prepare().
+////////////////////////////////////////////////////////////////////
+DataContext *CLP(GraphicsStateGuardian)::
+prepare_data(qpGeomVertexArrayData *data) {
+  cerr << "prepare_data\n";
+
+  if (_supports_buffers) {
+    CLP(DataContext) *gdc = new CLP(DataContext)(data);
+    _glGenBuffers(1, &gdc->_index);
+
+    _glBindBuffer(GL_ARRAY_BUFFER, gdc->_index);
+    _glBufferData(GL_ARRAY_BUFFER, gdc->_data->get_num_bytes(),
+                  gdc->_data->get_data(), 
+                  get_usage(gdc->_data->get_usage_hint()));
+    gdc->_modified = gdc->_data->get_modified();
+    
+    report_my_gl_errors();
+    return gdc;
+  }
+
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(GraphicsStateGuardian)::apply_data
+//       Access: Public, Virtual
+//  Description: Makes the data the currently available data for
+//               rendering.
+////////////////////////////////////////////////////////////////////
+void CLP(GraphicsStateGuardian)::
+apply_data(DataContext *dc) {
+  cerr << "apply_data\n";
+
+  nassertv(_supports_buffers);
+
+  CLP(DataContext) *gdc = DCAST(CLP(DataContext), dc);
+
+  add_to_data_record(gdc);
+  _glBindBuffer(GL_ARRAY_BUFFER, gdc->_index);
+  
+  if (gdc->_modified != gdc->_data->get_modified()) {
+    _glBufferSubData(GL_ARRAY_BUFFER, 0, gdc->_data->get_num_bytes(),
+                     gdc->_data->get_data());
+
+    gdc->_modified = gdc->_data->get_modified();
+  }
+
+  report_my_gl_errors();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(GraphicsStateGuardian)::release_data
+//       Access: Public, Virtual
+//  Description: Frees the GL resources previously allocated for the
+//               data.  This function should never be called
+//               directly; instead, call Data::release() (or simply
+//               let the Data destruct).
+////////////////////////////////////////////////////////////////////
+void CLP(GraphicsStateGuardian)::
+release_data(DataContext *dc) {
+  cerr << "release_data\n";
+
+  nassertv(_supports_buffers);
+  
+  CLP(DataContext) *gdc = DCAST(CLP(DataContext), dc);
+
+  _glDeleteBuffers(1, &gdc->_index);
+  report_my_gl_errors();
+
+  gdc->_index = 0;
+
+  delete gdc;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(GraphicsStateGuardian)::setup_array_data
+//       Access: Public
+//  Description: Internal function to bind a buffer object for the
+//               indicated data array, if appropriate, or to unbind a
+//               buffer object if it should be rendered from client
+//               memory.
+//
+//               If the buffer object is bound, this function returns
+//               NULL (reprsenting the start of the buffer object in
+//               server memory); if the buffer object is not bound,
+//               this function returns the pointer to the data array
+//               in client memory, that is, the data array passed in.
+////////////////////////////////////////////////////////////////////
+const unsigned char *CLP(GraphicsStateGuardian)::
+setup_array_data(const qpGeomVertexArrayData *data) {
+  if (!_supports_buffers) {
+    // No support for buffer objects; always render from client.
+    return data->get_data();
+  }
+  if (!vertex_buffers ||
+      data->get_usage_hint() == qpGeomVertexArrayData::UH_client) {
+    // The array specifies client rendering only, or buffer objects
+    // are configured off.
+    _glBindBuffer(GL_ARRAY_BUFFER, 0);
+    return data->get_data();
+  }
+
+  // Prepare the buffer object and bind it.
+  DataContext *dc = ((qpGeomVertexArrayData *)data)->prepare_now(get_prepared_objects(), this);
+  nassertr(dc != (DataContext *)NULL, data->get_data());
+
+  CLP(DataContext) *gdc = DCAST(CLP(DataContext), dc);
+  _glBindBuffer(GL_ARRAY_BUFFER, gdc->_index);
+
+  // NULL is the OpenGL convention for the first byte of the buffer.
+  return NULL;
+}
+
 #if 0
 #if 0
 static int logs[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048,
 static int logs[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048,
                       4096, 0 };
                       4096, 0 };
@@ -4406,6 +4576,32 @@ get_blend_func(ColorBlendAttrib::Operand operand) {
   return GL_ZERO;
   return GL_ZERO;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(GraphicsStateGuardian)::get_usage
+//       Access: Public, Static
+//  Description: Maps from UsageHint to the GL symbol.
+////////////////////////////////////////////////////////////////////
+GLenum CLP(GraphicsStateGuardian)::
+get_usage(qpGeomVertexArrayData::UsageHint usage_hint) {
+  switch (usage_hint) {
+  case qpGeomVertexArrayData::UH_stream:
+    return GL_STREAM_DRAW;
+
+  case qpGeomVertexArrayData::UH_static:
+    return GL_STATIC_DRAW;
+
+  case qpGeomVertexArrayData::UH_dynamic:
+    return GL_DYNAMIC_DRAW;
+
+  case qpGeomVertexArrayData::UH_client:
+    break;
+  }
+
+  GLCAT.error()
+    << "Unexpected usage_hint " << (int)usage_hint << endl;
+  return GL_STATIC_DRAW;
+}
+
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CLP(GraphicsStateGuardian)::print_gfx_visual
 //     Function: CLP(GraphicsStateGuardian)::print_gfx_visual

+ 14 - 0
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -37,6 +37,7 @@
 #include "graphicsWindow.h"
 #include "graphicsWindow.h"
 #include "pset.h"
 #include "pset.h"
 #include "pmap.h"
 #include "pmap.h"
+#include "qpgeomVertexArrayData.h"
 #ifdef HAVE_CGGL
 #ifdef HAVE_CGGL
 #include "cgShader.h"
 #include "cgShader.h"
 #endif
 #endif
@@ -103,6 +104,11 @@ public:
   virtual GeomContext *prepare_geom(Geom *geom);
   virtual GeomContext *prepare_geom(Geom *geom);
   virtual void release_geom(GeomContext *gc);
   virtual void release_geom(GeomContext *gc);
 
 
+  virtual DataContext *prepare_data(qpGeomVertexArrayData *data);
+  virtual void apply_data(DataContext *dc);
+  virtual void release_data(DataContext *dc);
+  const unsigned char *setup_array_data(const qpGeomVertexArrayData *data);
+
   virtual CPT(qpGeomMunger) get_geom_munger(const RenderState *state);
   virtual CPT(qpGeomMunger) get_geom_munger(const RenderState *state);
 
 
   virtual void framebuffer_copy_to_texture
   virtual void framebuffer_copy_to_texture
@@ -236,6 +242,7 @@ protected:
   static GLenum get_fog_mode_type(Fog::Mode m);
   static GLenum get_fog_mode_type(Fog::Mode m);
   static GLenum get_blend_equation_type(ColorBlendAttrib::Mode mode);
   static GLenum get_blend_equation_type(ColorBlendAttrib::Mode mode);
   static GLenum get_blend_func(ColorBlendAttrib::Operand operand);
   static GLenum get_blend_func(ColorBlendAttrib::Operand operand);
+  static GLenum get_usage(qpGeomVertexArrayData::UsageHint usage_hint);
 
 
   static CPT(RenderState) get_untextured_state();
   static CPT(RenderState) get_untextured_state();
   static CPT(RenderState) get_smooth_state();
   static CPT(RenderState) get_smooth_state();
@@ -326,6 +333,13 @@ public:
   PFNGLACTIVETEXTUREPROC _glActiveTexture;
   PFNGLACTIVETEXTUREPROC _glActiveTexture;
   PFNGLMULTITEXCOORD2FVPROC _glMultiTexCoord2fv;
   PFNGLMULTITEXCOORD2FVPROC _glMultiTexCoord2fv;
 
 
+  bool _supports_buffers;
+  PFNGLGENBUFFERSPROC _glGenBuffers;
+  PFNGLBINDBUFFERPROC _glBindBuffer;
+  PFNGLBUFFERDATAPROC _glBufferData;
+  PFNGLBUFFERSUBDATAPROC _glBufferSubData;
+  PFNGLDELETEBUFFERSPROC _glDeleteBuffers;
+
   PFNGLBLENDEQUATIONPROC _glBlendEquation;
   PFNGLBLENDEQUATIONPROC _glBlendEquation;
   PFNGLBLENDCOLORPROC _glBlendColor;
   PFNGLBLENDCOLORPROC _glBlendColor;
 
 

+ 1 - 0
panda/src/glstuff/glmisc_src.cxx

@@ -63,6 +63,7 @@ void CLP(init_classes)() {
   CLP(GraphicsStateGuardian)::init_type();
   CLP(GraphicsStateGuardian)::init_type();
   CLP(TextureContext)::init_type();
   CLP(TextureContext)::init_type();
   CLP(GeomContext)::init_type();
   CLP(GeomContext)::init_type();
+  CLP(DataContext)::init_type();
 
 
   PandaSystem *ps = PandaSystem::get_global_ptr();
   PandaSystem *ps = PandaSystem::get_global_ptr();
   ps->add_system(GLSYSTEM_NAME);
   ps->add_system(GLSYSTEM_NAME);

+ 1 - 0
panda/src/glstuff/glstuff_src.cxx

@@ -23,6 +23,7 @@
 
 
 #include "glmisc_src.cxx"
 #include "glmisc_src.cxx"
 #include "glTextureContext_src.cxx"
 #include "glTextureContext_src.cxx"
+#include "glDataContext_src.cxx"
 #include "glGeomContext_src.cxx"
 #include "glGeomContext_src.cxx"
 #include "glGeomMunger_src.cxx"
 #include "glGeomMunger_src.cxx"
 #include "glCgShaderContext_src.cxx"
 #include "glCgShaderContext_src.cxx"

+ 1 - 0
panda/src/glstuff/glstuff_src.h

@@ -41,6 +41,7 @@
 
 
 #include "glmisc_src.h"
 #include "glmisc_src.h"
 #include "glTextureContext_src.h"
 #include "glTextureContext_src.h"
+#include "glDataContext_src.h"
 #include "glGeomContext_src.h"
 #include "glGeomContext_src.h"
 #include "glGeomMunger_src.h"
 #include "glGeomMunger_src.h"
 #include "glCgShaderContext_src.h"
 #include "glCgShaderContext_src.h"

+ 10 - 2
panda/src/gobj/Sources.pp

@@ -11,7 +11,9 @@
 
 
   #define SOURCES \
   #define SOURCES \
     boundedObject.I boundedObject.h \
     boundedObject.I boundedObject.h \
-    config_gobj.h drawable.h \
+    config_gobj.h \
+    dataContext.I dataContext.h \
+    drawable.h \
     geom.I geom.h \
     geom.I geom.h \
     geomContext.I geomContext.h \
     geomContext.I geomContext.h \
     geomLine.h geomLinestrip.h geomPoint.h geomPolygon.h  \
     geomLine.h geomLinestrip.h geomPoint.h geomPolygon.h  \
@@ -23,6 +25,7 @@
     qpgeomTriangles.h \
     qpgeomTriangles.h \
     qpgeomTristrips.h \
     qpgeomTristrips.h \
     qpgeomTrifans.h \
     qpgeomTrifans.h \
+    qpgeomVertexArrayData.h qpgeomVertexArrayData.I \
     qpgeomVertexArrayFormat.h qpgeomVertexArrayFormat.I \
     qpgeomVertexArrayFormat.h qpgeomVertexArrayFormat.I \
     qpgeomVertexCacheManager.h qpgeomVertexCacheManager.I \
     qpgeomVertexCacheManager.h qpgeomVertexCacheManager.I \
     qpgeomVertexData.h qpgeomVertexData.I \
     qpgeomVertexData.h qpgeomVertexData.I \
@@ -44,7 +47,9 @@
     
     
   #define INCLUDED_SOURCES \
   #define INCLUDED_SOURCES \
     boundedObject.cxx \
     boundedObject.cxx \
-    config_gobj.cxx drawable.cxx geom.cxx  \
+    config_gobj.cxx \
+    dataContext.cxx \
+    drawable.cxx geom.cxx  \
     geomContext.cxx \
     geomContext.cxx \
     geomLine.cxx geomLinestrip.cxx geomPoint.cxx geomPolygon.cxx  \
     geomLine.cxx geomLinestrip.cxx geomPoint.cxx geomPolygon.cxx  \
     geomQuad.cxx geomSphere.cxx geomSprite.cxx geomTri.cxx  \
     geomQuad.cxx geomSphere.cxx geomSprite.cxx geomTri.cxx  \
@@ -55,6 +60,7 @@
     qpgeomTriangles.cxx \
     qpgeomTriangles.cxx \
     qpgeomTristrips.cxx \
     qpgeomTristrips.cxx \
     qpgeomTrifans.cxx \
     qpgeomTrifans.cxx \
+    qpgeomVertexArrayData.cxx \
     qpgeomVertexArrayFormat.cxx \
     qpgeomVertexArrayFormat.cxx \
     qpgeomVertexCacheManager.cxx \
     qpgeomVertexCacheManager.cxx \
     qpgeomVertexData.cxx \
     qpgeomVertexData.cxx \
@@ -73,6 +79,7 @@
   #define INSTALL_HEADERS \
   #define INSTALL_HEADERS \
     boundedObject.I boundedObject.h \
     boundedObject.I boundedObject.h \
     config_gobj.h \
     config_gobj.h \
+    dataContext.I dataContext.h \
     drawable.h geom.I geom.h \
     drawable.h geom.I geom.h \
     textureContext.I textureContext.h \
     textureContext.I textureContext.h \
     geomLine.h \
     geomLine.h \
@@ -85,6 +92,7 @@
     qpgeomTriangles.h \
     qpgeomTriangles.h \
     qpgeomTristrips.h \
     qpgeomTristrips.h \
     qpgeomTrifans.h \
     qpgeomTrifans.h \
+    qpgeomVertexArrayData.h qpgeomVertexArrayData.I \
     qpgeomVertexArrayFormat.h qpgeomVertexArrayFormat.I \
     qpgeomVertexArrayFormat.h qpgeomVertexArrayFormat.I \
     qpgeomVertexCacheManager.h qpgeomVertexCacheManager.I \
     qpgeomVertexCacheManager.h qpgeomVertexCacheManager.I \
     qpgeomVertexData.h qpgeomVertexData.I \
     qpgeomVertexData.h qpgeomVertexData.I \

+ 18 - 0
panda/src/gobj/config_gobj.cxx

@@ -28,6 +28,7 @@
 #include "qpgeomTriangles.h"
 #include "qpgeomTriangles.h"
 #include "qpgeomTristrips.h"
 #include "qpgeomTristrips.h"
 #include "qpgeomTrifans.h"
 #include "qpgeomTrifans.h"
+#include "qpgeomVertexArrayData.h"
 #include "qpgeomVertexArrayFormat.h"
 #include "qpgeomVertexArrayFormat.h"
 #include "qpgeomVertexData.h"
 #include "qpgeomVertexData.h"
 #include "qpgeomVertexFormat.h"
 #include "qpgeomVertexFormat.h"
@@ -38,6 +39,9 @@
 #include "lens.h"
 #include "lens.h"
 #include "texture.h"
 #include "texture.h"
 #include "textureStage.h"
 #include "textureStage.h"
+#include "textureContext.h"
+#include "geomContext.h"
+#include "dataContext.h"
 #include "internalName.h"
 #include "internalName.h"
 
 
 #include "dconfig.h"
 #include "dconfig.h"
@@ -81,6 +85,15 @@ ConfigVariableBool retained_mode
           "GSG.  Set it false to use only immediate mode, which sends 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."));
 
 
+ConfigVariableBool vertex_buffers
+("vertex-buffers", false,
+ PRC_DESC("Set this true to allow the use of vertex buffers (or buffer "
+          "objects, as OpenGL dubs them) for rendering vertex data.  This "
+          "can greatly improve rendering performance, especially on "
+          "higher-end graphics cards, at the cost of some additional "
+          "graphics memory (which might otherwise be used for textures "
+          "or offscreen buffers)."));
+
 ConfigVariableBool use_qpgeom
 ConfigVariableBool use_qpgeom
 ("use-qpgeom", false,
 ("use-qpgeom", false,
  PRC_DESC("A temporary variable while the experimental Geom rewrite is "
  PRC_DESC("A temporary variable while the experimental Geom rewrite is "
@@ -163,9 +176,13 @@ ConfigureFn(config_gobj) {
   qpGeomTriangles::init_type();
   qpGeomTriangles::init_type();
   qpGeomTristrips::init_type();
   qpGeomTristrips::init_type();
   qpGeomTrifans::init_type();
   qpGeomTrifans::init_type();
+  qpGeomVertexArrayData::init_type();
   qpGeomVertexArrayFormat::init_type();
   qpGeomVertexArrayFormat::init_type();
   qpGeomVertexData::init_type();
   qpGeomVertexData::init_type();
   qpGeomVertexFormat::init_type();
   qpGeomVertexFormat::init_type();
+  TextureContext::init_type();
+  GeomContext::init_type();
+  DataContext::init_type();
   Material::init_type();
   Material::init_type();
   OrthographicLens::init_type();
   OrthographicLens::init_type();
   MatrixLens::init_type();
   MatrixLens::init_type();
@@ -192,6 +209,7 @@ ConfigureFn(config_gobj) {
   qpGeomTriangles::register_with_read_factory();
   qpGeomTriangles::register_with_read_factory();
   qpGeomTristrips::register_with_read_factory();
   qpGeomTristrips::register_with_read_factory();
   qpGeomTrifans::register_with_read_factory();
   qpGeomTrifans::register_with_read_factory();
+  qpGeomVertexArrayData::register_with_read_factory();
   qpGeomVertexArrayFormat::register_with_read_factory();
   qpGeomVertexArrayFormat::register_with_read_factory();
   qpGeomVertexData::register_with_read_factory();
   qpGeomVertexData::register_with_read_factory();
   qpGeomVertexFormat::register_with_read_factory();
   qpGeomVertexFormat::register_with_read_factory();

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

@@ -51,6 +51,7 @@ extern EXPCL_PANDA ConfigVariableInt max_texture_dimension;
 extern EXPCL_PANDA ConfigVariableBool keep_texture_ram;
 extern EXPCL_PANDA ConfigVariableBool keep_texture_ram;
 extern EXPCL_PANDA ConfigVariableBool keep_geom_ram;
 extern EXPCL_PANDA ConfigVariableBool keep_geom_ram;
 extern EXPCL_PANDA ConfigVariableBool retained_mode;
 extern EXPCL_PANDA ConfigVariableBool retained_mode;
+extern EXPCL_PANDA ConfigVariableBool vertex_buffers;
 
 
 extern EXPCL_PANDA ConfigVariableBool use_qpgeom;
 extern EXPCL_PANDA ConfigVariableBool use_qpgeom;
 
 

+ 29 - 0
panda/src/gobj/dataContext.I

@@ -0,0 +1,29 @@
+// Filename: dataContext.I
+// Created by:  drose (17Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataContext::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE DataContext::
+DataContext(qpGeomVertexArrayData *data) :
+  _data(data)
+{
+}

+ 21 - 0
panda/src/gobj/dataContext.cxx

@@ -0,0 +1,21 @@
+// Filename: dataContext.cxx
+// Created by:  drose (17Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "dataContext.h"
+
+TypeHandle DataContext::_type_handle;

+ 71 - 0
panda/src/gobj/dataContext.h

@@ -0,0 +1,71 @@
+// Filename: dataContext.h
+// Created by:  drose (17Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DATACONTEXT_H
+#define DATACONTEXT_H
+
+#include "pandabase.h"
+
+#include "savedContext.h"
+#include "updateSeq.h"
+
+class qpGeomVertexArrayData;
+
+////////////////////////////////////////////////////////////////////
+//       Class : DataContext
+// Description : This is a special class object that holds all the
+//               information returned by a particular GSG to indicate
+//               the vertex data array's internal context identifier.
+//
+//               This allows the GSG to cache the vertex data array in
+//               whatever way makes sense.  For instance, DirectX can
+//               allocate a vertex buffer for the array.  OpenGL can
+//               create a buffer object.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA DataContext : public SavedContext {
+public:
+  INLINE DataContext(qpGeomVertexArrayData *data);
+
+  // This cannot be a PT(qpGeomVertexArrayData), because the data and
+  // the GSG both own their DataContexts!  That would create a
+  // circular reference count.
+  qpGeomVertexArrayData *_data;
+  UpdateSeq _modified;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    SavedContext::init_type();
+    register_type(_type_handle, "DataContext",
+                  SavedContext::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "dataContext.I"
+
+#endif
+

+ 2 - 0
panda/src/gobj/gobj_composite1.cxx

@@ -1,5 +1,6 @@
 
 
 #include "boundedObject.cxx"
 #include "boundedObject.cxx"
+#include "dataContext.cxx"
 #include "geom.cxx"
 #include "geom.cxx"
 #include "geomLine.cxx"
 #include "geomLine.cxx"
 #include "geomLinestrip.cxx"
 #include "geomLinestrip.cxx"
@@ -17,6 +18,7 @@
 #include "qpgeomTriangles.cxx"
 #include "qpgeomTriangles.cxx"
 #include "qpgeomTristrips.cxx"
 #include "qpgeomTristrips.cxx"
 #include "qpgeomTrifans.cxx"
 #include "qpgeomTrifans.cxx"
+#include "qpgeomVertexArrayData.cxx"
 #include "qpgeomVertexArrayFormat.cxx"
 #include "qpgeomVertexArrayFormat.cxx"
 #include "qpgeomVertexCacheManager.cxx"
 #include "qpgeomVertexCacheManager.cxx"
 #include "qpgeomVertexData.cxx"
 #include "qpgeomVertexData.cxx"

+ 140 - 0
panda/src/gobj/preparedGraphicsObjects.cxx

@@ -357,6 +357,146 @@ prepare_geom_now(Geom *geom, GraphicsStateGuardianBase *gsg) {
   return gc;
   return gc;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::enqueue_data
+//       Access: Public
+//  Description: Indicates that a data array would like to be put on the
+//               list to be prepared when the GSG is next ready to
+//               do this (presumably at the next frame).
+////////////////////////////////////////////////////////////////////
+void PreparedGraphicsObjects::
+enqueue_data(qpGeomVertexArrayData *data) {
+  MutexHolder holder(_lock);
+
+  _enqueued_datas.insert(data);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::dequeue_data
+//       Access: Public
+//  Description: Removes a data array from the queued list of data
+//               arrays to be prepared.  Normally it is not necessary
+//               to call this, unless you change your mind about
+//               preparing it at the last minute, since the data will
+//               automatically be dequeued and prepared at the next
+//               frame.
+//
+//               The return value is true if the data array is
+//               successfully dequeued, false if it had not been
+//               queued.
+////////////////////////////////////////////////////////////////////
+bool PreparedGraphicsObjects::
+dequeue_data(qpGeomVertexArrayData *data) {
+  MutexHolder holder(_lock);
+
+  EnqueuedDatas::iterator qi = _enqueued_datas.find(data);
+  if (qi != _enqueued_datas.end()) {
+    _enqueued_datas.erase(qi);
+    return true;
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::release_data
+//       Access: Public
+//  Description: Indicates that a data context, created by a
+//               previous call to prepare_data(), is no longer
+//               needed.  The driver resources will not be freed until
+//               some GSG calls update(), indicating it is at a
+//               stage where it is ready to release datas--this
+//               prevents conflicts from threading or multiple GSG's
+//               sharing datas (we have no way of knowing which
+//               graphics context is currently active, or what state
+//               it's in, at the time release_data is called).
+////////////////////////////////////////////////////////////////////
+void PreparedGraphicsObjects::
+release_data(DataContext *gc) {
+  MutexHolder holder(_lock);
+
+  gc->_data->clear_prepared(this);
+
+  // We have to set the Data pointer to NULL at this point, since
+  // the Data itself might destruct at any time after it has been
+  // released.
+  gc->_data = (qpGeomVertexArrayData *)NULL;
+
+  bool removed = (_prepared_datas.erase(gc) != 0);
+  nassertv(removed);
+
+  _released_datas.insert(gc);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::release_all_datas
+//       Access: Public
+//  Description: Releases all datas at once.  This will force them
+//               to be reloaded into data memory for all GSG's that
+//               share this object.  Returns the number of datas
+//               released.
+////////////////////////////////////////////////////////////////////
+int PreparedGraphicsObjects::
+release_all_datas() {
+  MutexHolder holder(_lock);
+
+  int num_datas = (int)_prepared_datas.size();
+
+  Datas::iterator gci;
+  for (gci = _prepared_datas.begin();
+       gci != _prepared_datas.end();
+       ++gci) {
+    DataContext *gc = (*gci);
+    gc->_data->clear_prepared(this);
+    gc->_data = (qpGeomVertexArrayData *)NULL;
+
+    _released_datas.insert(gc);
+  }
+
+  _prepared_datas.clear();
+
+  return num_datas;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::prepare_data_now
+//       Access: Public
+//  Description: Immediately creates a new DataContext for the
+//               indicated data and returns it.  This assumes that
+//               the GraphicsStateGuardian is the currently active
+//               rendering context and that it is ready to accept new
+//               datas.  If this is not necessarily the case, you
+//               should use enqueue_data() instead.
+//
+//               Normally, this function is not called directly.  Call
+//               Data::prepare_now() instead.
+//
+//               The DataContext contains all of the pertinent
+//               information needed by the GSG to keep track of this
+//               one particular data, and will exist as long as the
+//               data is ready to be rendered.
+//
+//               When either the Data or the
+//               PreparedGraphicsObjects object destructs, the
+//               DataContext will be deleted.
+////////////////////////////////////////////////////////////////////
+DataContext *PreparedGraphicsObjects::
+prepare_data_now(qpGeomVertexArrayData *data, GraphicsStateGuardianBase *gsg) {
+  MutexHolder holder(_lock);
+
+  // Ask the GSG to create a brand new DataContext.  There might
+  // be several GSG's sharing the same set of datas; if so, it
+  // doesn't matter which of them creates the context (since they're
+  // all shared anyway).
+  DataContext *gc = gsg->prepare_data(data);
+
+  if (gc != (DataContext *)NULL) {
+    bool prepared = _prepared_datas.insert(gc).second;
+    nassertr(prepared, gc);
+  }
+
+  return gc;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PreparedGraphicsObjects::update
 //     Function: PreparedGraphicsObjects::update
 //       Access: Public
 //       Access: Public

+ 16 - 0
panda/src/gobj/preparedGraphicsObjects.h

@@ -22,12 +22,16 @@
 #include "pandabase.h"
 #include "pandabase.h"
 #include "referenceCount.h"
 #include "referenceCount.h"
 #include "texture.h"
 #include "texture.h"
+#include "geom.h"
+#include "qpgeomVertexArrayData.h"
+#include "pointerTo.h"
 #include "pStatCollector.h"
 #include "pStatCollector.h"
 #include "pset.h"
 #include "pset.h"
 #include "pmutex.h"
 #include "pmutex.h"
 
 
 class TextureContext;
 class TextureContext;
 class GeomContext;
 class GeomContext;
+class DataContext;
 class GraphicsStateGuardianBase;
 class GraphicsStateGuardianBase;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -68,6 +72,14 @@ public:
 
 
   GeomContext *prepare_geom_now(Geom *geom, GraphicsStateGuardianBase *gsg);
   GeomContext *prepare_geom_now(Geom *geom, GraphicsStateGuardianBase *gsg);
 
 
+  void enqueue_data(qpGeomVertexArrayData *data);
+  bool dequeue_data(qpGeomVertexArrayData *data);
+  void release_data(DataContext *gc);
+  int release_all_datas();
+
+  DataContext *prepare_data_now(qpGeomVertexArrayData *data,
+                                GraphicsStateGuardianBase *gsg);
+
   void update(GraphicsStateGuardianBase *gsg);
   void update(GraphicsStateGuardianBase *gsg);
 
 
 private:
 private:
@@ -75,12 +87,16 @@ private:
   typedef phash_set< PT(Texture) > EnqueuedTextures;
   typedef phash_set< PT(Texture) > EnqueuedTextures;
   typedef phash_set<GeomContext *, pointer_hash> Geoms;
   typedef phash_set<GeomContext *, pointer_hash> Geoms;
   typedef phash_set< PT(Geom) > EnqueuedGeoms;
   typedef phash_set< PT(Geom) > EnqueuedGeoms;
+  typedef phash_set<DataContext *, pointer_hash> Datas;
+  typedef phash_set< PT(qpGeomVertexArrayData) > EnqueuedDatas;
 
 
   Mutex _lock;
   Mutex _lock;
   Textures _prepared_textures, _released_textures;  
   Textures _prepared_textures, _released_textures;  
   EnqueuedTextures _enqueued_textures;
   EnqueuedTextures _enqueued_textures;
   Geoms _prepared_geoms, _released_geoms;  
   Geoms _prepared_geoms, _released_geoms;  
   EnqueuedGeoms _enqueued_geoms;
   EnqueuedGeoms _enqueued_geoms;
+  Datas _prepared_datas, _released_datas;  
+  EnqueuedDatas _enqueued_datas;
 
 
   static PStatCollector _total_texusage_pcollector;
   static PStatCollector _total_texusage_pcollector;
 
 

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

@@ -389,7 +389,7 @@ recompute_bound() {
   int start = data_type->get_start();
   int start = data_type->get_start();
   int num_components = data_type->get_num_components();
   int num_components = data_type->get_num_components();
 
 
-  CPTA_uchar array_data = cdata->_data->get_array_data(array_index);
+  CPTA_uchar array_data = cdata->_data->get_array(array_index)->get_data();
 
 
   if (stride == 3 * sizeof(PN_float32) && start == 0 && num_components == 3 &&
   if (stride == 3 * sizeof(PN_float32) && start == 0 && num_components == 3 &&
       (array_data.size() % stride) == 0) {
       (array_data.size() % stride) == 0) {

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

@@ -560,7 +560,7 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
   int start = data_type->get_start();
   int start = data_type->get_start();
   int num_components = data_type->get_num_components();
   int num_components = data_type->get_num_components();
 
 
-  CPTA_uchar array_data = vertex_data->get_array_data(array_index);
+  CPTA_uchar array_data = vertex_data->get_array(array_index)->get_data();
 
 
   PTA_ushort::const_iterator ii;
   PTA_ushort::const_iterator ii;
   for (ii = cdata->_vertices.begin(); ii != cdata->_vertices.end(); ++ii) {
   for (ii = cdata->_vertices.begin(); ii != cdata->_vertices.end(); ++ii) {

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

@@ -0,0 +1,167 @@
+// Filename: qpgeomVertexArrayData.I
+// Created by:  drose (17Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::get_array_format
+//       Access: Published
+//  Description: Returns the format object that describes this array.
+////////////////////////////////////////////////////////////////////
+INLINE const qpGeomVertexArrayFormat *qpGeomVertexArrayData::
+get_array_format() const {
+  return _array_format;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::get_usage_hint
+//       Access: Published
+//  Description: Returns the usage hint that describes to the
+//               rendering backend how often the vertex data will be
+//               modified and/or rendered.  This is provided as a
+//               performance optimization only, and does not
+//               constraing actual usage; although it may be an
+//               important optimization.
+//
+//               This may only be specified to the GeomVertexArrayData
+//               constructor.  If you need to change it, you must
+//               create a new GeomVertexArrayData object (but you can
+//               just assign the same data pointer to the new object).
+////////////////////////////////////////////////////////////////////
+INLINE qpGeomVertexArrayData::UsageHint qpGeomVertexArrayData::
+get_usage_hint() const {
+  return _usage_hint;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::get_data
+//       Access: Published
+//  Description: Returns a const pointer to the actual vertex data,
+//               for application code to directly examine (but not
+//               modify).
+////////////////////////////////////////////////////////////////////
+INLINE CPTA_uchar qpGeomVertexArrayData::
+get_data() const {
+  CDReader cdata(_cycler);
+  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
+//  Description: Returns the number of vertices stored in the array,
+//               based on the number of bytes and the stride.  This
+//               should be the same for all arrays within a given
+//               GeomVertexData object.
+////////////////////////////////////////////////////////////////////
+INLINE int qpGeomVertexArrayData::
+get_num_vertices() const {
+  return get_num_bytes() / _array_format->get_stride();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::clear_vertices
+//       Access: Published
+//  Description: Removes all of the vertices in the array.
+//               Functionally equivalent to set_num_vertices(0).
+////////////////////////////////////////////////////////////////////
+INLINE void qpGeomVertexArrayData::
+clear_vertices() {
+  set_data(PTA_uchar());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::get_num_bytes
+//       Access: Published
+//  Description: Returns the number of bytes stored in the array.
+////////////////////////////////////////////////////////////////////
+INLINE int qpGeomVertexArrayData::
+get_num_bytes() const {
+  CDReader cdata(_cycler);
+  return cdata->_data.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::get_modified
+//       Access: Published
+//  Description: Returns a sequence number which is guaranteed to
+//               change at least every time the array vertex data is
+//               modified.
+////////////////////////////////////////////////////////////////////
+INLINE UpdateSeq qpGeomVertexArrayData::
+get_modified() const {
+  CDReader cdata(_cycler);
+  return cdata->_modified;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::CData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE qpGeomVertexArrayData::CData::
+CData() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::CData::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE qpGeomVertexArrayData::CData::
+CData(const qpGeomVertexArrayData::CData &copy) :
+  _data(copy._data),
+  _modified(copy._modified)
+{
+}

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

@@ -0,0 +1,360 @@
+// Filename: qpgeomVertexArrayData.cxx
+// Created by:  drose (17Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "qpgeomVertexArrayData.h"
+#include "preparedGraphicsObjects.h"
+#include "bamReader.h"
+#include "bamWriter.h"
+#include "pset.h"
+
+TypeHandle qpGeomVertexArrayData::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::Default Constructor
+//       Access: Private
+//  Description: Constructs an invalid object.  This is only used when
+//               reading from the bam file.
+////////////////////////////////////////////////////////////////////
+qpGeomVertexArrayData::
+qpGeomVertexArrayData() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+qpGeomVertexArrayData::
+qpGeomVertexArrayData(const qpGeomVertexArrayFormat *array_format,
+                      qpGeomVertexArrayData::UsageHint usage_hint) :
+  _array_format(array_format),
+  _usage_hint(usage_hint)
+{
+}
+  
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::Copy Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+qpGeomVertexArrayData::
+qpGeomVertexArrayData(const qpGeomVertexArrayData &copy) :
+  _array_format(copy._array_format),
+  _usage_hint(copy._usage_hint),
+  _cycler(copy._cycler)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::Copy Assignment Operator
+//       Access: Private
+//  Description: Directly copying ArrayData objects by assignment is
+//               disallowed.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexArrayData::
+operator = (const qpGeomVertexArrayData &) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::Destructor
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+qpGeomVertexArrayData::
+~qpGeomVertexArrayData() {
+  release_all();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::set_num_vertices
+//       Access: Published
+//  Description: Sets the length of the array to n vertices.
+//               Normally, you would not call this directly, since all
+//               of the arrays in a particular GeomVertexData must
+//               have the same number of vertices; instead, call
+//               GeomVertexData::set_num_vertices().
+//
+//               The return value is true if the number of vertices
+//               was changed, false if the object already contained n
+//               vertices (or if there was some error).
+////////////////////////////////////////////////////////////////////
+bool qpGeomVertexArrayData::
+set_num_vertices(int n) {
+  CDWriter cdata(_cycler);
+
+  int stride = _array_format->get_stride();
+  int delta = n - (cdata->_data.size() / stride);
+  
+  if (delta != 0) {
+    if (cdata->_data.get_ref_count() > 1) {
+      // Copy-on-write: the data is already reffed somewhere else,
+      // so we're just going to make a copy.
+      PTA_uchar new_data;
+      new_data.reserve(n * stride);
+      new_data.insert(new_data.end(), n * stride, uchar());
+      memcpy(new_data, cdata->_data, 
+             min((size_t)(n * stride), cdata->_data.size()));
+      cdata->_data = new_data;
+      
+    } else {
+      // We've got the only reference to the data, so we can change
+      // it directly.
+      if (delta > 0) {
+        cdata->_data.insert(cdata->_data.end(), delta * stride, uchar());
+        
+      } else {
+        cdata->_data.erase(cdata->_data.begin() + n * stride, 
+                           cdata->_data.end());
+      }
+    }
+
+    ++(cdata->_modified);
+
+    return true;
+  }
+  
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::prepare
+//       Access: Published
+//  Description: Indicates that the data should be enqueued to be
+//               prepared in the indicated prepared_objects at the
+//               beginning of the next frame.  This will ensure the
+//               data is already loaded into the GSG if it is expected
+//               to be rendered soon.
+//
+//               Use this function instead of prepare_now() to preload
+//               datas from a user interface standpoint.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexArrayData::
+prepare(PreparedGraphicsObjects *prepared_objects) {
+  prepared_objects->enqueue_data(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::prepare_now
+//       Access: Public
+//  Description: Creates a context for the data on the particular
+//               GSG, if it does not already exist.  Returns the new
+//               (or old) DataContext.  This assumes that the
+//               GraphicsStateGuardian is the currently active
+//               rendering context and that it is ready to accept new
+//               datas.  If this is not necessarily the case, you
+//               should use prepare() instead.
+//
+//               Normally, this is not called directly except by the
+//               GraphicsStateGuardian; a data does not need to be
+//               explicitly prepared by the user before it may be
+//               rendered.
+////////////////////////////////////////////////////////////////////
+DataContext *qpGeomVertexArrayData::
+prepare_now(PreparedGraphicsObjects *prepared_objects, 
+            GraphicsStateGuardianBase *gsg) {
+  Contexts::const_iterator ci;
+  ci = _contexts.find(prepared_objects);
+  if (ci != _contexts.end()) {
+    return (*ci).second;
+  }
+
+  DataContext *dc = prepared_objects->prepare_data_now(this, gsg);
+  if (dc != (DataContext *)NULL) {
+    _contexts[prepared_objects] = dc;
+  }
+  return dc;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::release
+//       Access: Public
+//  Description: Frees the data context only on the indicated object,
+//               if it exists there.  Returns true if it was released,
+//               false if it had not been prepared.
+////////////////////////////////////////////////////////////////////
+bool qpGeomVertexArrayData::
+release(PreparedGraphicsObjects *prepared_objects) {
+  Contexts::iterator ci;
+  ci = _contexts.find(prepared_objects);
+  if (ci != _contexts.end()) {
+    DataContext *dc = (*ci).second;
+    prepared_objects->release_data(dc);
+    return true;
+  }
+
+  // Maybe it wasn't prepared yet, but it's about to be.
+  return prepared_objects->dequeue_data(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::release_all
+//       Access: Public
+//  Description: Frees the context allocated on all objects for which
+//               the data has been declared.  Returns the number of
+//               contexts which have been freed.
+////////////////////////////////////////////////////////////////////
+int qpGeomVertexArrayData::
+release_all() {
+  // We have to traverse a copy of the _contexts list, because the
+  // PreparedGraphicsObjects object will call clear_prepared() in response
+  // to each release_data(), and we don't want to be modifying the
+  // _contexts list while we're traversing it.
+  Contexts temp = _contexts;
+  int num_freed = (int)_contexts.size();
+
+  Contexts::const_iterator ci;
+  for (ci = temp.begin(); ci != temp.end(); ++ci) {
+    PreparedGraphicsObjects *prepared_objects = (*ci).first;
+    DataContext *dc = (*ci).second;
+    prepared_objects->release_data(dc);
+  }
+
+  // Now that we've called release_data() on every known context,
+  // the _contexts list should have completely emptied itself.
+  nassertr(_contexts.empty(), num_freed);
+
+  return num_freed;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::clear_prepared
+//       Access: Private
+//  Description: Removes the indicated PreparedGraphicsObjects table
+//               from the data array's table, without actually
+//               releasing the data array.  This is intended to be
+//               called only from
+//               PreparedGraphicsObjects::release_data(); it should
+//               never be called by user code.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexArrayData::
+clear_prepared(PreparedGraphicsObjects *prepared_objects) {
+  Contexts::iterator ci;
+  ci = _contexts.find(prepared_objects);
+  if (ci != _contexts.end()) {
+    _contexts.erase(ci);
+  } else {
+    // If this assertion fails, clear_prepared() was given a
+    // prepared_objects which the data array didn't know about.
+    nassertv(false);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               qpGeomVertexArrayData.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexArrayData::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexArrayData::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  TypedWritable::write_datagram(manager, dg);
+
+  manager->write_cdata(dg, _cycler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type qpGeomVertexArrayData is encountered
+//               in the Bam file.  It should create the qpGeomVertexArrayData
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *qpGeomVertexArrayData::
+make_from_bam(const FactoryParams &params) {
+  qpGeomVertexArrayData *object = new qpGeomVertexArrayData;
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  object->fillin(scan, manager);
+
+  return object;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new qpGeomVertexArrayData.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexArrayData::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  TypedWritable::fillin(scan, manager);
+
+  manager->read_cdata(scan, _cycler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::CData::make_copy
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CycleData *qpGeomVertexArrayData::CData::
+make_copy() const {
+  return new CData(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::CData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexArrayData::CData::
+write_datagram(BamWriter *manager, Datagram &dg) const {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::CData::complete_pointers
+//       Access: Public, Virtual
+//  Description: Receives an array of pointers, one for each time
+//               manager->read_pointer() was called in fillin().
+//               Returns the number of pointers processed.
+////////////////////////////////////////////////////////////////////
+int qpGeomVertexArrayData::CData::
+complete_pointers(TypedWritable **p_list, BamReader *manager) {
+  int pi = CycleData::complete_pointers(p_list, manager);
+
+  return pi;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: qpGeomVertexArrayData::CData::fillin
+//       Access: Public, Virtual
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new qpGeomVertexArrayData.
+////////////////////////////////////////////////////////////////////
+void qpGeomVertexArrayData::CData::
+fillin(DatagramIterator &scan, BamReader *manager) {
+}

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

@@ -0,0 +1,164 @@
+// Filename: qpgeomVertexArrayData.h
+// Created by:  drose (17Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef qpGEOMVERTEXARRAYDATA_H
+#define qpGEOMVERTEXARRAYDATA_H
+
+#include "pandabase.h"
+#include "typedWritableReferenceCount.h"
+#include "qpgeomVertexArrayFormat.h"
+#include "pta_uchar.h"
+#include "updateSeq.h"
+#include "cycleData.h"
+#include "cycleDataReader.h"
+#include "cycleDataWriter.h"
+#include "pipelineCycler.h"
+#include "pmap.h"
+
+class PreparedGraphicsObjects;
+class DataContext;
+
+////////////////////////////////////////////////////////////////////
+//       Class : qpGeomVertexArrayData
+// Description : This is the data for one array of a GeomVertexData
+//               structure.  Many GeomVertexData structures will only
+//               define one array; some will define multiple arrays.
+//               DirectX calls this concept a "stream".
+//
+//               This object is just a block of data.  See
+//               GeomVertexData for the organizing structure.
+//
+//               This is part of the experimental Geom rewrite.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA qpGeomVertexArrayData : public TypedWritableReferenceCount {
+private:
+  qpGeomVertexArrayData();
+
+PUBLISHED:
+  enum UsageHint {
+    // UH_client: don't attempt to upload the data as a "vertex
+    // buffer"; 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.
+    UH_stream,
+
+    // UH_static: the data will be created once, and used to render
+    // many times, without modification.
+    UH_static,
+
+    // UH_dynamic: the data will be repeatedly modified and
+    // re-rendered.
+    UH_dynamic,
+  };
+
+  qpGeomVertexArrayData(const qpGeomVertexArrayFormat *array_format,
+                        UsageHint usage_hint);
+  qpGeomVertexArrayData(const qpGeomVertexArrayData &copy);
+private:
+  void operator = (const qpGeomVertexArrayData &copy);
+PUBLISHED:
+  virtual ~qpGeomVertexArrayData();
+
+  INLINE const qpGeomVertexArrayFormat *get_array_format() const;
+  INLINE UsageHint get_usage_hint() const;
+
+  INLINE CPTA_uchar get_data() const;
+  INLINE PTA_uchar modify_data();
+  INLINE void set_data(CPTA_uchar data);
+
+  INLINE int get_num_vertices() const;
+  bool set_num_vertices(int n);
+  INLINE void clear_vertices();
+
+  INLINE int get_num_bytes() const;
+  INLINE UpdateSeq get_modified() const;
+
+  void prepare(PreparedGraphicsObjects *prepared_objects);
+
+public:
+  DataContext *prepare_now(PreparedGraphicsObjects *prepared_objects, 
+                           GraphicsStateGuardianBase *gsg);
+  bool release(PreparedGraphicsObjects *prepared_objects);
+  int release_all();
+
+private:
+  void clear_prepared(PreparedGraphicsObjects *prepared_objects);
+
+  CPT(qpGeomVertexArrayFormat) _array_format;
+  UsageHint _usage_hint;
+
+  // A GeomVertexArrayData keeps a list (actually, a map) of all the
+  // PreparedGraphicsObjects tables that it has been prepared into.
+  // Each PGO conversely keeps a list (a set) of all the Geoms that
+  // have been prepared there.  When either destructs, it removes
+  // itself from the other's list.
+  typedef pmap<PreparedGraphicsObjects *, DataContext *> Contexts;
+  Contexts _contexts;
+
+  // This is the data that must be cycled between pipeline stages.
+  class EXPCL_PANDA CData : public CycleData {
+  public:
+    INLINE CData();
+    INLINE CData(const CData &copy);
+    virtual CycleData *make_copy() const;
+    virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
+    virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
+    virtual void fillin(DatagramIterator &scan, BamReader *manager);
+
+    PTA_uchar _data;
+    UpdateSeq _modified;
+  };
+
+  PipelineCycler<CData> _cycler;
+  typedef CycleDataReader<CData> CDReader;
+  typedef CycleDataWriter<CData> CDWriter;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedWritableReferenceCount::init_type();
+    register_type(_type_handle, "qpGeomVertexArrayData",
+                  TypedWritableReferenceCount::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+
+  friend class qpGeomVertexCacheManager;
+  friend class PreparedGraphicsObjects;
+};
+
+#include "qpgeomVertexArrayData.I"
+
+#endif

+ 11 - 7
panda/src/gobj/qpgeomVertexData.I

@@ -34,11 +34,15 @@ get_format() const {
 //  Description: Sets the length of the array to n vertices in all of
 //  Description: Sets the length of the array to n vertices in all of
 //               the various arrays (presumably by adding vertices).
 //               the various arrays (presumably by adding vertices).
 //               The new vertex data is uninitialized.
 //               The new vertex data is uninitialized.
+//
+//               The return value is true if the number of vertices
+//               was changed, false if the object already contained n
+//               vertices (or if there was some error).
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE void qpGeomVertexData::
+INLINE bool qpGeomVertexData::
 set_num_vertices(int n) {
 set_num_vertices(int n) {
   CDWriter cdata(_cycler);
   CDWriter cdata(_cycler);
-  do_set_num_vertices(n, cdata);
+  return do_set_num_vertices(n, cdata);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -55,17 +59,17 @@ get_num_arrays() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexData::get_array_data
+//     Function: qpGeomVertexData::get_array
 //       Access: Published
 //       Access: Published
 //  Description: Returns a const pointer to the vertex data for the
 //  Description: Returns a const pointer to the vertex data for the
 //               indicated array, for application code to directly
 //               indicated array, for application code to directly
 //               examine (but not modify) the underlying vertex data.
 //               examine (but not modify) the underlying vertex data.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE CPTA_uchar qpGeomVertexData::
-get_array_data(int array) const {
+INLINE const qpGeomVertexArrayData *qpGeomVertexData::
+get_array(int i) const {
   CDReader cdata(_cycler);
   CDReader cdata(_cycler);
-  nassertr(array >= 0 && array < (int)cdata->_arrays.size(), CPTA_uchar());
-  return cdata->_arrays[array];
+  nassertr(i >= 0 && i < (int)cdata->_arrays.size(), NULL);
+  return cdata->_arrays[i];
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 61 - 73
panda/src/gobj/qpgeomVertexData.cxx

@@ -43,15 +43,21 @@ qpGeomVertexData() {
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 qpGeomVertexData::
 qpGeomVertexData::
-qpGeomVertexData(const qpGeomVertexFormat *format) :
+qpGeomVertexData(const qpGeomVertexFormat *format,
+                 qpGeomVertexArrayData::UsageHint usage_hint) :
   _format(format)
   _format(format)
 {
 {
   nassertv(_format->is_registered());
   nassertv(_format->is_registered());
 
 
   // Create some empty arrays as required by the format.
   // Create some empty arrays as required by the format.
   CDWriter cdata(_cycler);
   CDWriter cdata(_cycler);
-  cdata->_arrays.insert(cdata->_arrays.end(), _format->get_num_arrays(), 
-                        PTA_uchar());
+
+  int num_arrays = _format->get_num_arrays();
+  for (int i = 0; i < num_arrays; i++) {
+    PT(qpGeomVertexArrayData) array = new qpGeomVertexArrayData
+      (_format->get_array(i), usage_hint);
+    cdata->_arrays.push_back(array);
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -125,7 +131,7 @@ get_num_vertices() const {
 
 
   // Look up the answer on the first array (since any array will do).
   // Look up the answer on the first array (since any array will do).
   int stride = _format->get_array(0)->get_stride();
   int stride = _format->get_array(0)->get_stride();
-  return cdata->_arrays[0].size() / stride;
+  return cdata->_arrays[0]->get_num_bytes() / stride;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -137,6 +143,7 @@ get_num_vertices() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void qpGeomVertexData::
 void qpGeomVertexData::
 clear_vertices() {
 clear_vertices() {
+  clear_cache();
   CDWriter cdata(_cycler);
   CDWriter cdata(_cycler);
   nassertv(_format->get_num_arrays() == (int)cdata->_arrays.size());
   nassertv(_format->get_num_arrays() == (int)cdata->_arrays.size());
 
 
@@ -144,12 +151,12 @@ clear_vertices() {
   for (ai = cdata->_arrays.begin();
   for (ai = cdata->_arrays.begin();
        ai != cdata->_arrays.end();
        ai != cdata->_arrays.end();
        ++ai) {
        ++ai) {
-    (*ai).clear();
+    (*ai)->clear_vertices();
   }
   }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexData::modify_array_data
+//     Function: qpGeomVertexData::modify_array
 //       Access: Published
 //       Access: Published
 //  Description: Returns a modifiable pointer to the indicated vertex
 //  Description: Returns a modifiable pointer to the indicated vertex
 //               array, so that application code may directly
 //               array, so that application code may directly
@@ -157,27 +164,24 @@ clear_vertices() {
 //               the length of this array, since all of the arrays
 //               the length of this array, since all of the arrays
 //               should be kept in sync--use add_vertices() instead.
 //               should be kept in sync--use add_vertices() instead.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-PTA_uchar qpGeomVertexData::
-modify_array_data(int array) {
+qpGeomVertexArrayData *qpGeomVertexData::
+modify_array(int i) {
   // Perform copy-on-write: if the reference count on the vertex data
   // Perform copy-on-write: if the reference count on the vertex data
   // is greater than 1, assume some other GeomVertexData has the same
   // is greater than 1, assume some other GeomVertexData has the same
   // pointer, so make a copy of it first.
   // pointer, so make a copy of it first.
-
+  clear_cache();
   CDWriter cdata(_cycler);
   CDWriter cdata(_cycler);
-  nassertr(array >= 0 && array < (int)cdata->_arrays.size(), PTA_uchar());
+  nassertr(i >= 0 && i < (int)cdata->_arrays.size(), NULL);
 
 
-  if (cdata->_arrays[array].get_ref_count() > 1) {
-    PTA_uchar orig_data = cdata->_arrays[array];
-    cdata->_arrays[array] = PTA_uchar();
-    cdata->_arrays[array].v() = orig_data.v();
+  if (cdata->_arrays[i]->get_ref_count() > 1) {
+    cdata->_arrays[i] = new qpGeomVertexArrayData(*cdata->_arrays[i]);
   }
   }
 
 
-  clear_cache();
-  return cdata->_arrays[array];
+  return cdata->_arrays[i];
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: qpGeomVertexData::set_array_data
+//     Function: qpGeomVertexData::set_array
 //       Access: Published
 //       Access: Published
 //  Description: Replaces the indicated vertex data array with
 //  Description: Replaces the indicated vertex data array with
 //               a completely new array.  You should be careful that
 //               a completely new array.  You should be careful that
@@ -185,10 +189,11 @@ modify_array_data(int array) {
 //               unless you know what you are doing.
 //               unless you know what you are doing.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void qpGeomVertexData::
 void qpGeomVertexData::
-set_array_data(int array, PTA_uchar array_data) {
+set_array(int i, const qpGeomVertexArrayData *array) {
+  clear_cache();
   CDWriter cdata(_cycler);
   CDWriter cdata(_cycler);
-  nassertv(array >= 0 && array < (int)cdata->_arrays.size());
-  cdata->_arrays[array] = array_data;
+  nassertv(i >= 0 && i < (int)cdata->_arrays.size());
+  cdata->_arrays[i] = (qpGeomVertexArrayData *)array;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -205,7 +210,7 @@ get_num_bytes() const {
 
 
   Arrays::const_iterator ai;
   Arrays::const_iterator ai;
   for (ai = cdata->_arrays.begin(); ai != cdata->_arrays.end(); ++ai) {
   for (ai = cdata->_arrays.begin(); ai != cdata->_arrays.end(); ++ai) {
-    num_bytes += (*ai).size();
+    num_bytes += (*ai)->get_num_bytes();
   }
   }
 
 
   return num_bytes;
   return num_bytes;
@@ -257,33 +262,34 @@ convert_to(const qpGeomVertexFormat *new_format) const {
   }
   }
   PStatTimer timer(_munge_data_pcollector);
   PStatTimer timer(_munge_data_pcollector);
 
 
-  PT(qpGeomVertexData) new_data = new qpGeomVertexData(new_format);
+  PT(qpGeomVertexData) new_data = 
+    new qpGeomVertexData(new_format, qpGeomVertexArrayData::UH_client);
 
 
   pset<int> done_arrays;
   pset<int> done_arrays;
 
 
   int num_arrays = _format->get_num_arrays();
   int num_arrays = _format->get_num_arrays();
-  int array;
+  int i;
 
 
   // First, check to see if any arrays can be simply appropriated for
   // First, check to see if any arrays can be simply appropriated for
   // the new format, without changing the data.
   // the new format, without changing the data.
-  for (array = 0; array < num_arrays; ++array) {
+  for (i = 0; i < num_arrays; ++i) {
     const qpGeomVertexArrayFormat *array_format = 
     const qpGeomVertexArrayFormat *array_format = 
-      _format->get_array(array);
+      _format->get_array(i);
 
 
     bool array_done = false;
     bool array_done = false;
 
 
     int new_num_arrays = new_format->get_num_arrays();
     int new_num_arrays = new_format->get_num_arrays();
-    for (int new_array = 0; 
-         new_array < new_num_arrays && !array_done; 
-         ++new_array) {
+    for (int new_i = 0; 
+         new_i < new_num_arrays && !array_done; 
+         ++new_i) {
       const qpGeomVertexArrayFormat *new_array_format = 
       const qpGeomVertexArrayFormat *new_array_format = 
-        new_format->get_array(new_array);
+        new_format->get_array(new_i);
       if (new_array_format->is_data_subset_of(*array_format)) {
       if (new_array_format->is_data_subset_of(*array_format)) {
         // Great!  Just use the same data for this one.
         // Great!  Just use the same data for this one.
-        new_data->set_array_data(new_array, (PTA_uchar &)get_array_data(array));
+        new_data->set_array(new_i, get_array(i));
         array_done = true;
         array_done = true;
 
 
-        done_arrays.insert(new_array);
+        done_arrays.insert(new_i);
       }
       }
     }
     }
   }
   }
@@ -292,27 +298,30 @@ convert_to(const qpGeomVertexFormat *new_format) const {
   new_data->set_num_vertices(num_vertices);
   new_data->set_num_vertices(num_vertices);
 
 
   // Now go back through and copy any data that's left over.
   // Now go back through and copy any data that's left over.
-  for (array = 0; array < num_arrays; ++array) {
-    CPTA_uchar array_data = get_array_data(array);
-    const qpGeomVertexArrayFormat *array_format = 
-      _format->get_array(array);
+  for (i = 0; i < num_arrays; ++i) {
+    CPTA_uchar data = get_array(i)->get_data();
+    const qpGeomVertexArrayFormat *array_format = _format->get_array(i);
     int num_data_types = array_format->get_num_data_types();
     int num_data_types = array_format->get_num_data_types();
     for (int di = 0; di < num_data_types; ++di) {
     for (int di = 0; di < num_data_types; ++di) {
       const qpGeomVertexDataType *data_type = array_format->get_data_type(di);
       const qpGeomVertexDataType *data_type = array_format->get_data_type(di);
 
 
-      int new_array = new_format->get_array_with(data_type->get_name());
-      if (new_array >= 0 && done_arrays.count(new_array) == 0) {
+      int new_i = new_format->get_array_with(data_type->get_name());
+      if (new_i >= 0 && done_arrays.count(new_i) == 0) {
         // The data type exists in the new format; we have to copy it.
         // The data type exists in the new format; we have to copy it.
-        PTA_uchar new_array_data = new_data->modify_array_data(new_array);
+        PT(qpGeomVertexArrayData) new_array_data = new
+          qpGeomVertexArrayData(*get_array(i));
+        new_data->set_array(new_i, new_array_data);
+        PTA_uchar new_data = new_array_data->modify_data();
+
         const qpGeomVertexArrayFormat *new_array_format = 
         const qpGeomVertexArrayFormat *new_array_format = 
-          new_format->get_array(new_array);
+          new_format->get_array(new_i);
         const qpGeomVertexDataType *new_data_type = 
         const qpGeomVertexDataType *new_data_type = 
           new_array_format->get_data_type(data_type->get_name());
           new_array_format->get_data_type(data_type->get_name());
 
 
         new_data_type->copy_records
         new_data_type->copy_records
-          (new_array_data + new_data_type->get_start(), 
+          (new_data + new_data_type->get_start(), 
            new_array_format->get_stride(),
            new_array_format->get_stride(),
-           array_data + data_type->get_start(), array_format->get_stride(),
+           data + data_type->get_start(), array_format->get_stride(),
            data_type, num_vertices);
            data_type, num_vertices);
       }
       }
     }
     }
@@ -397,7 +406,7 @@ set_data(int array, const qpGeomVertexDataType *data_type,
 
 
   {
   {
     CDReader cdata(_cycler);
     CDReader cdata(_cycler);
-    int array_size = (int)cdata->_arrays[array].size();
+    int array_size = (int)cdata->_arrays[array]->get_num_bytes();
     if (element + data_type->get_total_bytes() > array_size) {
     if (element + data_type->get_total_bytes() > array_size) {
       // Whoops, we need more vertices!
       // Whoops, we need more vertices!
       CDWriter cdataw(_cycler, cdata);
       CDWriter cdataw(_cycler, cdata);
@@ -405,7 +414,7 @@ set_data(int array, const qpGeomVertexDataType *data_type,
     }
     }
   }
   }
 
 
-  PTA_uchar array_data = modify_array_data(array);
+  PTA_uchar array_data = modify_array(array)->modify_data();
   nassertv(element >= 0 && element + data_type->get_total_bytes() <= (int)array_data.size());
   nassertv(element >= 0 && element + data_type->get_total_bytes() <= (int)array_data.size());
 
 
   switch (data_type->get_numeric_type()) {
   switch (data_type->get_numeric_type()) {
@@ -484,7 +493,7 @@ set_data(int array, const qpGeomVertexDataType *data_type,
 void qpGeomVertexData::
 void qpGeomVertexData::
 get_data(int array, const qpGeomVertexDataType *data_type,
 get_data(int array, const qpGeomVertexDataType *data_type,
          int vertex, float *data, int num_values) const {
          int vertex, float *data, int num_values) const {
-  CPTA_uchar array_data = get_array_data(array);
+  CPTA_uchar array_data = get_array(array)->get_data();
   int stride = _format->get_array(array)->get_stride();
   int stride = _format->get_array(array)->get_stride();
   int element = vertex * stride + data_type->get_start();
   int element = vertex * stride + data_type->get_start();
   nassertv(element >= 0 && element + data_type->get_total_bytes() <= (int)array_data.size());
   nassertv(element >= 0 && element + data_type->get_total_bytes() <= (int)array_data.size());
@@ -561,7 +570,8 @@ get_data(int array, const qpGeomVertexDataType *data_type,
 //               which case none of the output parameters are valid).
 //               which case none of the output parameters are valid).
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool qpGeomVertexData::
 bool qpGeomVertexData::
-get_array_info(const InternalName *name, CPTA_uchar &array_data,
+get_array_info(const InternalName *name, 
+               const qpGeomVertexArrayData *&array_data,
                int &num_components, 
                int &num_components, 
                qpGeomVertexDataType::NumericType &numeric_type, 
                qpGeomVertexDataType::NumericType &numeric_type, 
                int &start, int &stride) const {
                int &start, int &stride) const {
@@ -712,45 +722,23 @@ remove_cache_entry(const qpGeomVertexFormat *modifier) const {
 //       Access: Private
 //       Access: Private
 //  Description: The private implementation of set_num_vertices().
 //  Description: The private implementation of set_num_vertices().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void qpGeomVertexData::
-do_set_num_vertices(int n, CDWriter &cdata) {
-  nassertv(_format->get_num_arrays() == (int)cdata->_arrays.size());
+bool qpGeomVertexData::
+do_set_num_vertices(int n, qpGeomVertexData::CDWriter &cdata) {
+  nassertr(_format->get_num_arrays() == (int)cdata->_arrays.size(), false);
 
 
   bool any_changed = false;
   bool any_changed = false;
 
 
   for (size_t i = 0; i < cdata->_arrays.size(); i++) {
   for (size_t i = 0; i < cdata->_arrays.size(); i++) {
-    int stride = _format->get_array(i)->get_stride();
-    int delta = n - (cdata->_arrays[i].size() / stride);
-
-    if (delta != 0) {
+    if (cdata->_arrays[i]->set_num_vertices(n)) {
       any_changed = true;
       any_changed = true;
-      if (cdata->_arrays[i].get_ref_count() > 1) {
-        // Copy-on-write: the array is already reffed somewhere else,
-        // so we're just going to make a copy.
-        PTA_uchar new_array;
-        new_array.reserve(n * stride);
-        new_array.insert(new_array.end(), n * stride, uchar());
-        memcpy(new_array, cdata->_arrays[i], 
-               min((size_t)(n * stride), cdata->_arrays[i].size()));
-        cdata->_arrays[i] = new_array;
-
-      } else {
-        // We've got the only reference to the array, so we can change
-        // it directly.
-        if (delta > 0) {
-          cdata->_arrays[i].insert(cdata->_arrays[i].end(), delta * stride, uchar());
-          
-        } else {
-          cdata->_arrays[i].erase(cdata->_arrays[i].begin() + n * stride, 
-                                  cdata->_arrays[i].end());
-        }
-      }
     }
     }
   }
   }
 
 
   if (any_changed) {
   if (any_changed) {
     clear_cache();
     clear_cache();
   }
   }
+
+  return any_changed;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 11 - 9
panda/src/gobj/qpgeomVertexData.h

@@ -23,6 +23,7 @@
 #include "typedWritableReferenceCount.h"
 #include "typedWritableReferenceCount.h"
 #include "qpgeomVertexFormat.h"
 #include "qpgeomVertexFormat.h"
 #include "qpgeomVertexDataType.h"
 #include "qpgeomVertexDataType.h"
+#include "qpgeomVertexArrayData.h"
 #include "internalName.h"
 #include "internalName.h"
 #include "cycleData.h"
 #include "cycleData.h"
 #include "cycleDataReader.h"
 #include "cycleDataReader.h"
@@ -32,7 +33,6 @@
 #include "pointerTo.h"
 #include "pointerTo.h"
 #include "pmap.h"
 #include "pmap.h"
 #include "pvector.h"
 #include "pvector.h"
-#include "pta_uchar.h"
 
 
 class FactoryParams;
 class FactoryParams;
 class qpGeomVertexDataType;
 class qpGeomVertexDataType;
@@ -62,7 +62,8 @@ class EXPCL_PANDA qpGeomVertexData : public TypedWritableReferenceCount {
 private:
 private:
   qpGeomVertexData();
   qpGeomVertexData();
 PUBLISHED:
 PUBLISHED:
-  qpGeomVertexData(const qpGeomVertexFormat *format);
+  qpGeomVertexData(const qpGeomVertexFormat *format, 
+                   qpGeomVertexArrayData::UsageHint usage_hint);
   qpGeomVertexData(const qpGeomVertexData &copy);
   qpGeomVertexData(const qpGeomVertexData &copy);
   void operator = (const qpGeomVertexData &copy);
   void operator = (const qpGeomVertexData &copy);
   virtual ~qpGeomVertexData();
   virtual ~qpGeomVertexData();
@@ -70,13 +71,13 @@ PUBLISHED:
   INLINE const qpGeomVertexFormat *get_format() const;
   INLINE const qpGeomVertexFormat *get_format() const;
 
 
   int get_num_vertices() const;
   int get_num_vertices() const;
-  INLINE void set_num_vertices(int n);
+  INLINE bool set_num_vertices(int n);
   void clear_vertices();
   void clear_vertices();
 
 
   INLINE int get_num_arrays() const;
   INLINE int get_num_arrays() const;
-  INLINE CPTA_uchar get_array_data(int array) const;
-  PTA_uchar modify_array_data(int array);
-  void set_array_data(int array, PTA_uchar array_data);
+  INLINE const qpGeomVertexArrayData *get_array(int i) const;
+  qpGeomVertexArrayData *modify_array(int i);
+  void set_array(int i, const qpGeomVertexArrayData *array);
 
 
   int get_num_bytes() const;
   int get_num_bytes() const;
 
 
@@ -93,7 +94,8 @@ public:
   void get_data(int array, const qpGeomVertexDataType *data_type,
   void get_data(int array, const qpGeomVertexDataType *data_type,
                 int vertex, float *data, int num_values) const;
                 int vertex, float *data, int num_values) const;
 
 
-  bool get_array_info(const InternalName *name, CPTA_uchar &array_data,
+  bool get_array_info(const InternalName *name, 
+                      const qpGeomVertexArrayData *&array_data,
                       int &num_components,
                       int &num_components,
                       qpGeomVertexDataType::NumericType &numeric_type, 
                       qpGeomVertexDataType::NumericType &numeric_type, 
                       int &start, int &stride) const;
                       int &start, int &stride) const;
@@ -111,7 +113,7 @@ private:
 private:
 private:
   CPT(qpGeomVertexFormat) _format;
   CPT(qpGeomVertexFormat) _format;
 
 
-  typedef pvector<PTA_uchar> Arrays;
+  typedef pvector< PT(qpGeomVertexArrayData) > Arrays;
 
 
   // We have to use reference-counting pointers here instead of having
   // We have to use reference-counting pointers here instead of having
   // explicit cleanup in the GeomVertexFormat destructor, because the
   // explicit cleanup in the GeomVertexFormat destructor, because the
@@ -139,7 +141,7 @@ private:
   typedef CycleDataWriter<CData> CDWriter;
   typedef CycleDataWriter<CData> CDWriter;
 
 
 private:
 private:
-  void do_set_num_vertices(int n, CDWriter &cdata);
+  bool do_set_num_vertices(int n, CDWriter &cdata);
 
 
   static PStatCollector _munge_data_pcollector;
   static PStatCollector _munge_data_pcollector;
 
 

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

@@ -321,7 +321,7 @@ write_with_data(ostream &out, int indent_level,
   indent(out, indent_level)
   indent(out, indent_level)
     << data->get_num_vertices() << " vertices.\n";
     << data->get_num_vertices() << " vertices.\n";
   for (size_t i = 0; i < _arrays.size(); i++) {
   for (size_t i = 0; i < _arrays.size(); i++) {
-    CPTA_uchar array_data = data->get_array_data(i);
+    CPTA_uchar array_data = data->get_array(i)->get_data();
     indent(out, indent_level)
     indent(out, indent_level)
       << "Array " << i << " (" << (void *)array_data.p() << "):\n";
       << "Array " << i << " (" << (void *)array_data.p() << "):\n";
     _arrays[i]->write_with_data(out, indent_level + 2, data, i);
     _arrays[i]->write_with_data(out, indent_level + 2, data, i);

+ 5 - 0
panda/src/gsgbase/graphicsStateGuardianBase.h

@@ -30,6 +30,7 @@ class RenderBuffer;
 class GraphicsWindow;
 class GraphicsWindow;
 class NodePath;
 class NodePath;
 
 
+class DataContext;
 class GeomContext;
 class GeomContext;
 class GeomNode;
 class GeomNode;
 class Geom;
 class Geom;
@@ -44,6 +45,7 @@ class GeomTristrip;
 class GeomTrifan;
 class GeomTrifan;
 class GeomSphere;
 class GeomSphere;
 class qpGeomVertexData;
 class qpGeomVertexData;
+class qpGeomVertexArrayData;
 class qpGeomTriangles;
 class qpGeomTriangles;
 class qpGeomTristrips;
 class qpGeomTristrips;
 class qpGeomTrifans;
 class qpGeomTrifans;
@@ -132,6 +134,9 @@ public:
   virtual GeomContext *prepare_geom(Geom *geom)=0;
   virtual GeomContext *prepare_geom(Geom *geom)=0;
   virtual void release_geom(GeomContext *gc)=0;
   virtual void release_geom(GeomContext *gc)=0;
 
 
+  virtual DataContext *prepare_data(qpGeomVertexArrayData *data)=0;
+  virtual void release_data(DataContext *gc)=0;
+
   virtual CPT(qpGeomMunger) get_geom_munger(const RenderState *state)=0;
   virtual CPT(qpGeomMunger) get_geom_munger(const RenderState *state)=0;
 
 
   virtual void set_state_and_transform(const RenderState *state,
   virtual void set_state_and_transform(const RenderState *state,