diff --git a/direct/src/p3d/Packager.py b/direct/src/p3d/Packager.py index ccb8589d8c..784055b771 100644 --- a/direct/src/p3d/Packager.py +++ b/direct/src/p3d/Packager.py @@ -1848,7 +1848,7 @@ class Packager: def addEggFile(self, file): # Precompile egg files to bam's. - np = self.packager.loader.loadModel(file.filename) + np = self.packager.loader.loadModel(file.filename, self.packager.loaderOptions) if not np: raise Exception('Could not read egg file %s' % (file.filename)) @@ -1862,6 +1862,8 @@ class Packager: if not bamFile.openRead(file.filename): raise Exception('Could not read bam file %s' % (file.filename)) + bamFile.getReader().setLoaderOptions(self.packager.loaderOptions) + if not bamFile.resolve(): raise Exception('Could not resolve bam file %s' % (file.filename)) @@ -2492,6 +2494,14 @@ class Packager: self.sfxManagerList = None self.musicManager = None + # These options will be used when loading models and textures. By + # default we don't load textures beyond the header and don't store + # models in the RAM cache in order to conserve on memory usage. + opts = LoaderOptions() + opts.setFlags(opts.getFlags() | LoaderOptions.LFNoRamCache) + opts.setTextureFlags(opts.getTextureFlags() & ~LoaderOptions.TFPreload) + self.loaderOptions = opts + # This is filled in during readPackageDef(). self.packageList = [] diff --git a/direct/src/p3d/panda3d.pdef b/direct/src/p3d/panda3d.pdef index 5a993a1ada..3edf48f8d2 100644 --- a/direct/src/p3d/panda3d.pdef +++ b/direct/src/p3d/panda3d.pdef @@ -462,6 +462,8 @@ class packp3d(p3d): mainModule('direct.p3d.packp3d') + file('packp3d.prc', extract = True, text = "preload-textures false") + class ppackage(p3d): # As above, a packaging utility. This is the fully-general ppackage @@ -475,6 +477,8 @@ class ppackage(p3d): mainModule('direct.p3d.ppackage') + file('ppackage.prc', extract = True, text = "preload-textures false") + class ppatcher(p3d): # A handy utility to go along with ppackage. This builds diff --git a/direct/src/showbase/Loader.py b/direct/src/showbase/Loader.py index b22de7400b..24773b776b 100644 --- a/direct/src/showbase/Loader.py +++ b/direct/src/showbase/Loader.py @@ -909,7 +909,7 @@ class Loader(DirectObject): orig = origModelList[i].node() flat = models[i].node() orig.copyAllProperties(flat) - orig.replaceNode(flat) + flat.replaceNode(orig) if callback: if gotList: diff --git a/doc/ReleaseNotes b/doc/ReleaseNotes index f3bebcc191..37f755d200 100644 --- a/doc/ReleaseNotes +++ b/doc/ReleaseNotes @@ -6,6 +6,7 @@ This issue fixes several bugs that were still found in 1.9.2. * Fix crash using wx/tkinter on Mac as long as want-wx/tk is set * Fix loading models from 'models' package with models/ prefix * Fix random crashes in task system +* Fix various race conditions causing threading issues * Fix memory leaks in BulletTriangleMesh * Fix loading old models with MovingPart * Improve performance of CPU vertex animation somewhat @@ -26,12 +27,13 @@ This issue fixes several bugs that were still found in 1.9.2. * Fix issues with certain Cg shader inputs in DX9 * Support uint8 index buffers in DX9 * Fix occasional frame lag when loading a big model asynchronously -* Fix race condition reading string config var * Fix interrogate parsing issue with "const static" * Add back missing libp3pystub.a to Mac OS X SDK * Fix RAM caching of 2D texture arrays * Fix Ctrl+C interrupt propagation to runtime applications * Support for InvSphere, Box and Tube solids in bam2egg +* Add normalized() method to vectors +* asyncFlattenStrong with inPlace=True caused node to disappear ------------------------ RELEASE 1.9.2 ------------------------ diff --git a/panda/src/ode/odeTriMeshData.cxx b/panda/src/ode/odeTriMeshData.cxx index c9debb3f2c..afae75b753 100644 --- a/panda/src/ode/odeTriMeshData.cxx +++ b/panda/src/ode/odeTriMeshData.cxx @@ -54,14 +54,19 @@ print_data(const string &marker) { void OdeTriMeshData:: remove_data(OdeTriMeshData *data) { - odetrimeshdata_cat.debug() << get_class_type() << "::remove_data(" << data->get_id() << ")" << "\n"; - nassertv(_tri_mesh_data_map != (TriMeshDataMap *)NULL); - TriMeshDataMap::iterator iter; + if (odetrimeshdata_cat.is_debug()) { + odetrimeshdata_cat.debug() + << get_class_type() << "::remove_data(" << data->get_id() << ")" << "\n"; + } + if (_tri_mesh_data_map == (TriMeshDataMap *)NULL) { + return; + } + TriMeshDataMap::iterator iter; for (iter = _tri_mesh_data_map->begin(); iter != _tri_mesh_data_map->end(); ++iter) { - if ( iter->second == data ) { + if (iter->second == data) { break; } } @@ -72,7 +77,7 @@ remove_data(OdeTriMeshData *data) { for (iter = _tri_mesh_data_map->begin(); iter != _tri_mesh_data_map->end(); ++iter) { - if ( iter->second == data ) { + if (iter->second == data) { break; } } @@ -150,8 +155,13 @@ operator = (const OdeTriMeshData &other) { void OdeTriMeshData:: process_model(const NodePath& model, bool &use_normals) { // TODO: assert if _vertices is something other than 0. - ostream &out = odetrimeshdata_cat.debug(); - out << "process_model(" << model << ")" << "\n"; + if (odetrimeshdata_cat.is_debug()) { + odetrimeshdata_cat.debug() + << "process_model(" << model << ")" << "\n"; + } + if (model.is_empty()) { + return; + } NodePathCollection geomNodePaths = model.find_all_matches("**/+GeomNode"); if (model.node()->get_type() == GeomNode::get_class_type()) { diff --git a/panda/src/putil/copyOnWriteObject.I b/panda/src/putil/copyOnWriteObject.I index c8727d8e7c..304e7214c5 100644 --- a/panda/src/putil/copyOnWriteObject.I +++ b/panda/src/putil/copyOnWriteObject.I @@ -66,13 +66,22 @@ operator = (const CopyOnWriteObject ©) { #ifdef COW_THREADED /** - * See CachedTypedWritableReferenceCount::cache_ref(). + * @see CachedTypedWritableReferenceCount::cache_ref() */ INLINE void CopyOnWriteObject:: cache_ref() const { MutexHolder holder(_lock_mutex); CachedTypedWritableReferenceCount::cache_ref(); } + +/** + * @see CachedTypedWritableReferenceCount::cache_unref() + */ +INLINE bool CopyOnWriteObject:: +cache_unref() const { + MutexHolder holder(_lock_mutex); + return CachedTypedWritableReferenceCount::cache_unref(); +} #endif // COW_THREADED /** diff --git a/panda/src/putil/copyOnWriteObject.h b/panda/src/putil/copyOnWriteObject.h index 4799c727f1..8cada6bfd7 100644 --- a/panda/src/putil/copyOnWriteObject.h +++ b/panda/src/putil/copyOnWriteObject.h @@ -48,6 +48,7 @@ PUBLISHED: #ifdef COW_THREADED virtual bool unref() const; INLINE void cache_ref() const; + INLINE bool cache_unref() const; #endif // COW_THREADED protected: diff --git a/panda/src/putil/copyOnWritePointer.cxx b/panda/src/putil/copyOnWritePointer.cxx index 5ff01f1ccc..de0a2e8abf 100644 --- a/panda/src/putil/copyOnWritePointer.cxx +++ b/panda/src/putil/copyOnWritePointer.cxx @@ -12,7 +12,6 @@ */ #include "copyOnWritePointer.h" -#include "mutexHolder.h" #include "config_util.h" #include "config_pipeline.h" @@ -69,7 +68,7 @@ get_write_pointer() { Thread *current_thread = Thread::get_current_thread(); - MutexHolder holder(_cow_object->_lock_mutex); + _cow_object->_lock_mutex.acquire(); while (_cow_object->_lock_status == CopyOnWriteObject::LS_locked_write && _cow_object->_locking_thread != current_thread) { if (util_cat.is_debug()) { @@ -89,10 +88,20 @@ get_write_pointer() { << "Making copy of " << _cow_object->get_type() << " because it is locked in read mode.\n"; } + PT(CopyOnWriteObject) new_object = _cow_object->make_cow_copy(); - cache_unref_delete(_cow_object); + + // We can't call cache_unref_delete, because we hold the lock. + if (!_cow_object->CachedTypedWritableReferenceCount::cache_unref()) { + _cow_object->_lock_mutex.release(); + delete _cow_object; + } else { + _cow_object->_lock_mutex.release(); + } _cow_object = new_object; _cow_object->cache_ref(); + _cow_object->_lock_status = CopyOnWriteObject::LS_locked_write; + _cow_object->_locking_thread = current_thread; } else if (_cow_object->get_cache_ref_count() > 1) { // No one else has it specifically read-locked, but there are other @@ -106,9 +115,18 @@ get_write_pointer() { } PT(CopyOnWriteObject) new_object = _cow_object->make_cow_copy(); - cache_unref_delete(_cow_object); + + // We can't call cache_unref_delete, because we hold the lock. + if (!_cow_object->CachedTypedWritableReferenceCount::cache_unref()) { + _cow_object->_lock_mutex.release(); + delete _cow_object; + } else { + _cow_object->_lock_mutex.release(); + } _cow_object = new_object; _cow_object->cache_ref(); + _cow_object->_lock_status = CopyOnWriteObject::LS_locked_write; + _cow_object->_locking_thread = current_thread; } else { // No other thread has the pointer locked, and we're the only @@ -118,9 +136,10 @@ get_write_pointer() { // We can't assert that there are no outstanding ordinary references to // it, though, since the creator of the object might have saved himself a // reference. + _cow_object->_lock_status = CopyOnWriteObject::LS_locked_write; + _cow_object->_locking_thread = current_thread; + _cow_object->_lock_mutex.release(); } - _cow_object->_lock_status = CopyOnWriteObject::LS_locked_write; - _cow_object->_locking_thread = current_thread; return _cow_object; }