pgraph: Refactor RenderAttrib return_new code a little bit

- Allows the compiler to devirtualize the get_hash_impl call, in theory
- Reduces the overhead of checking the configuration variables every time in return_new
- Avoids recalculating the hash on calling get_unique
This commit is contained in:
rdb 2023-07-26 19:17:07 +02:00
parent c6f930a379
commit f5827963a3
6 changed files with 75 additions and 55 deletions

View File

@ -214,7 +214,7 @@ ConfigVariableBool transform_cache
"transforms, but imposes some overhead for maintaining the " "transforms, but imposes some overhead for maintaining the "
"cache itself.")); "cache itself."));
ConfigVariableBool state_cache ALIGN_16BYTE ConfigVariableBool state_cache
("state-cache", true, ("state-cache", true,
PRC_DESC("Set this true to enable the cache of RenderState objects, " PRC_DESC("Set this true to enable the cache of RenderState objects, "
"similar to the TransformState cache controlled via " "similar to the TransformState cache controlled via "
@ -236,7 +236,7 @@ ConfigVariableBool uniquify_states
"including the need to check for a composition cycle in " "including the need to check for a composition cycle in "
"the cache. It is highly recommended to keep this on.")); "the cache. It is highly recommended to keep this on."));
ConfigVariableBool uniquify_attribs ALIGN_16BYTE ConfigVariableBool uniquify_attribs
("uniquify-attribs", true, ("uniquify-attribs", true,
PRC_DESC("Set this true to ensure that equivalent RenderAttribs " PRC_DESC("Set this true to ensure that equivalent RenderAttribs "
"are pointerwise equal. This may improve caching performance, " "are pointerwise equal. This may improve caching performance, "

View File

@ -46,10 +46,10 @@ extern ConfigVariableBool auto_break_cycles;
extern EXPCL_PANDA_PGRAPH ConfigVariableBool garbage_collect_states; extern EXPCL_PANDA_PGRAPH ConfigVariableBool garbage_collect_states;
extern ConfigVariableDouble garbage_collect_states_rate; extern ConfigVariableDouble garbage_collect_states_rate;
extern ConfigVariableBool transform_cache; extern ConfigVariableBool transform_cache;
extern ConfigVariableBool state_cache; extern ALIGN_16BYTE EXPCL_PANDA_PGRAPH ConfigVariableBool state_cache;
extern ConfigVariableBool uniquify_transforms; extern ConfigVariableBool uniquify_transforms;
extern EXPCL_PANDA_PGRAPH ConfigVariableBool uniquify_states; extern EXPCL_PANDA_PGRAPH ConfigVariableBool uniquify_states;
extern ConfigVariableBool uniquify_attribs; extern ALIGN_16BYTE EXPCL_PANDA_PGRAPH ConfigVariableBool uniquify_attribs;
extern ConfigVariableBool retransform_sprites; extern ConfigVariableBool retransform_sprites;
extern ConfigVariableBool depth_offset_decals; extern ConfigVariableBool depth_offset_decals;
extern ConfigVariableInt max_collect_vertices; extern ConfigVariableInt max_collect_vertices;

View File

@ -79,7 +79,11 @@ get_hash() const {
*/ */
INLINE CPT(RenderAttrib) RenderAttrib:: INLINE CPT(RenderAttrib) RenderAttrib::
get_unique() const { get_unique() const {
return return_unique((RenderAttrib *)this); if (state_cache) {
return do_uniquify(this);
} else {
return this;
}
} }
/** /**
@ -93,6 +97,47 @@ calc_hash() {
_hash = int_hash::add_hash(hash, get_type().get_index()); _hash = int_hash::add_hash(hash, get_type().get_index());
} }
/**
* This function is used by derived RenderAttrib types to share a common
* RenderAttrib pointer for all equivalent RenderAttrib objects.
*
* This is different from return_unique() in that it does not actually
* guarantee a unique pointer, unless uniquify-attribs is set.
*/
INLINE CPT(RenderAttrib) RenderAttrib::
return_new(RenderAttrib *attrib) {
nassertr(attrib != nullptr, attrib);
attrib->calc_hash();
if (_uniquify_attribs) {
return do_uniquify(attrib);
} else {
return attrib;
}
}
/**
* This function is used by derived RenderAttrib types to share a common
* RenderAttrib pointer for all equivalent RenderAttrib objects.
*
* The make() function of the derived type should create a new RenderAttrib
* and pass it through return_new(), which will either save the pointer and
* return it unchanged (if this is the first similar such object) or delete it
* and return an equivalent pointer (if there was already a similar object
* saved).
*/
INLINE CPT(RenderAttrib) RenderAttrib::
return_unique(RenderAttrib *attrib) {
nassertr(attrib != nullptr, attrib);
attrib->calc_hash();
if (state_cache) {
return do_uniquify(attrib);
} else {
return attrib;
}
}
/** /**
* Adds the indicated TypeHandle to the registry, if it is not there already, * Adds the indicated TypeHandle to the registry, if it is not there already,
* and returns a unique slot number. See RenderAttribRegistry. * and returns a unique slot number. See RenderAttribRegistry.

View File

@ -14,7 +14,6 @@
#include "renderAttrib.h" #include "renderAttrib.h"
#include "bamReader.h" #include "bamReader.h"
#include "indent.h" #include "indent.h"
#include "config_pgraph.h"
#include "lightReMutexHolder.h" #include "lightReMutexHolder.h"
#include "pStatTimer.h" #include "pStatTimer.h"
@ -22,6 +21,7 @@ using std::ostream;
LightReMutex *RenderAttrib::_attribs_lock = nullptr; LightReMutex *RenderAttrib::_attribs_lock = nullptr;
RenderAttrib::Attribs RenderAttrib::_attribs; RenderAttrib::Attribs RenderAttrib::_attribs;
bool RenderAttrib::_uniquify_attribs = false;
TypeHandle RenderAttrib::_type_handle; TypeHandle RenderAttrib::_type_handle;
size_t RenderAttrib::_garbage_index = 0; size_t RenderAttrib::_garbage_index = 0;
@ -180,6 +180,10 @@ list_attribs(ostream &out) {
*/ */
int RenderAttrib:: int RenderAttrib::
garbage_collect() { garbage_collect() {
// This gets called periodically, so use this opportunity to reload the value
// from the Config.prc file.
_uniquify_attribs = uniquify_attribs && state_cache;
if (!garbage_collect_states) { if (!garbage_collect_states) {
return 0; return 0;
} }
@ -305,54 +309,15 @@ validate_attribs() {
} }
/** /**
* This function is used by derived RenderAttrib types to share a common * Private implementation of return_new, return_unique, and get_unique.
* RenderAttrib pointer for all equivalent RenderAttrib objects.
*
* This is different from return_unique() in that it does not actually
* guarantee a unique pointer, unless uniquify-attribs is set.
*/ */
CPT(RenderAttrib) RenderAttrib:: CPT(RenderAttrib) RenderAttrib::
return_new(RenderAttrib *attrib) { do_uniquify(const RenderAttrib *attrib) {
nassertr(attrib != nullptr, attrib);
if (!uniquify_attribs) {
attrib->calc_hash();
return attrib;
}
return return_unique(attrib);
}
/**
* This function is used by derived RenderAttrib types to share a common
* RenderAttrib pointer for all equivalent RenderAttrib objects.
*
* The make() function of the derived type should create a new RenderAttrib
* and pass it through return_new(), which will either save the pointer and
* return it unchanged (if this is the first similar such object) or delete it
* and return an equivalent pointer (if there was already a similar object
* saved).
*/
CPT(RenderAttrib) RenderAttrib::
return_unique(RenderAttrib *attrib) {
nassertr(attrib != nullptr, attrib);
attrib->calc_hash();
if (!state_cache) {
return attrib;
}
#ifndef NDEBUG
if (paranoid_const) {
nassertr(validate_attribs(), attrib);
}
#endif
LightReMutexHolder holder(*_attribs_lock); LightReMutexHolder holder(*_attribs_lock);
if (attrib->_saved_entry != -1) { if (attrib->_saved_entry != -1) {
// This attrib is already in the cache. nassertr(_attribs.find(attrib) // This attrib is already in the cache.
// == attrib->_saved_entry, attrib); //nassertr(_attribs.find(attrib) == attrib->_saved_entry, attrib);
return attrib; return attrib;
} }
@ -503,6 +468,12 @@ release_new() {
*/ */
void RenderAttrib:: void RenderAttrib::
init_attribs() { init_attribs() {
// These are copied here so that we can be sure that they are constructed.
ALIGN_16BYTE ConfigVariableBool uniquify_attribs("uniquify-attribs", true);
ALIGN_16BYTE ConfigVariableBool state_cache("state-cache", true);
_uniquify_attribs = uniquify_attribs && state_cache;
// TODO: we should have a global Panda mutex to allow us to safely create // TODO: we should have a global Panda mutex to allow us to safely create
// _attribs_lock without a startup race condition. For the meantime, this // _attribs_lock without a startup race condition. For the meantime, this
// is OK because we guarantee that this method is called at static init // is OK because we guarantee that this method is called at static init

View File

@ -16,6 +16,7 @@
#include "pandabase.h" #include "pandabase.h"
#include "config_pgraph.h"
#include "typedWritableReferenceCount.h" #include "typedWritableReferenceCount.h"
#include "renderAttribRegistry.h" #include "renderAttribRegistry.h"
#include "pointerTo.h" #include "pointerTo.h"
@ -161,8 +162,9 @@ PUBLISHED:
protected: protected:
INLINE void calc_hash(); INLINE void calc_hash();
static CPT(RenderAttrib) return_new(RenderAttrib *attrib); INLINE static CPT(RenderAttrib) return_new(RenderAttrib *attrib);
static CPT(RenderAttrib) return_unique(RenderAttrib *attrib); INLINE static CPT(RenderAttrib) return_unique(RenderAttrib *attrib);
static CPT(RenderAttrib) do_uniquify(const RenderAttrib *attrib);
virtual int compare_to_impl(const RenderAttrib *other) const; virtual int compare_to_impl(const RenderAttrib *other) const;
virtual size_t get_hash_impl() const; virtual size_t get_hash_impl() const;
virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const; virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
@ -184,8 +186,9 @@ private:
static LightReMutex *_attribs_lock; static LightReMutex *_attribs_lock;
typedef SimpleHashMap<const RenderAttrib *, std::nullptr_t, indirect_compare_to_hash<const RenderAttrib *> > Attribs; typedef SimpleHashMap<const RenderAttrib *, std::nullptr_t, indirect_compare_to_hash<const RenderAttrib *> > Attribs;
static Attribs _attribs; static Attribs _attribs;
static bool _uniquify_attribs;
int _saved_entry; mutable int _saved_entry;
size_t _hash; size_t _hash;
// This keeps track of our current position through the garbage collection // This keeps track of our current position through the garbage collection
@ -195,6 +198,7 @@ private:
static PStatCollector _garbage_collect_pcollector; static PStatCollector _garbage_collect_pcollector;
friend class RenderAttribRegistry; friend class RenderAttribRegistry;
friend class RenderState;
public: public:
virtual void write_datagram(BamWriter *manager, Datagram &dg); virtual void write_datagram(BamWriter *manager, Datagram &dg);

View File

@ -1287,8 +1287,8 @@ return_unique(RenderState *state) {
LightReMutexHolder holder(*_states_lock); LightReMutexHolder holder(*_states_lock);
if (state->_saved_entry != -1) { if (state->_saved_entry != -1) {
// This state is already in the cache. nassertr(_states.find(state) == // This state is already in the cache.
// state->_saved_entry, pt_state); //nassertr(_states.find(state) == state->_saved_entry, pt_state);
return state; return state;
} }
@ -1300,7 +1300,7 @@ return_unique(RenderState *state) {
while (slot >= 0) { while (slot >= 0) {
Attribute &attrib = state->_attributes[slot]; Attribute &attrib = state->_attributes[slot];
nassertd(attrib._attrib != nullptr) continue; nassertd(attrib._attrib != nullptr) continue;
attrib._attrib = attrib._attrib->get_unique(); attrib._attrib = RenderAttrib::do_uniquify(attrib._attrib);
mask.clear_bit(slot); mask.clear_bit(slot);
slot = mask.get_lowest_on_bit(); slot = mask.get_lowest_on_bit();
} }