From 732bbafaa9d7e03ccd55f591b9be49cce3fcfc01 Mon Sep 17 00:00:00 2001 From: David Rose Date: Mon, 21 May 2007 16:05:22 +0000 Subject: [PATCH] define NodePath::premunge_scene() and supporting implementation --- panda/src/display/standardMunger.cxx | 51 ++++- panda/src/display/standardMunger.h | 1 + panda/src/glstuff/glGeomMunger_src.I | 17 -- panda/src/glstuff/glGeomMunger_src.cxx | 249 +++++++++++++++++++++-- panda/src/glstuff/glGeomMunger_src.h | 12 +- panda/src/glstuff/glmisc_src.cxx | 17 ++ panda/src/glstuff/glmisc_src.h | 2 + panda/src/gobj/geomCacheManager.I | 13 ++ panda/src/gobj/geomCacheManager.cxx | 20 +- panda/src/gobj/geomCacheManager.h | 5 +- panda/src/gobj/geomMunger.I | 49 +++++ panda/src/gobj/geomMunger.cxx | 78 ++++++- panda/src/gobj/geomMunger.h | 18 +- panda/src/gobj/geomVertexArrayFormat.cxx | 41 +++- panda/src/gobj/geomVertexArrayFormat.h | 1 + panda/src/pgraph/geomNode.cxx | 30 +++ panda/src/pgraph/geomNode.h | 6 + panda/src/pgraph/geomTransformer.cxx | 28 +++ panda/src/pgraph/geomTransformer.h | 3 + panda/src/pgraph/nodePath.cxx | 27 +++ panda/src/pgraph/nodePath.h | 1 + panda/src/pgraph/renderState.cxx | 24 +++ panda/src/pgraph/renderState.h | 1 + panda/src/pgraph/sceneGraphReducer.I | 17 ++ panda/src/pgraph/sceneGraphReducer.cxx | 30 +++ panda/src/pgraph/sceneGraphReducer.h | 7 + 26 files changed, 704 insertions(+), 44 deletions(-) diff --git a/panda/src/display/standardMunger.cxx b/panda/src/display/standardMunger.cxx index 82f11c3516..bcc8cd0664 100644 --- a/panda/src/display/standardMunger.cxx +++ b/panda/src/display/standardMunger.cxx @@ -170,8 +170,55 @@ munge_data_impl(const GeomVertexData *data) { // Description: Converts a Geom and/or its data as necessary. //////////////////////////////////////////////////////////////////// bool StandardMunger:: -munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data, - Thread *current_thread) { +munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data, Thread *) { + int supported_geom_rendering = get_gsg()->get_supported_geom_rendering(); + + int unsupported_bits = geom->get_geom_rendering() & ~supported_geom_rendering; + if (unsupported_bits != 0) { + // Even beyond munging the vertex format, we have to convert the + // Geom itself into a new primitive type the GSG can render + // directly. + if ((unsupported_bits & Geom::GR_composite_bits) != 0) { + // This decomposes everything in the primitive, so that if (for + // instance) the primitive contained both strips and fans, but + // the GSG didn't support fans, it would decompose the strips + // too. To handle this correctly, we'd need a separate + // decompose_fans() and decompose_strips() call; but for now, + // we'll just say it's good enough. In practice, we don't have + // any GSG's that can support strips without also supporting + // fans. + geom = geom->decompose(); + + // Decomposing might produce an indexed Geom, so re-check the + // unsupported bits. + unsupported_bits = geom->get_geom_rendering() & ~supported_geom_rendering; + } + if ((unsupported_bits & Geom::GR_shade_model_bits) != 0) { + // Rotate the vertices to account for different shade-model + // expectations (e.g. SM_flat_last_vertex to + // SM_flat_first_vertex) + geom = geom->rotate(); + } + if ((unsupported_bits & Geom::GR_indexed_bits) != 0) { + // Convert indexed geometry to nonindexed geometry. + PT(Geom) new_geom = geom->make_copy(); + new_geom->set_vertex_data(vertex_data); + new_geom->make_nonindexed(false); + geom = new_geom; + vertex_data = new_geom->get_vertex_data(); + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: StandardMunger::premunge_geom_impl +// Access: Protected, Virtual +// Description: Converts a Geom and/or its data as necessary. +//////////////////////////////////////////////////////////////////// +bool StandardMunger:: +premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data) { int supported_geom_rendering = get_gsg()->get_supported_geom_rendering(); int unsupported_bits = geom->get_geom_rendering() & ~supported_geom_rendering; diff --git a/panda/src/display/standardMunger.h b/panda/src/display/standardMunger.h index 95235cc68b..1f69e84a43 100644 --- a/panda/src/display/standardMunger.h +++ b/panda/src/display/standardMunger.h @@ -49,6 +49,7 @@ protected: virtual int compare_to_impl(const GeomMunger *other) const; virtual bool munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data, Thread *current_thread); + virtual bool premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data); virtual int geom_compare_to_impl(const GeomMunger *other) const; virtual CPT(RenderState) munge_state_impl(const RenderState *state); diff --git a/panda/src/glstuff/glGeomMunger_src.I b/panda/src/glstuff/glGeomMunger_src.I index 42f22e5dc0..4b2b2a0f8e 100644 --- a/panda/src/glstuff/glGeomMunger_src.I +++ b/panda/src/glstuff/glGeomMunger_src.I @@ -16,20 +16,3 @@ // //////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////// -// Function: CLP(GeomMunger)::Constructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE CLP(GeomMunger):: -CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state) : - StandardMunger(gsg, state, 4, NT_uint8, C_color), - _texture(state->get_texture()), - _tex_gen(state->get_tex_gen()) -{ - // Set a callback to unregister ourselves when either the Texture or - // the TexGen object gets deleted. - _texture.set_callback(this); - _tex_gen.set_callback(this); -} diff --git a/panda/src/glstuff/glGeomMunger_src.cxx b/panda/src/glstuff/glGeomMunger_src.cxx index e84e5febfb..3ab3ab93d2 100644 --- a/panda/src/glstuff/glGeomMunger_src.cxx +++ b/panda/src/glstuff/glGeomMunger_src.cxx @@ -21,6 +21,31 @@ GeomMunger *CLP(GeomMunger)::_deleted_chain = NULL; TypeHandle CLP(GeomMunger)::_type_handle; +//////////////////////////////////////////////////////////////////// +// Function: CLP(GeomMunger)::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CLP(GeomMunger):: +CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state) : + StandardMunger(gsg, state, 4, NT_uint8, C_color), + _texture(state->get_texture()), + _tex_gen(state->get_tex_gen()) +{ + // Set a callback to unregister ourselves when either the Texture or + // the TexGen object gets deleted. + _texture.set_callback(this); + _tex_gen.set_callback(this); + + _flags = 0; + + if (CLP(interleaved_arrays)) { + _flags |= F_interleaved_arrays; + } else if (CLP(parallel_arrays)) { + _flags |= F_parallel_arrays; + } +} + //////////////////////////////////////////////////////////////////// // Function: CLP(GeomMunger)::Destructor // Access: Public, Virtual @@ -113,22 +138,211 @@ munge_format_impl(const GeomVertexFormat *orig, } } - /* - if (true) { + CPT(GeomVertexFormat) format = GeomVertexFormat::register_format(new_format); + + if ((_flags & F_parallel_arrays) != 0) { // Split out the interleaved array into n parallel arrays. - CPT(GeomVertexFormat) format = new_format; new_format = new GeomVertexFormat; - for (int i = 0; i < format->get_num_columns(); i++) { + for (int i = 0; i < format->get_num_columns(); ++i) { const GeomVertexColumn *column = format->get_column(i); PT(GeomVertexArrayFormat) new_array_format = new GeomVertexArrayFormat; new_array_format->add_column(column->get_name(), column->get_num_components(), - column->get_numeric_type()); + column->get_numeric_type(), column->get_contents()); new_format->add_array(new_array_format); } - } - */ + format = GeomVertexFormat::register_format(new_format); + + } else if ((_flags & F_interleaved_arrays) != 0) { + // Combine the primary data columns into a single array. + new_format = new GeomVertexFormat(*format); + PT(GeomVertexArrayFormat) new_array_format = new GeomVertexArrayFormat; + + const GeomVertexColumn *column = format->get_vertex_column(); + if (column != (const GeomVertexColumn *)NULL) { + new_array_format->add_column + (column->get_name(), column->get_num_components(), + column->get_numeric_type(), column->get_contents()); + new_format->remove_column(column->get_name()); + } - return GeomVertexFormat::register_format(new_format); + column = format->get_normal_column(); + if (column != (const GeomVertexColumn *)NULL) { + new_array_format->add_column + (column->get_name(), column->get_num_components(), + column->get_numeric_type(), column->get_contents()); + new_format->remove_column(column->get_name()); + } + + column = format->get_color_column(); + if (column != (const GeomVertexColumn *)NULL) { + new_array_format->add_column + (column->get_name(), column->get_num_components(), + column->get_numeric_type(), column->get_contents()); + new_format->remove_column(column->get_name()); + } + + // Put only the used texture coordinates into the interleaved + // array. + if (_texture != (TextureAttrib *)NULL) { + typedef pset UsedStages; + UsedStages used_stages; + + int num_stages = _texture->get_num_on_stages(); + for (int i = 0; i < num_stages; ++i) { + TextureStage *stage = _texture->get_on_stage(i); + if (_tex_gen == (TexGenAttrib *)NULL || + !_tex_gen->has_stage(stage)) { + InternalName *name = stage->get_texcoord_name(); + if (used_stages.insert(name).second) { + // This is the first time we've encountered this texcoord name. + const GeomVertexColumn *texcoord_type = format->get_column(name); + + if (texcoord_type != (const GeomVertexColumn *)NULL) { + new_array_format->add_column + (name, texcoord_type->get_num_values(), NT_float32, C_texcoord); + } else { + // We have to add something as a placeholder, even if the + // texture coordinates aren't defined. + new_array_format->add_column(name, 2, NT_float32, C_texcoord); + } + new_format->remove_column(name); + } + } + } + } + + new_format->insert_array(0, new_array_format); + format = GeomVertexFormat::register_format(new_format); + } + + return format; +} + +//////////////////////////////////////////////////////////////////// +// Function: CLP(GeomMunger)::premunge_format_impl +// Access: Protected, Virtual +// Description: Given a source GeomVertexFormat, converts it if +// necessary to the appropriate format for rendering. +//////////////////////////////////////////////////////////////////// +CPT(GeomVertexFormat) CLP(GeomMunger):: +premunge_format_impl(const GeomVertexFormat *orig) { + PT(GeomVertexFormat) new_format = new GeomVertexFormat(*orig); + + const GeomVertexColumn *color_type = orig->get_color_column(); + if (color_type != (GeomVertexColumn *)NULL && + color_type->get_numeric_type() == NT_packed_dabc) { + // We need to convert the color format; OpenGL doesn't support the + // byte order of DirectX's packed ARGB format. + int color_array = orig->get_array_with(InternalName::get_color()); + + PT(GeomVertexArrayFormat) new_array_format = new_format->modify_array(color_array); + + // Replace the existing color format with the new format. + new_array_format->add_column + (InternalName::get_color(), 4, NT_uint8, + C_color, color_type->get_start()); + } + + CPT(GeomVertexFormat) format = GeomVertexFormat::register_format(new_format); + + if ((_flags & F_parallel_arrays) != 0) { + // Split out the interleaved array into n parallel arrays. + new_format = new GeomVertexFormat; + for (int i = 0; i < format->get_num_columns(); ++i) { + const GeomVertexColumn *column = format->get_column(i); + PT(GeomVertexArrayFormat) new_array_format = new GeomVertexArrayFormat; + new_array_format->add_column(column->get_name(), column->get_num_components(), + column->get_numeric_type(), column->get_contents()); + new_format->add_array(new_array_format); + } + format = GeomVertexFormat::register_format(new_format); + + } else { + // Combine the primary data columns into a single array. Unlike + // the munge case, above, in the premunge case, we do this even if + // F_interleaved_arrays is not set (unless F_parallel_arrays is + // set), since the presumption is that you're more willing to pay + // the overhead of doing this step at load time than you might be + // at run time. + new_format = new GeomVertexFormat(*format); + PT(GeomVertexArrayFormat) new_array_format = new GeomVertexArrayFormat; + + const GeomVertexColumn *column = format->get_vertex_column(); + if (column != (const GeomVertexColumn *)NULL) { + new_array_format->add_column + (column->get_name(), column->get_num_components(), + column->get_numeric_type(), column->get_contents()); + new_format->remove_column(column->get_name()); + } + + column = format->get_normal_column(); + if (column != (const GeomVertexColumn *)NULL) { + new_array_format->add_column + (column->get_name(), column->get_num_components(), + column->get_numeric_type(), column->get_contents()); + new_format->remove_column(column->get_name()); + } + + column = format->get_color_column(); + if (column != (const GeomVertexColumn *)NULL) { + new_array_format->add_column + (column->get_name(), column->get_num_components(), + column->get_numeric_type(), column->get_contents()); + new_format->remove_column(column->get_name()); + } + + // Put only the used texture coordinates into the interleaved + // array. The others will be kept around, but in a parallel + // array. + if (_texture != (TextureAttrib *)NULL) { + typedef pset UsedStages; + UsedStages used_stages; + + int num_stages = _texture->get_num_on_stages(); + for (int i = 0; i < num_stages; ++i) { + TextureStage *stage = _texture->get_on_stage(i); + if (_tex_gen == (TexGenAttrib *)NULL || + !_tex_gen->has_stage(stage)) { + InternalName *name = stage->get_texcoord_name(); + if (used_stages.insert(name).second) { + // This is the first time we've encountered this texcoord name. + const GeomVertexColumn *texcoord_type = format->get_column(name); + + if (texcoord_type != (const GeomVertexColumn *)NULL) { + new_array_format->add_column + (name, texcoord_type->get_num_values(), NT_float32, C_texcoord); + } else { + // We have to add something as a placeholder, even if the + // texture coordinates aren't defined. + new_array_format->add_column(name, 2, NT_float32, C_texcoord); + } + new_format->remove_column(name); + } + } + } + } + + // Now go through the remaining arrays and make sure they are + // tightly packed. If not, repack them. + for (int i = 0; i < new_format->get_num_arrays(); ++i) { + CPT(GeomVertexArrayFormat) orig_a = new_format->get_array(i); + if (orig_a->count_unused_space() != 0) { + PT(GeomVertexArrayFormat) new_a = new GeomVertexArrayFormat; + for (int j = 0; j < orig_a->get_num_columns(); ++j) { + const GeomVertexColumn *column = orig_a->get_column(j); + new_a->add_column(column->get_name(), column->get_num_components(), + column->get_numeric_type(), column->get_contents()); + } + new_format->set_array(i, new_a); + } + } + + // Finally, insert the interleaved array first in the format. + new_format->insert_array(0, new_array_format); + format = GeomVertexFormat::register_format(new_format); + } + + return format; } //////////////////////////////////////////////////////////////////// @@ -148,6 +362,9 @@ compare_to_impl(const GeomMunger *other) const { if (_tex_gen != om->_tex_gen) { return _tex_gen < om->_tex_gen ? -1 : 1; } + if (_flags != om->_flags) { + return _flags < om->_flags ? -1 : 1; + } return StandardMunger::compare_to_impl(other); } @@ -162,8 +379,16 @@ compare_to_impl(const GeomMunger *other) const { //////////////////////////////////////////////////////////////////// int CLP(GeomMunger):: geom_compare_to_impl(const GeomMunger *other) const { - // We don't consider _texture and _tex_gen for this purpose; they - // affect only whether the GL display list should be regenerated or - // not, and don't require reconverting the vertices. - return StandardMunger::geom_compare_to_impl(other); + const CLP(GeomMunger) *om = DCAST(CLP(GeomMunger), other); + if (_texture != om->_texture) { + return _texture < om->_texture ? -1 : 1; + } + if (_tex_gen != om->_tex_gen) { + return _tex_gen < om->_tex_gen ? -1 : 1; + } + if (_flags != om->_flags) { + return _flags < om->_flags ? -1 : 1; + } + + return StandardMunger::compare_to_impl(other); } diff --git a/panda/src/glstuff/glGeomMunger_src.h b/panda/src/glstuff/glGeomMunger_src.h index 5834f24038..9622b831f0 100644 --- a/panda/src/glstuff/glGeomMunger_src.h +++ b/panda/src/glstuff/glGeomMunger_src.h @@ -35,7 +35,7 @@ class CLP(GeomContext); //////////////////////////////////////////////////////////////////// class EXPCL_GL CLP(GeomMunger) : public StandardMunger, public WeakPointerCallback { public: - INLINE CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state); + CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state); virtual ~CLP(GeomMunger)(); ALLOC_DELETED_CHAIN(CLP(GeomMunger)); @@ -43,7 +43,9 @@ public: protected: virtual CPT(GeomVertexFormat) munge_format_impl(const GeomVertexFormat *orig, - const GeomVertexAnimationSpec &animation); + const GeomVertexAnimationSpec &animation); + virtual CPT(GeomVertexFormat) premunge_format_impl(const GeomVertexFormat *orig); + virtual int compare_to_impl(const GeomMunger *other) const; virtual int geom_compare_to_impl(const GeomMunger *other) const; @@ -54,6 +56,12 @@ private: typedef pset GeomContexts; GeomContexts _geom_contexts; + enum Flags { + F_interleaved_arrays = 0x0001, + F_parallel_arrays = 0x0002, + }; + int _flags; + static GeomMunger *_deleted_chain; public: diff --git a/panda/src/glstuff/glmisc_src.cxx b/panda/src/glstuff/glmisc_src.cxx index 807d4a1f7f..7fb5e4c8b8 100644 --- a/panda/src/glstuff/glmisc_src.cxx +++ b/panda/src/glstuff/glmisc_src.cxx @@ -79,6 +79,23 @@ ConfigVariableBool CLP(compile_and_execute) "for the first time, by allowing the display list to be " "rendered at the same time it is being compiled.")); +ConfigVariableBool CLP(interleaved_arrays) + ("gl-interleaved-arrays", false, + PRC_DESC("Set this true to convert OpenGL geometry such that the " + "primary data columns vertex, normal, color, and texcoord " + "are interleaved into one array when possible, or false to " + "render geometry as it appears in the GeomVertexData. See " + "also gl-parallel-arrays.")); + +ConfigVariableBool CLP(parallel_arrays) + ("gl-parallel-arrays", false, + PRC_DESC("Set this true to convert OpenGL geometry such that each " + "data column is a separate array, or false to " + "render geometry as it appears in the GeomVertexData. See " + "also gl-interleaved-arrays.")); + +extern ConfigVariableBool CLP(parallel_arrays); + void CLP(init_classes)() { CLP(GeomContext)::init_type(); CLP(GeomMunger)::init_type(); diff --git a/panda/src/glstuff/glmisc_src.h b/panda/src/glstuff/glmisc_src.h index 541f30daad..f8b072862e 100644 --- a/panda/src/glstuff/glmisc_src.h +++ b/panda/src/glstuff/glmisc_src.h @@ -30,6 +30,8 @@ extern ConfigVariableBool CLP(force_mipmaps); extern ConfigVariableBool CLP(color_mask); extern ConfigVariableBool CLP(support_occlusion_query); extern ConfigVariableBool CLP(compile_and_execute); +extern ConfigVariableBool CLP(interleaved_arrays); +extern ConfigVariableBool CLP(parallel_arrays); extern EXPCL_GL void CLP(init_classes)(); diff --git a/panda/src/gobj/geomCacheManager.I b/panda/src/gobj/geomCacheManager.I index 35423ac221..2d657c6fdb 100644 --- a/panda/src/gobj/geomCacheManager.I +++ b/panda/src/gobj/geomCacheManager.I @@ -59,6 +59,19 @@ get_total_size() const { return _total_size; } +//////////////////////////////////////////////////////////////////// +// Function: GeomCacheManager::evict_old_entries +// Access: Public +// Description: Trims the cache size down to get_max_size() by +// evicting old cache entries as needed. It is assumed +// that you already hold the lock before calling this +// method. +//////////////////////////////////////////////////////////////////// +INLINE void GeomCacheManager:: +evict_old_entries() { + evict_old_entries(get_max_size(), true); +} + //////////////////////////////////////////////////////////////////// // Function: GeomCacheManager::flush_level // Access: Public, Static diff --git a/panda/src/gobj/geomCacheManager.cxx b/panda/src/gobj/geomCacheManager.cxx index fbbfd79d75..7d7ac299cd 100644 --- a/panda/src/gobj/geomCacheManager.cxx +++ b/panda/src/gobj/geomCacheManager.cxx @@ -56,6 +56,17 @@ GeomCacheManager:: nassertv(false); } +//////////////////////////////////////////////////////////////////// +// Function: GeomCacheManager::flush +// Access: Published +// Description: Immediately empties all elements in the cache. +//////////////////////////////////////////////////////////////////// +void GeomCacheManager:: +flush() { + MutexHolder holder(_lock); + evict_old_entries(0, false); +} + //////////////////////////////////////////////////////////////////// // Function: GeomCacheManager::get_global_ptr // Access: Published, Static @@ -71,23 +82,22 @@ get_global_ptr() { //////////////////////////////////////////////////////////////////// // Function: GeomCacheManager::evict_old_entries -// Access: Private -// Description: Trims the cache size down to get_max_size() by +// Access: Public +// Description: Trims the cache size down to the specified size by // evicting old cache entries as needed. It is assumed // that you already hold the lock before calling this // method. //////////////////////////////////////////////////////////////////// void GeomCacheManager:: -evict_old_entries() { +evict_old_entries(int max_size, bool keep_current) { int current_frame = ClockObject::get_global_clock()->get_frame_count(); int min_frames = geom_cache_min_frames; - int max_size = get_max_size(); while (_total_size > max_size) { PT(GeomCacheEntry) entry = _list->_next; nassertv(entry != _list); - if (current_frame - entry->_last_frame_used < min_frames) { + if (keep_current && current_frame - entry->_last_frame_used < min_frames) { // Never mind, this one is too new. if (gobj_cat.is_debug()) { gobj_cat.debug() diff --git a/panda/src/gobj/geomCacheManager.h b/panda/src/gobj/geomCacheManager.h index 0f97497436..ceebd7b078 100644 --- a/panda/src/gobj/geomCacheManager.h +++ b/panda/src/gobj/geomCacheManager.h @@ -55,10 +55,13 @@ PUBLISHED: INLINE int get_total_size() const; + void flush(); + static GeomCacheManager *get_global_ptr(); public: - void evict_old_entries(); + INLINE void evict_old_entries(); + void evict_old_entries(int max_size, bool keep_current); INLINE static void flush_level(); private: diff --git a/panda/src/gobj/geomMunger.I b/panda/src/gobj/geomMunger.I index 221c882097..eb5e25a472 100644 --- a/panda/src/gobj/geomMunger.I +++ b/panda/src/gobj/geomMunger.I @@ -106,6 +106,55 @@ munge_data(const GeomVertexData *data) const { return ((GeomMunger *)this)->munge_data_impl(data); } +//////////////////////////////////////////////////////////////////// +// Function: GeomMunger::premunge_format +// Access: Public +// Description: This is similar to munge_format(), but it is done at +// load time, to optimize a model for eventual rendering +// on a particular GSG. At this point, we do not +// necessarily know the final render state that will be +// applied, so we cannot make any destructive changes to +// the geom, its data, or its format. +//////////////////////////////////////////////////////////////////// +INLINE CPT(GeomVertexFormat) GeomMunger:: +premunge_format(const GeomVertexFormat *format) const { + return ((GeomMunger *)this)->do_premunge_format(format); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomMunger::premunge_data +// Access: Public +// Description: This is similar to munge_data(), but it is done at +// load time, to optimize a model for eventual rendering +// on a particular GSG. At this point, we do not +// necessarily know the final render state that will be +// applied, so we cannot make any destructive changes to +// the geom, its data, or its format. +//////////////////////////////////////////////////////////////////// +INLINE CPT(GeomVertexData) GeomMunger:: +premunge_data(const GeomVertexData *data) const { + return ((GeomMunger *)this)->premunge_data_impl(data); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomMunger::premunge_geom +// Access: Public +// Description: This is similar to munge_geom(), but it is done at +// load time, to optimize a model for eventual rendering +// on a particular GSG. At this point, we do not +// necessarily know the final render state that will be +// applied, so we cannot make any destructive changes to +// the geom, its data, or its format. +// +// Unlike munge_geom(), this result is not cached, since +// the assumption is that this operation is performed at +// load time once for each model. +//////////////////////////////////////////////////////////////////// +INLINE void GeomMunger:: +premunge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data) const { + ((GeomMunger *)this)->premunge_geom_impl(geom, data); +} + //////////////////////////////////////////////////////////////////// // Function: GeomMunger::compare_to // Access: Public diff --git a/panda/src/gobj/geomMunger.cxx b/panda/src/gobj/geomMunger.cxx index 57adbefd5f..bdfeff53ec 100644 --- a/panda/src/gobj/geomMunger.cxx +++ b/panda/src/gobj/geomMunger.cxx @@ -96,7 +96,7 @@ remove_data(const GeomVertexData *data) { //////////////////////////////////////////////////////////////////// // Function: GeomMunger::munge_geom -// Access: Published +// Access: Public // Description: Applies the indicated munger to the geom and its // data, and returns a (possibly different) geom and // data, according to the munger's whim. @@ -262,6 +262,82 @@ munge_geom_impl(CPT(Geom) &, CPT(GeomVertexData) &, Thread *) { return true; } +//////////////////////////////////////////////////////////////////// +// Function: GeomMunger::do_premunge_format +// Access: Protected +// Description: The protected implementation of premunge_format(). This +// exists just to cast away the const pointer. +//////////////////////////////////////////////////////////////////// +CPT(GeomVertexFormat) GeomMunger:: +do_premunge_format(const GeomVertexFormat *format) { + nassertr(_is_registered, NULL); + nassertr(format->is_registered(), NULL); + + MutexHolder holder(_formats_lock); + + Formats::iterator fi; + fi = _premunge_formats.find(format); + if (fi != _premunge_formats.end()) { + // This format was previously munged, so the answer will be the + // same. + return (*fi).second; + } + + // We have to munge this format for the first time. + CPT(GeomVertexFormat) derived_format = premunge_format_impl(format); + nassertr(derived_format->is_registered(), NULL); + + // Store the answer in the map, so we can quickly get it next time. + bool inserted = _premunge_formats.insert(Formats::value_type(format, derived_format)).second; + nassertr(inserted, NULL); + + return derived_format; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomMunger::premunge_format_impl +// Access: Protected, Virtual +// Description: Given a source GeomVertexFormat, converts it if +// necessary to the appropriate format for rendering. +//////////////////////////////////////////////////////////////////// +CPT(GeomVertexFormat) GeomMunger:: +premunge_format_impl(const GeomVertexFormat *orig) { + return orig; +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomMunger::premunge_data_impl +// Access: Protected, Virtual +// Description: Given a source GeomVertexData, converts it as +// necessary for rendering. +//////////////////////////////////////////////////////////////////// +CPT(GeomVertexData) GeomMunger:: +premunge_data_impl(const GeomVertexData *data) { + nassertr(_is_registered, NULL); + + CPT(GeomVertexFormat) orig_format = data->get_format(); + CPT(GeomVertexFormat) new_format = premunge_format(orig_format); + + if (new_format == orig_format) { + // Trivial case. + return data; + } + + return data->convert_to(new_format); +} + +//////////////////////////////////////////////////////////////////// +// Function: GeomMunger::premunge_geom_impl +// Access: Protected, Virtual +// Description: Converts a Geom and/or its data as necessary. +//////////////////////////////////////////////////////////////////// +bool GeomMunger:: +premunge_geom_impl(CPT(Geom) &, CPT(GeomVertexData) &) { + // The default implementation does nothing (the work has already + // been done in premunge_format_impl() and premunge_data_impl()). + return true; +} + //////////////////////////////////////////////////////////////////// // Function: GeomMunger::compare_to_impl // Access: Protected, Virtual diff --git a/panda/src/gobj/geomMunger.h b/panda/src/gobj/geomMunger.h index a9dcb2bc62..d81487f956 100644 --- a/panda/src/gobj/geomMunger.h +++ b/panda/src/gobj/geomMunger.h @@ -71,7 +71,7 @@ public: INLINE static void unregister_mungers_for_gsg(GraphicsStateGuardianBase *gsg); INLINE CPT(GeomVertexFormat) munge_format(const GeomVertexFormat *format, - const GeomVertexAnimationSpec &animation) const; + const GeomVertexAnimationSpec &animation) const; INLINE CPT(GeomVertexData) munge_data(const GeomVertexData *data) const; void remove_data(const GeomVertexData *data); @@ -79,6 +79,10 @@ public: void munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data, Thread *current_thread); + INLINE CPT(GeomVertexFormat) premunge_format(const GeomVertexFormat *format) const; + INLINE CPT(GeomVertexData) premunge_data(const GeomVertexData *data) const; + INLINE void premunge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data) const; + public: INLINE int compare_to(const GeomMunger &other) const; INLINE int geom_compare_to(const GeomMunger &other) const; @@ -87,13 +91,20 @@ protected: INLINE void unregister_myself(); CPT(GeomVertexFormat) do_munge_format(const GeomVertexFormat *format, - const GeomVertexAnimationSpec &animation); + const GeomVertexAnimationSpec &animation); virtual CPT(GeomVertexFormat) munge_format_impl(const GeomVertexFormat *orig, - const GeomVertexAnimationSpec &animation); + const GeomVertexAnimationSpec &animation); virtual CPT(GeomVertexData) munge_data_impl(const GeomVertexData *data); virtual bool munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data, Thread *current_thread); + + + CPT(GeomVertexFormat) do_premunge_format(const GeomVertexFormat *format); + virtual CPT(GeomVertexFormat) premunge_format_impl(const GeomVertexFormat *orig); + virtual CPT(GeomVertexData) premunge_data_impl(const GeomVertexData *data); + virtual bool premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data); + virtual int compare_to_impl(const GeomMunger *other) const; virtual int geom_compare_to_impl(const GeomMunger *other) const; @@ -116,6 +127,7 @@ private: typedef pmap Formats; typedef pmap FormatsByAnimation; FormatsByAnimation _formats_by_animation; + Formats _premunge_formats; // This mutex protects the above. Mutex _formats_lock; diff --git a/panda/src/gobj/geomVertexArrayFormat.cxx b/panda/src/gobj/geomVertexArrayFormat.cxx index 181e5faef8..fe9c94a7c3 100644 --- a/panda/src/gobj/geomVertexArrayFormat.cxx +++ b/panda/src/gobj/geomVertexArrayFormat.cxx @@ -221,7 +221,7 @@ add_column(InternalName *name, int num_components, } return add_column(GeomVertexColumn(name, num_components, - numeric_type, contents, start)); + numeric_type, contents, start)); } //////////////////////////////////////////////////////////////////// @@ -391,6 +391,35 @@ is_data_subset_of(const GeomVertexArrayFormat &other) const { return (i == _columns.size()); } +//////////////////////////////////////////////////////////////////// +// Function: GeomVertexArrayFormat::count_unused_space +// Access: Published +// Description: Returns the number of bytes per row that are not +// assigned to any column. +//////////////////////////////////////////////////////////////////// +int GeomVertexArrayFormat:: +count_unused_space() const { + consider_sort_columns(); + + int unused_space = 0; + int last_pos = 0; + + Columns::const_iterator ci; + for (ci = _columns.begin(); ci != _columns.end(); ++ci) { + const GeomVertexColumn *column = (*ci); + if (column->get_start() > last_pos) { + unused_space += (column->get_start() - last_pos); + } + last_pos = column->get_start() + column->get_total_bytes(); + } + + if (_stride > last_pos) { + unused_space += (_stride - last_pos); + } + + return unused_space; +} + //////////////////////////////////////////////////////////////////// // Function: GeomVertexArrayFormat::output // Access: Published @@ -399,11 +428,21 @@ is_data_subset_of(const GeomVertexArrayFormat &other) const { void GeomVertexArrayFormat:: output(ostream &out) const { Columns::const_iterator ci; + int last_pos = 0; out << "["; for (ci = _columns.begin(); ci != _columns.end(); ++ci) { const GeomVertexColumn *column = (*ci); + if (column->get_start() > last_pos) { + out << " ..." << (column->get_start() - last_pos) << "..."; + } out << " " << *column; + last_pos = column->get_start() + column->get_total_bytes(); } + + if (_stride > last_pos) { + out << " ..." << (_stride - last_pos) << "..."; + } + out << " ]"; } diff --git a/panda/src/gobj/geomVertexArrayFormat.h b/panda/src/gobj/geomVertexArrayFormat.h index f6bda642d4..e8098e6f54 100644 --- a/panda/src/gobj/geomVertexArrayFormat.h +++ b/panda/src/gobj/geomVertexArrayFormat.h @@ -104,6 +104,7 @@ PUBLISHED: INLINE bool has_column(const InternalName *name) const; bool is_data_subset_of(const GeomVertexArrayFormat &other) const; + int count_unused_space() const; void output(ostream &out) const; void write(ostream &out, int indent_level = 0) const; diff --git a/panda/src/pgraph/geomNode.cxx b/panda/src/pgraph/geomNode.cxx index 3c67853383..adb377df42 100644 --- a/panda/src/pgraph/geomNode.cxx +++ b/panda/src/pgraph/geomNode.cxx @@ -32,6 +32,7 @@ #include "indent.h" #include "pset.h" #include "config_pgraph.h" +#include "graphicsStateGuardianBase.h" TypeHandle GeomNode::_type_handle; @@ -639,6 +640,35 @@ is_geom_node() const { return true; } +//////////////////////////////////////////////////////////////////// +// Function: GeomNode::do_premunge +// Access: Public +// Description: Uses the indicated GSG to premunge the Geoms in this +// node to optimize them for eventual rendering. See +// SceneGraphReducer::premunge(). +//////////////////////////////////////////////////////////////////// +void GeomNode:: +do_premunge(GraphicsStateGuardianBase *gsg, + const RenderState *node_state, + GeomTransformer &transformer) { + Thread *current_thread = Thread::get_current_thread(); + + OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) { + CDStageWriter cdata(_cycler, pipeline_stage, current_thread); + + GeomList::iterator gi; + PT(GeomList) geoms = cdata->modify_geoms(); + for (gi = geoms->begin(); gi != geoms->end(); ++gi) { + GeomEntry &entry = (*gi); + CPT(RenderState) geom_state = node_state->compose(entry._state); + CPT(Geom) geom = entry._geom.get_read_pointer(); + PT(GeomMunger) munger = gsg->get_geom_munger(geom_state, current_thread); + entry._geom = transformer.premunge_geom(geom, munger); + } + } + CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler); +} + //////////////////////////////////////////////////////////////////// // Function: GeomNode::compute_internal_bounds // Access: Protected, Virtual diff --git a/panda/src/pgraph/geomNode.h b/panda/src/pgraph/geomNode.h index 70975502e2..7ee1eb02cf 100644 --- a/panda/src/pgraph/geomNode.h +++ b/panda/src/pgraph/geomNode.h @@ -29,6 +29,8 @@ #include "pvector.h" #include "copyOnWritePointer.h" +class GraphicsStateGuardianBase; + //////////////////////////////////////////////////////////////////// // Class : GeomNode // Description : A node that holds Geom objects, renderable pieces of @@ -89,6 +91,10 @@ public: virtual bool is_geom_node() const; + void do_premunge(GraphicsStateGuardianBase *gsg, + const RenderState *node_state, + GeomTransformer &transformer); + protected: virtual PT(BoundingVolume) compute_internal_bounds(int pipeline_stage, Thread *current_thread) const; diff --git a/panda/src/pgraph/geomTransformer.cxx b/panda/src/pgraph/geomTransformer.cxx index b208408b15..9b960e961e 100644 --- a/panda/src/pgraph/geomTransformer.cxx +++ b/panda/src/pgraph/geomTransformer.cxx @@ -29,6 +29,7 @@ #include "pStatTimer.h" #include "vector_int.h" #include "userVertexTransform.h" +#include "geomMunger.h" static PStatCollector apply_vertex_collector("*:Flatten:apply:vertex"); static PStatCollector apply_texcoord_collector("*:Flatten:apply:texcoord"); @@ -709,3 +710,30 @@ collect_vertex_data(GeomNode *node, int collect_bits) { return num_created; } + +//////////////////////////////////////////////////////////////////// +// Function: GeomTransformer::premunge_geom +// Access: Public +// Description: Uses the indicated munger to premunge the given Geom +// to optimize it for eventual rendering. See +// SceneGraphReducer::premunge(). +//////////////////////////////////////////////////////////////////// +PT(Geom) GeomTransformer:: +premunge_geom(const Geom *geom, GeomMunger *munger) { + // This method had been originally provided to cache the result for + // a particular geom/munger and vdata/munger combination, similar to + // the way other GeomTransformer methods work. On reflection, this + // additional caching is not necessary, since the GeomVertexFormat + // does its own caching, and there's no danger of that cache filling + // up during the span of one frame. + + CPT(GeomVertexData) vdata = geom->get_vertex_data(); + vdata = munger->premunge_data(vdata); + CPT(Geom) pgeom = geom; + munger->premunge_geom(pgeom, vdata); + + PT(Geom) geom_copy = geom->make_copy(); + geom_copy->set_vertex_data(vdata); + + return geom_copy; +} diff --git a/panda/src/pgraph/geomTransformer.h b/panda/src/pgraph/geomTransformer.h index cb70abd90c..822ac1563d 100644 --- a/panda/src/pgraph/geomTransformer.h +++ b/panda/src/pgraph/geomTransformer.h @@ -28,6 +28,7 @@ class GeomNode; class RenderState; class InternalName; +class GeomMunger; //////////////////////////////////////////////////////////////////// // Class : GeomTransformer @@ -73,6 +74,8 @@ public: int collect_vertex_data(Geom *geom, int collect_bits); int collect_vertex_data(GeomNode *node, int collect_bits); + PT(Geom) premunge_geom(const Geom *geom, GeomMunger *munger); + private: int _max_collect_vertices; diff --git a/panda/src/pgraph/nodePath.cxx b/panda/src/pgraph/nodePath.cxx index 345003ae76..39b12e9a6d 100644 --- a/panda/src/pgraph/nodePath.cxx +++ b/panda/src/pgraph/nodePath.cxx @@ -5570,6 +5570,33 @@ verify_complete(Thread *current_thread) const { return true; } +//////////////////////////////////////////////////////////////////// +// Function: NodePath::premunge_scene +// Access: Published +// Description: Walks through the scene graph beginning at the bottom +// node, and internally adjusts any GeomVertexFormats +// for optimal rendering on the indicated GSG. If this +// step is not done prior to rendering, the formats will +// be optimized at render time instead, for a small +// cost. +// +// It is not normally necessary to do this on a model +// loaded directly from disk, since the loader will do +// this by default. +//////////////////////////////////////////////////////////////////// +void NodePath:: +premunge_scene(GraphicsStateGuardianBase *gsg) { + nassertv_always(!is_empty()); + + CPT(RenderState) state = RenderState::make_empty(); + if (has_parent()) { + state = get_parent().get_net_state(); + } + + SceneGraphReducer gr; + gr.premunge(node(), gsg, state); +} + //////////////////////////////////////////////////////////////////// // Function: NodePath::prepare_scene // Access: Published diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index 27e5f18d8b..2b8269a08c 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -793,6 +793,7 @@ PUBLISHED: // Miscellaneous bool verify_complete(Thread *current_thread = Thread::get_current_thread()) const; + void premunge_scene(GraphicsStateGuardianBase *gsg); void prepare_scene(GraphicsStateGuardianBase *gsg); void show_bounds(); diff --git a/panda/src/pgraph/renderState.cxx b/panda/src/pgraph/renderState.cxx index 82604226c1..0c5cd48f7c 100644 --- a/panda/src/pgraph/renderState.cxx +++ b/panda/src/pgraph/renderState.cxx @@ -911,6 +911,30 @@ clear_cache() { return orig_size - new_size; } +//////////////////////////////////////////////////////////////////// +// Function: RenderState::clear_munger_cache +// Access: Published, Static +// Description: Completely empties the cache of state + gsg -> +// munger, for all states and all gsg's. Normally there +// is no need to empty this cache. +//////////////////////////////////////////////////////////////////// +void RenderState:: +clear_munger_cache() { + ReMutexHolder holder(*_states_lock); + + // First, we need to count the number of times each RenderState + // object is recorded in the cache. + typedef pmap StateCount; + StateCount state_count; + + States::iterator si; + for (si = _states->begin(); si != _states->end(); ++si) { + RenderState *state = (RenderState *)(*si); + state->_mungers.clear(); + state->_last_mi = state->_mungers.end(); + } +} + //////////////////////////////////////////////////////////////////// // Function: RenderState::list_cycles // Access: Published, Static diff --git a/panda/src/pgraph/renderState.h b/panda/src/pgraph/renderState.h index 4b541ce60d..b818f643d3 100644 --- a/panda/src/pgraph/renderState.h +++ b/panda/src/pgraph/renderState.h @@ -128,6 +128,7 @@ PUBLISHED: static int get_num_states(); static int get_num_unused_states(); static int clear_cache(); + static void clear_munger_cache(); static void list_cycles(ostream &out); static void list_states(ostream &out); static bool validate_states(); diff --git a/panda/src/pgraph/sceneGraphReducer.I b/panda/src/pgraph/sceneGraphReducer.I index 820b945bf7..4ca0c222c7 100644 --- a/panda/src/pgraph/sceneGraphReducer.I +++ b/panda/src/pgraph/sceneGraphReducer.I @@ -159,3 +159,20 @@ unify(PandaNode *root) { PStatTimer timer(_unify_collector); r_unify(root); } + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphReducer::premunge +// Access: Published +// Description: Walks the scene graph rooted at this node and below, +// and uses the indicated GSG to premunge every Geom +// found to optimize it for eventual rendering on the +// indicated GSG. +// +// This operation will also apply to stashed children. +//////////////////////////////////////////////////////////////////// +INLINE void SceneGraphReducer:: +premunge(PandaNode *root, GraphicsStateGuardianBase *gsg, + const RenderState *initial_state) { + PStatTimer timer(_premunge_collector); + r_premunge(root, gsg, initial_state); +} diff --git a/panda/src/pgraph/sceneGraphReducer.cxx b/panda/src/pgraph/sceneGraphReducer.cxx index f30770c37d..07bd05d1c6 100644 --- a/panda/src/pgraph/sceneGraphReducer.cxx +++ b/panda/src/pgraph/sceneGraphReducer.cxx @@ -31,6 +31,7 @@ PStatCollector SceneGraphReducer::_apply_collector("*:Flatten:apply"); PStatCollector SceneGraphReducer::_collect_collector("*:Flatten:collect"); PStatCollector SceneGraphReducer::_make_nonindexed_collector("*:Flatten:make nonindexed"); PStatCollector SceneGraphReducer::_unify_collector("*:Flatten:unify"); +PStatCollector SceneGraphReducer::_premunge_collector("*:Premunge"); //////////////////////////////////////////////////////////////////// // Function: SceneGraphReducer::flatten @@ -738,3 +739,32 @@ r_unify(PandaNode *node) { r_unify(children.get_child(i)); } } + +//////////////////////////////////////////////////////////////////// +// Function: SceneGraphReducer::r_premunge +// Access: Private +// Description: The recursive implementation of premunge(). +//////////////////////////////////////////////////////////////////// +void SceneGraphReducer:: +r_premunge(PandaNode *node, GraphicsStateGuardianBase *gsg, + const RenderState *state) { + CPT(RenderState) next_state = state->compose(node->get_state()); + + if (node->is_geom_node()) { + GeomNode *geom_node = DCAST(GeomNode, node); + geom_node->do_premunge(gsg, next_state, _transformer); + } + + int i; + PandaNode::Children children = node->get_children(); + int num_children = children.get_num_children(); + for (i = 0; i < num_children; ++i) { + r_premunge(children.get_child(i), gsg, next_state); + } + + PandaNode::Stashed stashed = node->get_stashed(); + int num_stashed = stashed.get_num_stashed(); + for (i = 0; i < num_stashed; ++i) { + r_premunge(stashed.get_stashed(i), gsg, next_state); + } +} diff --git a/panda/src/pgraph/sceneGraphReducer.h b/panda/src/pgraph/sceneGraphReducer.h index ff34944d6f..c776f4da8d 100644 --- a/panda/src/pgraph/sceneGraphReducer.h +++ b/panda/src/pgraph/sceneGraphReducer.h @@ -134,6 +134,9 @@ PUBLISHED: INLINE int make_nonindexed(PandaNode *root, int nonindexed_bits = ~0); INLINE void unify(PandaNode *root); + INLINE void premunge(PandaNode *root, GraphicsStateGuardianBase *gsg, + const RenderState *initial_state); + protected: void r_apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs, int attrib_types, GeomTransformer &transformer); @@ -164,6 +167,9 @@ protected: int r_make_nonindexed(PandaNode *node, int collect_bits); void r_unify(PandaNode *node); + void r_premunge(PandaNode *node, GraphicsStateGuardianBase *gsg, + const RenderState *state); + private: float _combine_radius; GeomTransformer _transformer; @@ -173,6 +179,7 @@ private: static PStatCollector _collect_collector; static PStatCollector _make_nonindexed_collector; static PStatCollector _unify_collector; + static PStatCollector _premunge_collector; }; #include "sceneGraphReducer.I"