Преглед на файлове

beginning dx8 vbuffer support

David Rose преди 21 години
родител
ревизия
0edc10b609

+ 13 - 3
panda/src/display/graphicsStateGuardian.cxx

@@ -47,8 +47,8 @@ PStatCollector GraphicsStateGuardian::_total_texusage_pcollector("Texture usage"
 PStatCollector GraphicsStateGuardian::_active_texusage_pcollector("Texture usage:Active");
 PStatCollector GraphicsStateGuardian::_total_geom_pcollector("Prepared Geoms");
 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_buffers_pcollector("Vertex Buffers");
+PStatCollector GraphicsStateGuardian::_active_buffers_pcollector("Vertex Buffers:Active");
 PStatCollector GraphicsStateGuardian::_total_geom_node_pcollector("Prepared GeomNodes");
 PStatCollector GraphicsStateGuardian::_active_geom_node_pcollector("Prepared GeomNodes:Active");
 PStatCollector GraphicsStateGuardian::_total_texmem_pcollector("Texture memory");
@@ -1446,6 +1446,7 @@ init_frame_pstats() {
     _active_texusage_pcollector.clear_level();
     _active_geom_pcollector.clear_level();
     _active_geom_node_pcollector.clear_level();
+    _active_buffers_pcollector.clear_level();
     
     // Also clear out our other counters while we're here.
     _vertices_tristrip_pcollector.clear_level();
@@ -1507,7 +1508,9 @@ 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());
+      int delta = dc->get_data()->get_num_bytes() - dc->get_num_bytes();
+      _total_buffers_pcollector.add_level(delta);
+      _active_buffers_pcollector.add_level(dc->get_data()->get_num_bytes());
     }
   }
 }
@@ -1555,6 +1558,13 @@ get_untextured_state() {
   return state;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::traverse_prepared_textures
+//       Access: Public
+//  Description: Calls the indicated function on all
+//               currently-prepared textures, or until the callback
+//               function returns false.
+////////////////////////////////////////////////////////////////////
 void GraphicsStateGuardian::
 traverse_prepared_textures(bool (*pertex_callbackfn)(TextureContext *,void *),void *callback_arg) {
   PreparedGraphicsObjects::Textures::const_iterator ti;

+ 2 - 0
panda/src/dxgsg8/Sources.pp

@@ -20,6 +20,7 @@
   // need to install these due to external projects that link directly with libpandadx (bartop)  
   #define INSTALL_HEADERS \
     dxgsg8base.h config_dxgsg8.h dxGraphicsStateGuardian8.I dxGraphicsStateGuardian8.h \
+    dxDataContext8.h \
     dxTextureContext8.h \
     dxGeomMunger8.h \
     d3dfont8.h \
@@ -36,6 +37,7 @@
     
   #define INCLUDED_SOURCES \
     config_dxgsg8.cxx \
+    dxDataContext8.cxx \
     dxTextureContext8.cxx \
     dxGeomMunger8.cxx \
     d3dfont8.cxx \

+ 2 - 0
panda/src/dxgsg8/config_dxgsg8.cxx

@@ -19,6 +19,7 @@
 #include "config_dxgsg8.h"
 #include "dxGraphicsStateGuardian8.h"
 #include "dxTextureContext8.h"
+#include "dxDataContext8.h"
 #include "dxGeomMunger8.h"
 #include "graphicsPipeSelection.h"
 #include "wdxGraphicsWindow8.h"
@@ -144,6 +145,7 @@ init_libdxgsg8() {
 
   DXGraphicsStateGuardian8::init_type();
   DXTextureContext8::init_type();
+  DXDataContext8::init_type();
   DXGeomMunger8::init_type();
 
   wdxGraphicsPipe8::init_type();

+ 18 - 0
panda/src/dxgsg8/dxDataContext8.I

@@ -0,0 +1,18 @@
+// Filename: dxDataContext8.I
+// Created by:  drose (18Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 150 - 0
panda/src/dxgsg8/dxDataContext8.cxx

@@ -0,0 +1,150 @@
+// Filename: dxDataContext8.cxx
+// Created by:  drose (18Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "dxDataContext8.h"
+#include "qpgeomVertexArrayData.h"
+#include "qpgeomVertexArrayFormat.h"
+#include "internalName.h"
+#include "config_dxgsg8.h"
+#include <d3dx8.h>
+
+TypeHandle DXDataContext8::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXDataContext8::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DXDataContext8::
+DXDataContext8(qpGeomVertexArrayData *data) :
+  DataContext(data),
+  _vbuffer(NULL)
+{
+  // Now fill in the FVF code.
+  const qpGeomVertexArrayFormat *array_format = data->get_array_format();
+
+  // We have to start with the vertex data, and work up from there in
+  // order, since that's the way the FVF is defined.
+  int n = 0;
+  int num_data_types = array_format->get_num_data_types();
+
+  _fvf = 0;
+  
+  if (n < num_data_types && 
+      array_format->get_data_type(n)->get_name() == InternalName::get_vertex()) {
+    _fvf |= D3DFVF_XYZ;
+    ++n;
+  }
+  if (n < num_data_types && 
+      array_format->get_data_type(n)->get_name() == InternalName::get_normal()) {
+    _fvf |= D3DFVF_NORMAL;
+    ++n;
+  }
+  if (n < num_data_types && 
+      array_format->get_data_type(n)->get_name() == InternalName::get_color()) {
+    _fvf |= D3DFVF_DIFFUSE;
+    ++n;
+  }
+
+  // For multitexture support, we will need to look for all of the
+  // texcoord names and enable them in order.
+  if (n < num_data_types && 
+      array_format->get_data_type(n)->get_name() == InternalName::get_texcoord()) {
+    const qpGeomVertexDataType *data_type = array_format->get_data_type(n);
+    switch (data_type->get_num_values()) {
+    case 1:
+      _fvf |= D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE1(0);
+      ++n;
+      break;
+    case 2:
+      _fvf |= D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0);
+      ++n;
+      break;
+    case 3:
+      _fvf |= D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE3(0);
+      ++n;
+      break;
+    case 4:
+      _fvf |= D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE4(0);
+      ++n;
+      break;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXDataContext8::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DXDataContext8::
+~DXDataContext8() {
+  if (_vbuffer != NULL) {
+    RELEASE(_vbuffer, dxgsg8, "vertex buffer", RELEASE_ONCE);
+    _vbuffer = NULL;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXDataContext8::create_vbuffer
+//       Access: Public
+//  Description: Creates a new vertex buffer and uploads data to it.
+////////////////////////////////////////////////////////////////////
+void DXDataContext8::
+create_vbuffer(DXScreenData &scrn) {
+  if (_vbuffer != NULL) {
+    RELEASE(_vbuffer, dxgsg8, "vertex buffer", RELEASE_ONCE);
+    _vbuffer = NULL;
+  }
+
+  HRESULT hr = scrn.pD3DDevice->CreateVertexBuffer
+    (get_data()->get_num_bytes(), D3DUSAGE_WRITEONLY,
+     _fvf, D3DPOOL_MANAGED, &_vbuffer);
+  if (FAILED(hr)) {
+    dxgsg8_cat.warning()
+      << "CreateVertexBuffer failed" << D3DERRORSTRING(hr);
+    _vbuffer = NULL;
+    
+  } else {
+    upload_data();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXDataContext8::upload_data
+//       Access: Public
+//  Description: Copies the latest data from the client store to
+//               DirectX.
+////////////////////////////////////////////////////////////////////
+void DXDataContext8::
+upload_data() {
+  nassertv(_vbuffer != NULL);
+
+  BYTE *local_pointer;
+  HRESULT hr = _vbuffer->Lock(0, 0, &local_pointer, 0);
+  if (FAILED(hr)) {
+    dxgsg8_cat.error()
+      << "VertexBuffer::Lock failed" << D3DERRORSTRING(hr);
+    return;
+  }
+
+  memcpy(local_pointer, get_data()->get_data(), get_data()->get_num_bytes());
+
+  _vbuffer->Unlock();
+}
+

+ 64 - 0
panda/src/dxgsg8/dxDataContext8.h

@@ -0,0 +1,64 @@
+// Filename: dxDataContext8.h
+// Created by:  drose (18Mar05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 DXDATACONTEXT8_H
+#define DXDATACONTEXT8_H
+
+#include "pandabase.h"
+#include "dxgsg8base.h"
+#include "dataContext.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : DXDataContext8
+// Description : Caches a GeomVertexArrayData in the DirectX device as
+//               a vertex buffer.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDADX DXDataContext8 : public DataContext {
+public:
+  DXDataContext8(qpGeomVertexArrayData *data);
+  virtual ~DXDataContext8();
+
+  void create_vbuffer(DXScreenData &scrn);
+  void upload_data();
+
+  IDirect3DVertexBuffer8 *_vbuffer;
+  int _fvf;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    DataContext::init_type();
+    register_type(_type_handle, "DXDataContext8",
+                  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 "dxDataContext8.I"
+
+#endif
+
+

+ 118 - 61
panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx

@@ -55,6 +55,8 @@
 #include "qpgeomTristrips.h"
 #include "qpgeomTrifans.h"
 #include "dxGeomMunger8.h"
+#include "config_gobj.h"
+#include "dxDataContext8.h"
 
 #ifdef DO_PSTATS
 #include "pStatTimer.h"
@@ -198,6 +200,9 @@ DXGraphicsStateGuardian8(const FrameBufferProperties &properties) :
     _pFvfBufBasePtr = NULL;
     _index_buf=NULL;
 
+    _vbuffer_active = false;
+    _ibuffer_active = false;
+
     //    _max_light_range = __D3DLIGHT_RANGE_MAX;
 
     // non-dx obj values inited here should not change if resize is
@@ -2613,60 +2618,12 @@ begin_draw_primitives(const qpGeomVertexData *vertex_data) {
 
   // The munger should have put the "vertex" data at the beginning of
   // the first array.
-  const qpGeomVertexArrayFormat *array_format = format->get_array(0);
-
-  // Now we have to start with the vertex data, and work up from there
-  // in order, since that's the way the FVF is defined.
-  int n = 0;
-  int num_data_types = array_format->get_num_data_types();
-
-  DWORD fvf = 0;
-  
-  if (n < num_data_types && 
-      array_format->get_data_type(n)->get_name() == InternalName::get_vertex()) {
-    fvf |= D3DFVF_XYZ;
-    ++n;
-  } else {
-    // No vertex data, no vertices.
-    return false;
-  }
-  if (n < num_data_types && 
-      array_format->get_data_type(n)->get_name() == InternalName::get_normal()) {
-    fvf |= D3DFVF_NORMAL;
-    ++n;
-  }
-  if (n < num_data_types && 
-      array_format->get_data_type(n)->get_name() == InternalName::get_color()) {
-    fvf |= D3DFVF_DIFFUSE;
-    ++n;
-  }
+  const qpGeomVertexArrayData *data = _vertex_data->get_array(0);
 
-  // For multitexture support, we will need to look for all of the
-  // texcoord names and enable them in order.
-  if (n < num_data_types && 
-      array_format->get_data_type(n)->get_name() == InternalName::get_texcoord()) {
-    const qpGeomVertexDataType *data_type = array_format->get_data_type(n);
-    switch (data_type->get_num_values()) {
-    case 1:
-      fvf |= D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE1(0);
-      ++n;
-      break;
-    case 2:
-      fvf |= D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0);
-      ++n;
-      break;
-    case 3:
-      fvf |= D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE3(0);
-      ++n;
-      break;
-    case 4:
-      fvf |= D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE4(0);
-      ++n;
-      break;
-    }
-  }
+  DataContext *dc = ((qpGeomVertexArrayData *)data)->prepare_now(get_prepared_objects(), this);
+  nassertr(dc != (DataContext *)NULL, false);
+  apply_data(dc);
 
-  set_vertex_format(fvf);
   return true;
 }
 
@@ -2677,15 +2634,24 @@ begin_draw_primitives(const qpGeomVertexData *vertex_data) {
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian8::
 draw_triangles(const qpGeomTriangles *primitive) {
-  _pD3DDevice->DrawIndexedPrimitiveUP
-    (D3DPT_TRIANGLELIST, 
-     primitive->get_min_vertex(),
-     primitive->get_max_vertex() - primitive->get_min_vertex() + 1,
-     primitive->get_num_primitives(), 
-     primitive->get_flat_first_vertices(),
-     D3DFMT_INDEX16,
-     _vertex_data->get_array(0)->get_data(),
-     _vertex_data->get_format()->get_array(0)->get_stride());
+  if (_vbuffer_active && _ibuffer_active) {
+    _pD3DDevice->DrawIndexedPrimitive
+      (D3DPT_TRIANGLELIST,
+       primitive->get_min_vertex(),
+       primitive->get_max_vertex() - primitive->get_min_vertex() + 1,
+       0, primitive->get_num_primitives());
+
+  } else {
+    _pD3DDevice->DrawIndexedPrimitiveUP
+      (D3DPT_TRIANGLELIST, 
+       primitive->get_min_vertex(),
+       primitive->get_max_vertex() - primitive->get_min_vertex() + 1,
+       primitive->get_num_primitives(), 
+       primitive->get_flat_first_vertices(),
+       D3DFMT_INDEX16,
+       _vertex_data->get_array(0)->get_data(),
+       _vertex_data->get_format()->get_array(0)->get_stride());
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -2936,6 +2902,97 @@ release_texture(TextureContext *tc) {
   delete gtc;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian8::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 *DXGraphicsStateGuardian8::
+prepare_data(qpGeomVertexArrayData *data) {
+  if (dxgsg8_cat.is_debug()) {
+    dxgsg8_cat.debug()
+      << "prepare_data(" << (void *)data << ")\n";
+  }
+
+  DXDataContext8 *ddc = new DXDataContext8(data);
+
+  if (vertex_buffers) {
+    ddc->create_vbuffer(*_pScrn);
+    ddc->mark_loaded();
+  }
+
+  return ddc;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian8::apply_data
+//       Access: Public
+//  Description: Makes the data the currently available data for
+//               rendering.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian8::
+apply_data(DataContext *dc) {
+  DXDataContext8 *ddc = DCAST(DXDataContext8, dc);
+
+  if (ddc->_vbuffer != NULL) {
+    add_to_data_record(ddc);
+  
+    if (ddc->was_modified()) {
+      if (dxgsg8_cat.is_debug()) {
+        dxgsg8_cat.debug()
+          << "apply_data(" << (void *)dc->get_data() << ")\n";
+      }
+      if (ddc->changed_size()) {
+        // Here we have to destroy the old vertex buffer and create a
+        // new one.
+        ddc->create_vbuffer(*_pScrn);
+
+      } else {
+        // Here we just copy the new data to the vertex buffer.
+        ddc->upload_data();
+      }
+      
+      ddc->mark_loaded();
+    }
+
+    _pD3DDevice->SetStreamSource
+      (0, ddc->_vbuffer, ddc->get_data()->get_array_format()->get_stride());
+    _vbuffer_active = true;
+
+  } else {
+    _vbuffer_active = false;
+  }
+
+  set_vertex_format(ddc->_fvf);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian8::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 DXGraphicsStateGuardian8::
+release_data(DataContext *dc) {
+  if (dxgsg8_cat.is_debug()) {
+    dxgsg8_cat.debug()
+      << "release_data(" << (void *)dc->get_data() << ")\n";
+  }
+
+  DXDataContext8 *ddc = DCAST(DXDataContext8, dc);
+  delete ddc;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DXGraphicsStateGuardian8::get_geom_munger
 //       Access: Public, Virtual

+ 6 - 0
panda/src/dxgsg8/dxGraphicsStateGuardian8.h

@@ -99,6 +99,10 @@ public:
   virtual void apply_texture(TextureContext *tc);
   virtual void release_texture(TextureContext *tc);
 
+  virtual DataContext *prepare_data(qpGeomVertexArrayData *data);
+  void apply_data(DataContext *dc);
+  virtual void release_data(DataContext *dc);
+
   virtual CPT(qpGeomMunger) get_geom_munger(const RenderState *state);
 
   virtual void framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr,
@@ -313,6 +317,8 @@ protected:
   int _projection_mat_stack_count;
 
   CPT(DisplayRegion) _actual_display_region;
+  bool _vbuffer_active;
+  bool _ibuffer_active;
 
   // Color/Alpha Matrix Transition stuff
   INLINE void transform_color(Colorf &InColor,D3DCOLOR &OutColor);

+ 1 - 0
panda/src/dxgsg8/dxgsg8_composite1.cxx

@@ -1,6 +1,7 @@
 #include "dxgsg8base.h"
 #include "config_dxgsg8.cxx"
 #include "dxTextureContext8.cxx"
+#include "dxDataContext8.cxx"
 #include "dxGeomMunger8.cxx"
 #include "d3dfont8.cxx"
 #include "wdxGraphicsPipe8.cxx"

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

@@ -21,7 +21,8 @@
 
 ////////////////////////////////////////////////////////////////////
 //       Class : GLDataContext
-// Description :
+// Description : Caches a GeomVertexArrayData on the GL as a buffer
+//               object.
 ////////////////////////////////////////////////////////////////////
 class EXPCL_GL CLP(DataContext) : public DataContext {
 public:

+ 23 - 18
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -2357,11 +2357,12 @@ prepare_data(qpGeomVertexArrayData *data) {
     CLP(DataContext) *gdc = new CLP(DataContext)(data);
     _glGenBuffers(1, &gdc->_index);
 
+    add_to_data_record(gdc);
     _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();
+    _glBufferData(GL_ARRAY_BUFFER, gdc->get_data()->get_num_bytes(),
+                  gdc->get_data()->get_data(), 
+                  get_usage(gdc->get_data()->get_usage_hint()));
+    gdc->mark_loaded();
     
     report_my_gl_errors();
     return gdc;
@@ -2372,17 +2373,12 @@ prepare_data(qpGeomVertexArrayData *data) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: CLP(GraphicsStateGuardian)::apply_data
-//       Access: Public, Virtual
+//       Access: Public
 //  Description: Makes the data the currently available data for
 //               rendering.
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsStateGuardian)::
 apply_data(DataContext *dc) {
-  if (GLCAT.is_debug()) {
-    GLCAT.debug()
-      << "apply_data(" << (void *)dc->_data << ")\n";
-  }
-
   nassertv(_supports_buffers);
 
   CLP(DataContext) *gdc = DCAST(CLP(DataContext), dc);
@@ -2390,11 +2386,22 @@ apply_data(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());
+  if (gdc->was_modified()) {
+    if (GLCAT.is_debug()) {
+      GLCAT.debug()
+        << "apply_data(" << (void *)dc->get_data() << ")\n";
+    }
+    if (gdc->changed_size()) {
+      _glBufferData(GL_ARRAY_BUFFER, gdc->get_data()->get_num_bytes(),
+                    gdc->get_data()->get_data(), 
+                    get_usage(gdc->get_data()->get_usage_hint()));
+
+    } else {
+      _glBufferSubData(GL_ARRAY_BUFFER, 0, gdc->get_num_bytes(),
+                       gdc->get_data()->get_data());
+    }
 
-    gdc->_modified = gdc->_data->get_modified();
+    gdc->mark_loaded();
   }
 
   report_my_gl_errors();
@@ -2412,7 +2419,7 @@ void CLP(GraphicsStateGuardian)::
 release_data(DataContext *dc) {
   if (GLCAT.is_debug()) {
     GLCAT.debug()
-      << "release_data(" << (void *)dc->_data << ")\n";
+      << "release_data(" << (void *)dc->get_data() << ")\n";
   }
 
   nassertv(_supports_buffers);
@@ -2458,9 +2465,7 @@ setup_array_data(const qpGeomVertexArrayData *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);
+  apply_data(dc);
 
   // NULL is the OpenGL convention for the first byte of the buffer.
   return NULL;

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

@@ -105,7 +105,7 @@ public:
   virtual void release_geom(GeomContext *gc);
 
   virtual DataContext *prepare_data(qpGeomVertexArrayData *data);
-  virtual void apply_data(DataContext *dc);
+  void apply_data(DataContext *dc);
   virtual void release_data(DataContext *dc);
   const unsigned char *setup_array_data(const qpGeomVertexArrayData *data);
 

+ 63 - 1
panda/src/gobj/dataContext.I

@@ -24,6 +24,68 @@
 ////////////////////////////////////////////////////////////////////
 INLINE DataContext::
 DataContext(qpGeomVertexArrayData *data) :
-  _data(data)
+  _data(data),
+  // Initially, the number of bytes is zero, until the data has been
+  // loaded (and mark_loaded() is called).
+  _num_bytes(0)
 {
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataContext::get_data
+//       Access: Public
+//  Description: Returns the pointer to the client-side array data
+//               object.
+////////////////////////////////////////////////////////////////////
+INLINE qpGeomVertexArrayData *DataContext::
+get_data() const {
+  return _data;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataContext::get_num_bytes
+//       Access: Public
+//  Description: Returns the number of bytes previously reported for
+//               the data object.  This is used to track changes in
+//               the data object's allocated size; if it changes from
+//               this, we need to create a new buffer.
+////////////////////////////////////////////////////////////////////
+INLINE int DataContext::
+get_num_bytes() const {
+  return _num_bytes;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataContext::changed_size
+//       Access: Public
+//  Description: Returns true if the data has changed size since the
+//               last time mark_loaded() was called.
+////////////////////////////////////////////////////////////////////
+INLINE bool DataContext::
+changed_size() const {
+  return get_num_bytes() != _data->get_num_bytes();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataContext::was_modified
+//       Access: Public
+//  Description: Returns true if the data has been modified since the
+//               last time mark_loaded() was called.
+////////////////////////////////////////////////////////////////////
+INLINE bool DataContext::
+was_modified() const {
+  return _modified != _data->get_modified();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DataContext::mark_loaded
+//       Access: Public
+//  Description: Should be called after the DataContext has been
+//               loaded into graphics memory, this updates the
+//               internal flags for changed_size() and modified().
+////////////////////////////////////////////////////////////////////
+INLINE void DataContext::
+mark_loaded() {
+  _num_bytes = _data->get_num_bytes();
+  _modified = _data->get_modified();
+}

+ 13 - 2
panda/src/gobj/dataContext.h

@@ -23,8 +23,7 @@
 
 #include "savedContext.h"
 #include "updateSeq.h"
-
-class qpGeomVertexArrayData;
+#include "qpgeomVertexArrayData.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : DataContext
@@ -41,11 +40,21 @@ class EXPCL_PANDA DataContext : public SavedContext {
 public:
   INLINE DataContext(qpGeomVertexArrayData *data);
 
+  INLINE qpGeomVertexArrayData *get_data() const;
+
+  INLINE int get_num_bytes() const;
+  INLINE bool changed_size() const;
+  INLINE bool was_modified() const;
+
+  INLINE void mark_loaded();
+
+private:
   // 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;
+  int _num_bytes;
 
 public:
   static TypeHandle get_class_type() {
@@ -63,6 +72,8 @@ public:
 
 private:
   static TypeHandle _type_handle;
+
+  friend class PreparedGraphicsObjects;
 };
 
 #include "dataContext.I"

+ 62 - 23
panda/src/gobj/preparedGraphicsObjects.cxx

@@ -18,9 +18,13 @@
 
 #include "preparedGraphicsObjects.h"
 #include "textureContext.h"
+#include "texture.h"
+#include "geom.h"
+#include "qpgeomVertexArrayData.h"
 #include "mutexHolder.h"
 
 PStatCollector PreparedGraphicsObjects::_total_texusage_pcollector("Texture usage");
+PStatCollector PreparedGraphicsObjects::_total_buffers_pcollector("Vertex Buffers");
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PreparedGraphicsObjects::Constructor
@@ -69,6 +73,19 @@ PreparedGraphicsObjects::
   _prepared_geoms.clear();
   _released_geoms.clear();
   _enqueued_geoms.clear();
+
+  Datas::iterator dci;
+  for (dci = _prepared_datas.begin();
+       dci != _prepared_datas.end();
+       ++dci) {
+    DataContext *dc = (*dci);
+    _total_texusage_pcollector.sub_level(dc->get_num_bytes());
+    dc->_data->clear_prepared(this);
+  }
+
+  _prepared_datas.clear();
+  _released_datas.clear();
+  _enqueued_datas.clear();
 }
 
 
@@ -411,20 +428,21 @@ dequeue_data(qpGeomVertexArrayData *data) {
 //               it's in, at the time release_data is called).
 ////////////////////////////////////////////////////////////////////
 void PreparedGraphicsObjects::
-release_data(DataContext *gc) {
+release_data(DataContext *dc) {
   MutexHolder holder(_lock);
 
-  gc->_data->clear_prepared(this);
+  dc->_data->clear_prepared(this);
+  _total_buffers_pcollector.sub_level(dc->get_num_bytes());
 
   // 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;
+  dc->_data = (qpGeomVertexArrayData *)NULL;
 
-  bool removed = (_prepared_datas.erase(gc) != 0);
+  bool removed = (_prepared_datas.erase(dc) != 0);
   nassertv(removed);
 
-  _released_datas.insert(gc);
+  _released_datas.insert(dc);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -441,15 +459,16 @@ release_all_datas() {
 
   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;
+  Datas::iterator dci;
+  for (dci = _prepared_datas.begin();
+       dci != _prepared_datas.end();
+       ++dci) {
+    DataContext *dc = (*dci);
+    dc->_data->clear_prepared(this);
+    _total_buffers_pcollector.sub_level(dc->get_num_bytes());
+    dc->_data = (qpGeomVertexArrayData *)NULL;
 
-    _released_datas.insert(gc);
+    _released_datas.insert(dc);
   }
 
   _prepared_datas.clear();
@@ -487,14 +506,16 @@ prepare_data_now(qpGeomVertexArrayData *data, GraphicsStateGuardianBase *gsg) {
   // 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);
+  DataContext *dc = gsg->prepare_data(data);
 
-  if (gc != (DataContext *)NULL) {
-    bool prepared = _prepared_datas.insert(gc).second;
-    nassertr(prepared, gc);
+  if (dc != (DataContext *)NULL) {
+    bool prepared = _prepared_datas.insert(dc).second;
+    nassertr(prepared, dc);
+
+    _total_buffers_pcollector.add_level(dc->get_num_bytes());
   }
 
-  return gc;
+  return dc;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -513,7 +534,8 @@ void PreparedGraphicsObjects::
 update(GraphicsStateGuardianBase *gsg) {
   MutexHolder holder(_lock);
 
-  // First, release all the textures awaiting release.
+  // First, release all the textures, geoms, and data arrays awaiting
+  // release.
   Textures::iterator tci;
   for (tci = _released_textures.begin();
        tci != _released_textures.end();
@@ -524,7 +546,6 @@ update(GraphicsStateGuardianBase *gsg) {
 
   _released_textures.clear();
 
-  // Next, release all the geoms awaiting release.
   Geoms::iterator gci;
   for (gci = _released_geoms.begin();
        gci != _released_geoms.end();
@@ -535,7 +556,18 @@ update(GraphicsStateGuardianBase *gsg) {
 
   _released_geoms.clear();
 
-  // Now prepare all the textures awaiting preparation.
+  Datas::iterator dci;
+  for (dci = _released_datas.begin();
+       dci != _released_datas.end();
+       ++dci) {
+    DataContext *dc = (*dci);
+    gsg->release_data(dc);
+  }
+
+  _released_datas.clear();
+
+  // Now prepare all the textures, geoms, and data arrays awaiting
+  // preparation.
   EnqueuedTextures::iterator qti;
   for (qti = _enqueued_textures.begin();
        qti != _enqueued_textures.end();
@@ -546,7 +578,6 @@ update(GraphicsStateGuardianBase *gsg) {
 
   _enqueued_textures.clear();
 
-  // And finally prepare all the geoms awaiting preparation.
   EnqueuedGeoms::iterator qgi;
   for (qgi = _enqueued_geoms.begin();
        qgi != _enqueued_geoms.end();
@@ -555,5 +586,13 @@ update(GraphicsStateGuardianBase *gsg) {
     geom->prepare_now(this, gsg);
   }
 
-  _enqueued_geoms.clear();
+  EnqueuedDatas::iterator qdi;
+  for (qdi = _enqueued_datas.begin();
+       qdi != _enqueued_datas.end();
+       ++qdi) {
+    qpGeomVertexArrayData *data = (*qdi);
+    data->prepare_now(this, gsg);
+  }
+
+  _enqueued_datas.clear();
 }

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

@@ -99,6 +99,7 @@ private:
   EnqueuedDatas _enqueued_datas;
 
   static PStatCollector _total_texusage_pcollector;
+  static PStatCollector _total_buffers_pcollector;
 
   friend class GraphicsStateGuardian;
 };

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

@@ -32,13 +32,16 @@
 
 class PreparedGraphicsObjects;
 class DataContext;
+class GraphicsStateGuardianBase;
 
 ////////////////////////////////////////////////////////////////////
 //       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".
+//               define one array, with all data elements interleaved
+//               (DirectX 8.0 and before insisted on this format);
+//               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.