diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index c84d7328a6..0758976113 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -2494,6 +2494,10 @@ draw_tristrips(const qpGeomTristrips *primitive) { const unsigned short *client_pointer = setup_primitive(primitive); if (connect_triangle_strips && _render_mode != RenderModeAttrib::M_wireframe) { + GLCAT.debug() + << "Connected triangle strips\n"; + primitive->write(GLCAT.debug(), 2); + // One long triangle strip, connected by the degenerate vertices // that have already been set up within the primitive. _vertices_tristrip_pcollector.add_level(primitive->get_num_vertices()); @@ -2504,6 +2508,10 @@ draw_tristrips(const qpGeomTristrips *primitive) { GL_UNSIGNED_SHORT, client_pointer); } else { + GLCAT.debug() + << "Separate triangle strips\n"; + primitive->write(GLCAT.debug(), 2); + // Send the individual triangle strips, stepping over the // degenerate vertices. CPTA_int ends = primitive->get_ends(); diff --git a/panda/src/gobj/qpgeom.cxx b/panda/src/gobj/qpgeom.cxx index 8103045cab..cc64315de1 100644 --- a/panda/src/gobj/qpgeom.cxx +++ b/panda/src/gobj/qpgeom.cxx @@ -141,6 +141,45 @@ set_vertex_data(const qpGeomVertexData *data) { reset_point_rendering(cdata); } +//////////////////////////////////////////////////////////////////// +// Function: qpGeom::offset_vertices +// Access: Published +// Description: Replaces a Geom's vertex table with a new table, and +// simultaneously adds the indicated offset to all +// vertex references within the Geom's primitives. This +// is intended to be used to combine multiple +// GeomVertexDatas from different Geoms into a single +// big buffer, with each Geom referencing a subset of +// the vertices in the buffer. +//////////////////////////////////////////////////////////////////// +void qpGeom:: +offset_vertices(const qpGeomVertexData *data, int offset) { + clear_cache(); + CDWriter cdata(_cycler); + cdata->_data = (qpGeomVertexData *)data; + +#ifndef NDEBUG + bool all_is_valid = true; +#endif + Primitives::iterator pi; + for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); ++pi) { + (*pi)->offset_vertices(offset); + +#ifndef NDEBUG + if (!(*pi)->check_valid(data)) { + all_is_valid = false; + } +#endif + } + + cdata->_got_usage_hint = false; + cdata->_modified = qpGeom::get_next_modified(); + mark_bound_stale(); + reset_point_rendering(cdata); + + nassertv(all_is_valid); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeom::set_primitive // Access: Published diff --git a/panda/src/gobj/qpgeom.h b/panda/src/gobj/qpgeom.h index 606fd15a17..05ac4e72db 100644 --- a/panda/src/gobj/qpgeom.h +++ b/panda/src/gobj/qpgeom.h @@ -98,6 +98,7 @@ PUBLISHED: INLINE CPT(qpGeomVertexData) get_vertex_data() const; PT(qpGeomVertexData) modify_vertex_data(); void set_vertex_data(const qpGeomVertexData *data); + void offset_vertices(const qpGeomVertexData *data, int offset); INLINE int get_num_primitives() const; INLINE const qpGeomPrimitive *get_primitive(int i) const; diff --git a/panda/src/gobj/qpgeomPrimitive.cxx b/panda/src/gobj/qpgeomPrimitive.cxx index 2e70688a1a..627588f9fc 100644 --- a/panda/src/gobj/qpgeomPrimitive.cxx +++ b/panda/src/gobj/qpgeomPrimitive.cxx @@ -274,6 +274,28 @@ clear_vertices() { cdata->_got_minmax = false; } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomPrimitive::offset_vertices +// Access: Published +// Description: Adds the indicated offset to all vertices used by the +// primitive. +//////////////////////////////////////////////////////////////////// +void qpGeomPrimitive:: +offset_vertices(int offset) { + clear_cache(); + CDWriter cdata(_cycler); + + cdata->_rotated_vertices.clear(); + cdata->_mins.clear(); + cdata->_maxs.clear(); + cdata->_got_minmax = false; + + PTA_ushort::iterator vi; + for (vi = cdata->_vertices.begin(); vi != cdata->_vertices.end(); ++vi) { + (*vi) += offset; + } +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomPrimitive::get_num_primitives // Access: Published diff --git a/panda/src/gobj/qpgeomPrimitive.h b/panda/src/gobj/qpgeomPrimitive.h index bf34ee8e50..7f0cf6bce7 100644 --- a/panda/src/gobj/qpgeomPrimitive.h +++ b/panda/src/gobj/qpgeomPrimitive.h @@ -119,6 +119,7 @@ PUBLISHED: void add_next_vertices(int num_vertices); bool close_primitive(); void clear_vertices(); + void offset_vertices(int offset); int get_num_primitives() const; int get_primitive_start(int n) const; diff --git a/panda/src/gobj/qpgeomVertexFormat.I b/panda/src/gobj/qpgeomVertexFormat.I index d9447bdf64..d7d8eac4fb 100644 --- a/panda/src/gobj/qpgeomVertexFormat.I +++ b/panda/src/gobj/qpgeomVertexFormat.I @@ -197,6 +197,37 @@ get_vector(int n) const { return _vectors[n]; } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexFormat::get_num_texcoords +// Access: Published +// Description: Returns the number of columns within the format +// that represent texture coordinates. +// +// This may only be called after the format has been +// registered. +//////////////////////////////////////////////////////////////////// +INLINE int qpGeomVertexFormat:: +get_num_texcoords() const { + nassertr(_is_registered, 0); + return _texcoords.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexFormat::get_texcoord +// Access: Published +// Description: Returns the name of the nth texcoord column. This +// represents a texture coordinate. +// +// This may only be called after the format has been +// registered. +//////////////////////////////////////////////////////////////////// +INLINE const InternalName *qpGeomVertexFormat:: +get_texcoord(int n) const { + nassertr(_is_registered, NULL); + nassertr(n >= 0 && n < (int)_texcoords.size(), NULL); + return _texcoords[n]; +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomVertexFormat::get_num_morphs // Access: Published diff --git a/panda/src/gobj/qpgeomVertexFormat.cxx b/panda/src/gobj/qpgeomVertexFormat.cxx index a5286c9c74..a142ac29f8 100644 --- a/panda/src/gobj/qpgeomVertexFormat.cxx +++ b/panda/src/gobj/qpgeomVertexFormat.cxx @@ -546,6 +546,11 @@ do_register() { _vectors.push_back(column->get_name()); break; + case qpGeomVertexColumn::C_texcoord: + // It's a texcoord. + _texcoords.push_back(column->get_name()); + break; + case qpGeomVertexColumn::C_morph_delta: { // It's a morph description. @@ -604,6 +609,7 @@ do_unregister() { _columns_by_name.clear(); _points.clear(); _vectors.clear(); + _texcoords.clear(); _morphs.clear(); } diff --git a/panda/src/gobj/qpgeomVertexFormat.h b/panda/src/gobj/qpgeomVertexFormat.h index 9c4607100b..3f347950c6 100644 --- a/panda/src/gobj/qpgeomVertexFormat.h +++ b/panda/src/gobj/qpgeomVertexFormat.h @@ -104,6 +104,9 @@ PUBLISHED: INLINE int get_num_vectors() const; INLINE const InternalName *get_vector(int n) const; + INLINE int get_num_texcoords() const; + INLINE const InternalName *get_texcoord(int n) const; + INLINE int get_num_morphs() const; INLINE const InternalName *get_morph_slider(int n) const; INLINE const InternalName *get_morph_base(int n) const; @@ -190,6 +193,7 @@ private: typedef pvector< CPT(InternalName) > Columns; Columns _points; Columns _vectors; + Columns _texcoords; class MorphRecord { public: diff --git a/panda/src/gobj/qpgeomVertexRewriter.I b/panda/src/gobj/qpgeomVertexRewriter.I index 210b758035..b9e76dac2c 100644 --- a/panda/src/gobj/qpgeomVertexRewriter.I +++ b/panda/src/gobj/qpgeomVertexRewriter.I @@ -159,7 +159,7 @@ set_column(const InternalName *name) { // The return value is true if the data type is valid, // false otherwise. //////////////////////////////////////////////////////////////////// -bool qpGeomVertexRewriter:: +INLINE bool qpGeomVertexRewriter:: set_column(int array, const qpGeomVertexColumn *column) { // It's important to invoke the writer first, then the reader. See // set_vertex(). diff --git a/panda/src/pgraph/config_pgraph.cxx b/panda/src/pgraph/config_pgraph.cxx index e352ca5f40..ac14bd759d 100644 --- a/panda/src/pgraph/config_pgraph.cxx +++ b/panda/src/pgraph/config_pgraph.cxx @@ -154,6 +154,14 @@ ConfigVariableBool auto_break_cycles "is false, you must explicitly call TransformState.clear_cache() " "from time to time to prevent gradual memory bloat.")); +ConfigVariableInt max_collect_vertices +("max-collect-vertices", 4096, + PRC_DESC("Specifies the maximum number of vertices that are allowed to be " + "accumulated into any one GeomVertexData structure as a result " + "of collecting objects together during a flatten operation. This " + "does not impose a limit on the original size of any one " + "GeomVertexData structure.")); + ConfigVariableBool polylight_info ("polylight-info", false, PRC_DESC("Set this true to view some info statements regarding the polylight. " diff --git a/panda/src/pgraph/config_pgraph.h b/panda/src/pgraph/config_pgraph.h index 6abdfd5401..6c4328635e 100644 --- a/panda/src/pgraph/config_pgraph.h +++ b/panda/src/pgraph/config_pgraph.h @@ -23,6 +23,7 @@ #include "notifyCategoryProxy.h" #include "dconfig.h" #include "configVariableBool.h" +#include "configVariableInt.h" #include "configVariableDouble.h" #include "configVariableList.h" @@ -40,6 +41,7 @@ extern ConfigVariableBool paranoid_compose; extern ConfigVariableBool compose_componentwise; extern ConfigVariableBool paranoid_const; extern ConfigVariableBool auto_break_cycles; +extern ConfigVariableInt max_collect_vertices; extern ConfigVariableBool polylight_info; extern ConfigVariableDouble lod_fade_time; diff --git a/panda/src/pgraph/geomTransformer.I b/panda/src/pgraph/geomTransformer.I index 14d80920b4..eb1b1efff6 100644 --- a/panda/src/pgraph/geomTransformer.I +++ b/panda/src/pgraph/geomTransformer.I @@ -17,6 +17,30 @@ //////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::get_max_collect_vertices +// Access: Public +// Description: Returns the maximum number of vertices that may be +// put into a single GeomVertexData as a result of +// collecting multiple objects in collect_vertex_data(). +//////////////////////////////////////////////////////////////////// +INLINE int GeomTransformer:: +get_max_collect_vertices() const { + return _max_collect_vertices; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::set_max_collect_vertices +// Access: Public +// Description: Specifies the maximum number of vertices that may be +// put into a single GeomVertexData as a result of +// collecting multiple objects in collect_vertex_data(). +//////////////////////////////////////////////////////////////////// +INLINE void GeomTransformer:: +set_max_collect_vertices(int max_collect_vertices) { + _max_collect_vertices = max_collect_vertices; +} + //////////////////////////////////////////////////////////////////// // Function: GeomTransformer::qpSourceVertices::Ordering Operator // Access: Public @@ -113,3 +137,19 @@ operator < (const GeomTransformer::SourceColors &other) const { } return (_scale.compare_to(other._scale) < 0); } + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::NewCollectedKey::Ordering Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool GeomTransformer::NewCollectedKey:: +operator < (const GeomTransformer::NewCollectedKey &other) const { + if (_format != other._format) { + return _format < other._format; + } + if (_usage_hint != other._usage_hint) { + return (int)_usage_hint < (int)other._usage_hint; + } + return _name < other._name; +} diff --git a/panda/src/pgraph/geomTransformer.cxx b/panda/src/pgraph/geomTransformer.cxx index c3223e6b2a..cc5534186d 100644 --- a/panda/src/pgraph/geomTransformer.cxx +++ b/panda/src/pgraph/geomTransformer.cxx @@ -28,7 +28,21 @@ // Description: //////////////////////////////////////////////////////////////////// GeomTransformer:: -GeomTransformer() { +GeomTransformer() : + // The default value here comes from the Config file. + _max_collect_vertices(max_collect_vertices) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +GeomTransformer:: +GeomTransformer(const GeomTransformer ©) : + _max_collect_vertices(copy._max_collect_vertices) +{ } //////////////////////////////////////////////////////////////////// @@ -510,3 +524,117 @@ apply_state(GeomNode *node, const RenderState *state) { return any_changed; } + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::collect_vertex_data +// Access: Public +// Description: Transforms the vertices and the normals in the +// indicated Geom by the indicated matrix. Returns true +// if the Geom was changed, false otherwise. +//////////////////////////////////////////////////////////////////// +bool GeomTransformer:: +collect_vertex_data(Geom *geom, bool keep_names) { + if (!geom->is_of_type(qpGeom::get_class_type())) { + return false; + } + + qpGeom *qpgeom = DCAST(qpGeom, geom); + + const qpGeomVertexData *vdata = qpgeom->get_vertex_data(); + + if (vdata->get_num_vertices() > _max_collect_vertices) { + // Don't even bother. + return false; + } + + const qpGeomVertexFormat *format = vdata->get_format(); + + NewCollectedKey key; + if (keep_names) { + key._name = vdata->get_name(); + } + key._format = format; + key._usage_hint = vdata->get_usage_hint(); + + AlreadyCollected::const_iterator ai; + ai = _already_collected.find(vdata); + if (ai != _already_collected.end()) { + // We've previously collected this vertex data; reuse it. + const AlreadyCollectedData &acd = (*ai).second; + qpgeom->offset_vertices(acd._data, acd._offset); + return true; + } + + // We haven't collected this vertex data yet; append the vertices + // onto the new data. + + NewCollectedData::iterator ni = _new_collected_data.find(key); + PT(qpGeomVertexData) new_data; + if (ni != _new_collected_data.end()) { + new_data = (*ni).second; + } else { + new_data = new qpGeomVertexData(vdata->get_name(), format, + vdata->get_usage_hint()); + _new_collected_data[key] = new_data; + } + + int offset = new_data->get_num_vertices(); + int new_num_vertices = offset + vdata->get_num_vertices(); + if (new_num_vertices > _max_collect_vertices) { + // Whoa, hold the phone! Too many vertices going into this one + // GeomVertexData object; we'd better start over. + new_data = new qpGeomVertexData(vdata->get_name(), format, + vdata->get_usage_hint()); + _new_collected_data[key] = new_data; + offset = 0; + new_num_vertices = vdata->get_num_vertices(); + } + + new_data->set_num_vertices(new_num_vertices); + + for (int i = 0; i < vdata->get_num_arrays(); ++i) { + qpGeomVertexArrayData *new_array = new_data->modify_array(i); + const qpGeomVertexArrayData *old_array = vdata->get_array(i); + int stride = format->get_array(i)->get_stride(); + int start_byte = offset * stride; + int copy_bytes = old_array->get_data_size_bytes(); + nassertr(start_byte + copy_bytes == new_array->get_data_size_bytes(), false); + + memcpy(new_array->modify_data() + start_byte, + old_array->get_data(), copy_bytes); + } + + qpgeom->offset_vertices(new_data, offset); + AlreadyCollectedData &acd = _already_collected[vdata]; + acd._data = new_data; + acd._offset = offset; + + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::collect_vertex_data +// Access: Public +// Description: Collects together individual GeomVertexData +// structures that share the same format into one big +// GeomVertexData structure. This is designed to +// minimize context switches on the graphics card. +//////////////////////////////////////////////////////////////////// +bool GeomTransformer:: +collect_vertex_data(GeomNode *node, bool keep_names) { + bool any_changed = false; + + GeomNode::CDWriter cdata(node->_cycler); + GeomNode::Geoms::iterator gi; + for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) { + GeomNode::GeomEntry &entry = (*gi); + PT(Geom) new_geom = entry._geom->make_copy(); + if (collect_vertex_data(new_geom, keep_names)) { + entry._geom = new_geom; + any_changed = true; + } + } + + return any_changed; +} diff --git a/panda/src/pgraph/geomTransformer.h b/panda/src/pgraph/geomTransformer.h index 8ff8424e49..20c2d1fe2e 100644 --- a/panda/src/pgraph/geomTransformer.h +++ b/panda/src/pgraph/geomTransformer.h @@ -48,8 +48,12 @@ class InternalName; class EXPCL_PANDA GeomTransformer { public: GeomTransformer(); + GeomTransformer(const GeomTransformer ©); ~GeomTransformer(); + INLINE int get_max_collect_vertices() const; + INLINE void set_max_collect_vertices(int max_collect_vertices); + bool transform_vertices(Geom *geom, const LMatrix4f &mat); bool transform_vertices(GeomNode *node, const LMatrix4f &mat); @@ -68,7 +72,12 @@ public: bool apply_state(GeomNode *node, const RenderState *state); + bool collect_vertex_data(Geom *geom, bool keep_names); + bool collect_vertex_data(GeomNode *node, bool keep_names); + private: + int _max_collect_vertices; + class qpSourceVertices { public: INLINE bool operator < (const qpSourceVertices &other) const; @@ -153,6 +162,25 @@ private: }; typedef pmap TColors; TColors _tcolors; + + class AlreadyCollectedData { + public: + CPT(qpGeomVertexData) _data; + int _offset; + }; + typedef pmap< CPT(qpGeomVertexData), AlreadyCollectedData> AlreadyCollected; + AlreadyCollected _already_collected; + + class NewCollectedKey { + public: + INLINE bool operator < (const NewCollectedKey &other) const; + + string _name; + CPT(qpGeomVertexFormat) _format; + qpGeomUsageHint::UsageHint _usage_hint; + }; + typedef pmap< NewCollectedKey, PT(qpGeomVertexData) > NewCollectedData; + NewCollectedData _new_collected_data; }; #include "geomTransformer.I" diff --git a/panda/src/pgraph/sceneGraphReducer.I b/panda/src/pgraph/sceneGraphReducer.I index e7c78d885b..15bae4ee7d 100644 --- a/panda/src/pgraph/sceneGraphReducer.I +++ b/panda/src/pgraph/sceneGraphReducer.I @@ -73,3 +73,22 @@ apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs, int attrib_types, GeomTransformer &transformer) { r_apply_attribs(node, attribs, attrib_types, transformer); } + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphReducer::collect_vertex_data +// Access: Published +// Description: Collects all different GeomVertexData blocks that +// have compatible formats at this node and below into a +// single, unified block (or at least multiple larger +// blocks). This is intended to reduce rendering +// overhead incurred by switching vertex buffers. +// +// The set of bits passed in collect_bits indicates +// which properties are used to differentiate +// GeomVertexData blocks. If it is 0, then more blocks +// will be combined together than if it is nonzero. +//////////////////////////////////////////////////////////////////// +INLINE int SceneGraphReducer:: +collect_vertex_data(PandaNode *root, int collect_bits) { + return r_collect_vertex_data(root, collect_bits, _transformer); +} diff --git a/panda/src/pgraph/sceneGraphReducer.cxx b/panda/src/pgraph/sceneGraphReducer.cxx index 68a8b267b7..edb9248f18 100644 --- a/panda/src/pgraph/sceneGraphReducer.cxx +++ b/panda/src/pgraph/sceneGraphReducer.cxx @@ -19,7 +19,7 @@ #include "sceneGraphReducer.h" #include "config_pgraph.h" #include "accumulatedAttribs.h" - +#include "modelNode.h" #include "pointerTo.h" #include "plist.h" #include "pmap.h" @@ -564,3 +564,47 @@ choose_name(PandaNode *preserve, PandaNode *source1, PandaNode *source2) { preserve->set_name(name); } } + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphReducer::r_collect_vertex_data +// Access: Private +// Description: The recursive implementation of +// collect_vertex_data(). +//////////////////////////////////////////////////////////////////// +int SceneGraphReducer:: +r_collect_vertex_data(PandaNode *node, int collect_bits, + GeomTransformer &transformer) { + int num_collected = 0; + + if ((collect_bits & CVD_model) != 0 && + node->is_of_type(ModelNode::get_class_type())) { + // When we come to a model node, start a new collection. + GeomTransformer new_transformer(transformer); + + PandaNode::Children children = node->get_children(); + int num_children = children.get_num_children(); + for (int i = 0; i < num_children; ++i) { + num_collected += + r_collect_vertex_data(children.get_child(i), collect_bits, new_transformer); + } + return num_collected; + } + + if (node->is_geom_node()) { + // When we come to geom node, collect. + bool keep_names = ((collect_bits & SceneGraphReducer::CVD_name) != 0); + if (transformer.collect_vertex_data(DCAST(GeomNode, node), keep_names)) { + ++num_collected; + } + } + + // Then recurse. + PandaNode::Children children = node->get_children(); + int num_children = children.get_num_children(); + for (int i = 0; i < num_children; ++i) { + num_collected += + r_collect_vertex_data(children.get_child(i), collect_bits, transformer); + } + + return num_collected; +} diff --git a/panda/src/pgraph/sceneGraphReducer.h b/panda/src/pgraph/sceneGraphReducer.h index 3b3755cb03..2c48f6ff5c 100644 --- a/panda/src/pgraph/sceneGraphReducer.h +++ b/panda/src/pgraph/sceneGraphReducer.h @@ -61,12 +61,19 @@ PUBLISHED: CS_recurse = 0x004, }; + enum CollectVertexData { + CVD_name = 0x001, + CVD_model = 0x002, + }; + INLINE void apply_attribs(PandaNode *node, int attrib_types = ~0); INLINE void apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs, int attrib_types, GeomTransformer &transformer); int flatten(PandaNode *root, int combine_siblings_bits); + INLINE int collect_vertex_data(PandaNode *root, int collect_bits = ~0); + protected: void r_apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs, int attrib_types, GeomTransformer &transformer); @@ -92,6 +99,9 @@ protected: void choose_name(PandaNode *preserve, PandaNode *source1, PandaNode *source2); + int r_collect_vertex_data(PandaNode *node, int collect_bits, + GeomTransformer &transformer); + private: GeomTransformer _transformer; };