mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-03 10:22:45 -04:00
progress on multithreaded Panda
This commit is contained in:
parent
08693413e8
commit
c3d8f81581
@ -188,7 +188,13 @@ ConfigVariableBool auto_break_cycles
|
||||
PRC_DESC("Set this true to automatically detect and break reference-count "
|
||||
"cycles in the TransformState and RenderState caches. When this "
|
||||
"is false, you must explicitly call TransformState.clear_cache() "
|
||||
"from time to time to prevent gradual memory bloat."));
|
||||
"from time to time to prevent gradual memory bloat. This has "
|
||||
"no meaning when garbage-collect-states is true."));
|
||||
|
||||
ConfigVariableBool garbage_collect_states
|
||||
("garbage-collect-states", false,
|
||||
PRC_DESC("This temporary config variable is used for development only. "
|
||||
"Do not set!"));
|
||||
|
||||
ConfigVariableBool transform_cache
|
||||
("transform-cache", true,
|
||||
|
@ -44,6 +44,7 @@ extern ConfigVariableBool compose_componentwise;
|
||||
extern ConfigVariableBool uniquify_matrix;
|
||||
extern ConfigVariableBool paranoid_const;
|
||||
extern ConfigVariableBool auto_break_cycles;
|
||||
extern ConfigVariableBool garbage_collect_states;
|
||||
extern ConfigVariableBool transform_cache;
|
||||
extern ConfigVariableBool state_cache;
|
||||
extern ConfigVariableBool uniquify_transforms;
|
||||
|
@ -144,6 +144,11 @@ cull_callback(CullTraverser *, const CullTraverserData &) const {
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool RenderAttrib::
|
||||
unref() const {
|
||||
// This is flawed, but this is development only.
|
||||
if (garbage_collect_states) {
|
||||
return ReferenceCount::unref();
|
||||
}
|
||||
|
||||
// We always have to grab the lock, since we will definitely need to
|
||||
// be holding it if we happen to drop the reference count to 0.
|
||||
LightReMutexHolder holder(*_attribs_lock);
|
||||
|
@ -419,11 +419,9 @@ compose(const RenderState *other) const {
|
||||
return this;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (!state_cache) {
|
||||
return do_compose(other);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
LightReMutexHolder holder(*_states_lock);
|
||||
|
||||
@ -516,11 +514,9 @@ invert_compose(const RenderState *other) const {
|
||||
return make_empty();
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (!state_cache) {
|
||||
return do_invert_compose(other);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
LightReMutexHolder holder(*_states_lock);
|
||||
|
||||
@ -706,6 +702,11 @@ adjust_all_priorities(int adjustment) const {
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool RenderState::
|
||||
unref() const {
|
||||
// This is flawed, but this is development only.
|
||||
if (garbage_collect_states) {
|
||||
return ReferenceCount::unref();
|
||||
}
|
||||
|
||||
// We always have to grab the lock, since we will definitely need to
|
||||
// be holding it if we happen to drop the reference count to 0.
|
||||
LightReMutexHolder holder(*_states_lock);
|
||||
@ -1460,11 +1461,9 @@ CPT(RenderState) RenderState::
|
||||
return_unique(RenderState *state) {
|
||||
nassertr(state != (RenderState *)NULL, state);
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (!state_cache) {
|
||||
return state;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (paranoid_const) {
|
||||
|
@ -579,10 +579,6 @@ set_shear2d(float shear) const {
|
||||
////////////////////////////////////////////////////////////////////
|
||||
CPT(TransformState) TransformState::
|
||||
compose(const TransformState *other) const {
|
||||
// This method isn't strictly const, because it updates the cache,
|
||||
// but we pretend that it is because it's only a cache which is
|
||||
// transparent to the rest of the interface.
|
||||
|
||||
// We handle identity as a trivial special case.
|
||||
if (is_identity()) {
|
||||
return other;
|
||||
@ -599,72 +595,37 @@ compose(const TransformState *other) const {
|
||||
return other;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (!transform_cache) {
|
||||
return do_compose(other);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
LightReMutexHolder holder(*_states_lock);
|
||||
|
||||
// Is this composition already cached?
|
||||
CPT(TransformState) result;
|
||||
{
|
||||
LightReMutexHolder holder(*_states_lock);
|
||||
int index = _composition_cache.find(other);
|
||||
if (index != -1) {
|
||||
Composition &comp = ((TransformState *)this)->_composition_cache.modify_data(index);
|
||||
if (comp._result == (const TransformState *)NULL) {
|
||||
// Well, it wasn't cached already, but we already had an entry
|
||||
// (probably created for the reverse direction), so use the same
|
||||
// entry to store the new result.
|
||||
CPT(TransformState) result = do_compose(other);
|
||||
comp._result = result;
|
||||
|
||||
if (result != (const TransformState *)this) {
|
||||
// See the comments below about the need to up the reference
|
||||
// count only when the result is not the same as this.
|
||||
result->cache_ref();
|
||||
const Composition &comp = _composition_cache.get_data(index);
|
||||
result = comp._result;
|
||||
}
|
||||
}
|
||||
// Here's the cache!
|
||||
if (result != (TransformState *)NULL) {
|
||||
_cache_stats.inc_hits();
|
||||
return comp._result;
|
||||
}
|
||||
_cache_stats.inc_misses();
|
||||
|
||||
// We need to make a new cache entry, both in this object and in the
|
||||
// other object. We make both records so the other TransformState
|
||||
// object will know to delete the entry from this object when it
|
||||
// destructs, and vice-versa.
|
||||
|
||||
// The cache entry in this object is the only one that indicates the
|
||||
// result; the other will be NULL for now.
|
||||
CPT(TransformState) result = do_compose(other);
|
||||
|
||||
_cache_stats.add_total_size(1);
|
||||
_cache_stats.inc_adds(_composition_cache.get_size() == 0);
|
||||
|
||||
((TransformState *)this)->_composition_cache[other]._result = result;
|
||||
|
||||
if (other != this) {
|
||||
_cache_stats.add_total_size(1);
|
||||
_cache_stats.inc_adds(other->_composition_cache.get_size() == 0);
|
||||
((TransformState *)other)->_composition_cache[this]._result = NULL;
|
||||
}
|
||||
|
||||
if (result != (const TransformState *)this) {
|
||||
// If the result of compose() is something other than this,
|
||||
// explicitly increment the reference count. We have to be sure
|
||||
// to decrement it again later, when the composition entry is
|
||||
// removed from the cache.
|
||||
result->cache_ref();
|
||||
|
||||
// (If the result was just this again, we still store the
|
||||
// result, but we don't increment the reference count, since
|
||||
// that would be a self-referential leak.)
|
||||
}
|
||||
|
||||
_cache_stats.maybe_report("TransformState");
|
||||
|
||||
if (result != (TransformState *)NULL) {
|
||||
// Success!
|
||||
return result;
|
||||
}
|
||||
|
||||
// Not in the cache. Compute a new result. It's important that we
|
||||
// don't hold the lock while we do this, or we lose the benefit of
|
||||
// parallelization.
|
||||
result = do_compose(other);
|
||||
|
||||
// It's OK to cast away the constness of this pointer, because the
|
||||
// cache is a transparent property of the class.
|
||||
return ((TransformState *)this)->store_compose(other, result);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
@ -704,69 +665,38 @@ invert_compose(const TransformState *other) const {
|
||||
return make_identity();
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (!transform_cache) {
|
||||
return do_invert_compose(other);
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
LightReMutexHolder holder(*_states_lock);
|
||||
|
||||
// Is this composition already cached?
|
||||
CPT(TransformState) result;
|
||||
{
|
||||
LightReMutexHolder holder(*_states_lock);
|
||||
int index = _invert_composition_cache.find(other);
|
||||
if (index != -1) {
|
||||
Composition &comp = ((TransformState *)this)->_invert_composition_cache.modify_data(index);
|
||||
if (comp._result == (const TransformState *)NULL) {
|
||||
// Well, it wasn't cached already, but we already had an entry
|
||||
// (probably created for the reverse direction), so use the same
|
||||
// entry to store the new result.
|
||||
CPT(TransformState) result = do_invert_compose(other);
|
||||
comp._result = result;
|
||||
|
||||
if (result != (const TransformState *)this) {
|
||||
// See the comments below about the need to up the reference
|
||||
// count only when the result is not the same as this.
|
||||
result->cache_ref();
|
||||
const Composition &comp = _invert_composition_cache.get_data(index);
|
||||
result = comp._result;
|
||||
}
|
||||
}
|
||||
// Here's the cache!
|
||||
if (result != (TransformState *)NULL) {
|
||||
_cache_stats.inc_hits();
|
||||
return comp._result;
|
||||
}
|
||||
_cache_stats.inc_misses();
|
||||
|
||||
// We need to make a new cache entry, both in this object and in the
|
||||
// other object. We make both records so the other TransformState
|
||||
// object will know to delete the entry from this object when it
|
||||
// destructs, and vice-versa.
|
||||
|
||||
// The cache entry in this object is the only one that indicates the
|
||||
// result; the other will be NULL for now.
|
||||
CPT(TransformState) result = do_invert_compose(other);
|
||||
|
||||
_cache_stats.add_total_size(1);
|
||||
_cache_stats.inc_adds(_invert_composition_cache.get_size() == 0);
|
||||
((TransformState *)this)->_invert_composition_cache[other]._result = result;
|
||||
|
||||
if (other != this) {
|
||||
_cache_stats.add_total_size(1);
|
||||
_cache_stats.inc_adds(other->_invert_composition_cache.get_size() == 0);
|
||||
((TransformState *)other)->_invert_composition_cache[this]._result = NULL;
|
||||
}
|
||||
|
||||
if (result != (const TransformState *)this) {
|
||||
// If the result of compose() is something other than this,
|
||||
// explicitly increment the reference count. We have to be sure
|
||||
// to decrement it again later, when the composition entry is
|
||||
// removed from the cache.
|
||||
result->cache_ref();
|
||||
|
||||
// (If the result was just this again, we still store the
|
||||
// result, but we don't increment the reference count, since
|
||||
// that would be a self-referential leak.)
|
||||
}
|
||||
|
||||
if (result != (TransformState *)NULL) {
|
||||
// Success!
|
||||
return result;
|
||||
}
|
||||
|
||||
// Not in the cache. Compute a new result. It's important that we
|
||||
// don't hold the lock while we do this, or we lose the benefit of
|
||||
// parallelization.
|
||||
result = do_invert_compose(other);
|
||||
|
||||
// It's OK to cast away the constness of this pointer, because the
|
||||
// cache is a transparent property of the class.
|
||||
return ((TransformState *)this)->store_invert_compose(other, result);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
@ -781,6 +711,11 @@ invert_compose(const TransformState *other) const {
|
||||
////////////////////////////////////////////////////////////////////
|
||||
bool TransformState::
|
||||
unref() const {
|
||||
// This is flawed, but this is development only.
|
||||
if (garbage_collect_states) {
|
||||
return ReferenceCount::unref();
|
||||
}
|
||||
|
||||
// We always have to grab the lock, since we will definitely need to
|
||||
// be holding it if we happen to drop the reference count to 0.
|
||||
LightReMutexHolder holder(*_states_lock);
|
||||
@ -1526,11 +1461,9 @@ CPT(TransformState) TransformState::
|
||||
return_unique(TransformState *state) {
|
||||
nassertr(state != (TransformState *)NULL, state);
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (!transform_cache) {
|
||||
return state;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (paranoid_const) {
|
||||
@ -1647,6 +1580,158 @@ do_compose(const TransformState *other) const {
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: TransformState::store_compose
|
||||
// Access: Private
|
||||
// Description: Stores the result of a composition in the cache.
|
||||
// Returns the stored result (it may be a different
|
||||
// object than the one passed in, due to another thread
|
||||
// having computed the composition first).
|
||||
////////////////////////////////////////////////////////////////////
|
||||
CPT(TransformState) TransformState::
|
||||
store_compose(const TransformState *other, const TransformState *result) {
|
||||
// Identity should have already been screened.
|
||||
nassertr(!is_identity(), other);
|
||||
nassertr(!other->is_identity(), this);
|
||||
|
||||
// So should have validity.
|
||||
nassertr(!is_invalid(), this);
|
||||
nassertr(!other->is_invalid(), other);
|
||||
|
||||
LightReMutexHolder holder(*_states_lock);
|
||||
|
||||
// Is this composition already cached?
|
||||
int index = _composition_cache.find(other);
|
||||
if (index != -1) {
|
||||
Composition &comp = _composition_cache.modify_data(index);
|
||||
if (comp._result == (const TransformState *)NULL) {
|
||||
// Well, it wasn't cached already, but we already had an entry
|
||||
// (probably created for the reverse direction), so use the same
|
||||
// entry to store the new result.
|
||||
comp._result = result;
|
||||
|
||||
if (result != (const TransformState *)this) {
|
||||
// See the comments below about the need to up the reference
|
||||
// count only when the result is not the same as this.
|
||||
result->cache_ref();
|
||||
}
|
||||
}
|
||||
// Here's the cache!
|
||||
_cache_stats.inc_hits();
|
||||
return comp._result;
|
||||
}
|
||||
_cache_stats.inc_misses();
|
||||
|
||||
// We need to make a new cache entry, both in this object and in the
|
||||
// other object. We make both records so the other TransformState
|
||||
// object will know to delete the entry from this object when it
|
||||
// destructs, and vice-versa.
|
||||
|
||||
// The cache entry in this object is the only one that indicates the
|
||||
// result; the other will be NULL for now.
|
||||
_cache_stats.add_total_size(1);
|
||||
_cache_stats.inc_adds(_composition_cache.get_size() == 0);
|
||||
|
||||
_composition_cache[other]._result = result;
|
||||
|
||||
if (other != this) {
|
||||
_cache_stats.add_total_size(1);
|
||||
_cache_stats.inc_adds(other->_composition_cache.get_size() == 0);
|
||||
((TransformState *)other)->_composition_cache[this]._result = NULL;
|
||||
}
|
||||
|
||||
if (result != (TransformState *)this) {
|
||||
// If the result of do_compose() is something other than this,
|
||||
// explicitly increment the reference count. We have to be sure
|
||||
// to decrement it again later, when the composition entry is
|
||||
// removed from the cache.
|
||||
result->cache_ref();
|
||||
|
||||
// (If the result was just this again, we still store the
|
||||
// result, but we don't increment the reference count, since
|
||||
// that would be a self-referential leak.)
|
||||
}
|
||||
|
||||
_cache_stats.maybe_report("TransformState");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: TransformState::store_invert_compose
|
||||
// Access: Private
|
||||
// Description: Stores the result of a composition in the cache.
|
||||
// Returns the stored result (it may be a different
|
||||
// object than the one passed in, due to another thread
|
||||
// having computed the composition first).
|
||||
////////////////////////////////////////////////////////////////////
|
||||
CPT(TransformState) TransformState::
|
||||
store_invert_compose(const TransformState *other, const TransformState *result) {
|
||||
// Identity should have already been screened.
|
||||
nassertr(!is_identity(), other);
|
||||
|
||||
// So should have validity.
|
||||
nassertr(!is_invalid(), this);
|
||||
nassertr(!other->is_invalid(), other);
|
||||
|
||||
nassertr(other != this, make_identity());
|
||||
|
||||
LightReMutexHolder holder(*_states_lock);
|
||||
|
||||
// Is this composition already cached?
|
||||
int index = _invert_composition_cache.find(other);
|
||||
if (index != -1) {
|
||||
Composition &comp = ((TransformState *)this)->_invert_composition_cache.modify_data(index);
|
||||
if (comp._result == (const TransformState *)NULL) {
|
||||
// Well, it wasn't cached already, but we already had an entry
|
||||
// (probably created for the reverse direction), so use the same
|
||||
// entry to store the new result.
|
||||
comp._result = result;
|
||||
|
||||
if (result != (const TransformState *)this) {
|
||||
// See the comments below about the need to up the reference
|
||||
// count only when the result is not the same as this.
|
||||
result->cache_ref();
|
||||
}
|
||||
}
|
||||
// Here's the cache!
|
||||
_cache_stats.inc_hits();
|
||||
return comp._result;
|
||||
}
|
||||
_cache_stats.inc_misses();
|
||||
|
||||
// We need to make a new cache entry, both in this object and in the
|
||||
// other object. We make both records so the other TransformState
|
||||
// object will know to delete the entry from this object when it
|
||||
// destructs, and vice-versa.
|
||||
|
||||
// The cache entry in this object is the only one that indicates the
|
||||
// result; the other will be NULL for now.
|
||||
_cache_stats.add_total_size(1);
|
||||
_cache_stats.inc_adds(_invert_composition_cache.get_size() == 0);
|
||||
_invert_composition_cache[other]._result = result;
|
||||
|
||||
if (other != this) {
|
||||
_cache_stats.add_total_size(1);
|
||||
_cache_stats.inc_adds(other->_invert_composition_cache.get_size() == 0);
|
||||
((TransformState *)other)->_invert_composition_cache[this]._result = NULL;
|
||||
}
|
||||
|
||||
if (result != (TransformState *)this) {
|
||||
// If the result of compose() is something other than this,
|
||||
// explicitly increment the reference count. We have to be sure
|
||||
// to decrement it again later, when the composition entry is
|
||||
// removed from the cache.
|
||||
result->cache_ref();
|
||||
|
||||
// (If the result was just this again, we still store the
|
||||
// result, but we don't increment the reference count, since
|
||||
// that would be a self-referential leak.)
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: TransformState::do_invert_compose
|
||||
// Access: Private
|
||||
|
@ -234,7 +234,9 @@ private:
|
||||
static CPT(TransformState) return_unique(TransformState *state);
|
||||
|
||||
CPT(TransformState) do_compose(const TransformState *other) const;
|
||||
CPT(TransformState) store_compose(const TransformState *other, const TransformState *result);
|
||||
CPT(TransformState) do_invert_compose(const TransformState *other) const;
|
||||
CPT(TransformState) store_invert_compose(const TransformState *other, const TransformState *result);
|
||||
static bool r_detect_cycles(const TransformState *start_state,
|
||||
const TransformState *current_state,
|
||||
int length, UpdateSeq this_seq,
|
||||
|
@ -213,16 +213,20 @@ load_gridded_models(WindowFramework *window,
|
||||
// Load up all the files indicated in the list of gridded filenames
|
||||
// and store them in the given vector.
|
||||
|
||||
Loader loader;
|
||||
LoaderOptions options;
|
||||
options.set_flags(options.get_flags() | LoaderOptions::LF_no_ram_cache);
|
||||
|
||||
// First, load up each model from disk once, and store them all
|
||||
// separate from the scene graph. Also count up the total number of
|
||||
// models we'll be putting in the grid.
|
||||
int grid_count = 0;
|
||||
NodePath models("models");
|
||||
GriddedFilenames::iterator fi;
|
||||
for (fi = filenames.begin(); fi != filenames.end(); ++fi) {
|
||||
GriddedFilename &gf = (*fi);
|
||||
gf._model = window->load_model(models, gf._filename);
|
||||
if (!gf._model.is_empty()) {
|
||||
PT(PandaNode) node = loader.load_sync(gf._filename, options);
|
||||
if (node != (PandaNode *)NULL) {
|
||||
gf._model = NodePath(node);
|
||||
grid_count += gf._count;
|
||||
}
|
||||
}
|
||||
@ -254,7 +258,7 @@ load_gridded_models(WindowFramework *window,
|
||||
int passnum = 0;
|
||||
bool loaded_any;
|
||||
|
||||
NodePath render = window->get_render();
|
||||
NodePath root = window->get_panda_framework()->get_models();
|
||||
do {
|
||||
loaded_any = false;
|
||||
|
||||
@ -265,9 +269,15 @@ load_gridded_models(WindowFramework *window,
|
||||
// Copy this model into the scene graph, and assign it a
|
||||
// position on the grid.
|
||||
|
||||
string model_name = format_string(++model_count);
|
||||
NodePath model = render.attach_new_node(model_name);
|
||||
gf._model.copy_to(model);
|
||||
++model_count;
|
||||
PT(PandaNode) node = loader.load_sync(gf._filename, options);
|
||||
NodePath model;
|
||||
if (node == (PandaNode *)NULL) {
|
||||
model = gf._model.copy_to(NodePath());
|
||||
} else {
|
||||
model = NodePath(node);
|
||||
}
|
||||
model.reparent_to(root);
|
||||
|
||||
gridded_file_info info;
|
||||
info.node = model.node();
|
||||
@ -369,14 +379,6 @@ load_gridded_models(WindowFramework *window,
|
||||
|
||||
passnum++;
|
||||
} while (loaded_any);
|
||||
|
||||
// Finally, remove the source models we loaded up. Not a real big deal.
|
||||
for (fi = filenames.begin(); fi != filenames.end(); ++fi) {
|
||||
GriddedFilename &gf = (*fi);
|
||||
if (!gf._model.is_empty()) {
|
||||
gf._model.remove_node();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
@ -402,13 +404,15 @@ main(int argc, char *argv[]) {
|
||||
|
||||
window->enable_keyboard();
|
||||
window->setup_trackball();
|
||||
window->load_models(window->get_render(), static_filenames);
|
||||
window->load_models(framework.get_models(), static_filenames);
|
||||
framework.get_models().instance_to(window->get_render());
|
||||
|
||||
GriddedInfoArray info_arr;
|
||||
load_gridded_models(window, gridded_filenames, info_arr);
|
||||
|
||||
window->loop_animations();
|
||||
window->stagger_animations();
|
||||
window->center_trackball(framework.get_models());
|
||||
|
||||
framework.enable_default_keys();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user