From 372532346e810b59912a5a4c92da09b0eadeaa6b Mon Sep 17 00:00:00 2001 From: David Rose Date: Sat, 12 Mar 2011 21:42:34 +0000 Subject: [PATCH] GeomVertexData::reserve_num_rows() and related changes--big performance improvement, especially on Windows --- panda/src/egg2pg/eggLoader.cxx | 22 +++- panda/src/gobj/geomPrimitive.cxx | 54 ++++++++++ panda/src/gobj/geomPrimitive.h | 1 + panda/src/gobj/geomVertexArrayData.I | 15 +++ panda/src/gobj/geomVertexArrayData.cxx | 107 +++++++++++++++++-- panda/src/gobj/geomVertexArrayData.h | 2 + panda/src/gobj/geomVertexData.I | 17 +++ panda/src/gobj/geomVertexData.cxx | 27 ++++- panda/src/gobj/geomVertexData.h | 2 + panda/src/gobj/geomVertexWriter.cxx | 31 ++++++ panda/src/gobj/geomVertexWriter.h | 1 + panda/src/gobj/vertexDataBuffer.I | 104 +++++++++++------- panda/src/gobj/vertexDataBuffer.cxx | 139 ++++++++++++++++++------- panda/src/gobj/vertexDataBuffer.h | 15 ++- panda/src/parametrics/ropeNode.cxx | 94 +++++++++++------ panda/src/parametrics/ropeNode.h | 9 +- panda/src/parametrics/sheetNode.cxx | 50 ++++++--- panda/src/text/textAssembler.cxx | 1 + 18 files changed, 545 insertions(+), 146 deletions(-) diff --git a/panda/src/egg2pg/eggLoader.cxx b/panda/src/egg2pg/eggLoader.cxx index 9dac921bef..e4d407bced 100644 --- a/panda/src/egg2pg/eggLoader.cxx +++ b/panda/src/egg2pg/eggLoader.cxx @@ -411,7 +411,14 @@ make_polyset(EggBin *egg_bin, PandaNode *parent, const LMatrix4d *transform, // Add each new primitive to the Geom. Primitives::const_iterator pi; for (pi = primitives.begin(); pi != primitives.end(); ++pi) { - GeomPrimitive *primitive = (*pi); + PT(GeomPrimitive) primitive = (*pi); + + if (primitive->is_indexed()) { + // Since we may have over-allocated while we were filling up + // the primitives, down-allocate now. + primitive->reserve_num_vertices(primitive->get_num_vertices()); + } + geom->add_primitive(primitive); } @@ -2313,6 +2320,7 @@ make_vertex_data(const EggRenderState *render_state, // dynamic. PT(GeomVertexData) vertex_data = new GeomVertexData(name, format, Geom::UH_static); + vertex_data->reserve_num_rows(vertex_pool->size()); vertex_data->set_transform_blend_table(blend_table); if (slider_table != (SliderTable *)NULL) { @@ -2543,6 +2551,12 @@ make_primitive(const EggRenderState *render_state, EggPrimitive *egg_prim, if (result.second) { // This was the first primitive of this type. Store it. primitives.push_back(primitive); + + if (egg2pg_cat.is_debug()) { + egg2pg_cat.debug() + << "First primitive of type " << primitive->get_type() + << ": " << primitive << "\n"; + } } GeomPrimitive *orig_prim = (*result.first).second; @@ -2556,6 +2570,12 @@ make_primitive(const EggRenderState *render_state, EggPrimitive *egg_prim, // If the old primitive is full, keep the new primitive from now // on. (*result.first).second = primitive; + + if (egg2pg_cat.is_debug()) { + egg2pg_cat.debug() + << "Next primitive of type " << primitive->get_type() + << ": " << primitive << "\n"; + } primitives.push_back(primitive); } diff --git a/panda/src/gobj/geomPrimitive.cxx b/panda/src/gobj/geomPrimitive.cxx index ecde0dc2b8..711c5ebf62 100644 --- a/panda/src/gobj/geomPrimitive.cxx +++ b/panda/src/gobj/geomPrimitive.cxx @@ -188,6 +188,11 @@ void GeomPrimitive:: add_vertex(int vertex) { CDWriter cdata(_cycler, true); + if (gobj_cat.is_spam()) { + gobj_cat.spam() + << this << ".add_vertex(" << vertex << ")\n"; + } + consider_elevate_index_type(cdata, vertex); int num_primitives = get_num_primitives(); @@ -320,6 +325,42 @@ add_next_vertices(int num_vertices) { } } +//////////////////////////////////////////////////////////////////// +// Function: GeomPrimitive::reserve_num_vertices +// Access: Published +// Description: This ensures that enough memory space for n vertices +// is allocated, so that you may increase the number of +// vertices to n without causing a new memory +// allocation. This is a performance optimization only; +// it is especially useful when you know ahead of time +// that you will be adding n vertices to the primitive. +// +// Note that the total you specify here should also +// include implicit vertices which may be added at each +// close_primitive() call, according to +// get_num_unused_vertices_per_primitive(). +// +// Note also that making this call will implicitly make +// the primitive indexed if it is not already, which +// could result in a performance *penalty*. If you +// would prefer not to lose the nonindexed nature of +// your existing GeomPrimitives, check is_indexed() +// before making this call. +//////////////////////////////////////////////////////////////////// +void GeomPrimitive:: +reserve_num_vertices(int num_vertices) { + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << this << ".reserve_num_vertices(" << num_vertices << ")\n"; + } + + CDWriter cdata(_cycler, true); + consider_elevate_index_type(cdata, num_vertices); + do_make_indexed(cdata); + PT(GeomVertexArrayData) array_obj = cdata->_vertices.get_write_pointer(); + array_obj->reserve_num_rows(num_vertices); +} + //////////////////////////////////////////////////////////////////// // Function: GeomPrimitive::close_primitive // Access: Published @@ -1646,7 +1687,9 @@ recompute_minmax(GeomPrimitive::CData *cdata) { cdata->_maxs = make_index_data(); GeomVertexWriter mins(cdata->_mins.get_write_pointer(), 0); + mins.reserve_num_rows(cdata->_ends.size()); GeomVertexWriter maxs(cdata->_maxs.get_write_pointer(), 0); + maxs.reserve_num_rows(cdata->_ends.size()); int pi = 0; @@ -1711,9 +1754,15 @@ recompute_minmax(GeomPrimitive::CData *cdata) { void GeomPrimitive:: do_make_indexed(CData *cdata) { if (cdata->_vertices.is_null()) { + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << this << ".make_indexed()\n"; + } + nassertv(cdata->_num_vertices != -1); cdata->_vertices = make_index_data(); GeomVertexWriter index(cdata->_vertices.get_write_pointer(), 0); + index.reserve_num_rows(cdata->_num_vertices); for (int i = 0; i < cdata->_num_vertices; ++i) { index.add_data1i(i + cdata->_first_vertex); } @@ -1762,6 +1811,11 @@ void GeomPrimitive:: do_set_index_type(CData *cdata, GeomPrimitive::NumericType index_type) { cdata->_index_type = index_type; + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << this << ".set_index_type(" << index_type << ")\n"; + } + if (!cdata->_vertices.is_null()) { CPT(GeomVertexArrayFormat) new_format = get_index_format(); diff --git a/panda/src/gobj/geomPrimitive.h b/panda/src/gobj/geomPrimitive.h index c218cf98e7..aa729fc5ce 100644 --- a/panda/src/gobj/geomPrimitive.h +++ b/panda/src/gobj/geomPrimitive.h @@ -105,6 +105,7 @@ PUBLISHED: INLINE void add_vertices(int v1, int v2, int v3, int v4); void add_consecutive_vertices(int start, int num_vertices); void add_next_vertices(int num_vertices); + void reserve_num_vertices(int num_vertices); bool close_primitive(); void clear_vertices(); void offset_vertices(int offset); diff --git a/panda/src/gobj/geomVertexArrayData.I b/panda/src/gobj/geomVertexArrayData.I index ed717fa383..a0acbbf39f 100644 --- a/panda/src/gobj/geomVertexArrayData.I +++ b/panda/src/gobj/geomVertexArrayData.I @@ -106,6 +106,21 @@ unclean_set_num_rows(int n) { return modify_handle()->unclean_set_num_rows(n); } +//////////////////////////////////////////////////////////////////// +// Function: GeomVertexArrayData::reserve_num_rows +// Access: Published +// Description: This ensures that enough memory space for n rows is +// allocated, so that you may increase the number of +// rows to n without causing a new memory allocation. +// This is a performance optimization only; it is +// especially useful when you know ahead of time that +// you will be adding n rows to the data. +//////////////////////////////////////////////////////////////////// +INLINE bool GeomVertexArrayData:: +reserve_num_rows(int n) { + return modify_handle()->reserve_num_rows(n); +} + //////////////////////////////////////////////////////////////////// // Function: GeomVertexArrayData::clear_rows // Access: Published diff --git a/panda/src/gobj/geomVertexArrayData.cxx b/panda/src/gobj/geomVertexArrayData.cxx index f6fd745dc0..900cc89f09 100644 --- a/panda/src/gobj/geomVertexArrayData.cxx +++ b/panda/src/gobj/geomVertexArrayData.cxx @@ -24,6 +24,7 @@ #include "configVariableInt.h" #include "simpleAllocator.h" #include "vertexDataBuffer.h" +#include "texture.h" ConfigVariableInt max_independent_vertex_data ("max-independent-vertex-data", -1, @@ -641,12 +642,14 @@ fillin(DatagramIterator &scan, BamReader *manager, void *extra_data) { PTA_uchar new_data; READ_PTA(manager, scan, array_data->read_raw_data, new_data); _buffer.unclean_realloc(new_data.size()); + _buffer.set_size(new_data.size()); memcpy(_buffer.get_write_pointer(), &new_data[0], new_data.size()); } else { // Now, the array data is just stored directly. size_t size = scan.get_uint32(); _buffer.unclean_realloc(size); + _buffer.set_size(size); const unsigned char *source_data = (const unsigned char *)scan.get_datagram().get_data(); @@ -712,8 +715,29 @@ set_num_rows(int n) { size_t new_size = n * stride; size_t orig_size = _cdata->_buffer.get_size(); + if (gobj_cat.is_spam()) { + gobj_cat.spam() + << _object << ".set_num_rows(" << n << "), size = " << new_size << "\n"; + } + if (new_size != orig_size) { - _cdata->_buffer.clean_realloc(new_size); + size_t orig_reserved_size = _cdata->_buffer.get_reserved_size(); + if (new_size > orig_reserved_size) { + // Add more rows. Go up to the next power of two bytes, mainly + // to reduce the number of allocs needed. + size_t new_reserved_size = (size_t)Texture::up_to_power_2((int)new_size); + nassertr(new_reserved_size >= new_size, false); + + _cdata->_buffer.clean_realloc(new_reserved_size); + + } else if (new_size == 0) { + // If we set the number of rows to 0, go ahead and clear the + // buffer altogether, and let the user build it up again from + // nothing, to try to reduce frivolous memory waste. + _cdata->_buffer.clear(); + } + + _cdata->_buffer.set_size(new_size); // Now ensure that the newly-added rows are initialized to 0. if (new_size > orig_size) { @@ -726,9 +750,12 @@ set_num_rows(int n) { if (get_current_thread()->get_pipeline_stage() == 0) { _object->set_lru_size(_cdata->_buffer.get_size()); } + + nassertr(get_num_rows() == n, true); return true; } + nassertr(get_num_rows() == n, false); return false; } @@ -745,16 +772,26 @@ unclean_set_num_rows(int n) { int stride = _object->_array_format->get_stride(); size_t new_size = n * stride; size_t orig_size = _cdata->_buffer.get_size(); + size_t orig_reserved_size = _cdata->_buffer.get_reserved_size(); + + if (new_size != orig_size || new_size != orig_reserved_size) { + // Since this is unclean_set_num_rows(), we won't be using it to + // incrementally increase the array; instead, it will generally be + // used only to create an array initially. So it makes sense to + // set the reserved size to precisely the same as the target size. - if (new_size != orig_size) { _cdata->_buffer.unclean_realloc(new_size); + _cdata->_buffer.set_size(new_size); + // No need to fill to zero or copy the old buffer, since this is // unclean_set_num_rows(). - _cdata->_modified = Geom::get_next_modified(); + if (new_size != orig_size) { + _cdata->_modified = Geom::get_next_modified(); - if (get_current_thread()->get_pipeline_stage() == 0) { - _object->set_lru_size(_cdata->_buffer.get_size()); + if (get_current_thread()->get_pipeline_stage() == 0) { + _object->set_lru_size(_cdata->_buffer.get_size()); + } } return true; } @@ -762,6 +799,38 @@ unclean_set_num_rows(int n) { return false; } +//////////////////////////////////////////////////////////////////// +// Function: GeomVertexArrayDataHandle::reserve_num_rows +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool GeomVertexArrayDataHandle:: +reserve_num_rows(int n) { + nassertr(_writable, false); + mark_used(); + + int stride = _object->_array_format->get_stride(); + size_t new_reserved_size = n * stride; + new_reserved_size = max(_cdata->_buffer.get_size(), new_reserved_size); + size_t orig_reserved_size = _cdata->_buffer.get_reserved_size(); + + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << _object << ".reserve_num_rows(" << n << "), size = " << new_reserved_size << "\n"; + } + + if (new_reserved_size != orig_reserved_size) { + // We allow the user to set the alloc point smaller with this + // call, assuming the user knows what he's doing. This allows the + // user to reduce wasted memory after completely filling up a + // buffer. + _cdata->_buffer.clean_realloc(new_reserved_size); + return true; + } + + return false; +} + //////////////////////////////////////////////////////////////////// // Function: GeomVertexArrayDataHandle::copy_data_from // Access: Public @@ -773,11 +842,12 @@ copy_data_from(const GeomVertexArrayDataHandle *other) { mark_used(); other->mark_used(); - _cdata->_buffer.unclean_realloc(other->_cdata->_buffer.get_size()); + size_t size = other->_cdata->_buffer.get_size(); + _cdata->_buffer.unclean_realloc(size); + _cdata->_buffer.set_size(size); unsigned char *dest = _cdata->_buffer.get_write_pointer(); const unsigned char *source = other->_cdata->_buffer.get_read_pointer(true); - size_t size = other->_cdata->_buffer.get_size(); memcpy(dest, source, size); _cdata->_modified = Geom::get_next_modified(); @@ -819,11 +889,18 @@ copy_subdata_from(size_t to_start, size_t to_size, memmove(pointer + to_start + to_size, pointer + to_start + from_size, to_buffer_orig_size - (to_start + to_size)); - to_buffer.clean_realloc(to_buffer_orig_size + from_size - to_size); + to_buffer.set_size(to_buffer_orig_size + from_size - to_size); } else if (to_size < from_size) { // Expand the array. - to_buffer.clean_realloc(to_buffer_orig_size + from_size - to_size); + size_t needed_size = to_buffer_orig_size + from_size - to_size; + size_t to_buffer_orig_reserved_size = to_buffer.get_reserved_size(); + if (needed_size > to_buffer_orig_reserved_size) { + size_t new_reserved_size = (size_t)Texture::up_to_power_2((int)needed_size); + to_buffer.clean_realloc(new_reserved_size); + } + to_buffer.set_size(needed_size); + unsigned char *pointer = to_buffer.get_write_pointer(); memmove(pointer + to_start + to_size, pointer + to_start + from_size, @@ -854,6 +931,7 @@ set_data(const string &data) { mark_used(); _cdata->_buffer.unclean_realloc(data.size()); + _cdata->_buffer.set_size(data.size()); memcpy(_cdata->_buffer.get_write_pointer(), data.data(), data.size()); _cdata->_modified = Geom::get_next_modified(); @@ -891,11 +969,18 @@ set_subdata(size_t start, size_t size, const string &data) { memmove(pointer + start + from_size, pointer + start + size, to_buffer_orig_size - (start + size)); - to_buffer.clean_realloc(to_buffer_orig_size + from_size - size); + to_buffer.set_size(to_buffer_orig_size + from_size - size); } else if (size < from_size) { // Expand the array. - to_buffer.clean_realloc(to_buffer_orig_size + from_size - size); + size_t needed_size = to_buffer_orig_size + from_size - size; + size_t to_buffer_orig_reserved_size = to_buffer.get_reserved_size(); + if (needed_size > to_buffer_orig_reserved_size) { + size_t new_reserved_size = (size_t)Texture::up_to_power_2((int)needed_size); + to_buffer.clean_realloc(new_reserved_size); + } + to_buffer.set_size(needed_size); + unsigned char *pointer = to_buffer.get_write_pointer(); memmove(pointer + start + from_size, pointer + start + size, diff --git a/panda/src/gobj/geomVertexArrayData.h b/panda/src/gobj/geomVertexArrayData.h index 52ff617f34..53ef9695e0 100644 --- a/panda/src/gobj/geomVertexArrayData.h +++ b/panda/src/gobj/geomVertexArrayData.h @@ -87,6 +87,7 @@ PUBLISHED: INLINE int get_num_rows() const; INLINE bool set_num_rows(int n); INLINE bool unclean_set_num_rows(int n); + INLINE bool reserve_num_rows(int n); INLINE void clear_rows(); INLINE int get_data_size_bytes() const; @@ -274,6 +275,7 @@ PUBLISHED: INLINE int get_num_rows() const; bool set_num_rows(int n); bool unclean_set_num_rows(int n); + bool reserve_num_rows(int n); INLINE void clear_rows(); INLINE int get_data_size_bytes() const; diff --git a/panda/src/gobj/geomVertexData.I b/panda/src/gobj/geomVertexData.I index 9ef045e8d0..e489566fe6 100644 --- a/panda/src/gobj/geomVertexData.I +++ b/panda/src/gobj/geomVertexData.I @@ -143,6 +143,23 @@ unclean_set_num_rows(int n) { return writer.unclean_set_num_rows(n); } +//////////////////////////////////////////////////////////////////// +// Function: GeomVertexData::reserve_num_rows +// Access: Published +// Description: This ensures that enough memory space for n rows is +// allocated, so that you may increase the number of +// rows to n without causing a new memory allocation. +// This is a performance optimization only; it is +// especially useful when you know ahead of time that +// you will be adding n rows to the data. +//////////////////////////////////////////////////////////////////// +INLINE bool GeomVertexData:: +reserve_num_rows(int n) { + GeomVertexDataPipelineWriter writer(this, true, Thread::get_current_thread()); + writer.check_array_writers(); + return writer.reserve_num_rows(n); +} + //////////////////////////////////////////////////////////////////// // Function: GeomVertexData::get_num_arrays // Access: Published diff --git a/panda/src/gobj/geomVertexData.cxx b/panda/src/gobj/geomVertexData.cxx index 2265c768c1..723e780f83 100644 --- a/panda/src/gobj/geomVertexData.cxx +++ b/panda/src/gobj/geomVertexData.cxx @@ -542,6 +542,7 @@ copy_from(const GeomVertexData *source, bool keep_data_objects, } // Now make sure the arrays we didn't share are all filled in. + reserve_num_rows(num_rows); set_num_rows(num_rows); // Now go back through and copy any data that's left over. @@ -2273,8 +2274,9 @@ unclean_set_num_rows(int n) { for (size_t i = 0; i < _cdata->_arrays.size(); i++) { if (_array_writers[i]->get_num_rows() != n) { - _array_writers[i]->unclean_set_num_rows(n); - any_changed = true; + if (_array_writers[i]->unclean_set_num_rows(n)) { + any_changed = true; + } } } @@ -2287,6 +2289,27 @@ unclean_set_num_rows(int n) { return any_changed; } +//////////////////////////////////////////////////////////////////// +// Function: GeomVertexDataPipelineWriter::reserve_num_rows +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +bool GeomVertexDataPipelineWriter:: +reserve_num_rows(int n) { + nassertr(_got_array_writers, false); + nassertr(_cdata->_format->get_num_arrays() == (int)_cdata->_arrays.size(), false); + + bool any_changed = false; + + for (size_t i = 0; i < _cdata->_arrays.size(); i++) { + if (_array_writers[i]->reserve_num_rows(n)) { + any_changed = true; + } + } + + return any_changed; +} + //////////////////////////////////////////////////////////////////// // Function: GeomVertexDataPipelineWriter::modify_array // Access: Public diff --git a/panda/src/gobj/geomVertexData.h b/panda/src/gobj/geomVertexData.h index ea9735c542..57db6ef421 100644 --- a/panda/src/gobj/geomVertexData.h +++ b/panda/src/gobj/geomVertexData.h @@ -105,6 +105,7 @@ PUBLISHED: INLINE int get_num_rows() const; INLINE bool set_num_rows(int n); INLINE bool unclean_set_num_rows(int n); + INLINE bool reserve_num_rows(int n); void clear_rows(); INLINE int get_num_arrays() const; @@ -487,6 +488,7 @@ public: int get_num_rows() const; bool set_num_rows(int n); bool unclean_set_num_rows(int n); + bool reserve_num_rows(int n); private: void make_array_writers(); diff --git a/panda/src/gobj/geomVertexWriter.cxx b/panda/src/gobj/geomVertexWriter.cxx index b97d7457f3..a534bac049 100644 --- a/panda/src/gobj/geomVertexWriter.cxx +++ b/panda/src/gobj/geomVertexWriter.cxx @@ -65,6 +65,37 @@ set_column(int array, const GeomVertexColumn *column) { return false; } +//////////////////////////////////////////////////////////////////// +// Function: GeomVertexWriter::reserve_num_rows +// Access: Published +// Description: This ensures that enough memory space for num_rows is +// allocated, so that you may add up to num_rows rows +// without causing a new memory allocation. This is a +// performance optimization only; it is especially +// useful when you know the number of rows you will be +// adding ahead of time. +//////////////////////////////////////////////////////////////////// +bool GeomVertexWriter:: +reserve_num_rows(int num_rows) { + bool result; + + if (_vertex_data != (GeomVertexData *)NULL) { + // If we have a whole GeomVertexData, we must set the length of + // all its arrays at once. + GeomVertexDataPipelineWriter writer(_vertex_data, true, _current_thread); + writer.check_array_writers(); + result = writer.reserve_num_rows(num_rows); + _handle = writer.get_array_writer(_array); + + } else { + // Otherwise, we can get away with modifying only the one array + // we're using. + result = _handle->reserve_num_rows(num_rows); + } + + return result; +} + //////////////////////////////////////////////////////////////////// // Function: GeomVertexWriter::output // Access: Published diff --git a/panda/src/gobj/geomVertexWriter.h b/panda/src/gobj/geomVertexWriter.h index 1c35cf9be7..bacecbb733 100644 --- a/panda/src/gobj/geomVertexWriter.h +++ b/panda/src/gobj/geomVertexWriter.h @@ -101,6 +101,7 @@ PUBLISHED: INLINE bool set_column(const InternalName *name); bool set_column(int array, const GeomVertexColumn *column); INLINE void clear(); + bool reserve_num_rows(int num_rows); INLINE bool has_column() const; INLINE int get_array() const; diff --git a/panda/src/gobj/vertexDataBuffer.I b/panda/src/gobj/vertexDataBuffer.I index 0ceb50b434..d9064b1599 100644 --- a/panda/src/gobj/vertexDataBuffer.I +++ b/panda/src/gobj/vertexDataBuffer.I @@ -21,7 +21,8 @@ INLINE VertexDataBuffer:: VertexDataBuffer() : _resident_data(NULL), - _size(0) + _size(0), + _reserved_size(0) { } @@ -33,9 +34,11 @@ VertexDataBuffer() : INLINE VertexDataBuffer:: VertexDataBuffer(size_t size) : _resident_data(NULL), - _size(0) + _size(0), + _reserved_size(0) { do_unclean_realloc(size); + _size = size; } //////////////////////////////////////////////////////////////////// @@ -46,7 +49,8 @@ VertexDataBuffer(size_t size) : INLINE VertexDataBuffer:: VertexDataBuffer(const VertexDataBuffer ©) : _resident_data(NULL), - _size(0) + _size(0), + _reserved_size(0) { (*this) = copy; } @@ -81,6 +85,7 @@ get_read_pointer(bool force) const { } nassertr(_block != (VertexDataBlock *)NULL, NULL); + nassertr(_reserved_size >= _size, NULL); // We don't necessarily need to page the buffer all the way into // independent status; it's sufficient just to return the block's @@ -97,9 +102,10 @@ INLINE unsigned char *VertexDataBuffer:: get_write_pointer() { LightMutexHolder holder(_lock); - if (_resident_data == (unsigned char *)NULL && _size != 0) { + if (_resident_data == (unsigned char *)NULL && _reserved_size != 0) { do_page_in(); } + nassertr(_reserved_size >= _size, NULL); return _resident_data; } @@ -114,17 +120,58 @@ get_size() const { } //////////////////////////////////////////////////////////////////// -// Function: VertexDataBuffer::clean_realloc +// Function: VertexDataBuffer::get_reserved_size // Access: Public -// Description: Changes the size of the buffer, preserving its data -// (except for any data beyond the new end of the -// buffer, if the buffer is being reduced). If the -// buffer is expanded, the new data is uninitialized. +// Description: Returns the total number of bytes "reserved" in the +// buffer. This may be greater than or equal to +// get_size(). If it is greater, the additional bytes +// are extra unused bytes in the buffer, and this +// indicates the maximum value that may be passed to +// set_size() without first calling one of the realloc +// methods. +//////////////////////////////////////////////////////////////////// +INLINE size_t VertexDataBuffer:: +get_reserved_size() const { + return _reserved_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: VertexDataBuffer::set_size +// Access: Public +// Description: Changes the size of the buffer. The new size must be +// less than or equal to the "reserved" size, which can +// only be changed via clean_realloc() or +// unclean_realloc(). //////////////////////////////////////////////////////////////////// INLINE void VertexDataBuffer:: -clean_realloc(size_t size) { +set_size(size_t size) { LightMutexHolder holder(_lock); - do_clean_realloc(size); + nassertv(size <= _reserved_size); + + if (size != _size) { + if (_resident_data == (unsigned char *)NULL && _reserved_size != 0) { + do_page_in(); + } + + _size = size; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: VertexDataBuffer::clean_realloc +// Access: Public +// Description: Changes the "reserved" size of the buffer, preserving +// its data (except for any data beyond the new end of +// the buffer, if the buffer is being reduced). If the +// buffer is expanded, the new data is uninitialized. +// +// It is an error to set the reserved size smaller than +// the size specified with set_size(). +//////////////////////////////////////////////////////////////////// +INLINE void VertexDataBuffer:: +clean_realloc(size_t reserved_size) { + LightMutexHolder holder(_lock); + do_clean_realloc(reserved_size); } //////////////////////////////////////////////////////////////////// @@ -133,11 +180,14 @@ clean_realloc(size_t size) { // Description: Changes the size of the buffer, without regard to // preserving its data. The buffer may contain random // data after this call. +// +// It is an error to set the reserved size smaller than +// the size specified with set_size(). //////////////////////////////////////////////////////////////////// INLINE void VertexDataBuffer:: -unclean_realloc(size_t size) { +unclean_realloc(size_t reserved_size) { LightMutexHolder holder(_lock); - do_unclean_realloc(size); + do_unclean_realloc(reserved_size); } //////////////////////////////////////////////////////////////////// @@ -147,7 +197,9 @@ unclean_realloc(size_t size) { //////////////////////////////////////////////////////////////////// INLINE void VertexDataBuffer:: clear() { - unclean_realloc(0); + LightMutexHolder holder(_lock); + _size = 0; + do_unclean_realloc(0); } //////////////////////////////////////////////////////////////////// @@ -165,27 +217,3 @@ page_out(VertexDataBook &book) { LightMutexHolder holder(_lock); do_page_out(book); } - -//////////////////////////////////////////////////////////////////// -// Function: VertexDataBuffer::swap -// Access: Public -// Description: Swaps the data buffers between this one and the other -// one. -//////////////////////////////////////////////////////////////////// -INLINE void VertexDataBuffer:: -swap(VertexDataBuffer &other) { - LightMutexHolder holder(_lock); - LightMutexHolder holder2(other._lock); - - unsigned char *resident_data = _resident_data; - size_t size = _size; - PT(VertexDataBlock) block = _block; - - _resident_data = other._resident_data; - _size = other._size; - _block = other._block; - - other._resident_data = resident_data; - other._size = size; - other._block = block; -} diff --git a/panda/src/gobj/vertexDataBuffer.cxx b/panda/src/gobj/vertexDataBuffer.cxx index aed159d4ce..fbca451608 100644 --- a/panda/src/gobj/vertexDataBuffer.cxx +++ b/panda/src/gobj/vertexDataBuffer.cxx @@ -28,89 +28,134 @@ operator = (const VertexDataBuffer ©) { LightMutexHolder holder2(copy._lock); if (_resident_data != (unsigned char *)NULL) { - nassertv(_size != 0); - get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size); + nassertv(_reserved_size != 0); + get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_reserved_size); PANDA_FREE_ARRAY(_resident_data); _resident_data = NULL; } - if (copy._resident_data != (unsigned char *)NULL) { - nassertv(copy._size != 0); + if (copy._resident_data != (unsigned char *)NULL && copy._size != 0) { + // We only allocate _size bytes, not the full _reserved_size + // allocated by the original copy. get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)copy._size); _resident_data = (unsigned char *)PANDA_MALLOC_ARRAY(copy._size); memcpy(_resident_data, copy._resident_data, copy._size); } _size = copy._size; + _reserved_size = copy._size; _block = copy._block; + nassertv(_reserved_size >= _size); +} + +//////////////////////////////////////////////////////////////////// +// Function: VertexDataBuffer::swap +// Access: Public +// Description: Swaps the data buffers between this one and the other +// one. +//////////////////////////////////////////////////////////////////// +void VertexDataBuffer:: +swap(VertexDataBuffer &other) { + LightMutexHolder holder(_lock); + LightMutexHolder holder2(other._lock); + + unsigned char *resident_data = _resident_data; + size_t size = _size; + size_t reserved_size = _reserved_size; + PT(VertexDataBlock) block = _block; + + _resident_data = other._resident_data; + _size = other._size; + _reserved_size = other._reserved_size; + _block = other._block; + + other._resident_data = resident_data; + other._size = size; + other._reserved_size = reserved_size; + other._block = block; + nassertv(_reserved_size >= _size); } //////////////////////////////////////////////////////////////////// // Function: VertexDataBuffer::do_clean_realloc // Access: Private -// Description: Changes the size of the buffer, preserving its data -// (except for any data beyond the new end of the -// buffer, if the buffer is being reduced). If the +// Description: Changes the reserved size of the buffer, preserving +// its data (except for any data beyond the new end of +// the buffer, if the buffer is being reduced). If the // buffer is expanded, the new data is uninitialized. // // Assumes the lock is already held. //////////////////////////////////////////////////////////////////// void VertexDataBuffer:: -do_clean_realloc(size_t size) { - if (size != _size) { - if (size == 0) { - do_unclean_realloc(size); +do_clean_realloc(size_t reserved_size) { + if (reserved_size != _reserved_size) { + if (reserved_size == 0 || _size == 0) { + do_unclean_realloc(reserved_size); return; } + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << this << ".clean_realloc(" << reserved_size << ")\n"; + } + // Page in if we're currently paged out. - if (_size != 0 && _resident_data == (unsigned char *)NULL) { + if (_reserved_size != 0 && _resident_data == (unsigned char *)NULL) { do_page_in(); } - get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)size - (int)_size); - if (_size == 0) { + get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)reserved_size - (int)_reserved_size); + if (_reserved_size == 0) { nassertv(_resident_data == (unsigned char *)NULL); - _resident_data = (unsigned char *)PANDA_MALLOC_ARRAY(size); + _resident_data = (unsigned char *)PANDA_MALLOC_ARRAY(reserved_size); } else { nassertv(_resident_data != (unsigned char *)NULL); - _resident_data = (unsigned char *)PANDA_REALLOC_ARRAY(_resident_data, size); + _resident_data = (unsigned char *)PANDA_REALLOC_ARRAY(_resident_data, reserved_size); } nassertv(_resident_data != (unsigned char *)NULL); - _size = size; + _reserved_size = reserved_size; } + + _size = min(_size, _reserved_size); } //////////////////////////////////////////////////////////////////// // Function: VertexDataBuffer::do_unclean_realloc // Access: Private -// Description: Changes the size of the buffer, without regard to -// preserving its data. The buffer may contain random -// data after this call. +// Description: Changes the reserved size of the buffer, without +// regard to preserving its data. This implicitly +// resets the size to 0. // // Assumes the lock is already held. //////////////////////////////////////////////////////////////////// void VertexDataBuffer:: -do_unclean_realloc(size_t size) { - if (size != _size || _resident_data == (unsigned char *)NULL) { +do_unclean_realloc(size_t reserved_size) { + if (reserved_size != _reserved_size || _resident_data == (unsigned char *)NULL) { + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << this << ".unclean_realloc(" << reserved_size << ")\n"; + } + // If we're paged out, discard the page. _block = NULL; if (_resident_data != (unsigned char *)NULL) { - nassertv(_size != 0); + nassertv(_reserved_size != 0); - get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size); + get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_reserved_size); PANDA_FREE_ARRAY(_resident_data); _resident_data = NULL; - _size = 0; + _reserved_size = 0; } - if (size != 0) { - get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)size); + if (reserved_size != 0) { + get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)reserved_size); nassertv(_resident_data == (unsigned char *)NULL); - _resident_data = (unsigned char *)PANDA_MALLOC_ARRAY(size); + _resident_data = (unsigned char *)PANDA_MALLOC_ARRAY(reserved_size); } - _size = size; + _reserved_size = reserved_size; } + + _size = 0; } //////////////////////////////////////////////////////////////////// @@ -127,21 +172,36 @@ do_unclean_realloc(size_t size) { //////////////////////////////////////////////////////////////////// void VertexDataBuffer:: do_page_out(VertexDataBook &book) { - if (_block != (VertexDataBlock *)NULL || _size == 0) { + if (_block != (VertexDataBlock *)NULL || _reserved_size == 0) { // We're already paged out. return; } nassertv(_resident_data != (unsigned char *)NULL); - _block = book.alloc(_size); - nassertv(_block != (VertexDataBlock *)NULL); - unsigned char *pointer = _block->get_pointer(true); - nassertv(pointer != (unsigned char *)NULL); - memcpy(pointer, _resident_data, _size); + if (_size == 0) { + // It's an empty buffer. Just deallocate it; don't bother to + // create a block. + get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_reserved_size); + PANDA_FREE_ARRAY(_resident_data); + _resident_data = NULL; + _reserved_size = 0; - get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size); - PANDA_FREE_ARRAY(_resident_data); - _resident_data = NULL; + } else { + // It's a nonempty buffer, so write _size bytes (but not the full + // _reserved_size bytes) to a block. + _block = book.alloc(_size); + nassertv(_block != (VertexDataBlock *)NULL); + unsigned char *pointer = _block->get_pointer(true); + nassertv(pointer != (unsigned char *)NULL); + memcpy(pointer, _resident_data, _size); + + get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_reserved_size); + PANDA_FREE_ARRAY(_resident_data); + _resident_data = NULL; + + _reserved_size = _size; + } + nassertv(_reserved_size >= _size); } //////////////////////////////////////////////////////////////////// @@ -155,12 +215,13 @@ do_page_out(VertexDataBook &book) { //////////////////////////////////////////////////////////////////// void VertexDataBuffer:: do_page_in() { - if (_resident_data != (unsigned char *)NULL || _size == 0) { + if (_resident_data != (unsigned char *)NULL || _reserved_size == 0) { // We're already paged in. return; } nassertv(_block != (VertexDataBlock *)NULL); + nassertv(_reserved_size == _size); get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)_size); _resident_data = (unsigned char *)PANDA_MALLOC_ARRAY(_size); diff --git a/panda/src/gobj/vertexDataBuffer.h b/panda/src/gobj/vertexDataBuffer.h index e90e7ff702..3f810d9378 100644 --- a/panda/src/gobj/vertexDataBuffer.h +++ b/panda/src/gobj/vertexDataBuffer.h @@ -33,14 +33,16 @@ // // independent - the buffer's memory is resident, and // owned by the VertexDataBuffer object itself (in -// _resident_data). +// _resident_data). In this state, _reserved_size might +// be greater than or equal to _size. // // paged - the buffer's memory is owned by a // VertexDataBlock. That block might itself be // resident, compressed, or paged to disk. If it is // resident, the memory may still be accessed directly // from the block. However, this memory is considered -// read-only. +// read-only. In this state, _reserved_size will always +// equal _size. // // VertexDataBuffers start out in independent state. // They get moved to paged state when their owning @@ -68,13 +70,15 @@ public: INLINE unsigned char *get_write_pointer(); INLINE size_t get_size() const; - INLINE void clean_realloc(size_t size); - INLINE void unclean_realloc(size_t size); + INLINE size_t get_reserved_size() const; + INLINE void set_size(size_t size); + INLINE void clean_realloc(size_t reserved_size); + INLINE void unclean_realloc(size_t reserved_size); INLINE void clear(); INLINE void page_out(VertexDataBook &book); - INLINE void swap(VertexDataBuffer &other); + void swap(VertexDataBuffer &other); private: void do_clean_realloc(size_t size); @@ -85,6 +89,7 @@ private: unsigned char *_resident_data; size_t _size; + size_t _reserved_size; PT(VertexDataBlock) _block; LightMutex _lock; diff --git a/panda/src/parametrics/ropeNode.cxx b/panda/src/parametrics/ropeNode.cxx index 8a33d64663..c5b71752d3 100644 --- a/panda/src/parametrics/ropeNode.cxx +++ b/panda/src/parametrics/ropeNode.cxx @@ -26,7 +26,7 @@ #include "datagramIterator.h" #include "pStatTimer.h" #include "geom.h" -#include "geomLinestrips.h" +#include "geomLines.h" #include "geomTristrips.h" #include "geomVertexWriter.h" #include "boundingSphere.h" @@ -352,27 +352,27 @@ void RopeNode:: render_thread(CullTraverser *trav, CullTraverserData &data, NurbsCurveResult *result) const { CurveSegments curve_segments; - get_connected_segments(curve_segments, result); + int num_curve_verts = get_connected_segments(curve_segments, result); // Now we have stored one or more sequences of vertices down the - // center strips. Go back through and calculate the vertices on - // either side. + // thread. These map directly to primitive vertices. PT(GeomVertexData) vdata = new GeomVertexData ("rope", get_format(false), Geom::UH_stream); - - compute_thread_vertices(vdata, curve_segments); - - PT(GeomLinestrips) strip = new GeomLinestrips(Geom::UH_stream); - CurveSegments::const_iterator si; - for (si = curve_segments.begin(); si != curve_segments.end(); ++si) { - const CurveSegment &segment = (*si); - - strip->add_next_vertices(segment.size()); - strip->close_primitive(); + compute_thread_vertices(vdata, curve_segments, num_curve_verts); + + // We use GeomLines instead of GeomLinestrips, since that can more + // easily be rendered directly. + PT(GeomLines) lines = new GeomLines(Geom::UH_stream); + lines->reserve_num_vertices((num_curve_verts - 1) * 2); + + for (int vi = 0; vi < num_curve_verts - 1; ++vi) { + lines->add_vertex(vi); + lines->add_vertex(vi + 1); + lines->close_primitive(); } PT(Geom) geom = new Geom(vdata); - geom->add_primitive(strip); + geom->add_primitive(lines); CPT(RenderAttrib) thick = RenderModeAttrib::make(RenderModeAttrib::M_unchanged, get_thickness()); CPT(RenderState) state = data._state->add_attrib(thick); @@ -402,7 +402,7 @@ void RopeNode:: render_tape(CullTraverser *trav, CullTraverserData &data, NurbsCurveResult *result) const { CurveSegments curve_segments; - get_connected_segments(curve_segments, result); + int num_curve_verts = get_connected_segments(curve_segments, result); // Now we have stored one or more sequences of vertices down the // center strips. Go back through and calculate the vertices on @@ -411,8 +411,10 @@ render_tape(CullTraverser *trav, CullTraverserData &data, ("rope", get_format(false), Geom::UH_stream); compute_billboard_vertices(vdata, -get_tube_up(), - curve_segments, result); - + curve_segments, num_curve_verts, result); + + // Since this will be a nonindexed primitive, no need to pre-reserve + // the number of vertices. PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream); CurveSegments::const_iterator si; for (si = curve_segments.begin(); si != curve_segments.end(); ++si) { @@ -459,7 +461,7 @@ render_billboard(CullTraverser *trav, CullTraverserData &data, LVector3f camera_vec = LVector3f::forward() * rel_transform->get_mat(); CurveSegments curve_segments; - get_connected_segments(curve_segments, result); + int num_curve_verts = get_connected_segments(curve_segments, result); // Now we have stored one or more sequences of vertices down the // center strips. Go back through and calculate the vertices on @@ -468,8 +470,10 @@ render_billboard(CullTraverser *trav, CullTraverserData &data, ("rope", get_format(false), Geom::UH_stream); compute_billboard_vertices(vdata, camera_vec, - curve_segments, result); + curve_segments, num_curve_verts, result); + // Since this will be a nonindexed primitive, no need to pre-reserve + // the number of vertices. PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream); CurveSegments::const_iterator si; for (si = curve_segments.begin(); si != curve_segments.end(); ++si) { @@ -508,7 +512,7 @@ void RopeNode:: render_tube(CullTraverser *trav, CullTraverserData &data, NurbsCurveResult *result) const { CurveSegments curve_segments; - get_connected_segments(curve_segments, result); + int num_curve_verts = get_connected_segments(curve_segments, result); // Now, we build up a table of vertices, in a series of rings // around the circumference of the tube. @@ -520,11 +524,13 @@ render_tube(CullTraverser *trav, CullTraverserData &data, ("rope", get_format(true), Geom::UH_stream); compute_tube_vertices(vdata, num_verts_per_slice, - curve_segments, result); + curve_segments, num_curve_verts, result); + // Finally, go through and build up the index array, to tie all the + // triangle strips together. This is difficult to pre-calculate the + // number of vertices we'll use, so we'll just let it dynamically + // allocate. PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream); - // Finally, go through build up the index array, to tie all the - // triangle strips together. int vi = 0; CurveSegments::const_iterator si; for (si = curve_segments.begin(); si != curve_segments.end(); ++si) { @@ -571,10 +577,15 @@ render_tube(CullTraverser *trav, CullTraverserData &data, // segments from the NurbsCurveEvaluator into a single // CurveSegment, if they happen to be connected (as most // will be). +// +// The return value is the total number of points across +// all segments. //////////////////////////////////////////////////////////////////// -void RopeNode:: +int RopeNode:: get_connected_segments(RopeNode::CurveSegments &curve_segments, const NurbsCurveResult *result) const { + int num_curve_verts = 0; + int num_verts = get_num_subdiv() + 1; int num_segments = result->get_num_segments(); bool use_vertex_color = get_use_vertex_color(); @@ -610,6 +621,7 @@ get_connected_segments(RopeNode::CurveSegments &curve_segments, } curve_segment->push_back(vtx); + ++num_curve_verts; } // Store all the remaining points in this segment. @@ -631,10 +643,13 @@ get_connected_segments(RopeNode::CurveSegments &curve_segments, } curve_segment->push_back(vtx); + ++num_curve_verts; last_point = vtx._p; } } + + return num_curve_verts; } //////////////////////////////////////////////////////////////////// @@ -646,7 +661,10 @@ get_connected_segments(RopeNode::CurveSegments &curve_segments, //////////////////////////////////////////////////////////////////// void RopeNode:: compute_thread_vertices(GeomVertexData *vdata, - const RopeNode::CurveSegments &curve_segments) const { + const RopeNode::CurveSegments &curve_segments, + int num_curve_verts) const { + vdata->set_num_rows(num_curve_verts); + GeomVertexWriter vertex(vdata, InternalName::get_vertex()); GeomVertexWriter color(vdata, InternalName::get_color()); GeomVertexWriter texcoord(vdata, InternalName::get_texcoord()); @@ -678,6 +696,8 @@ compute_thread_vertices(GeomVertexData *vdata, } } } + + nassertv(vdata->get_num_rows() == num_curve_verts); } //////////////////////////////////////////////////////////////////// @@ -691,7 +711,11 @@ void RopeNode:: compute_billboard_vertices(GeomVertexData *vdata, const LVector3f &camera_vec, const RopeNode::CurveSegments &curve_segments, + int num_curve_verts, NurbsCurveResult *result) const { + int expected_num_verts = num_curve_verts * 2; + vdata->set_num_rows(expected_num_verts); + GeomVertexWriter vertex(vdata, InternalName::get_vertex()); GeomVertexWriter color(vdata, InternalName::get_color()); GeomVertexWriter texcoord(vdata, InternalName::get_texcoord()); @@ -741,6 +765,8 @@ compute_billboard_vertices(GeomVertexData *vdata, } } } + + nassertv(vdata->get_num_rows() == expected_num_verts); } //////////////////////////////////////////////////////////////////// @@ -754,12 +780,8 @@ void RopeNode:: compute_tube_vertices(GeomVertexData *vdata, int &num_verts_per_slice, const RopeNode::CurveSegments &curve_segments, + int num_curve_verts, NurbsCurveResult *result) const { - GeomVertexWriter vertex(vdata, InternalName::get_vertex()); - GeomVertexWriter normal(vdata, InternalName::get_normal()); - GeomVertexWriter color(vdata, InternalName::get_color()); - GeomVertexWriter texcoord(vdata, InternalName::get_texcoord()); - int num_slices = get_num_slices(); num_verts_per_slice = num_slices; @@ -780,6 +802,14 @@ compute_tube_vertices(GeomVertexData *vdata, ++num_verts_per_slice; } + int expected_num_verts = num_curve_verts * num_verts_per_slice; + vdata->set_num_rows(expected_num_verts); + + GeomVertexWriter vertex(vdata, InternalName::get_vertex()); + GeomVertexWriter normal(vdata, InternalName::get_normal()); + GeomVertexWriter color(vdata, InternalName::get_color()); + GeomVertexWriter texcoord(vdata, InternalName::get_texcoord()); + LVector3f up = get_tube_up(); float dist = 0.0f; @@ -827,6 +857,8 @@ compute_tube_vertices(GeomVertexData *vdata, } } } + + nassertv(vdata->get_num_rows() == expected_num_verts); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/parametrics/ropeNode.h b/panda/src/parametrics/ropeNode.h index 58ea30226c..ad6cebd71a 100644 --- a/panda/src/parametrics/ropeNode.h +++ b/panda/src/parametrics/ropeNode.h @@ -173,18 +173,21 @@ private: typedef pvector CurveSegment; typedef pvector CurveSegments; - void get_connected_segments(CurveSegments &curve_segments, - const NurbsCurveResult *result) const; + int get_connected_segments(CurveSegments &curve_segments, + const NurbsCurveResult *result) const; void compute_thread_vertices(GeomVertexData *vdata, - const CurveSegments &curve_segments) const; + const CurveSegments &curve_segments, + int num_curve_verts) const; void compute_billboard_vertices(GeomVertexData *vdata, const LVector3f &camera_vec, const CurveSegments &curve_segments, + int num_curve_verts, NurbsCurveResult *result) const; void compute_tube_vertices(GeomVertexData *vdata, int &num_verts_per_slice, const CurveSegments &curve_segments, + int num_curve_verts, NurbsCurveResult *result) const; static void compute_tangent(LVector3f &tangent, const CurveSegment &segment, diff --git a/panda/src/parametrics/sheetNode.cxx b/panda/src/parametrics/sheetNode.cxx index 5611961b5b..1a7d7a9d7d 100644 --- a/panda/src/parametrics/sheetNode.cxx +++ b/panda/src/parametrics/sheetNode.cxx @@ -300,18 +300,18 @@ render_sheet(CullTraverser *trav, CullTraverserData &data, } PT(GeomVertexData) vdata = new GeomVertexData ("sheet", format, Geom::UH_stream); + int expected_num_vertices = num_u_segments * (num_u_verts + 1) * num_v_segments * num_v_verts; + vdata->reserve_num_rows(expected_num_vertices); + GeomVertexWriter vertex(vdata, InternalName::get_vertex()); GeomVertexWriter normal(vdata, InternalName::get_normal()); GeomVertexWriter color(vdata, InternalName::get_color()); GeomVertexWriter texcoord(vdata, InternalName::get_texcoord()); - - PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream); + for (int ui = 0; ui < num_u_segments; ui++) { - for (int uni = 0; uni < num_u_verts; uni++) { + for (int uni = 0; uni <= num_u_verts; uni++) { float u0 = (float)uni / (float)num_u_verts; - float u1 = (float)(uni + 1) / (float)num_u_verts; float u0_tc = result->get_segment_u(ui, u0); - float u1_tc = result->get_segment_u(ui, u1); for (int vi = 0; vi < num_v_segments; vi++) { for (int vni = 0; vni < num_v_verts; vni++) { @@ -326,26 +326,44 @@ render_sheet(CullTraverser *trav, CullTraverserData &data, normal.add_data3f(norm); texcoord.add_data2f(u0_tc, v_tc); - result->eval_segment_point(ui, vi, u1, v, point); - result->eval_segment_normal(ui, vi, u1, v, norm); - vertex.add_data3f(point); - normal.add_data3f(norm); - texcoord.add_data2f(u1_tc, v_tc); - if (use_vertex_color) { - Colorf c0, c1; + Colorf c0; result->eval_segment_extended_points(ui, vi, u0, v, 0, &c0[0], 4); - result->eval_segment_extended_points(ui, vi, u1, v, 0, &c1[0], 4); - color.add_data4f(c0); - color.add_data4f(c1); } } - strip->add_next_vertices(num_v_verts * 2); + } + } + } + nassertv(vdata->get_num_rows() == expected_num_vertices); + + PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream); + + int expected_num_tristrips = num_u_segments * num_u_verts * num_v_segments; + int expected_verts_per_tristrip = num_v_verts * 2; + + int expected_prim_vertices = (expected_num_tristrips - 1) * (expected_verts_per_tristrip + strip->get_num_unused_vertices_per_primitive()) + expected_verts_per_tristrip; + + strip->reserve_num_vertices(expected_prim_vertices); + + int verts_per_row = num_v_segments * num_v_verts; + + for (int ui = 0; ui < num_u_segments; ui++) { + for (int uni = 0; uni < num_u_verts; uni++) { + int row_start_index = ((ui * (num_u_verts + 1)) + uni) * verts_per_row; + + for (int vi = 0; vi < num_v_segments; vi++) { + for (int vni = 0; vni < num_v_verts; vni++) { + int vert_index_0 = row_start_index + (vi * num_v_verts) + vni; + int vert_index_1 = vert_index_0 + verts_per_row; + strip->add_vertex(vert_index_0); + strip->add_vertex(vert_index_1); + } strip->close_primitive(); } } } + nassertv(strip->get_num_vertices() == expected_prim_vertices); PT(Geom) geom = new Geom(vdata); geom->add_primitive(strip); diff --git a/panda/src/text/textAssembler.cxx b/panda/src/text/textAssembler.cxx index 3023151bc9..f9b04a3b46 100644 --- a/panda/src/text/textAssembler.cxx +++ b/panda/src/text/textAssembler.cxx @@ -1584,6 +1584,7 @@ draw_underscore(TextAssembler::PlacedGlyphs &row_placed_glyphs, CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3cp(); PT(GeomVertexData) vdata = new GeomVertexData("text", format, Geom::UH_static); + vdata->reserve_num_rows(2); GeomVertexWriter vertex(vdata, InternalName::get_vertex()); GeomVertexWriter color(vdata, InternalName::get_color());