define NodePath::premunge_scene() and supporting implementation

This commit is contained in:
David Rose 2007-05-21 16:05:22 +00:00
parent 872c141de0
commit 732bbafaa9
26 changed files with 704 additions and 44 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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<CLP(GeomContext) *> GeomContexts;
GeomContexts _geom_contexts;
enum Flags {
F_interleaved_arrays = 0x0001,
F_parallel_arrays = 0x0002,
};
int _flags;
static GeomMunger *_deleted_chain;
public:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<CPT(GeomVertexFormat), CPT(GeomVertexFormat) > Formats;
typedef pmap<GeomVertexAnimationSpec, Formats> FormatsByAnimation;
FormatsByAnimation _formats_by_animation;
Formats _premunge_formats;
// This mutex protects the above.
Mutex _formats_lock;

View File

@ -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 << " ]";
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<const RenderState *, int> 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

View File

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

View File

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

View File

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

View File

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