diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index eb3b06e6c5..139fc5d686 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/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; diff --git a/panda/src/dxgsg8/Sources.pp b/panda/src/dxgsg8/Sources.pp index 118007edc5..b0ae6dbf9a 100644 --- a/panda/src/dxgsg8/Sources.pp +++ b/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 \ diff --git a/panda/src/dxgsg8/config_dxgsg8.cxx b/panda/src/dxgsg8/config_dxgsg8.cxx index e6de91ee25..4f490f3fea 100644 --- a/panda/src/dxgsg8/config_dxgsg8.cxx +++ b/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(); diff --git a/panda/src/dxgsg8/dxDataContext8.I b/panda/src/dxgsg8/dxDataContext8.I new file mode 100644 index 0000000000..bf3f65af53 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + diff --git a/panda/src/dxgsg8/dxDataContext8.cxx b/panda/src/dxgsg8/dxDataContext8.cxx new file mode 100644 index 0000000000..37d1dfd093 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "dxDataContext8.h" +#include "qpgeomVertexArrayData.h" +#include "qpgeomVertexArrayFormat.h" +#include "internalName.h" +#include "config_dxgsg8.h" +#include + +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(); +} + diff --git a/panda/src/dxgsg8/dxDataContext8.h b/panda/src/dxgsg8/dxDataContext8.h new file mode 100644 index 0000000000..e69dbac78c --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 + + diff --git a/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx b/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx index 669bd500c1..c340398c5f 100644 --- a/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx +++ b/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); + const qpGeomVertexArrayData *data = _vertex_data->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(); + DataContext *dc = ((qpGeomVertexArrayData *)data)->prepare_now(get_prepared_objects(), this); + nassertr(dc != (DataContext *)NULL, false); + apply_data(dc); - 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; - } - - // 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; - } - } - - 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 diff --git a/panda/src/dxgsg8/dxGraphicsStateGuardian8.h b/panda/src/dxgsg8/dxGraphicsStateGuardian8.h index 12d8aaae14..fe62fa0f8e 100644 --- a/panda/src/dxgsg8/dxGraphicsStateGuardian8.h +++ b/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); diff --git a/panda/src/dxgsg8/dxgsg8_composite1.cxx b/panda/src/dxgsg8/dxgsg8_composite1.cxx index dea3a23184..f9fb485380 100644 --- a/panda/src/dxgsg8/dxgsg8_composite1.cxx +++ b/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" diff --git a/panda/src/glstuff/glDataContext_src.h b/panda/src/glstuff/glDataContext_src.h index f35705d6bf..dd1fb61423 100644 --- a/panda/src/glstuff/glDataContext_src.h +++ b/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: diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 3e08ae7e8b..3ba84388a6 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/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())); - gdc->_modified = gdc->_data->get_modified(); + } else { + _glBufferSubData(GL_ARRAY_BUFFER, 0, gdc->get_num_bytes(), + gdc->get_data()->get_data()); + } + + 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; diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index 600b9641be..57d4dc1386 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/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); diff --git a/panda/src/gobj/dataContext.I b/panda/src/gobj/dataContext.I index ef7ffea77b..461b846e3c 100644 --- a/panda/src/gobj/dataContext.I +++ b/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(); +} diff --git a/panda/src/gobj/dataContext.h b/panda/src/gobj/dataContext.h index 02abd9a11f..3a69c31215 100644 --- a/panda/src/gobj/dataContext.h +++ b/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" diff --git a/panda/src/gobj/preparedGraphicsObjects.cxx b/panda/src/gobj/preparedGraphicsObjects.cxx index 0d4f1c6707..3a7f9fa720 100644 --- a/panda/src/gobj/preparedGraphicsObjects.cxx +++ b/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(); } diff --git a/panda/src/gobj/preparedGraphicsObjects.h b/panda/src/gobj/preparedGraphicsObjects.h index b84b16bbf4..7a9198ce20 100644 --- a/panda/src/gobj/preparedGraphicsObjects.h +++ b/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; }; diff --git a/panda/src/gobj/qpgeomVertexArrayData.h b/panda/src/gobj/qpgeomVertexArrayData.h index 385520c33c..933a10e221 100644 --- a/panda/src/gobj/qpgeomVertexArrayData.h +++ b/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.