A lot of performance changes, particularly wrt dcasts and state management

This commit is contained in:
rdb 2015-07-27 16:32:57 +02:00
parent 6cf2da9e56
commit f33e450823
100 changed files with 1322 additions and 511 deletions

View File

@ -61,7 +61,9 @@ get_type_index() const {
////////////////////////////////////////////////////////////////////
INLINE bool TypedObject::
is_of_type(TypeHandle handle) const {
return get_type().is_derived_from(handle, (TypedObject *)this);
// Shortcut for the common case where the type matches exactly.
TypeHandle my_type = get_type();
return handle == my_type || my_type.is_derived_from(handle, (TypedObject *)this);
}
////////////////////////////////////////////////////////////////////

View File

@ -420,8 +420,7 @@ find_joint(const string &name) const {
int num_bundles = get_num_bundles();
for (int i = 0; i < num_bundles; ++i) {
PartGroup *part = get_bundle(i)->find_child(name);
if (part != (PartGroup *)NULL &&
part->is_of_type(CharacterJoint::get_class_type())) {
if (part != (PartGroup *)NULL && part->is_character_joint()) {
return DCAST(CharacterJoint, part);
}
}
@ -728,7 +727,7 @@ r_merge_bundles(Character::JointMap &joint_map,
PartGroup *old_group, PartGroup *new_group) {
joint_map[old_group] = new_group;
if (new_group->is_of_type(CharacterJoint::get_class_type())) {
if (new_group->is_character_joint()) {
CharacterJoint *new_joint;
DCAST_INTO_V(new_joint, new_group);
@ -736,7 +735,7 @@ r_merge_bundles(Character::JointMap &joint_map,
new_joint->_character = this;
if (old_group != new_group &&
old_group->is_of_type(CharacterJoint::get_class_type())) {
old_group->is_character_joint()) {
CharacterJoint *old_joint;
DCAST_INTO_V(old_joint, old_group);
@ -763,7 +762,7 @@ r_merge_bundles(Character::JointMap &joint_map,
if (old_group == new_group) {
return;
}
int i = 0, j = 0;
int old_num_children = old_group->get_num_children();
int new_num_children = new_group->get_num_children();
@ -809,7 +808,7 @@ r_merge_bundles(Character::JointMap &joint_map,
// new_group. Duplicate it.
PartGroup *new_pc = pc->make_copy();
new_children.push_back(new_pc);
r_merge_bundles(joint_map, pc, new_pc);
i++;
}
@ -968,7 +967,7 @@ copy_geom(const Geom *source, const Character::JointMap &joint_map,
void Character::
copy_node_pointers(const Character::NodeMap &node_map,
PartGroup *dest, const PartGroup *source) {
if (dest->is_of_type(CharacterJoint::get_class_type())) {
if (dest->is_character_joint()) {
nassertv(dest != source);
const CharacterJoint *source_joint;
CharacterJoint *dest_joint;
@ -1204,7 +1203,7 @@ redirect_slider(const VertexSlider *vs, Character::GeomSliderMap &gsmap) {
////////////////////////////////////////////////////////////////////
void Character::
r_clear_joint_characters(PartGroup *part) {
if (part->is_of_type(CharacterJoint::get_class_type())) {
if (part->is_character_joint()) {
CharacterJoint *joint = DCAST(CharacterJoint, part);
// It is possible for the joint to reference a different Character

View File

@ -102,8 +102,8 @@ r_set_character(PartGroup *group, Character *character) {
return;
}
if (group->is_of_type(CharacterJoint::get_class_type())) {
DCAST(CharacterJoint, group)->set_character(character);
if (group->is_character_joint()) {
((CharacterJoint *)group)->set_character(character);
}
Children::const_iterator ci;

View File

@ -64,8 +64,8 @@ add_object(CullableObject *object, Thread *current_thread) {
return;
}
const GeometricBoundingVolume *gbv;
DCAST_INTO_V(gbv, volume);
const GeometricBoundingVolume *gbv = volume->as_geometric_bounding_volume();
nassertv(gbv != NULL);
LPoint3 center = gbv->get_approx_center();
nassertv(object->_internal_transform != (const TransformState *)NULL);

View File

@ -64,8 +64,8 @@ add_object(CullableObject *object, Thread *current_thread) {
return;
}
const GeometricBoundingVolume *gbv;
DCAST_INTO_V(gbv, volume);
const GeometricBoundingVolume *gbv = volume->as_geometric_bounding_volume();
nassertv(gbv != NULL);
LPoint3 center = gbv->get_approx_center();
nassertv(object->_internal_transform != (const TransformState *)NULL);

View File

@ -597,11 +597,16 @@ get_region_pixels_i(int i, int &xo, int &yo, int &w, int &h) const {
// to all upstream pipeline stages.
////////////////////////////////////////////////////////////////////
INLINE void DisplayRegion::
set_cull_result(CullResult *cull_result, SceneSetup *scene_setup,
set_cull_result(PT(CullResult) cull_result, PT(SceneSetup) scene_setup,
Thread *current_thread) {
CDCullWriter cdata(_cycler_cull, true, current_thread);
cdata->_cull_result = cull_result;
cdata->_scene_setup = scene_setup;
#ifdef USE_MOVE_SEMANTICS
cdata->_cull_result = std::move(cull_result);
cdata->_scene_setup = std::move(scene_setup);
#else
swap(cdata->_cull_result, cull_result);
swap(cdata->_scene_setup, scene_setup);
#endif
}
////////////////////////////////////////////////////////////////////

View File

@ -161,7 +161,7 @@ public:
virtual bool supports_pixel_zoom() const;
INLINE void set_cull_result(CullResult *cull_result, SceneSetup *scene_setup,
INLINE void set_cull_result(PT(CullResult) cull_result, PT(SceneSetup) scene_setup,
Thread *current_thread);
INLINE CullResult *get_cull_result(Thread *current_thread) const;
INLINE SceneSetup *get_scene_setup(Thread *current_thread) const;

View File

@ -41,7 +41,6 @@
#include "bamCache.h"
#include "cullableObject.h"
#include "geomVertexArrayData.h"
#include "omniBoundingVolume.h"
#include "vertexDataSaveFile.h"
#include "vertexDataBook.h"
#include "vertexDataPage.h"
@ -1248,12 +1247,12 @@ do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
// from the lens.
PT(BoundingVolume) bv = scene_setup->get_cull_bounds();
if (bv != (BoundingVolume *)NULL &&
bv->is_of_type(GeometricBoundingVolume::get_class_type()) &&
!bv->is_of_type(OmniBoundingVolume::get_class_type())) {
if (bv != (BoundingVolume *)NULL && !bv->is_infinite() &&
bv->as_geometric_bounding_volume() != NULL) {
// Transform it into the appropriate coordinate space.
PT(GeometricBoundingVolume) local_frustum;
local_frustum = DCAST(GeometricBoundingVolume, bv->make_copy());
local_frustum = bv->make_copy()->as_geometric_bounding_volume();
nassertv(!local_frustum.is_null());
NodePath scene_parent = scene_setup->get_scene_root().get_parent(current_thread);
CPT(TransformState) cull_center_transform =
@ -1580,7 +1579,7 @@ cull_to_bins(GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread) {
}
// Save the results for next frame.
dr->set_cull_result(cull_result, scene_setup, current_thread);
dr->set_cull_result(MOVE(cull_result), MOVE(scene_setup), current_thread);
}
////////////////////////////////////////////////////////////////////

View File

@ -40,6 +40,7 @@
#include "graphicsOutput.h"
#include "texturePool.h"
#include "geomMunger.h"
#include "stateMunger.h"
#include "ambientLight.h"
#include "directionalLight.h"
#include "pointLight.h"
@ -814,38 +815,42 @@ dispatch_compute(int num_groups_x, int num_groups_y, int num_groups_z) {
////////////////////////////////////////////////////////////////////
PT(GeomMunger) GraphicsStateGuardian::
get_geom_munger(const RenderState *state, Thread *current_thread) {
// Before we even look up the map, see if the _last_mi value points
// to this GSG. This is likely because we tend to visit the same
// state multiple times during a frame. Also, this might well be
// the only GSG in the world anyway.
if (!state->_mungers.empty()) {
RenderState::Mungers::const_iterator mi = state->_last_mi;
if (!(*mi).first.was_deleted() && (*mi).first == this) {
if ((*mi).second->is_registered()) {
return (*mi).second;
RenderState::Mungers &mungers = state->_mungers;
if (mungers.is_empty()) {
// Before we even look up the map, see if the _last_mi value points
// to this GSG. This is likely because we tend to visit the same
// state multiple times during a frame. Also, this might well be
// the only GSG in the world anyway.
int mi = state->_last_mi;
if (mi >= 0 && mungers.has_element(mi) && mungers.get_key(mi) == this) {
PT(GeomMunger) munger = mungers.get_data(mi);
if (munger->is_registered()) {
return munger;
}
}
}
// Nope, we have to look it up in the map.
RenderState::Mungers::iterator mi = state->_mungers.find(this);
if (mi != state->_mungers.end() && !(*mi).first.was_deleted()) {
if ((*mi).second->is_registered()) {
state->_last_mi = mi;
return (*mi).second;
// Nope, we have to look it up in the map.
mi = mungers.find(this);
if (mi >= 0) {
PT(GeomMunger) munger = mungers.get_data(mi);
if (munger->is_registered()) {
state->_last_mi = mi;
return munger;
} else {
// This GeomMunger is no longer registered. Remove it from
// the map.
mungers.remove_element(mi);
}
}
// This GeomMunger is no longer registered. Remove it from the
// map.
state->_mungers.erase(mi);
}
// Nothing in the map; create a new entry.
PT(GeomMunger) munger = make_geom_munger(state, current_thread);
nassertr(munger != (GeomMunger *)NULL && munger->is_registered(), munger);
nassertr(munger->is_of_type(StateMunger::get_class_type()), munger);
mi = state->_mungers.insert(RenderState::Mungers::value_type(this, munger)).first;
state->_last_mi = mi;
state->_last_mi = mungers.store(this, munger);
return munger;
}

View File

@ -15,7 +15,6 @@
#include "standardMunger.h"
#include "renderState.h"
#include "graphicsStateGuardian.h"
#include "dcast.h"
#include "config_gobj.h"
#include "displayRegion.h"
@ -43,16 +42,15 @@ StandardMunger(GraphicsStateGuardianBase *gsg, const RenderState *state,
_auto_shader(false),
_shader_skinning(false)
{
_render_mode = DCAST(RenderModeAttrib, state->get_attrib(RenderModeAttrib::get_class_slot()));
_render_mode = (const RenderModeAttrib *)
state->get_attrib(RenderModeAttrib::get_class_slot());
if (!get_gsg()->get_runtime_color_scale()) {
// We might need to munge the colors.
const ColorAttrib *color_attrib = (const ColorAttrib *)
state->get_attrib(ColorAttrib::get_class_slot());
const ColorScaleAttrib *color_scale_attrib = (const ColorScaleAttrib *)
state->get_attrib(ColorScaleAttrib::get_class_slot());
const ColorAttrib *color_attrib;
const ColorScaleAttrib *color_scale_attrib;
if (color_attrib != (ColorAttrib *)NULL &&
if (state->get_attrib(color_attrib) &&
color_attrib->get_color_type() == ColorAttrib::T_flat) {
if (!get_gsg()->get_color_scale_via_lighting()) {
@ -60,7 +58,7 @@ StandardMunger(GraphicsStateGuardianBase *gsg, const RenderState *state,
// can't cheat the color via lighting (presumably, in this case,
// by applying a material).
_color = color_attrib->get_color();
if (color_scale_attrib != (ColorScaleAttrib *)NULL &&
if (state->get_attrib(color_scale_attrib) &&
color_scale_attrib->has_scale()) {
const LVecBase4 &cs = color_scale_attrib->get_scale();
_color.set(_color[0] * cs[0],
@ -70,11 +68,11 @@ StandardMunger(GraphicsStateGuardianBase *gsg, const RenderState *state,
}
_munge_color = true;
}
} else if (color_scale_attrib != (ColorScaleAttrib *)NULL &&
} else if (state->get_attrib(color_scale_attrib) &&
color_scale_attrib->has_scale()) {
_color_scale = color_scale_attrib->get_scale();
const TextureAttrib *tex_attrib = (const TextureAttrib *)
state->get_attrib(TextureAttrib::get_class_slot());
@ -106,7 +104,7 @@ StandardMunger(GraphicsStateGuardianBase *gsg, const RenderState *state,
////////////////////////////////////////////////////////////////////
// Function: StandardMunger::Destructor
// Access: Public, Virtual
// Description:
// Description:
////////////////////////////////////////////////////////////////////
StandardMunger::
~StandardMunger() {
@ -126,7 +124,7 @@ munge_data_impl(const GeomVertexData *data) {
new_data = new_data->set_color(_color, _num_components, _numeric_type,
_contents);
} else if (_munge_color_scale) {
new_data = new_data->scale_color(_color_scale, _num_components,
new_data = new_data->scale_color(_color_scale, _num_components,
_numeric_type, _contents);
}
@ -142,9 +140,9 @@ munge_data_impl(const GeomVertexData *data) {
const TransformBlendTable *table = new_data->get_transform_blend_table();
if (table != (TransformBlendTable *)NULL &&
table->get_num_transforms() != 0 &&
table->get_max_simultaneous_transforms() <=
table->get_max_simultaneous_transforms() <=
get_gsg()->get_max_vertex_transforms()) {
if (matrix_palette &&
if (matrix_palette &&
table->get_num_transforms() <= get_gsg()->get_max_vertex_transform_indices()) {
if (table->get_num_transforms() == table->get_max_simultaneous_transforms()) {
@ -186,7 +184,7 @@ munge_data_impl(const GeomVertexData *data) {
// Description: Converts a Geom and/or its data as necessary.
////////////////////////////////////////////////////////////////////
void StandardMunger::
munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data,
munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data,
Thread *) {
int supported_geom_rendering = get_gsg()->get_supported_geom_rendering();
@ -289,7 +287,7 @@ premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data) {
////////////////////////////////////////////////////////////////////
int StandardMunger::
compare_to_impl(const GeomMunger *other) const {
const StandardMunger *om = DCAST(StandardMunger, other);
const StandardMunger *om = (const StandardMunger *)other;
if (_render_mode != om->_render_mode) {
return _render_mode < om->_render_mode ? -1 : 1;
@ -335,7 +333,8 @@ compare_to_impl(const GeomMunger *other) const {
////////////////////////////////////////////////////////////////////
int StandardMunger::
geom_compare_to_impl(const GeomMunger *other) const {
const StandardMunger *om = DCAST(StandardMunger, other);
const StandardMunger *om = (const StandardMunger *)other;
if (_munge_color != om->_munge_color) {
return (int)_munge_color - (int)om->_munge_color;
}

View File

@ -156,7 +156,7 @@ PandaNode *CharacterMaker::
part_to_node(PartGroup *part, const string &name) const {
PandaNode *node = _character_node;
if (part->is_of_type(CharacterJoint::get_class_type())) {
if (part->is_character_joint()) {
CharacterJoint *joint = DCAST(CharacterJoint, part);
if (joint->_geom_node != (PandaNode *)NULL) {
node = joint->_geom_node;
@ -343,7 +343,7 @@ build_joint_hierarchy(EggNode *egg_node, PartGroup *part, int index) {
////////////////////////////////////////////////////////////////////
void CharacterMaker::
parent_joint_nodes(PartGroup *part) {
if (part->is_of_type(CharacterJoint::get_class_type())) {
if (part->is_character_joint()) {
CharacterJoint *joint = DCAST(CharacterJoint, part);
PandaNode *joint_node = joint->_geom_node;
if (joint_node != NULL) {

View File

@ -57,16 +57,7 @@ prepare_node(EggNode *node) {
////////////////////////////////////////////////////////////////////
int EggBinner::
get_bin_number(const EggNode *node) {
if (node->is_of_type(EggNurbsSurface::get_class_type())) {
return (int)BN_nurbs_surface;
} else if (node->is_of_type(EggNurbsCurve::get_class_type())) {
return (int)BN_nurbs_curve;
} else if (node->is_of_type(EggPatch::get_class_type())) {
return (int)BN_patches;
} else if (node->is_of_type(EggPrimitive::get_class_type())) {
if (node->is_of_type(EggPrimitive::get_class_type())) {
return (int)BN_polyset;
} else if (node->is_of_type(EggGroup::get_class_type())) {
@ -74,6 +65,15 @@ get_bin_number(const EggNode *node) {
if (group->has_lod()) {
return (int)BN_lod;
}
} else if (node->is_of_type(EggNurbsSurface::get_class_type())) {
return (int)BN_nurbs_surface;
} else if (node->is_of_type(EggNurbsCurve::get_class_type())) {
return (int)BN_nurbs_curve;
} else if (node->is_of_type(EggPatch::get_class_type())) {
return (int)BN_patches;
}
return (int)BN_none;

View File

@ -991,8 +991,8 @@ update_shader_texture_bindings(ShaderContext *prev) {
// We get the TextureAttrib directly from the _target_rs, not the
// filtered TextureAttrib in _target_texture.
const TextureAttrib *texattrib = DCAST(TextureAttrib, _glgsg->_target_rs->get_attrib_def(TextureAttrib::get_class_slot()));
nassertv(texattrib != (TextureAttrib *)NULL);
const TextureAttrib *texattrib;
_glgsg->_target_rs->get_attrib_def(texattrib);
for (int i = 0; i < (int)_shader->_tex_spec.size(); ++i) {
Shader::ShaderTexSpec &spec = _shader->_tex_spec[i];
@ -1053,8 +1053,9 @@ update_shader_texture_bindings(ShaderContext *prev) {
continue;
}
_glgsg->apply_texture(tc);
_glgsg->apply_sampler(texunit, sampler, tc);
CLP(TextureContext) *gtc = (CLP(TextureContext) *)tc;
_glgsg->apply_texture(gtc);
_glgsg->apply_sampler(texunit, sampler, gtc);
}
cg_report_errors();

View File

@ -26,8 +26,8 @@ ALLOC_DELETED_CHAIN_DEF(CLP(GeomMunger));
CLP(GeomMunger)::
CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state) :
StandardMunger(gsg, state, 4, NT_uint8, C_color),
_texture(DCAST(TextureAttrib, state->get_attrib(TextureAttrib::get_class_slot()))),
_tex_gen(DCAST(TexGenAttrib, state->get_attrib(TexGenAttrib::get_class_slot())))
_texture((const TextureAttrib *)state->get_attrib(TextureAttrib::get_class_slot())),
_tex_gen((const TexGenAttrib *)state->get_attrib(TexGenAttrib::get_class_slot()))
{
// Set a callback to unregister ourselves when either the Texture or
// the TexGen object gets deleted.
@ -475,7 +475,7 @@ premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data) {
////////////////////////////////////////////////////////////////////
int CLP(GeomMunger)::
compare_to_impl(const GeomMunger *other) const {
const CLP(GeomMunger) *om = DCAST(CLP(GeomMunger), other);
const CLP(GeomMunger) *om = (CLP(GeomMunger) *)other;
if (_texture != om->_texture) {
return _texture < om->_texture ? -1 : 1;
}
@ -499,7 +499,7 @@ compare_to_impl(const GeomMunger *other) const {
////////////////////////////////////////////////////////////////////
int CLP(GeomMunger)::
geom_compare_to_impl(const GeomMunger *other) const {
const CLP(GeomMunger) *om = DCAST(CLP(GeomMunger), other);
const CLP(GeomMunger) *om = (CLP(GeomMunger) *)other;
#ifdef OPENGLES
if (get_render_mode() != om->get_render_mode()) {
return get_render_mode() < om->get_render_mode() ? -1 : 1;

View File

@ -4512,13 +4512,14 @@ prepare_texture(Texture *tex, int view) {
////////////////////////////////////////////////////////////////////
bool CLP(GraphicsStateGuardian)::
update_texture(TextureContext *tc, bool force) {
CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
CLP(TextureContext) *gtc;
DCAST_INTO_R(gtc, tc, false);
if (gtc->was_image_modified() || !gtc->_has_storage) {
PStatGPUTimer timer(this, _texture_update_pcollector);
// If the texture image was modified, reload the texture.
apply_texture(tc);
apply_texture(gtc);
Texture *tex = tc->get_texture();
if (gtc->was_properties_modified()) {
@ -4536,7 +4537,7 @@ update_texture(TextureContext *tc, bool force) {
// If only the properties have been modified, we don't necessarily
// need to reload the texture.
apply_texture(tc);
apply_texture(gtc);
Texture *tex = tc->get_texture();
if (specify_texture(gtc, tex->get_default_sampler())) {
@ -9649,8 +9650,10 @@ update_standard_texture_bindings() {
glDisable(target);
continue;
}
apply_texture(tc);
apply_sampler(i, _target_texture->get_on_sampler(stage), tc);
// Don't DCAST(); we already did the verification in update_texture.
CLP(TextureContext) *gtc = (CLP(TextureContext) *)tc;
apply_texture(gtc);
apply_sampler(i, _target_texture->get_on_sampler(stage), gtc);
if (stage->involves_color_scale() && _color_scale_enabled) {
LColor color = stage->get_color();
@ -10487,9 +10490,7 @@ specify_texture(CLP(TextureContext) *gtc, const SamplerState &sampler) {
// for rendering.
////////////////////////////////////////////////////////////////////
bool CLP(GraphicsStateGuardian)::
apply_texture(TextureContext *tc) {
CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
apply_texture(CLP(TextureContext) *gtc) {
gtc->set_active(true);
GLenum target = get_texture_target(gtc->get_texture()->get_texture_type());
if (target == GL_NONE) {
@ -10520,9 +10521,7 @@ apply_texture(TextureContext *tc) {
// context instead.
////////////////////////////////////////////////////////////////////
bool CLP(GraphicsStateGuardian)::
apply_sampler(GLuint unit, const SamplerState &sampler, TextureContext *tc) {
CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
apply_sampler(GLuint unit, const SamplerState &sampler, CLP(TextureContext) *gtc) {
#ifndef OPENGLES
if (_supports_sampler_objects) {
// We support sampler objects. Prepare the sampler object and
@ -10547,7 +10546,7 @@ apply_sampler(GLuint unit, const SamplerState &sampler, TextureContext *tc) {
// texture and change the texture parameters if they don't match.
if (gtc->_active_sampler != sampler) {
_glActiveTexture(GL_TEXTURE0 + unit);
apply_texture(tc);
apply_texture(gtc);
specify_texture(gtc, sampler);
}
}
@ -10555,7 +10554,7 @@ apply_sampler(GLuint unit, const SamplerState &sampler, TextureContext *tc) {
if (sampler.uses_mipmaps() && !gtc->_uses_mipmaps) {
// The texture wasn't created with mipmaps, but we are trying
// to sample it with mipmaps. We will need to reload it.
apply_texture(tc);
apply_texture(gtc);
gtc->mark_needs_reload();
bool okflag = upload_texture(gtc, false, true);
if (!okflag) {

View File

@ -531,8 +531,8 @@ protected:
#endif // NDEBUG
bool specify_texture(CLP(TextureContext) *gtc, const SamplerState &sampler);
bool apply_texture(TextureContext *tc);
bool apply_sampler(GLuint unit, const SamplerState &sampler, TextureContext *tc);
bool apply_texture(CLP(TextureContext) *gtc);
bool apply_sampler(GLuint unit, const SamplerState &sampler, CLP(TextureContext) *gtc);
bool upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps);
bool upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload,
bool uses_mipmaps, int mipmap_bias,

View File

@ -832,7 +832,7 @@ get_modified() const {
INLINE GeomContext *GeomPipelineReader::
prepare_now(PreparedGraphicsObjects *prepared_objects,
GraphicsStateGuardianBase *gsg) const {
return ((Geom *)_object.p())->prepare_now(prepared_objects, gsg);
return ((Geom *)_object)->prepare_now(prepared_objects, gsg);
}
INLINE ostream &

View File

@ -1897,7 +1897,7 @@ check_usage_hint() const {
#ifdef DO_PIPELINING
unref_delete((CycleData *)_cdata);
#endif
Geom::CDWriter fresh_cdata(((Geom *)_object.p())->_cycler,
Geom::CDWriter fresh_cdata(((Geom *)_object)->_cycler,
false, _current_thread);
((GeomPipelineReader *)this)->_cdata = fresh_cdata;
#ifdef DO_PIPELINING
@ -1906,7 +1906,7 @@ check_usage_hint() const {
if (!fresh_cdata->_got_usage_hint) {
// The cache is still stale. We have to do the work of
// freshening it.
((Geom *)_object.p())->reset_usage_hint(fresh_cdata);
((Geom *)_object)->reset_usage_hint(fresh_cdata);
nassertv(fresh_cdata->_got_usage_hint);
}

View File

@ -397,6 +397,10 @@ private:
// Class : GeomPipelineReader
// Description : Encapsulates the data from a Geom,
// pre-fetched for one stage of the pipeline.
//
// Does not hold a reference to the Geom. The caller
// must ensure that the Geom persists for at least the
// lifetime of the GeomPipelineReader.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_GOBJ GeomPipelineReader : public GeomEnums {
public:
@ -434,7 +438,7 @@ public:
bool force) const;
private:
CPT(Geom) _object;
const Geom *_object;
Thread *_current_thread;
const Geom::CData *_cdata;

View File

@ -107,8 +107,8 @@ is_composite() const {
////////////////////////////////////////////////////////////////////
INLINE bool GeomPrimitive::
is_indexed() const {
GeomPrimitivePipelineReader reader(this, Thread::get_current_thread());
return reader.is_indexed();
CDReader cdata(_cycler);
return (!cdata->_vertices.is_null());
}
////////////////////////////////////////////////////////////////////

View File

@ -84,6 +84,18 @@ write(ostream &out, int indent_level) const {
indent(out, indent_level) << *this << "\n";
}
////////////////////////////////////////////////////////////////////
// Function: BoundingVolume::as_geometric_bounding_volume
// Access: Public, Virtual
// Description: Virtual downcast method. Returns this object as a
// pointer of the indicated type, if it is in fact that
// type. Returns NULL if it is not that type.
////////////////////////////////////////////////////////////////////
GeometricBoundingVolume *BoundingVolume::
as_geometric_bounding_volume() {
return NULL;
}
////////////////////////////////////////////////////////////////////
// Function: BoundingVolume::as_geometric_bounding_volume
// Access: Public, Virtual

View File

@ -112,6 +112,7 @@ PUBLISHED:
};
public:
virtual GeometricBoundingVolume *as_geometric_bounding_volume();
virtual const GeometricBoundingVolume *as_geometric_bounding_volume() const;
virtual const FiniteBoundingVolume *as_finite_bounding_volume() const;
virtual const BoundingSphere *as_bounding_sphere() const;

View File

@ -19,7 +19,19 @@ TypeHandle GeometricBoundingVolume::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: GeometricBoundingVolume::as_geometric_bounding_volume
// Access: Public, Virtual
// Access: Public, Virtual, Final
// Description: Virtual downcast method. Returns this object as a
// pointer of the indicated type, if it is in fact that
// type. Returns NULL if it is not that type.
////////////////////////////////////////////////////////////////////
GeometricBoundingVolume *GeometricBoundingVolume::
as_geometric_bounding_volume() {
return this;
}
////////////////////////////////////////////////////////////////////
// Function: GeometricBoundingVolume::as_geometric_bounding_volume
// Access: Public, Virtual, Final
// Description: Virtual downcast method. Returns this object as a
// pointer of the indicated type, if it is in fact that
// type. Returns NULL if it is not that type.

View File

@ -53,7 +53,8 @@ PUBLISHED:
virtual void xform(const LMatrix4 &mat)=0;
public:
virtual const GeometricBoundingVolume *as_geometric_bounding_volume() const;
virtual GeometricBoundingVolume *as_geometric_bounding_volume() FINAL;
virtual const GeometricBoundingVolume *as_geometric_bounding_volume() const FINAL;
protected:
// Some virtual functions to implement fundamental bounding

View File

@ -76,13 +76,13 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int AlphaTestAttrib::
compare_to_impl(const RenderAttrib *other) const {
const AlphaTestAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const AlphaTestAttrib *ta = (const AlphaTestAttrib *)other;
int compare_result = ((int)_mode - (int)ta->_mode) ;
if (compare_result != 0) {
return compare_result;
}
if (_reference_alpha != ta->_reference_alpha) {
return _reference_alpha < ta->_reference_alpha ? -1 : 1;
}

View File

@ -65,7 +65,7 @@ public:
protected:
static TypedWritable *make_from_bam(const FactoryParams &params);
void fillin(DatagramIterator &scan, BamReader *manager);
public:
static TypeHandle get_class_type() {
return _type_handle;
@ -74,7 +74,7 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "AlphaTestAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100, new AlphaTestAttrib);
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -75,11 +75,7 @@ make(unsigned short mode) {
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) AntialiasAttrib::
make_default() {
if (default_antialias_enable) {
return return_new(new AntialiasAttrib(M_auto));
} else {
return return_new(new AntialiasAttrib(M_none));
}
return RenderAttribRegistry::quick_get_global_ptr()->get_slot_default(_attrib_slot);
}
////////////////////////////////////////////////////////////////////
@ -146,8 +142,8 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int AntialiasAttrib::
compare_to_impl(const RenderAttrib *other) const {
const AntialiasAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const AntialiasAttrib *ta = (const AntialiasAttrib *)other;
if (_mode != ta->_mode) {
return (int)_mode - (int)ta->_mode;
}
@ -190,8 +186,7 @@ get_hash_impl() const {
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) AntialiasAttrib::
compose_impl(const RenderAttrib *other) const {
const AntialiasAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const AntialiasAttrib *ta = (const AntialiasAttrib *)other;
unsigned short mode_type;
unsigned short mode_quality;
@ -277,3 +272,26 @@ fillin(DatagramIterator &scan, BamReader *manager) {
_mode = scan.get_uint16();
}
////////////////////////////////////////////////////////////////////
// Function: AntialiasAttrib::init_type
// Access: Public, Static
// Description:
////////////////////////////////////////////////////////////////////
void AntialiasAttrib::
init_type() {
RenderAttrib::init_type();
register_type(_type_handle, "AntialiasAttrib",
RenderAttrib::get_class_type());
// This is defined here, since we have otherwise no guarantee that
// the config var has already been constructed by the time we call
// init_type() at static init time.
static ConfigVariableBool default_antialias_enable
("default-antialias-enable", false,
PRC_DESC("Set this true to enable the M_auto antialiasing mode for all "
"nodes by default."));
_attrib_slot = register_slot(_type_handle, 100,
new AntialiasAttrib(default_antialias_enable ? M_auto : M_none));
}

View File

@ -80,17 +80,12 @@ public:
protected:
static TypedWritable *make_from_bam(const FactoryParams &params);
void fillin(DatagramIterator &scan, BamReader *manager);
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
RenderAttrib::init_type();
register_type(_type_handle, "AntialiasAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
}
static void init_type();
virtual TypeHandle get_type() const {
return get_class_type();
}

View File

@ -147,8 +147,7 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int AudioVolumeAttrib::
compare_to_impl(const RenderAttrib *other) const {
const AudioVolumeAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const AudioVolumeAttrib *ta = (const AudioVolumeAttrib *)other;
if (_off != ta->_off) {
return (int)_off - (int)ta->_off;
@ -198,8 +197,7 @@ get_hash_impl() const {
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) AudioVolumeAttrib::
compose_impl(const RenderAttrib *other) const {
const AudioVolumeAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const AudioVolumeAttrib *ta = (const AudioVolumeAttrib *)other;
if (ta->is_off()) {
return ta;
@ -223,8 +221,8 @@ invert_compose_impl(const RenderAttrib *other) const {
if (is_off()) {
return other;
}
const AudioVolumeAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const AudioVolumeAttrib *ta = (const AudioVolumeAttrib *)other;
PN_stdfloat new_volume = _volume == 0.0f ? 1.0f : ta->_volume / _volume;
AudioVolumeAttrib *attrib = new AudioVolumeAttrib(false, new_volume);

View File

@ -73,7 +73,7 @@ public:
protected:
static TypedWritable *make_from_bam(const FactoryParams &params);
void fillin(DatagramIterator &scan, BamReader *manager);
public:
static TypeHandle get_class_type() {
return _type_handle;
@ -82,7 +82,7 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "AudioVolumeAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100, new AudioVolumeAttrib(false, 1));
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -88,8 +88,8 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int AuxBitplaneAttrib::
compare_to_impl(const RenderAttrib *other) const {
const AuxBitplaneAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const AuxBitplaneAttrib *ta = (const AuxBitplaneAttrib *)other;
int compare_result = _outputs - ta->_outputs;
if (compare_result != 0) {
return compare_result;

View File

@ -108,7 +108,7 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "AuxBitplaneAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100, new AuxBitplaneAttrib(0));
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -148,7 +148,7 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "ClipPlaneAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100, new ClipPlaneAttrib);
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -122,8 +122,8 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int ColorAttrib::
compare_to_impl(const RenderAttrib *other) const {
const ColorAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const ColorAttrib *ta = (const ColorAttrib *)other;
if (_type != ta->_type) {
return (int)_type - (int)ta->_type;
}

View File

@ -86,7 +86,8 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "ColorAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100,
new ColorAttrib(T_off, LColor(1, 1, 1, 1)));
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -113,8 +113,7 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int ColorBlendAttrib::
compare_to_impl(const RenderAttrib *other) const {
const ColorBlendAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const ColorBlendAttrib *ta = (const ColorBlendAttrib *)other;
if (_mode != ta->_mode) {
return (int)_mode - (int)ta->_mode;

View File

@ -129,7 +129,7 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "ColorBlendAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100, new ColorBlendAttrib);
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -186,8 +186,7 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int ColorScaleAttrib::
compare_to_impl(const RenderAttrib *other) const {
const ColorScaleAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const ColorScaleAttrib *ta = (const ColorScaleAttrib *)other;
if (is_off() != ta->is_off()) {
return (int)is_off() - (int)ta->is_off();
@ -233,8 +232,7 @@ get_hash_impl() const {
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) ColorScaleAttrib::
compose_impl(const RenderAttrib *other) const {
const ColorScaleAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const ColorScaleAttrib *ta = (const ColorScaleAttrib *)other;
if (ta->is_off()) {
return ta;
@ -263,8 +261,8 @@ invert_compose_impl(const RenderAttrib *other) const {
if (is_off()) {
return other;
}
const ColorScaleAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const ColorScaleAttrib *ta = (const ColorScaleAttrib *)other;
LVecBase4 new_scale(_scale[0] == 0.0f ? 1.0f : ta->_scale[0] / _scale[0],
_scale[1] == 0.0f ? 1.0f : ta->_scale[1] / _scale[1],
_scale[2] == 0.0f ? 1.0f : ta->_scale[2] / _scale[2],

View File

@ -92,7 +92,8 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "ColorScaleAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100,
new ColorScaleAttrib(false, LVecBase4(1, 1, 1, 1)));
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -89,8 +89,7 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int ColorWriteAttrib::
compare_to_impl(const RenderAttrib *other) const {
const ColorWriteAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const ColorWriteAttrib *ta = (const ColorWriteAttrib *)other;
return (int)_channels - (int)ta->_channels;
}

View File

@ -85,7 +85,7 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "ColorWriteAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100, new ColorWriteAttrib);
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -310,11 +310,6 @@ ConfigVariableInt max_lenses
"this can be used as a simple sanity check. Set it larger or "
"smaller to suit your needs."));
ConfigVariableBool default_antialias_enable
("default-antialias-enable", false,
PRC_DESC("Set this true to enable the M_auto antialiasing mode for all "
"nodes by default."));
ConfigVariableBool polylight_info
("polylight-info", false,
PRC_DESC("Set this true to view some info statements regarding the polylight. "

View File

@ -58,7 +58,6 @@ extern EXPCL_PANDA_PGRAPH ConfigVariableBool premunge_data;
extern ConfigVariableBool preserve_geom_nodes;
extern ConfigVariableBool flatten_geoms;
extern EXPCL_PANDA_PGRAPH ConfigVariableInt max_lenses;
extern ConfigVariableBool default_antialias_enable;
extern ConfigVariableBool polylight_info;

View File

@ -84,8 +84,8 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int CullBinAttrib::
compare_to_impl(const RenderAttrib *other) const {
const CullBinAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const CullBinAttrib *ta = (const CullBinAttrib *)other;
if (_draw_order != ta->_draw_order) {
return _draw_order - ta->_draw_order;
}

View File

@ -73,7 +73,7 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "CullBinAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100, new CullBinAttrib);
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -154,8 +154,8 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int CullFaceAttrib::
compare_to_impl(const RenderAttrib *other) const {
const CullFaceAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const CullFaceAttrib *ta = (const CullFaceAttrib *)other;
if (_mode != ta->_mode) {
return (int)_mode - (int)ta->_mode;
}
@ -199,8 +199,7 @@ get_hash_impl() const {
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) CullFaceAttrib::
compose_impl(const RenderAttrib *other) const {
const CullFaceAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const CullFaceAttrib *ta = (const CullFaceAttrib *)other;
if (!_reverse && ta->_mode != M_cull_unchanged) {
// The normal case (there is nothing funny going on): the second
@ -231,8 +230,7 @@ compose_impl(const RenderAttrib *other) const {
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) CullFaceAttrib::
invert_compose_impl(const RenderAttrib *other) const {
const CullFaceAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const CullFaceAttrib *ta = (const CullFaceAttrib *)other;
// The invert case is the same as the normal case, except that the
// meaning of _reverse is inverted. See compose_impl(), above.

View File

@ -84,7 +84,8 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "CullFaceAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100,
new CullFaceAttrib(M_cull_clockwise, false));
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -237,9 +237,8 @@ add_object(CullableObject *object, const CullTraverser *traverser) {
}
// Check for a special wireframe setting.
const RenderModeAttrib *rmode = (const RenderModeAttrib *)
object->_state->get_attrib(RenderModeAttrib::get_class_slot());
if (rmode != (const RenderModeAttrib *)NULL) {
const RenderModeAttrib *rmode;
if (object->_state->get_attrib(rmode)) {
if (rmode->get_mode() == RenderModeAttrib::M_filled_wireframe) {
CullableObject *wireframe_part = new CullableObject(*object);
wireframe_part->_state = get_wireframe_overlay_state(rmode);

View File

@ -93,7 +93,9 @@ apply_transform_and_state(CullTraverser *trav,
// Copy the bounding volumes for the frustums so we can
// transform them.
if (_view_frustum != (GeometricBoundingVolume *)NULL) {
_view_frustum = DCAST(GeometricBoundingVolume, _view_frustum->make_copy());
_view_frustum = _view_frustum->make_copy()->as_geometric_bounding_volume();
nassertv(_view_frustum != (GeometricBoundingVolume *)NULL);
_view_frustum->xform(inv_transform->get_mat());
}
@ -106,9 +108,9 @@ apply_transform_and_state(CullTraverser *trav,
if (clip_plane_cull) {
_cull_planes = _cull_planes->apply_state(trav, this,
DCAST(ClipPlaneAttrib, node_state->get_attrib(ClipPlaneAttrib::get_class_slot())),
(const ClipPlaneAttrib *)node_state->get_attrib(ClipPlaneAttrib::get_class_slot()),
DCAST(ClipPlaneAttrib, off_clip_planes),
DCAST(OccluderEffect, node_effects->get_effect(OccluderEffect::get_class_type())));
(const OccluderEffect *)node_effects->get_effect(OccluderEffect::get_class_type()));
}
}
@ -122,7 +124,8 @@ is_in_view_impl() {
const GeometricBoundingVolume *node_gbv = NULL;
if (_view_frustum != (GeometricBoundingVolume *)NULL) {
DCAST_INTO_R(node_gbv, _node_reader.get_bounds(), false)
node_gbv = _node_reader.get_bounds()->as_geometric_bounding_volume();
nassertr(node_gbv != (const GeometricBoundingVolume *)NULL, false);
int result = _view_frustum->contains(node_gbv);
@ -168,7 +171,8 @@ is_in_view_impl() {
if (!_cull_planes->is_empty()) {
if (node_gbv == (const GeometricBoundingVolume *)NULL) {
DCAST_INTO_R(node_gbv, _node_reader.get_bounds(), false)
node_gbv = _node_reader.get_bounds()->as_geometric_bounding_volume();
nassertr(node_gbv != (const GeometricBoundingVolume *)NULL, false);
}
// Also cull against the current clip planes.

View File

@ -140,8 +140,7 @@ munge_geom(GraphicsStateGuardianBase *gsg,
}
}
StateMunger *state_munger;
DCAST_INTO_R(state_munger, munger, false);
StateMunger *state_munger = (StateMunger *)munger;
_state = state_munger->munge_state(_state);
// If there is any animation left in the vertex data after it

View File

@ -95,8 +95,8 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int DepthOffsetAttrib::
compare_to_impl(const RenderAttrib *other) const {
const DepthOffsetAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const DepthOffsetAttrib *ta = (const DepthOffsetAttrib *)other;
if (_offset != ta->_offset) {
return _offset - ta->_offset;
}
@ -147,8 +147,8 @@ get_hash_impl() const {
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) DepthOffsetAttrib::
compose_impl(const RenderAttrib *other) const {
const DepthOffsetAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const DepthOffsetAttrib *ta = (const DepthOffsetAttrib *)other;
int new_offset = ta->_offset + _offset;
DepthOffsetAttrib *attrib = new DepthOffsetAttrib(new_offset, ta->_min_value, ta->_max_value);
@ -166,8 +166,8 @@ compose_impl(const RenderAttrib *other) const {
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) DepthOffsetAttrib::
invert_compose_impl(const RenderAttrib *other) const {
const DepthOffsetAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const DepthOffsetAttrib *ta = (const DepthOffsetAttrib *)other;
int new_offset = ta->_offset - _offset;
DepthOffsetAttrib *attrib = new DepthOffsetAttrib(new_offset, ta->_min_value, ta->_max_value);

View File

@ -107,7 +107,8 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "DepthOffsetAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100,
new DepthOffsetAttrib(0, 0, 1));
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -74,8 +74,8 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int DepthTestAttrib::
compare_to_impl(const RenderAttrib *other) const {
const DepthTestAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const DepthTestAttrib *ta = (const DepthTestAttrib *)other;
return (int)_mode - (int)ta->_mode;
}

View File

@ -69,7 +69,7 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "DepthTestAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100, new DepthTestAttrib);
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -81,8 +81,8 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int DepthWriteAttrib::
compare_to_impl(const RenderAttrib *other) const {
const DepthWriteAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const DepthWriteAttrib *ta = (const DepthWriteAttrib *)other;
return (int)_mode - (int)ta->_mode;
}

View File

@ -75,7 +75,7 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "DepthWriteAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100, new DepthWriteAttrib);
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -91,8 +91,7 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int FogAttrib::
compare_to_impl(const RenderAttrib *other) const {
const FogAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const FogAttrib *ta = (const FogAttrib *)other;
// Comparing pointers by subtraction is problematic. Instead of
// doing this, we'll just depend on the built-in != and < operators

View File

@ -72,7 +72,7 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "FogAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100, new FogAttrib);
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -696,8 +696,7 @@ write(ostream &out, int indent_level) const {
////////////////////////////////////////////////////////////////////
int LightAttrib::
compare_to_impl(const RenderAttrib *other) const {
const LightAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const LightAttrib *ta = (const LightAttrib *)other;
if (_off_all_lights != ta->_off_all_lights) {
return (int)_off_all_lights - (int)ta->_off_all_lights;
@ -803,8 +802,7 @@ get_hash_impl() const {
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) LightAttrib::
compose_impl(const RenderAttrib *other) const {
const LightAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const LightAttrib *ta = (const LightAttrib *)other;
if (ta->_off_all_lights) {
// If the other type turns off all lights, it doesn't matter what

View File

@ -162,7 +162,7 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "LightAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 20, make_default);
_attrib_slot = register_slot(_type_handle, 20, new LightAttrib);
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -241,8 +241,8 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int LightRampAttrib::
compare_to_impl(const RenderAttrib *other) const {
const LightRampAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const LightRampAttrib *ta = (const LightRampAttrib *)other;
int compare_result = ((int)_mode - (int)ta->_mode) ;
if (compare_result != 0) {
return compare_result;

View File

@ -95,7 +95,7 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "LightRampAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100, new LightRampAttrib);
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -92,8 +92,7 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int MaterialAttrib::
compare_to_impl(const RenderAttrib *other) const {
const MaterialAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const MaterialAttrib *ta = (const MaterialAttrib *)other;
// Comparing pointers by subtraction is problematic. Instead of
// doing this, we'll just depend on the built-in != and < operators

View File

@ -75,7 +75,7 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "MaterialAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100, new MaterialAttrib);
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -49,20 +49,6 @@ invert_compose(const RenderAttrib *other) const {
return invert_compose_impl(other);
}
////////////////////////////////////////////////////////////////////
// Function: RenderAttrib::always_reissue
// Access: Public
// Description: Returns true if the RenderAttrib should be reissued
// to the GSG with every state change, even if it is the
// same pointer as it was before; or false for the
// normal case, to reissue only when the RenderAttrib
// pointer changes.
////////////////////////////////////////////////////////////////////
INLINE bool RenderAttrib::
always_reissue() const {
return _always_reissue;
}
////////////////////////////////////////////////////////////////////
// Function: RenderAttrib::compare_to
// Access: Published
@ -160,8 +146,7 @@ calc_hash() {
// number. See RenderAttribRegistry.
////////////////////////////////////////////////////////////////////
INLINE int RenderAttrib::
register_slot(TypeHandle type_handle, int sort,
RenderAttribRegistry::MakeDefaultFunc *make_default_func) {
register_slot(TypeHandle type_handle, int sort, RenderAttrib *default_attrib) {
RenderAttribRegistry *reg = RenderAttribRegistry::get_global_ptr();
return reg->register_slot(type_handle, sort, make_default_func);
return reg->register_slot(type_handle, sort, default_attrib);
}

View File

@ -38,8 +38,6 @@ RenderAttrib() {
init_attribs();
}
_saved_entry = -1;
_always_reissue = false;
}
////////////////////////////////////////////////////////////////////
@ -435,13 +433,14 @@ return_unique(RenderAttrib *attrib) {
return attrib;
}
// Save the attrib in a local PointerTo so that it will be freed at
// the end of this function if no one else uses it.
CPT(RenderAttrib) pt_attrib = attrib;
int si = _attribs->find(attrib);
if (si != -1) {
// There's an equivalent attrib already in the set. Return it.
// If this is a newly created RenderAttrib, though, be sure to
// delete it.
if (attrib->get_ref_count() == 0) {
delete attrib;
}
return _attribs->get_key(si);
}
@ -456,7 +455,7 @@ return_unique(RenderAttrib *attrib) {
// Save the index and return the input attrib.
attrib->_saved_entry = si;
return pt_attrib;
return attrib;
}
////////////////////////////////////////////////////////////////////

View File

@ -73,8 +73,6 @@ PUBLISHED:
virtual bool lower_attrib_can_override() const;
public:
INLINE bool always_reissue() const;
virtual bool has_cull_callback() const;
virtual bool cull_callback(CullTraverser *trav, const CullTraverserData &data) const;
@ -190,14 +188,11 @@ protected:
public:
INLINE static int register_slot(TypeHandle type_handle, int sort,
RenderAttribRegistry::MakeDefaultFunc *make_default_func);
RenderAttrib *default_attrib);
private:
void release_new();
protected:
bool _always_reissue;
public:
static void init_attribs();
@ -218,6 +213,8 @@ private:
static PStatCollector _garbage_collect_pcollector;
friend class RenderAttribRegistry;
public:
virtual void write_datagram(BamWriter *manager, Datagram &dg);
static TypedWritable *change_this(TypedWritable *old_ptr, BamReader *manager);

View File

@ -30,7 +30,7 @@ get_slot(TypeHandle type_handle) const {
////////////////////////////////////////////////////////////////////
// Function: RenderAttribRegistry::get_max_slots
// Access: Published
// Access: Published, Static
// Description: Returns the maximum number that any slot number is
// allowed to grow. Actually, this number will be one
// higher than the highest possible slot number. This
@ -41,8 +41,8 @@ get_slot(TypeHandle type_handle) const {
// This number will not change during the lifetime of
// the application.
////////////////////////////////////////////////////////////////////
INLINE int RenderAttribRegistry::
get_max_slots() const {
CONSTEXPR int RenderAttribRegistry::
get_max_slots() {
return _max_slots;
}
@ -80,6 +80,20 @@ get_slot_sort(int slot) const {
return _registry[slot]._sort;
}
////////////////////////////////////////////////////////////////////
// Function: RenderAttribRegistry::get_slot_default
// Access: Published
// Description: Returns the default RenderAttrib object associated
// with slot n. This is the attrib that should be
// applied in the absence of any other attrib of this
// type.
////////////////////////////////////////////////////////////////////
INLINE const RenderAttrib *RenderAttribRegistry::
get_slot_default(int slot) const {
nassertr(slot >= 0 && slot < (int)_registry.size(), 0);
return _registry[slot]._default_attrib;
}
////////////////////////////////////////////////////////////////////
// Function: RenderAttribRegistry::get_num_sorted_slots
// Access: Published
@ -104,18 +118,6 @@ get_sorted_slot(int n) const {
return _sorted_slots[n];
}
////////////////////////////////////////////////////////////////////
// Function: RenderAttribRegistry::get_array_chain
// Access: Published
// Description: Returns the DeletedBufferChain object that may be
// used to allocated appropriately-sized arrays of
// RenderState::Attribute objects.
////////////////////////////////////////////////////////////////////
INLINE DeletedBufferChain *RenderAttribRegistry::
get_array_chain() const {
return _array_chain;
}
////////////////////////////////////////////////////////////////////
// Function: RenderAttribRegistry::get_global_ptr
// Access: Published, Static
@ -154,9 +156,21 @@ SortSlots(RenderAttribRegistry *reg) : _reg(reg) {
////////////////////////////////////////////////////////////////////
// Function: RenderAttribRegistry::SortSlots::operator ()
// Access: Public
// Description:
// Description:
////////////////////////////////////////////////////////////////////
INLINE bool RenderAttribRegistry::SortSlots::
operator () (int a, int b) const {
return _reg->get_slot_sort(a) < _reg->get_slot_sort(b);
}
////////////////////////////////////////////////////////////////////
// Function: RenderAttribRegistry::RegistryNode::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE RenderAttribRegistry::RegistryNode::
RegistryNode(TypeHandle type, int sort, const RenderAttrib *default_attrib) :
_type(type),
_sort(sort),
_default_attrib(default_attrib) {
}

View File

@ -26,37 +26,12 @@ RenderAttribRegistry *RenderAttribRegistry::_global_ptr;
////////////////////////////////////////////////////////////////////
RenderAttribRegistry::
RenderAttribRegistry() {
ConfigVariableInt max_attribs
("max-attribs", SlotMask::get_max_num_bits(),
PRC_DESC("This specifies the maximum number of different RenderAttrib "
"types that may be defined at runtime. Normally you should "
"never need to change this, but if the default value is too "
"low for the number of attribs that Panda actually defines, "
"you may need to raise this number."));
// Assign this number once, at startup, and never change it again.
_max_slots = max((int)max_attribs, 1);
if (_max_slots > SlotMask::get_max_num_bits()) {
pgraph_cat->warning()
<< "Value for max-attribs too large: cannot exceed "
<< SlotMask::get_max_num_bits()
<< " in this build. To raise this limit, change the typedef "
<< "for SlotMask in renderAttribRegistry.h and recompile.\n";
_max_slots = SlotMask::get_max_num_bits();
}
// Get a DeletedBufferChain to manage the arrays of RenderAttribs that are
// allocated within each RenderState object.
init_memory_hook();
_array_chain = memory_hook->get_deleted_chain(_max_slots * sizeof(RenderState::Attribute));
_registry.reserve(_max_slots);
_sorted_slots.reserve(_max_slots);
// Reserve slot 0 for TypeHandle::none(), and for types that exceed
// max_slots.
RegistryNode node;
node._sort = 0;
node._make_default_func = NULL;
_registry.push_back(node);
_registry.push_back(RegistryNode(TypeHandle::none(), 0, NULL));
}
////////////////////////////////////////////////////////////////////
@ -86,9 +61,9 @@ RenderAttribRegistry::
// RenderAttribs (that is, those which are more
// expensive to change) have lower sort values.
//
// The make_default_func pointer is a function that may
// be called to generate a default RenderAttrib to apply
// in the absence of any other attrib of this type.
// The default_attrib pointer should be a newly
// created instance of this attribute that represents
// the default state for this attribute.
//
// register_slot() is intended to be called at
// application start for each different RenderAttrib
@ -96,8 +71,11 @@ RenderAttribRegistry::
// slot number to each one.
////////////////////////////////////////////////////////////////////
int RenderAttribRegistry::
register_slot(TypeHandle type_handle, int sort,
RenderAttribRegistry::MakeDefaultFunc *make_default_func) {
register_slot(TypeHandle type_handle, int sort, RenderAttrib *default_attrib) {
// Sanity check; if this triggers, you either passed a wrong argument,
// or you didn't use the type system correctly.
nassertr(default_attrib->get_type() == type_handle, 0);
int type_index = type_handle.get_index();
while (type_index >= (int)_slots_by_type.size()) {
_slots_by_type.push_back(0);
@ -111,19 +89,33 @@ register_slot(TypeHandle type_handle, int sort,
int slot = (int)_registry.size();
if (slot >= _max_slots) {
pgraph_cat->error()
<< "Too many registered RenderAttribs; not registering "
<< "Too many registered RenderAttribs; not registering "
<< type_handle << "\n";
nassertr(false, 0);
return 0;
}
// Register the default attribute. We don't use return_unique and
// register it even if the state cache is disabled, because we can't read
// the state_cache config variable yet at this time. It probably doesn't
// hurt to have these 32 entries around in the attrib cache.
if (default_attrib != (RenderAttrib *)NULL) {
default_attrib->calc_hash();
if (default_attrib->_saved_entry == -1) {
// If this attribute was already registered, something odd is going on.
nassertr(RenderAttrib::_attribs->find(default_attrib) == -1, 0);
default_attrib->_saved_entry =
RenderAttrib::_attribs->store(default_attrib, RenderAttrib::Empty());
}
// It effectively lives forever. Might as well make it official.
default_attrib->local_object();
}
_slots_by_type[type_index] = slot;
RegistryNode node;
node._type = type_handle;
node._sort = sort;
node._make_default_func = make_default_func;
_registry.push_back(node);
_registry.push_back(RegistryNode(type_handle, sort, default_attrib));
_sorted_slots.push_back(slot);
::sort(_sorted_slots.begin(), _sorted_slots.end(), SortSlots(this));
@ -143,27 +135,12 @@ set_slot_sort(int slot, int sort) {
// Re-sort the slot list.
_sorted_slots.clear();
_sorted_slots.reserve(_registry.size() - 1);
for (int i = 1; i < (int)_registry.size(); ++i) {
_sorted_slots.push_back(i);
}
::sort(_sorted_slots.begin(), _sorted_slots.end(), SortSlots(this));
}
////////////////////////////////////////////////////////////////////
// Function: RenderAttribRegistry::get_slot_default
// Access: Published
// Description: Returns the default RenderAttrib object associated
// with slot n. This is the attrib that should be
// applied in the absence of any other attrib of this
// type.
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) RenderAttribRegistry::
get_slot_default(int slot) const {
nassertr(slot >= 0 && slot < (int)_registry.size(), 0);
return (*_registry[slot]._make_default_func)();
}
////////////////////////////////////////////////////////////////////
// Function: RenderAttribRegistry::init_global_ptr
// Access: Private, Static

View File

@ -48,24 +48,26 @@ public:
// or a DoubleBitMask<BitMask32> will be faster on a 32-bit machine.
typedef BitMask32 SlotMask;
// Raise this number whenever we add a new attrib. This used to be
// determined at runtime, but it's better to have it as a constexpr.
static const int _max_slots = 29;
int register_slot(TypeHandle type_handle, int sort,
MakeDefaultFunc *make_default_func);
RenderAttrib *default_attrib);
PUBLISHED:
INLINE int get_slot(TypeHandle type_handle) const;
INLINE int get_max_slots() const;
static CONSTEXPR int get_max_slots();
INLINE int get_num_slots() const;
INLINE TypeHandle get_slot_type(int slot) const;
INLINE int get_slot_sort(int slot) const;
void set_slot_sort(int slot, int sort);
CPT(RenderAttrib) get_slot_default(int slot) const;
INLINE const RenderAttrib *get_slot_default(int slot) const;
INLINE int get_num_sorted_slots() const;
INLINE int get_sorted_slot(int n) const;
INLINE DeletedBufferChain *get_array_chain() const;
INLINE static RenderAttribRegistry *get_global_ptr();
public:
@ -75,8 +77,6 @@ private:
static void init_global_ptr();
private:
int _max_slots;
class SortSlots {
public:
INLINE SortSlots(RenderAttribRegistry *reg);
@ -86,9 +86,12 @@ private:
class RegistryNode {
public:
INLINE RegistryNode(TypeHandle type, int sort,
const RenderAttrib *default_attrib);
TypeHandle _type;
int _sort;
MakeDefaultFunc *_make_default_func;
const RenderAttrib *_default_attrib;
};
typedef pvector<RegistryNode> Registry;
Registry _registry;
@ -96,12 +99,9 @@ private:
vector_int _slots_by_type;
vector_int _sorted_slots;
DeletedBufferChain *_array_chain;
static RenderAttribRegistry *_global_ptr;
};
#include "renderAttribRegistry.I"
#endif

View File

@ -128,8 +128,8 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int RenderModeAttrib::
compare_to_impl(const RenderAttrib *other) const {
const RenderModeAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const RenderModeAttrib *ta = (const RenderModeAttrib *)other;
if (_mode != ta->_mode) {
return (int)_mode - (int)ta->_mode;
}
@ -186,8 +186,7 @@ get_hash_impl() const {
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) RenderModeAttrib::
compose_impl(const RenderAttrib *other) const {
const RenderModeAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const RenderModeAttrib *ta = (const RenderModeAttrib *)other;
// The special mode M_unchanged means to keep the current mode.
Mode mode = ta->get_mode();

View File

@ -106,7 +106,8 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "RenderModeAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100,
new RenderModeAttrib(M_filled, 1, false));
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -58,35 +58,9 @@ has_cull_callback() const {
////////////////////////////////////////////////////////////////////
INLINE CPT(RenderState) RenderState::
make_empty() {
// The empty state is asked for so often, we make it a special case
// and store a pointer forever once we find it the first time.
if (_empty_state == (RenderState *)NULL) {
RenderState *state = new RenderState;
_empty_state = return_unique(state);
}
return _empty_state;
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::make_full_default
// Access: Published, Static
// Description: Returns a RenderState with all possible attributes
// set to their default value.
////////////////////////////////////////////////////////////////////
INLINE CPT(RenderState) RenderState::
make_full_default() {
// The empty state is asked for so often, we make it a special case
// and store a pointer forever once we find it the first time.
if (_full_default_state == (RenderState *)NULL) {
RenderState *state = new RenderState;
state->fill_default();
_full_default_state = return_unique(state);
}
return _full_default_state;
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::remove_attrib
// Access: Published
@ -144,7 +118,7 @@ get_attrib(TypeHandle type) const {
// index, or NULL if there is no such RenderAttrib in
// the state.
////////////////////////////////////////////////////////////////////
INLINE const RenderAttrib *RenderState::
ALWAYS_INLINE const RenderAttrib *RenderState::
get_attrib(int slot) const {
return _attributes[slot]._attrib;
}
@ -161,7 +135,8 @@ get_attrib_def(int slot) const {
if (_attributes[slot]._attrib != (RenderAttrib *)NULL) {
return _attributes[slot]._attrib;
}
return make_full_default()->get_attrib(slot);
RenderAttribRegistry *reg = RenderAttribRegistry::quick_get_global_ptr();
return reg->get_slot_default(slot);
}
////////////////////////////////////////////////////////////////////

View File

@ -40,8 +40,7 @@
LightReMutex *RenderState::_states_lock = NULL;
RenderState::States *RenderState::_states = NULL;
CPT(RenderState) RenderState::_empty_state;
CPT(RenderState) RenderState::_full_default_state;
const RenderState *RenderState::_empty_state = NULL;
UpdateSeq RenderState::_last_cycle_detect;
int RenderState::_garbage_index = 0;
@ -72,20 +71,11 @@ RenderState() :
_auto_shader_state(NULL),
_lock("RenderState")
{
// Allocate the _attributes array.
RenderAttribRegistry *reg = RenderAttribRegistry::get_global_ptr();
_attributes = (Attribute *)reg->get_array_chain()->allocate(reg->get_max_slots() * sizeof(Attribute), get_class_type());
// Also make sure each element gets initialized.
for (int i = 0; i < reg->get_max_slots(); ++i) {
new(&_attributes[i]) Attribute();
}
if (_states == (States *)NULL) {
init_states();
}
_saved_entry = -1;
_last_mi = _mungers.end();
_last_mi = -1;
_cache_stats.add_num_states(1);
_read_overrides = NULL;
_generated_shader = NULL;
@ -103,17 +93,13 @@ RenderState(const RenderState &copy) :
_auto_shader_state(NULL),
_lock("RenderState")
{
// Allocate the _attributes array.
RenderAttribRegistry *reg = RenderAttribRegistry::get_global_ptr();
_attributes = (Attribute *)reg->get_array_chain()->allocate(reg->get_max_slots() * sizeof(Attribute), get_class_type());
// Also make sure each element gets initialized, as a copy.
for (int i = 0; i < reg->get_max_slots(); ++i) {
new(&_attributes[i]) Attribute(copy._attributes[i]);
// Copy over the attributes.
for (int i = 0; i < RenderAttribRegistry::_max_slots; ++i) {
_attributes[i] = copy._attributes[i];
}
_saved_entry = -1;
_last_mi = _mungers.end();
_last_mi = -1;
_cache_stats.add_num_states(1);
_read_overrides = NULL;
_generated_shader = NULL;
@ -159,14 +145,6 @@ RenderState::
// longer true now, probably we've been double-deleted.
nassertv(get_ref_count() == 0);
_cache_stats.add_num_states(-1);
// Free the _attributes array.
RenderAttribRegistry *reg = RenderAttribRegistry::get_global_ptr();
for (int i = 0; i < reg->get_max_slots(); ++i) {
_attributes[i].~Attribute();
}
reg->get_array_chain()->deallocate(_attributes, get_class_type());
_attributes = NULL;
}
////////////////////////////////////////////////////////////////////
@ -360,7 +338,7 @@ make(const RenderAttrib *attrib1,
CPT(RenderState) RenderState::
make(const RenderAttrib * const *attrib, int num_attribs, int override) {
if (num_attribs == 0) {
return make_empty();
return _empty_state;
}
RenderState *state = new RenderState;
for (int i = 0; i < num_attribs; i++) {
@ -490,7 +468,7 @@ invert_compose(const RenderState *other) const {
if (other == this) {
// a->invert_compose(a) always produces identity.
return make_empty();
return _empty_state;
}
if (!state_cache) {
@ -633,7 +611,7 @@ remove_attrib(int slot) const {
// Will this bring us down to the empty state?
if (_filled_slots.get_num_on_bits() == 1) {
return make_empty();
return _empty_state;
}
RenderState *new_state = new RenderState(*this);
@ -1110,7 +1088,7 @@ clear_munger_cache() {
}
RenderState *state = (RenderState *)(_states->get_key(si));
state->_mungers.clear();
state->_last_mi = state->_mungers.end();
state->_last_mi = -1;
}
}
@ -1314,17 +1292,17 @@ validate_states() {
////////////////////////////////////////////////////////////////////
int RenderState::
get_geom_rendering(int geom_rendering) const {
const RenderModeAttrib *render_mode = DCAST(RenderModeAttrib, get_attrib(RenderModeAttrib::get_class_slot()));
const TexGenAttrib *tex_gen = DCAST(TexGenAttrib, get_attrib(TexGenAttrib::get_class_slot()));
const TexMatrixAttrib *tex_matrix = DCAST(TexMatrixAttrib, get_attrib(TexMatrixAttrib::get_class_slot()));
const RenderModeAttrib *render_mode;
const TexGenAttrib *tex_gen;
const TexMatrixAttrib *tex_matrix;
if (render_mode != (const RenderModeAttrib *)NULL) {
if (get_attrib(render_mode)) {
geom_rendering = render_mode->get_geom_rendering(geom_rendering);
}
if (tex_gen != (const TexGenAttrib *)NULL) {
if (get_attrib(tex_gen)) {
geom_rendering = tex_gen->get_geom_rendering(geom_rendering);
}
if (tex_matrix != (const TexMatrixAttrib *)NULL) {
if (get_attrib(tex_matrix)) {
geom_rendering = tex_matrix->get_geom_rendering(geom_rendering);
}
@ -1507,7 +1485,7 @@ return_new(RenderState *state) {
////////////////////////////////////////////////////////////////////
CPT(RenderState) RenderState::
return_unique(RenderState *state) {
nassertr(state != (RenderState *)NULL, state);
nassertr(state != (RenderState *)NULL, NULL);
if (!state_cache) {
return state;
@ -1523,14 +1501,10 @@ return_unique(RenderState *state) {
if (state->_saved_entry != -1) {
// This state is already in the cache.
//nassertr(_states->find(state) == state->_saved_entry, state);
//nassertr(_states->find(state) == state->_saved_entry, pt_state);
return state;
}
// Save the state in a local PointerTo so that it will be freed at
// the end of this function if no one else uses it.
CPT(RenderState) pt_state = state;
// Ensure each of the individual attrib pointers has been uniquified
// before we add the state to the cache.
if (!uniquify_attribs && !state->is_empty()) {
@ -1538,7 +1512,7 @@ return_unique(RenderState *state) {
int slot = mask.get_lowest_on_bit();
while (slot >= 0) {
Attribute &attrib = state->_attributes[slot];
nassertr(attrib._attrib != (RenderAttrib *)NULL, state);
nassertd(attrib._attrib != (RenderAttrib *)NULL) continue;
attrib._attrib = attrib._attrib->get_unique();
mask.clear_bit(slot);
slot = mask.get_lowest_on_bit();
@ -1548,6 +1522,11 @@ return_unique(RenderState *state) {
int si = _states->find(state);
if (si != -1) {
// There's an equivalent state already in the set. Return it.
// The state that was passed may be newly created and therefore
// may not be automatically deleted. Do that if necessary.
if (state->get_ref_count() == 0) {
delete state;
}
return _states->get_key(si);
}
@ -1562,7 +1541,7 @@ return_unique(RenderState *state) {
// Save the index and return the input state.
state->_saved_entry = si;
return pt_state;
return state;
}
////////////////////////////////////////////////////////////////////
@ -1621,15 +1600,6 @@ do_compose(const RenderState *other) const {
slot = mask.get_lowest_on_bit();
}
// If we have any ShaderAttrib with auto-shader enabled,
// remove any shader inputs on it. This is a workaround for an
// issue that makes the shader-generator regenerate the shader
// every time a shader input changes.
CPT(ShaderAttrib) sattrib = DCAST(ShaderAttrib, new_state->get_attrib_def(ShaderAttrib::get_class_slot()));
if (sattrib->auto_shader()) {
sattrib = DCAST(ShaderAttrib, sattrib->clear_all_shader_inputs());
}
return return_new(new_state);
}
@ -1660,9 +1630,8 @@ do_invert_compose(const RenderState *other) const {
} else if (b._attrib == NULL) {
// A wins. Invert it.
CPT(RenderState) full_default = make_full_default();
CPT(RenderAttrib) default_attrib = full_default->get_attrib(slot);
result.set(a._attrib->invert_compose(default_attrib), 0);
RenderAttribRegistry *reg = RenderAttribRegistry::quick_get_global_ptr();
result.set(a._attrib->invert_compose(reg->get_slot_default(slot)), 0);
} else {
// Both are good. (Overrides are not used in invert_compose.)
@ -2162,8 +2131,14 @@ init_states() {
_states_lock = new LightReMutex("RenderState::_states_lock");
_cache_stats.init();
nassertv(Thread::get_current_thread() == Thread::get_main_thread());
}
// Initialize the empty state object as well. It is used so often
// that it is declared globally, and lives forever.
RenderState *state = new RenderState;
state->local_object();
state->_saved_entry = _states->store(state, Empty());
_empty_state = state;
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::register_with_read_factory

View File

@ -31,10 +31,11 @@
#include "lightMutex.h"
#include "deletedChain.h"
#include "simpleHashMap.h"
#include "weakKeyHashMap.h"
#include "cacheStats.h"
#include "renderAttribRegistry.h"
#include "graphicsStateGuardianBase.h"
class GraphicsStateGuardianBase;
class FactoryParams;
class ShaderAttrib;
@ -76,7 +77,6 @@ PUBLISHED:
bool cull_callback(CullTraverser *trav, const CullTraverserData &data) const;
INLINE static CPT(RenderState) make_empty();
INLINE static CPT(RenderState) make_full_default();
static CPT(RenderState) make(const RenderAttrib *attrib, int override = 0);
static CPT(RenderState) make(const RenderAttrib *attrib1,
const RenderAttrib *attrib2, int override = 0);
@ -104,7 +104,7 @@ PUBLISHED:
INLINE bool has_attrib(TypeHandle type) const;
INLINE bool has_attrib(int slot) const;
INLINE const RenderAttrib *get_attrib(TypeHandle type) const;
INLINE const RenderAttrib *get_attrib(int slot) const;
ALWAYS_INLINE const RenderAttrib *get_attrib(int slot) const;
INLINE const RenderAttrib *get_attrib_def(int slot) const;
INLINE int get_override(TypeHandle type) const;
INLINE int get_override(int slot) const;
@ -233,8 +233,7 @@ private:
};
typedef SimpleHashMap<const RenderState *, Empty, indirect_compare_to_hash<const RenderState *> > States;
static States *_states;
static CPT(RenderState) _empty_state;
static CPT(RenderState) _full_default_state;
static const RenderState *_empty_state;
// This iterator records the entry corresponding to this
// RenderState object in the above global set. We keep the index
@ -270,9 +269,9 @@ private:
// since there are likely to be far fewer GSG's than RenderStates.
// The code to manage this map lives in
// GraphicsStateGuardian::get_geom_munger().
typedef pmap<WCPT(GraphicsStateGuardianBase), PT(GeomMunger) > Mungers;
typedef WeakKeyHashMap<GraphicsStateGuardianBase, PT(GeomMunger) > Mungers;
mutable Mungers _mungers;
mutable Mungers::const_iterator _last_mi;
mutable int _last_mi;
// This is used to mark nodes as we visit them to detect cycles.
UpdateSeq _cycle_detect;
@ -307,7 +306,7 @@ private:
CPT(RenderAttrib) _attrib;
int _override;
};
Attribute *_attributes;
Attribute _attributes[RenderAttribRegistry::_max_slots];
// We also store a bitmask of the non-NULL attributes in the above
// array. This is redundant, but it is a useful cache.

View File

@ -25,6 +25,19 @@ RescaleNormalAttrib(RescaleNormalAttrib::Mode mode) :
{
}
////////////////////////////////////////////////////////////////////
// Function: RescaleNormalAttrib::make_default
// Access: Published, Static
// Description: Constructs a RescaleNormalAttrib object that's
// suitable for putting at the top of a scene graph.
// This will contain whatever attrib was suggested by
// the user's rescale-normals Config variable.
////////////////////////////////////////////////////////////////////
INLINE CPT(RenderAttrib) RescaleNormalAttrib::
make_default() {
return RenderAttribRegistry::quick_get_global_ptr()->get_slot_default(_attrib_slot);
}
////////////////////////////////////////////////////////////////////
// Function: RescaleNormalAttrib::get_mode
// Access: Published

View File

@ -25,18 +25,7 @@
TypeHandle RescaleNormalAttrib::_type_handle;
int RescaleNormalAttrib::_attrib_slot;
// This variable is defined here instead of in config_pgraph.cxx,
// because it depends on rescaleNormalAttrib.h having already been
// included.
static ConfigVariableEnum<RescaleNormalAttrib::Mode> rescale_normals
("rescale-normals", RescaleNormalAttrib::M_auto,
PRC_DESC("Specifies the kind of RescaleNormalAttrib that should be "
"created for the top of the scene graph. This can automatically "
"ensure that your lighting normals are unit-length, which may be "
"particularly necessary in the presence of scales in the scene "
"graph. Turning it off ('none') may produce a small performance "
"benefit."));
CPT(RenderAttrib) RescaleNormalAttrib::_attribs[RescaleNormalAttrib::M_auto + 1];
////////////////////////////////////////////////////////////////////
// Function: RescaleNormalAttrib::make
@ -47,28 +36,18 @@ static ConfigVariableEnum<RescaleNormalAttrib::Mode> rescale_normals
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) RescaleNormalAttrib::
make(RescaleNormalAttrib::Mode mode) {
RescaleNormalAttrib *attrib = new RescaleNormalAttrib(mode);
return return_new(attrib);
}
////////////////////////////////////////////////////////////////////
// Function: RescaleNormalAttrib::make_default
// Access: Published, Static
// Description: Constructs a RescaleNoramlAttrib object that's
// suitable for putting at the top of a scene graph.
// This will contain whatever attrib was suggested by
// the user's rescale-normals Config variable.
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) RescaleNormalAttrib::
make_default() {
RescaleNormalAttrib *attrib = new RescaleNormalAttrib(rescale_normals);
return return_new(attrib);
if (_attribs[mode].is_null()) {
// Don't bother with return_new, since this is the only way a
// RescaleNormalAttrib can be made anyway.
_attribs[mode] = new RescaleNormalAttrib(mode);
}
return _attribs[mode];
}
////////////////////////////////////////////////////////////////////
// Function: RescaleNormalAttrib::output
// Access: Public, Virtual
// Description:
// Description:
////////////////////////////////////////////////////////////////////
void RescaleNormalAttrib::
output(ostream &out) const {
@ -92,8 +71,7 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int RescaleNormalAttrib::
compare_to_impl(const RenderAttrib *other) const {
const RescaleNormalAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const RescaleNormalAttrib *ta = (const RescaleNormalAttrib *)other;
return (int)_mode - (int)ta->_mode;
}
@ -187,6 +165,35 @@ fillin(DatagramIterator &scan, BamReader *manager) {
_mode = (Mode)scan.get_int8();
}
////////////////////////////////////////////////////////////////////
// Function: RescaleNormalAttrib::init_type
// Access: Public, Static
// Description:
////////////////////////////////////////////////////////////////////
void RescaleNormalAttrib::
init_type() {
RenderAttrib::init_type();
register_type(_type_handle, "RescaleNormalAttrib",
RenderAttrib::get_class_type());
// This is defined here, since we have otherwise no guarantee that
// the config var has already been constructed by the time we call
// init_type() at static init time.
static ConfigVariableEnum<RescaleNormalAttrib::Mode> rescale_normals
("rescale-normals", RescaleNormalAttrib::M_auto,
PRC_DESC("Specifies the kind of RescaleNormalAttrib that should be "
"created for the top of the scene graph. This can automatically "
"ensure that your lighting normals are unit-length, which may be "
"particularly necessary in the presence of scales in the scene "
"graph. Turning it off ('none') may produce a small performance "
"benefit."));
Mode mode = rescale_normals;
RescaleNormalAttrib *attrib = new RescaleNormalAttrib(mode);
_attrib_slot = register_slot(_type_handle, 100, attrib);
_attribs[mode] = attrib;
}
////////////////////////////////////////////////////////////////////
// Function: RescaleNormalAttrib::Mode output operator
// Description:

View File

@ -49,7 +49,7 @@ private:
PUBLISHED:
static CPT(RenderAttrib) make(Mode mode);
static CPT(RenderAttrib) make_default();
INLINE static CPT(RenderAttrib) make_default();
INLINE Mode get_mode() const;
@ -64,6 +64,10 @@ protected:
private:
Mode _mode;
// There are so few possible combinations, and it's used fairly often, so
// we keep an array of the possible attributes.
static CPT(RenderAttrib) _attribs[M_auto + 1];
PUBLISHED:
static int get_class_slot() {
return _attrib_slot;
@ -79,17 +83,12 @@ public:
protected:
static TypedWritable *make_from_bam(const FactoryParams &params);
void fillin(DatagramIterator &scan, BamReader *manager);
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
RenderAttrib::init_type();
register_type(_type_handle, "RescaleNormalAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
}
static void init_type();
virtual TypeHandle get_type() const {
return get_class_type();
}

View File

@ -113,8 +113,7 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int ScissorAttrib::
compare_to_impl(const RenderAttrib *other) const {
const ScissorAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const ScissorAttrib *ta = (const ScissorAttrib *)other;
if (!_off && !ta->_off) {
return 0;
@ -173,8 +172,7 @@ compose_impl(const RenderAttrib *other) const {
return other;
}
const ScissorAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const ScissorAttrib *ta = (const ScissorAttrib *)other;
if (ta->_off) {
return this;

View File

@ -89,7 +89,10 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "ScissorAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
ScissorAttrib *attrib = new ScissorAttrib(LVecBase4(0, 1, 0, 1));
attrib->_off = true;
_attrib_slot = register_slot(_type_handle, 100, attrib);
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -84,8 +84,7 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int ShadeModelAttrib::
compare_to_impl(const RenderAttrib *other) const {
const ShadeModelAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const ShadeModelAttrib *ta = (const ShadeModelAttrib *)other;
return (int)_mode - (int)ta->_mode;
}
@ -125,8 +124,7 @@ get_hash_impl() const {
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) ShadeModelAttrib::
compose_impl(const RenderAttrib *other) const {
const ShadeModelAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const ShadeModelAttrib *ta = (const ShadeModelAttrib *)other;
Mode mode = ta->get_mode();
return make(mode);

View File

@ -77,7 +77,7 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "ShadeModelAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100, new ShadeModelAttrib(M_smooth));
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -542,8 +542,7 @@ get_shader() const {
////////////////////////////////////////////////////////////////////
int ShaderAttrib::
compare_to_impl(const RenderAttrib *other) const {
const ShaderAttrib *that;
DCAST_INTO_R(that, other, 0);
const ShaderAttrib *that = (const ShaderAttrib *)other;
if (this->_shader != that->_shader) {
return (this->_shader < that->_shader) ? -1 : 1;
@ -643,8 +642,8 @@ get_hash_impl() const {
CPT(RenderAttrib) ShaderAttrib::
compose_impl(const RenderAttrib *other) const {
ShaderAttrib *attr = new ShaderAttrib(*this);
const ShaderAttrib *over;
DCAST_INTO_R(over, other, 0);
const ShaderAttrib *over = (const ShaderAttrib *)other;
// Update the shader portion.
if (over->_has_shader) {
if ((attr->_has_shader == false) ||

View File

@ -156,7 +156,7 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "ShaderAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 10, make_default);
_attrib_slot = register_slot(_type_handle, 10, new ShaderAttrib);
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -32,18 +32,15 @@ StateMunger::
////////////////////////////////////////////////////////////////////
CPT(RenderState) StateMunger::
munge_state(const RenderState *state) {
WCPT(RenderState) pt_state = state;
StateMap::iterator mi = _state_map.find(pt_state);
if (mi != _state_map.end()) {
if (!(*mi).first.was_deleted() &&
!(*mi).second.was_deleted()) {
return (*mi).second.p();
int mi = _state_map.find(state);
if (mi != -1) {
if (!_state_map.get_data(mi).was_deleted()) {
return _state_map.get_data(mi).p();
}
}
CPT(RenderState) result = munge_state_impl(state);
_state_map[pt_state] = result;
_state_map.store(state, result.p());
return result;
}

View File

@ -18,7 +18,7 @@
#include "pandabase.h"
#include "geomMunger.h"
#include "renderState.h"
#include "weakPointerTo.h"
#include "weakKeyHashMap.h"
////////////////////////////////////////////////////////////////////
// Class : StateMunger
@ -36,7 +36,7 @@ public:
protected:
virtual CPT(RenderState) munge_state_impl(const RenderState *state);
typedef pmap< WCPT(RenderState), WCPT(RenderState) > StateMap;
typedef WeakKeyHashMap<RenderState, WCPT(RenderState) > StateMap;
StateMap _state_map;
public:

View File

@ -311,8 +311,7 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int StencilAttrib::
compare_to_impl(const RenderAttrib *other) const {
const StencilAttrib *sa;
DCAST_INTO_R(sa, other, 0);
const StencilAttrib *sa = (const StencilAttrib *)other;
int a;
int b;

View File

@ -175,7 +175,7 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "StencilAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100, new StencilAttrib);
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -297,8 +297,7 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int TexGenAttrib::
compare_to_impl(const RenderAttrib *other) const {
const TexGenAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const TexGenAttrib *ta = (const TexGenAttrib *)other;
Stages::const_iterator ai, bi;
ai = _stages.begin();
@ -383,8 +382,7 @@ get_hash_impl() const {
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) TexGenAttrib::
compose_impl(const RenderAttrib *other) const {
const TexGenAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const TexGenAttrib *ta = (const TexGenAttrib *)other;
// The composition is the union of the two attribs. In the case
// when a stage is in both attribs, we compose the stages.
@ -441,8 +439,7 @@ compose_impl(const RenderAttrib *other) const {
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) TexGenAttrib::
invert_compose_impl(const RenderAttrib *other) const {
const TexGenAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const TexGenAttrib *ta = (const TexGenAttrib *)other;
// The inverse composition works a lot like the composition, except
// we invert the ai stages.

View File

@ -139,7 +139,7 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "TexGenAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100, new TexGenAttrib);
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -247,9 +247,8 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int TexMatrixAttrib::
compare_to_impl(const RenderAttrib *other) const {
const TexMatrixAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const TexMatrixAttrib *ta = (const TexMatrixAttrib *)other;
Stages::const_iterator ai, bi;
ai = _stages.begin();
bi = ta->_stages.begin();
@ -326,8 +325,7 @@ get_hash_impl() const {
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) TexMatrixAttrib::
compose_impl(const RenderAttrib *other) const {
const TexMatrixAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const TexMatrixAttrib *ta = (const TexMatrixAttrib *)other;
// The composition is the union of the two attribs. In the case
// when a stage is in both attribs, we compose the stages.
@ -396,8 +394,7 @@ compose_impl(const RenderAttrib *other) const {
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) TexMatrixAttrib::
invert_compose_impl(const RenderAttrib *other) const {
const TexMatrixAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const TexMatrixAttrib *ta = (const TexMatrixAttrib *)other;
// The inverse composition works a lot like the composition, except
// we invert the ai stages.

View File

@ -123,7 +123,7 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "TexMatrixAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100, new TexMatrixAttrib);
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -507,8 +507,7 @@ cull_callback(CullTraverser *trav, const CullTraverserData &data) const {
////////////////////////////////////////////////////////////////////
int TextureAttrib::
compare_to_impl(const RenderAttrib *other) const {
const TextureAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const TextureAttrib *ta = (const TextureAttrib *)other;
if (_off_all_stages != ta->_off_all_stages) {
return (int)_off_all_stages - (int)ta->_off_all_stages;
@ -664,8 +663,7 @@ get_hash_impl() const {
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) TextureAttrib::
compose_impl(const RenderAttrib *other) const {
const TextureAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const TextureAttrib *ta = (const TextureAttrib *)other;
if (ta->_off_all_stages) {
// If the other type turns off all stages, it doesn't matter what

View File

@ -179,7 +179,7 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "TextureAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 30, make_default);
_attrib_slot = register_slot(_type_handle, 30, new TextureAttrib);
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -101,8 +101,8 @@ output(ostream &out) const {
////////////////////////////////////////////////////////////////////
int TransparencyAttrib::
compare_to_impl(const RenderAttrib *other) const {
const TransparencyAttrib *ta;
DCAST_INTO_R(ta, other, 0);
const TransparencyAttrib *ta = (const TransparencyAttrib *)other;
return (int)_mode - (int)ta->_mode;
}

View File

@ -92,7 +92,7 @@ public:
RenderAttrib::init_type();
register_type(_type_handle, "TransparencyAttrib",
RenderAttrib::get_class_type());
_attrib_slot = register_slot(_type_handle, 100, make_default);
_attrib_slot = register_slot(_type_handle, 100, new TransparencyAttrib);
}
virtual TypeHandle get_type() const {
return get_class_type();

View File

@ -23,6 +23,7 @@
#include "vector_typedWritable.cxx"
#include "vector_ushort.cxx"
#include "vector_writable.cxx"
#include "weakKeyHashMap.cxx"
#include "writableConfigurable.cxx"
#include "writableParam.cxx"

View File

@ -0,0 +1,714 @@
// Filename: weakKeyHashMap.I
// Created by: rdb (13Jul15)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
INLINE WeakKeyHashMap<Key, Value>::
WeakKeyHashMap() :
_table(NULL),
_deleted_chain(NULL),
_table_size(0),
_num_entries(0)
{
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
INLINE WeakKeyHashMap<Key, Value>::
~WeakKeyHashMap() {
clear();
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::swap
// Access: Public
// Description: Quickly exchanges the contents of this map and the
// other map.
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
INLINE void WeakKeyHashMap<Key, Value>::
swap(WeakKeyHashMap<Key, Value> &other) {
TableEntry *t0 = _table;
_table = other._table;
other._table = t0;
DeletedBufferChain *t1 = _deleted_chain;
_deleted_chain = other._deleted_chain;
other._deleted_chain = t1;
size_t t2 = _table_size;
_table_size = other._table_size;
other._table_size = t2;
size_t t3 = _num_entries;
_num_entries = other._num_entries;
other._num_entries = t3;
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::find
// Access: Public
// Description: Searches for the indicated key in the table. Returns
// its index number if it is found, or -1 if it is not
// present in the table.
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
int WeakKeyHashMap<Key, Value>::
find(const Key *key) const {
if (_table_size == 0) {
// Special case: the table is empty.
return -1;
}
size_t index = get_hash(key);
if (!has_element(index)) {
return -1;
}
if (is_element(index, key)) {
return index;
}
// There was some other key at the hashed slot. That's a hash
// conflict. Maybe our entry was recorded at a later slot position;
// scan the subsequent positions until we find the entry or an
// unused slot, indicating the end of the scan.
size_t i = index;
i = (i + 1) & (_table_size - 1);
while (i != index && has_element(i)) {
if (is_element(i, key)) {
return i;
}
i = (i + 1) & (_table_size - 1);
}
// The key is not in the table.
return -1;
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::store
// Access: Public
// Description: Records the indicated key/data pair in the map. If
// the key was already present, silently replaces it.
// Returns the index at which it was stored.
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
int WeakKeyHashMap<Key, Value>::
store(const Key *key, const Value &data) {
if (_table_size == 0) {
// Special case: the first key in an empty table.
nassertr(_num_entries == 0, -1);
new_table();
size_t index = get_hash(key);
store_new_element(index, key, data);
++_num_entries;
#ifdef _DEBUG
nassertr(validate(), index);
#endif
return index;
}
size_t index = get_hash(key);
if (!has_element(index)) {
// This element is not already in the map; add it.
if (consider_expand_table()) {
return store(key, data);
}
store_new_element(index, key, data);
++_num_entries;
#ifdef _DEBUG
nassertr(validate(), index);
#endif
return index;
}
if (is_element(index, key)) {
// This element is already in the map; replace the data at that
// key.
_table[index]._data = data;
#ifdef _DEBUG
nassertr(validate(), index);
#endif
return index;
}
// There was some other key at the hashed slot. That's a hash
// conflict. Record this entry at a later position.
size_t i = index;
i = (i + 1) & (_table_size - 1);
while (i != index) {
if (!has_element(i)) {
if (consider_expand_table()) {
return store(key, data);
}
store_new_element(i, key, data);
++_num_entries;
#ifdef _DEBUG
nassertr(validate(), i);
#endif
return i;
}
if (is_element(i, key)) {
_table[i]._data = data;
#ifdef _DEBUG
nassertr(validate(), i);
#endif
return i;
}
i = (i + 1) & (_table_size - 1);
}
// Shouldn't get here unless _num_entries == _table_size, which
// shouldn't be possible due to consider_expand_table().
nassertr(false, -1);
return -1; // To satisfy compiler
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::remove
// Access: Public
// Description: Removes the indicated key and its associated data
// from the table. Returns true if the key was removed,
// false if it was not present.
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
INLINE bool WeakKeyHashMap<Key, Value>::
remove(const Key *key) {
int index = find(key);
if (index == -1) {
return false;
}
remove_element(index);
return true;
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::clear
// Access: Public
// Description: Completely empties the table.
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
void WeakKeyHashMap<Key, Value>::
clear() {
if (_table_size != 0) {
for (size_t i = 0; i < _table_size; ++i) {
if (get_exists_array()[i] != 0) {
clear_element(i);
}
}
_deleted_chain->deallocate(_table, TypeHandle::none());
_table = NULL;
_deleted_chain = NULL;
_table_size = 0;
_num_entries = 0;
}
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::operator []
// Access: Public
// Description: Returns a modifiable reference to the data associated
// with the indicated key, or creates a new data entry
// and returns its reference.
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
INLINE Value &WeakKeyHashMap<Key, Value>::
operator [] (const Key *key) {
int index = find(key);
if (index == -1) {
index = store(key, Value());
}
return modify_data(index);
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::get_size
// Access: Public
// Description: Returns the total number of slots in the table.
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
INLINE int WeakKeyHashMap<Key, Value>::
get_size() const {
return _table_size;
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::has_element
// Access: Public
// Description: Returns true if there is an element stored in the nth
// slot, false otherwise.
//
// n should be in the range 0 <= n < get_size().
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
INLINE bool WeakKeyHashMap<Key, Value>::
has_element(int n) const {
nassertr(n >= 0 && n < (int)_table_size, false);
return (get_exists_array()[n] != 0 && !_table[n]._key.was_deleted());
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::get_key
// Access: Public
// Description: Returns the key in the nth slot of the table.
//
// It is an error to call this if there is nothing
// stored in the nth slot (use has_element() to check
// this first). n should be in the range 0 <= n <
// get_size().
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
INLINE const Key *WeakKeyHashMap<Key, Value>::
get_key(int n) const {
nassertr(has_element(n), _table[n]._key);
return _table[n]._key;
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::get_data
// Access: Public
// Description: Returns the data in the nth slot of the table.
//
// It is an error to call this if there is nothing
// stored in the nth slot (use has_element() to check
// this first). n should be in the range 0 <= n <
// get_size().
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
INLINE const Value &WeakKeyHashMap<Key, Value>::
get_data(int n) const {
nassertr(has_element(n), _table[n]._data);
return _table[n]._data;
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::modify_data
// Access: Public
// Description: Returns a modifiable reference to the data in the nth
// slot of the table.
//
// It is an error to call this if there is nothing
// stored in the nth slot (use has_element() to check
// this first). n should be in the range 0 <= n <
// get_size().
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
INLINE Value &WeakKeyHashMap<Key, Value>::
modify_data(int n) {
nassertr(has_element(n), _table[n]._data);
return _table[n]._data;
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::set_data
// Access: Public
// Description: Changes the data for the nth slot of the table.
//
// It is an error to call this if there is nothing
// stored in the nth slot (use has_element() to check
// this first). n should be in the range 0 <= n <
// get_size().
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
INLINE void WeakKeyHashMap<Key, Value>::
set_data(int n, const Value &data) {
nassertv(has_element(n));
_table[n]._data = data;
}
#ifdef USE_MOVE_SEMANTICS
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::set_data
// Access: Public
// Description: Changes the data for the nth slot of the table.
//
// It is an error to call this if there is nothing
// stored in the nth slot (use has_element() to check
// this first). n should be in the range 0 <= n <
// get_size().
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
INLINE void WeakKeyHashMap<Key, Value>::
set_data(int n, Value &&data) {
nassertv(has_element(n));
_table[n]._data = std::move(data);
}
#endif // USE_MOVE_SEMANTICS
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::remove_element
// Access: Public
// Description: Removes the nth slot from the table.
//
// It is an error to call this if there is nothing
// stored in the nth slot (use has_element() to check
// this first). n should be in the range 0 <= n <
// get_size().
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
void WeakKeyHashMap<Key, Value>::
remove_element(int n) {
nassertv(get_exists_array()[n] != 0);
clear_element(n);
nassertv(_num_entries > 0);
--_num_entries;
// Now we have put a hole in the table. If there was a hash
// conflict in the slot following this one, we have to move it down
// to close the hole.
size_t i = n;
i = (i + 1) & (_table_size - 1);
while (get_exists_array()[i] != 0) {
if (_table[i]._key.was_deleted()) {
// It was deleted. Forget about it.
clear_element(i);
--_num_entries;
} else {
size_t wants_index = get_hash(_table[i]._key);
if (wants_index != i) {
// This one was a hash conflict; try to put it where it belongs.
// We can't just put it in n, since maybe it belongs somewhere
// after n.
while (wants_index != i && has_element(wants_index)) {
// Hash conflict; move it up.
wants_index = (wants_index + 1) & (_table_size - 1);
}
if (wants_index != i) {
store_new_element(wants_index, _table[i]._key, _table[i]._data);
clear_element(i);
}
}
}
// Continue until we encounter the next unused slot. Until we do,
// we can't be sure we've found all of the potential hash
// conflicts.
i = (i + 1) & (_table_size - 1);
}
#ifdef _DEBUG
nassertv(validate());
#endif
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::get_num_entries
// Access: Public
// Description: Returns the number of active entries in the table.
// This is not necessarily related to the number of
// slots in the table as reported by get_size(). Use
// get_size() to iterate through all of the slots, not
// get_num_entries().
//
// This is merely an upper bound on the number of
// entries; it may also count false positives for
// pointers that were recently deleted.
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
INLINE int WeakKeyHashMap<Key, Value>::
get_num_entries() const {
return _num_entries;
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::is_empty
// Access: Public
// Description: Returns true if the table is empty;
// i.e. get_num_entries() == 0. This may return a
// false negatives if a pointer was recently deleted;
// if this returns true, though, you can be sure it's
// empty.
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
INLINE bool WeakKeyHashMap<Key, Value>::
is_empty() const {
return (_num_entries == 0);
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::output
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
void WeakKeyHashMap<Key, Value>::
output(ostream &out) const {
out << "WeakKeyHashMap (" << _num_entries << " entries): [";
for (size_t i = 0; i < _table_size; ++i) {
if (get_exists_array()[i] == 0) {
out << " *";
} else {
out << " " << _table[i]._key;
size_t index = get_hash(_table[i]._key.get_orig());
if (index != i) {
// This was misplaced as the result of a hash conflict.
// Report how far off it is.
out << "(" << ((_table_size + i - index) & (_table_size - 1)) << ")";
}
}
}
out << " ]";
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::write
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
void WeakKeyHashMap<Key, Value>::
write(ostream &out) const {
output(out);
out << "\n";
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::validate
// Access: Public
// Description: Returns true if the internal table appears to be
// consistent, false if there are some internal errors.
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
bool WeakKeyHashMap<Key, Value>::
validate() const {
size_t count = 0;
for (size_t i = 0; i < _table_size; ++i) {
if (get_exists_array()[i] != 0) {
++count;
if (_table[i]._key.was_deleted()) {
continue;
}
size_t ideal_index = get_hash(_table[i]._key.get_orig());
size_t wants_index = ideal_index;
while (wants_index != i && get_exists_array()[i] != 0) {
wants_index = (wants_index + 1) & (_table_size - 1);
}
if (wants_index != i) {
util_cat.error()
<< "WeakKeyHashMap is invalid: key " << _table[i]._key
<< " should be in slot " << wants_index << " instead of "
<< i << " (ideal is " << ideal_index << ")\n";
write(util_cat.error(false));
return false;
}
}
}
if (count != _num_entries) {
util_cat.error()
<< "WeakKeyHashMap is invalid: reports " << _num_entries
<< " entries, actually has " << count << "\n";
write(util_cat.error(false));
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::get_hash
// Access: Private
// Description: Computes an appropriate index number to store the
// given pointer.
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
INLINE size_t WeakKeyHashMap<Key, Value>::
get_hash(const Key *key) const {
/*
// We want a hash constant 0 < k < 1. This one is suggested by
// Knuth:
static const double hash_constant = (sqrt(5.0) - 1.0) / 2.0;
double f = ((double)(size_t)key * hash_constant);
f -= floor(f);
return (size_t)floor(f * _table_size);
*/
return (((size_t)key * (size_t)9973) >> 8) & (_table_size - 1);
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::is_element
// Access: Private
// Description: Returns true if element n matches key.
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
INLINE bool WeakKeyHashMap<Key, Value>::
is_element(int n, const Key *key) const {
nassertr(has_element(n), false);
return _table[n]._key == key;
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::store_new_element
// Access: Private
// Description: Constructs a new TableEntry at position n, storing
// the indicated key and value.
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
INLINE void WeakKeyHashMap<Key, Value>::
store_new_element(int n, const Key *key, const Value &data) {
if (get_exists_array()[n] != 0) {
// There was already an element in this spot. This can happen
// if it was a pointer that had already been deleted.
nassertv(_table[n]._key.was_deleted());
_table[n].~TableEntry();
--_num_entries;
}
new(&_table[n]) TableEntry(key, data);
get_exists_array()[n] = true;
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::clear_element
// Access: Private
// Description: Destructs the TableEntry at position n.
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
INLINE void WeakKeyHashMap<Key, Value>::
clear_element(int n) {
_table[n].~TableEntry();
get_exists_array()[n] = false;
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::get_exists_array
// Access: Private
// Description: Returns the beginning of the array of _table_size
// unsigned chars that are the boolean flags for whether
// each element exists (has been constructed) within the
// table.
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
INLINE unsigned char *WeakKeyHashMap<Key, Value>::
get_exists_array() const {
return (unsigned char *)(_table + _table_size);
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::new_table
// Access: Private
// Description: Allocates a brand new table.
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
void WeakKeyHashMap<Key, Value>::
new_table() {
nassertv(_table_size == 0 && _num_entries == 0);
// Pick a good initial table size. For now, we make it really
// small. Maybe that's the right answer.
_table_size = 4;
// We allocate enough bytes for _table_size elements of TableEntry,
// plus _table_size more bytes at the end (for the exists array).
size_t alloc_size = _table_size * sizeof(TableEntry) + _table_size;
_deleted_chain = memory_hook->get_deleted_chain(alloc_size);
_table = (TableEntry *)_deleted_chain->allocate(alloc_size, TypeHandle::none());
memset(get_exists_array(), 0, _table_size);
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::consider_expand_table
// Access: Private
// Description: Expands the table if it will need it (assuming one
// more element is about to be added). Returns true if
// the table was modified, false otherwise.
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
INLINE bool WeakKeyHashMap<Key, Value>::
consider_expand_table() {
if (_num_entries >= (_table_size >> 1)) {
// Actually, first, we should see if there are any deleted pointers.
// Clean those up and see how much space we save.
for (size_t i = 0; i < _table_size; ++i) {
if (get_exists_array()[i] != 0 && _table[i]._key.was_deleted()) {
remove_element(i);
}
}
if (_num_entries >= (_table_size >> 1)) {
// Still not enough space.
expand_table();
}
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////
// Function: WeakKeyHashMap::expand_table
// Access: Private
// Description: Doubles the size of the existing table.
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
void WeakKeyHashMap<Key, Value>::
expand_table() {
nassertv(_table_size != 0);
WeakKeyHashMap<Key, Value> old_map;
swap(old_map);
// Double the table size.
size_t old_table_size = old_map._table_size;
_table_size = (old_table_size << 1);
nassertv(_table == NULL);
// We allocate enough bytes for _table_size elements of TableEntry,
// plus _table_size more bytes at the end (for the exists array).
size_t alloc_size = _table_size * sizeof(TableEntry) + _table_size;
_deleted_chain = memory_hook->get_deleted_chain(alloc_size);
_table = (TableEntry *)_deleted_chain->allocate(alloc_size, TypeHandle::none());
unsigned char *exists_array = get_exists_array();
memset(exists_array, 0, _table_size);
nassertv(_num_entries == 0);
// Now copy the entries from the old table into the new table.
int num_added = 0;
for (size_t i = 0; i < old_table_size; ++i) {
if (old_map.has_element(i)) {
size_t new_index = get_hash(old_map._table[i]._key.get_orig());
while (exists_array[new_index] != 0) {
// Hash conflict; look for a better spot. This has to succeed.
new_index = (new_index + 1) & (_table_size - 1);
}
#ifdef USE_MOVE_SEMANTICS
// Use C++11 rvalue references to invoke the move constructor,
// which may be more efficient.
new(&_table[new_index]) TableEntry(move(old_map._table[i]));
#else
new(&_table[new_index]) TableEntry(old_map._table[i]);
#endif
exists_array[new_index] = true;
++_num_entries;
}
}
nassertv(validate());
nassertv(old_map.validate());
nassertv(_num_entries == old_map._num_entries);
}

View File

@ -0,0 +1,15 @@
// Filename: weakKeyHashMap.cxx
// Created by: rdb (13Jul15)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "weakKeyHashMap.h"

View File

@ -0,0 +1,115 @@
// Filename: weakKeyHashMap.h
// Created by: rdb (13Jul15)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef WEAKKEYHASHMAP_H
#define WEAKKEYHASHMAP_H
#include "pandabase.h"
#include "pvector.h"
#include "config_util.h"
#include "weakPointerTo.h"
////////////////////////////////////////////////////////////////////
// Class : WeakKeyHashMap
// Description : This is a variation on WeakKeyHashMap that stores
// weak pointers as keys, and automatically frees up
// entries from the map when the associated key has
// been deleted.
//
// This is more efficient than using a naive map of
// WeakPointerTo keys since that would incur the cost
// of constructing a weak reference every time a find
// operation is used.
////////////////////////////////////////////////////////////////////
template<class Key, class Value>
class WeakKeyHashMap {
public:
#ifndef CPPPARSER
INLINE WeakKeyHashMap();
INLINE ~WeakKeyHashMap();
INLINE void swap(WeakKeyHashMap &other);
int find(const Key *key) const;
int store(const Key *key, const Value &data);
INLINE bool remove(const Key *key);
void clear();
INLINE Value &operator [] (const Key *key);
INLINE int get_size() const;
INLINE bool has_element(int n) const;
INLINE const Key *get_key(int n) const;
INLINE const Value &get_data(int n) const;
INLINE Value &modify_data(int n);
INLINE void set_data(int n, const Value &data);
#ifdef USE_MOVE_SEMANTICS
INLINE void set_data(int n, Value &&data);
#endif
void remove_element(int n);
INLINE int get_num_entries() const;
INLINE bool is_empty() const;
void output(ostream &out) const;
void write(ostream &out) const;
bool validate() const;
private:
INLINE size_t get_hash(const Key *key) const;
INLINE bool is_element(int n, const Key *key) const;
INLINE void store_new_element(int n, const Key *key, const Value &data);
INLINE void clear_element(int n);
INLINE unsigned char *get_exists_array() const;
void new_table();
INLINE bool consider_expand_table();
void expand_table();
class TableEntry {
public:
INLINE TableEntry(const Key *key, const Value &data) :
_key(key),
_data(data) {}
INLINE TableEntry(const TableEntry &copy) :
_key(copy._key),
_data(copy._data) {}
#ifdef USE_MOVE_SEMANTICS
INLINE TableEntry(TableEntry &&from) NOEXCEPT :
_key(move(from._key)),
_data(move(from._data)) {}
#endif
WCPT(Key) _key;
Value _data;
};
TableEntry *_table;
DeletedBufferChain *_deleted_chain;
size_t _table_size;
size_t _num_entries;
#endif // CPPPARSER
};
template<class Key, class Value>
inline ostream &operator << (ostream &out, const WeakKeyHashMap<Key, Value> &shm) {
shm.output(out);
return out;
}
#ifndef CPPPARSER
#include "weakKeyHashMap.I"
#endif // CPPPARSER
#endif