GeomVertexData::reserve_num_rows() and related changes--big performance improvement, especially on Windows

This commit is contained in:
David Rose 2011-03-12 21:42:34 +00:00
parent d114ab235e
commit 372532346e
18 changed files with 545 additions and 146 deletions

View File

@ -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);
}

View File

@ -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();

View File

@ -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);

View File

@ -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

View File

@ -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,

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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;

View File

@ -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 &copy) :
_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;
}

View File

@ -28,89 +28,134 @@ operator = (const VertexDataBuffer &copy) {
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);

View File

@ -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;

View File

@ -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);
}
////////////////////////////////////////////////////////////////////

View File

@ -173,18 +173,21 @@ private:
typedef pvector<CurveVertex> CurveSegment;
typedef pvector<CurveSegment> 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,

View File

@ -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);

View File

@ -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());