diff --git a/dtool/Config.Irix.pp b/dtool/Config.Irix.pp index 45edc47183..57c1022582 100644 --- a/dtool/Config.Irix.pp +++ b/dtool/Config.Irix.pp @@ -93,3 +93,6 @@ // Do we have RTTI (and )? #define HAVE_RTTI 1 + +// Must global operator new and delete functions throw exceptions? +#define GLOBAL_OPERATOR_NEW_EXCEPTIONS diff --git a/dtool/Config.Linux.pp b/dtool/Config.Linux.pp index f929041c46..7494d7d65e 100644 --- a/dtool/Config.Linux.pp +++ b/dtool/Config.Linux.pp @@ -93,3 +93,9 @@ // Do we have RTTI (and )? #define HAVE_RTTI 1 + +// Must global operator new and delete functions throw exceptions? +#define GLOBAL_OPERATOR_NEW_EXCEPTIONS 1 + +// Do we expect the old gcc custom STL allocator? +#define GCC_STYLE_ALLOCATOR 1 diff --git a/dtool/Config.Win32.pp b/dtool/Config.Win32.pp index 670f7a3727..d76fa0d1ef 100644 --- a/dtool/Config.Win32.pp +++ b/dtool/Config.Win32.pp @@ -94,8 +94,9 @@ // Do we have RTTI (and )? #define HAVE_RTTI 1 +// Must global operator new and delete functions throw exceptions? +#define GLOBAL_OPERATOR_NEW_EXCEPTIONS + // can Intel C++ build this directory successfully (if not, change CC to msvc) #define NOT_INTEL_BUILDABLE false - - diff --git a/dtool/LocalSetup.pp b/dtool/LocalSetup.pp index e4591f89c9..961eb41033 100644 --- a/dtool/LocalSetup.pp +++ b/dtool/LocalSetup.pp @@ -181,6 +181,12 @@ $[cdefine HAVE_SYS_SOUNDCARD_H] /* Do we have RTTI (and )? */ $[cdefine HAVE_RTTI] +/* Must global operator new and delete functions throw exceptions? */ +$[cdefine GLOBAL_OPERATOR_NEW_EXCEPTIONS] + +/* Do we expect the old gcc custom STL allocator? */ +$[cdefine GCC_STYLE_ALLOCATOR] + #end dtool_config.h #endif // BUILD_TYPE diff --git a/dtool/src/dtoolbase/Sources.pp b/dtool/src/dtoolbase/Sources.pp index c202e40df3..9e640c62a3 100644 --- a/dtool/src/dtoolbase/Sources.pp +++ b/dtool/src/dtoolbase/Sources.pp @@ -6,8 +6,12 @@ #define SOURCES \ dtoolbase.cxx dtoolbase.h dtoolbase_cc.h dtoolsymbols.h \ - fakestringstream.h + fakestringstream.h \ + pallocator.T pallocator.h \ + pdeque.h plist.h pmap.h pset.h pvector.h #define INSTALL_HEADERS \ - dtoolbase.h dtoolbase_cc.h dtoolsymbols.h fakestringstream.h + dtoolbase.h dtoolbase_cc.h dtoolsymbols.h fakestringstream.h \ + pallocator.T pallocator.h \ + pdeque.h plist.h pmap.h pset.h pvector.h #end lib_target diff --git a/dtool/src/dtoolbase/dtoolbase.cxx b/dtool/src/dtoolbase/dtoolbase.cxx index 0a011eba21..749d7d05d5 100644 --- a/dtool/src/dtoolbase/dtoolbase.cxx +++ b/dtool/src/dtoolbase/dtoolbase.cxx @@ -17,3 +17,21 @@ //////////////////////////////////////////////////////////////////// #include "dtoolbase.h" + + +#ifndef NDEBUG + +void *default_operator_new(size_t size) { + return malloc(size); +} + +void default_operator_delete(void *ptr) { + free(ptr); +} + +// We absolutely depend on the static initialization of these pointers +// to happen at load time, before any static constructors are called. +void *(*global_operator_new)(size_t size) = &default_operator_new; +void (*global_operator_delete)(void *ptr) = &default_operator_delete; + +#endif // NDEBUG diff --git a/dtool/src/dtoolbase/dtoolbase_cc.h b/dtool/src/dtoolbase/dtoolbase_cc.h index 64e1b95223..b4dd099227 100644 --- a/dtool/src/dtoolbase/dtoolbase_cc.h +++ b/dtool/src/dtoolbase/dtoolbase_cc.h @@ -95,5 +95,45 @@ using namespace std; #endif // CPPPARSER +// Now redefine global operators new and delete so we can optionally +// provide custom handlers for them. The MemoryUsage class in Panda +// takes advantage of this to track the size of allocated pointers. +#ifndef NDEBUG +EXPCL_DTOOL void *default_operator_new(size_t size); +EXPCL_DTOOL void default_operator_delete(void *ptr); + +extern EXPCL_DTOOL void *(*global_operator_new)(size_t size); +extern EXPCL_DTOOL void (*global_operator_delete)(void *ptr); + +#ifdef GLOBAL_OPERATOR_NEW_EXCEPTIONS +INLINE void *operator new(size_t size) throw (std::bad_alloc) { + return (*global_operator_new)(size); +} +INLINE void *operator new[](size_t size) throw (std::bad_alloc) { + return (*global_operator_new)(size); +} + +INLINE void operator delete(void *ptr) throw() { + (*global_operator_delete)(ptr); +} +INLINE void operator delete[](void *ptr) throw() { + (*global_operator_delete)(ptr); +} +#else // GLOBAL_OPERATOR_NEW_EXCEPTIONS +INLINE void *operator new(size_t size) { + return (*global_operator_new)(size); +} +INLINE void *operator new[](size_t size) { + return (*global_operator_new)(size); +} + +INLINE void operator delete(void *ptr) { + (*global_operator_delete)(ptr); +} +INLINE void operator delete[](void *ptr) { + (*global_operator_delete)(ptr); +} +#endif // GLOBAL_OPERATOR_NEW_EXCEPTIONS +#endif // NDEBUG #endif diff --git a/dtool/src/dtoolbase/pallocator.T b/dtool/src/dtoolbase/pallocator.T new file mode 100644 index 0000000000..b4450eba7e --- /dev/null +++ b/dtool/src/dtoolbase/pallocator.T @@ -0,0 +1,51 @@ +// Filename: pallocator.T +// Created by: drose (05Jun01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifdef GCC_STYLE_ALLOCATOR + +#ifndef NDEBUG +template +INLINE void *pallocator:: +allocate(size_t n) { + return (*global_operator_new)(n); +} + +template +INLINE void pallocator:: +deallocate(void *p, size_t) { + return (*global_operator_delete)(p); +} +#endif // NDEBUG + +#else // GCC_STYLE_ALLOCATOR + +#ifndef NDEBUG +template +INLINE pallocator::pointer pallocator:: +allocate(pallocator::size_type n, allocator::const_pointer) { + return (*global_operator_new)(n * sizeof(Type)); +} + +template +INLINE void pallocator:: +deallocate(pallocator::pointer p, allocator::size_type) { + return (*global_operator_delete)(p); +} +#endif // NDEBUG + +#endif // GCC_STYLE_ALLOCATOR diff --git a/dtool/src/dtoolbase/pallocator.h b/dtool/src/dtoolbase/pallocator.h new file mode 100644 index 0000000000..7cd9af5cf8 --- /dev/null +++ b/dtool/src/dtoolbase/pallocator.h @@ -0,0 +1,66 @@ +// Filename: pallocator.h +// Created by: drose (05Jun01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef PALLOCATOR_H +#define PALLOCATOR_H + +#include "dtoolbase.h" + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : pallocator +// Description : This is our own Panda specialization on the default +// STL allocator. Its main purpose is to call the hooks +// for MemoryUsage to properly track STL-allocated +// memory. +// +// pvector, pmap, etc. are all defined in this directory +// to use a pallocator. +//////////////////////////////////////////////////////////////////// + +#ifdef GCC_STYLE_ALLOCATOR +// Early versions of gcc used its own kind of allocator, somewhat +// different from the STL standard. + +template +class pallocator : public alloc { +public: +#ifndef NDEBUG + static void *allocate(size_t n); + static void deallocate(void *p, size_t n); +#endif // NDEBUG +}; + +#else // GCC_STYLE_ALLOCATOR + +template +class pallocator : public allocator { +public: +#ifndef NDEBUG + INLINE pointer allocate(size_type n, allocator::const_pointer hint = 0); + INLINE void deallocate(pointer p, size_type n); +#endif // NDEBUG +}; +#endif // GCC_STYLE_ALLOCATOR + +#include "pallocator.T" + +#endif + diff --git a/dtool/src/dtoolbase/pdeque.h b/dtool/src/dtoolbase/pdeque.h new file mode 100644 index 0000000000..05a2520b62 --- /dev/null +++ b/dtool/src/dtoolbase/pdeque.h @@ -0,0 +1,44 @@ +// Filename: pdeque.h +// Created by: drose (05Jun01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef PDEQUE_H +#define PDEQUE_H + +#include "dtoolbase.h" +#include "pallocator.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : pdeque +// Description : This is our own Panda specialization on the default +// STL deque. Its main purpose is to call the hooks +// for MemoryUsage to properly track STL-allocated +// memory. +//////////////////////////////////////////////////////////////////// +template +class pdeque : public deque > { +public: + pdeque() : deque >() { } + pdeque(const pdeque ©) : deque >(copy) { } + pdeque(size_type n) : deque >(n) { } + pdeque(size_type n, const Type &value) : deque >(n, value) { } +}; + +#endif + diff --git a/dtool/src/dtoolbase/plist.h b/dtool/src/dtoolbase/plist.h new file mode 100644 index 0000000000..8ef2b398fb --- /dev/null +++ b/dtool/src/dtoolbase/plist.h @@ -0,0 +1,44 @@ +// Filename: plist.h +// Created by: drose (05Jun01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef PLIST_H +#define PLIST_H + +#include "dtoolbase.h" +#include "pallocator.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : plist +// Description : This is our own Panda specialization on the default +// STL list. Its main purpose is to call the hooks +// for MemoryUsage to properly track STL-allocated +// memory. +//////////////////////////////////////////////////////////////////// +template +class plist : public list > { +public: + plist() : list >() { } + plist(const plist ©) : list >(copy) { } + plist(size_type n) : list >(n) { } + plist(size_type n, const Type &value) : list >(n, value) { } +}; + +#endif + diff --git a/dtool/src/dtoolbase/pmap.h b/dtool/src/dtoolbase/pmap.h new file mode 100644 index 0000000000..149caa43ba --- /dev/null +++ b/dtool/src/dtoolbase/pmap.h @@ -0,0 +1,56 @@ +// Filename: pmap.h +// Created by: drose (05Jun01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef PMAP_H +#define PMAP_H + +#include "dtoolbase.h" +#include "pallocator.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : pmap +// Description : This is our own Panda specialization on the default +// STL map. Its main purpose is to call the hooks +// for MemoryUsage to properly track STL-allocated +// memory. +//////////////////////////////////////////////////////////////////// +template > +class pmap : public map > > { +public: + pmap() : map > >() { } + pmap(const pmap ©) : map > >(copy) { } +}; + +//////////////////////////////////////////////////////////////////// +// Class : pmultimap +// Description : This is our own Panda specialization on the default +// STL multimap. Its main purpose is to call the hooks +// for MemoryUsage to properly track STL-allocated +// memory. +//////////////////////////////////////////////////////////////////// +template > +class pmultimap : public multimap > > { +public: + pmultimap() : multimap > >() { } + pmultimap(const pmultimap ©) : multimap > >(copy) { } +}; + +#endif + diff --git a/dtool/src/dtoolbase/pset.h b/dtool/src/dtoolbase/pset.h new file mode 100644 index 0000000000..10fe483894 --- /dev/null +++ b/dtool/src/dtoolbase/pset.h @@ -0,0 +1,56 @@ +// Filename: pset.h +// Created by: drose (05Jun01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef PSET_H +#define PSET_H + +#include "dtoolbase.h" +#include "pallocator.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : pset +// Description : This is our own Panda specialization on the default +// STL set. Its main purpose is to call the hooks +// for MemoryUsage to properly track STL-allocated +// memory. +//////////////////////////////////////////////////////////////////// +template > +class pset : public set > { +public: + pset() : set >() { } + pset(const pset ©) : set >(copy) { } +}; + +//////////////////////////////////////////////////////////////////// +// Class : pmultiset +// Description : This is our own Panda specialization on the default +// STL multiset. Its main purpose is to call the hooks +// for MemoryUsage to properly track STL-allocated +// memory. +//////////////////////////////////////////////////////////////////// +template > +class pmultiset : public multiset > { +public: + pmultiset() : multiset >() { } + pmultiset(const pmultiset ©) : multiset >(copy) { } +}; + +#endif + diff --git a/dtool/src/dtoolbase/pvector.h b/dtool/src/dtoolbase/pvector.h new file mode 100644 index 0000000000..7ed317f488 --- /dev/null +++ b/dtool/src/dtoolbase/pvector.h @@ -0,0 +1,44 @@ +// Filename: pvector.h +// Created by: drose (05Jun01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef PVECTOR_H +#define PVECTOR_H + +#include "dtoolbase.h" +#include "pallocator.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : pvector +// Description : This is our own Panda specialization on the default +// STL vector. Its main purpose is to call the hooks +// for MemoryUsage to properly track STL-allocated +// memory. +//////////////////////////////////////////////////////////////////// +template +class pvector : public vector > { +public: + pvector() : vector >() { } + pvector(const pvector ©) : vector >(copy) { } + pvector(size_type n) : vector >(n) { } + pvector(size_type n, const Type &value) : vector >(n, value) { } +}; + +#endif + diff --git a/dtool/src/dtoolutil/vector_src.h b/dtool/src/dtoolutil/vector_src.h index b61e0c0988..bfd83f9d15 100644 --- a/dtool/src/dtoolutil/vector_src.h +++ b/dtool/src/dtoolutil/vector_src.h @@ -45,7 +45,7 @@ // // They will automatically be undefined at the end of the file. -#include +#include "pvector.h" #if defined(WIN32_VC) && !defined(CPPPARSER) @@ -65,7 +65,7 @@ EXPORT_TEMPLATE_CLASS(EXPCL, EXPTP, std::vector) #endif // Now make a typedef for the vector. -typedef std::vector NAME; +typedef pvector NAME; // Finally, we must define a non-inline function that performs the // insert operation given a range of pointers. We do this because diff --git a/panda/src/cull/cullTraverser.cxx b/panda/src/cull/cullTraverser.cxx index 658c4604d9..b23632526c 100644 --- a/panda/src/cull/cullTraverser.cxx +++ b/panda/src/cull/cullTraverser.cxx @@ -143,6 +143,35 @@ clear_bins() { nassertv(_default_bin == (GeomBin *)NULL); } +//////////////////////////////////////////////////////////////////// +// Function: CullTraverser::clear_state +// Access: Public +// Description: Removes all the cached state information stored +// within the CullTraverser, and forces it to rebuild +// this state from scratch the next frame. +// +// This can be used to clear out state that has gone +// bad, as well as to free up memory pointers that +// otherwise may be held for another frame. +//////////////////////////////////////////////////////////////////// +void CullTraverser:: +clear_state() { + _states.clear(); + _lookup.clear(); + + ToplevelBins::const_iterator tbi; + for (tbi = _toplevel_bins.begin(); tbi != _toplevel_bins.end(); ++tbi) { + (*tbi).second->clear_current_states(); + } + SubBins::const_iterator sbi; + for (sbi = _sub_bins.begin(); sbi != _sub_bins.end(); ++sbi) { + (*sbi).second->clear_current_states(); + } + _default_bin->clear_current_states(); + + _initial_state = AllAttributesWrapper(); +} + //////////////////////////////////////////////////////////////////// // Function: CullTraverser::output // Access: Public diff --git a/panda/src/cull/cullTraverser.h b/panda/src/cull/cullTraverser.h index 1d6736b30f..0cabdaa502 100644 --- a/panda/src/cull/cullTraverser.h +++ b/panda/src/cull/cullTraverser.h @@ -59,6 +59,8 @@ PUBLISHED: GeomBin *get_bin(const string &name) const; void clear_bins(); + void clear_state(); + void output(ostream &out) const; void write(ostream &out, int indent_level = 0) const; diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index c31d1f4b4f..90a6791c6d 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -125,6 +125,27 @@ release_all_textures() { nassertv(_prepared_textures.empty()); } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::clear_attribute +// Access: Public +// Description: Explicitly clear the indicated attribute, specified +// by the TypeHandle of its associated transition. If +// the attribute is not set already, this does nothing; +// if it is set, it resets it to its default value. +//////////////////////////////////////////////////////////////////// +void GraphicsStateGuardian:: +clear_attribute(TypeHandle type) { + NodeAttributes::iterator ai = _state.find(type); + if (ai != _state.end()) { + // The state is already set; get the initial value and reset it. + PT(NodeAttribute) initial = (*ai).second->make_initial(); + initial->issue(this); + + // Now remove the state entry from the set. + _state.erase(ai); + } +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::reset // Access: Public, Virtual diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index 6867cc9d5f..83ba209c16 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -78,10 +78,11 @@ PUBLISHED: } void enable_frame_clear(bool clear_color, bool clear_depth); - -public: void release_all_textures(); + void clear_attribute(TypeHandle type); + +public: virtual void clear(const RenderBuffer &buffer)=0; virtual void clear(const RenderBuffer &buffer, const DisplayRegion* region)=0; diff --git a/panda/src/downloader/downloader_headers.h b/panda/src/downloader/downloader_headers.h index 2ea607a914..f4fa338349 100644 --- a/panda/src/downloader/downloader_headers.h +++ b/panda/src/downloader/downloader_headers.h @@ -16,9 +16,11 @@ // //////////////////////////////////////////////////////////////////// +#ifdef WIN32_VC #define WINDOWS_LEAN_AND_MEAN #include #undef WINDOWS_LEAN_AND_MEAN +#endif #include #include diff --git a/panda/src/express/Sources.pp b/panda/src/express/Sources.pp index af45358aa2..1e17cb2f98 100644 --- a/panda/src/express/Sources.pp +++ b/panda/src/express/Sources.pp @@ -20,8 +20,11 @@ datagramOutputFile.h datagramSink.I datagramSink.h \ get_config_path.h hashGeneratorBase.I hashGeneratorBase.h \ hashVal.I hashVal.h indent.I indent.h littleEndian.h \ - memoryUsage.I memoryUsage.h memoryUsagePointers.I \ - memoryUsagePointers.h multifile.I multifile.h namable.I \ + memoryInfo.I memoryInfo.h \ + memoryUsage.I memoryUsage.h \ + memoryUsagePointerCounts.I memoryUsagePointerCounts.h \ + memoryUsagePointers.I memoryUsagePointers.h \ + multifile.I multifile.h namable.I \ namable.h nativeNumericData.I nativeNumericData.h \ numeric_types.h pointerTo.I pointerTo.h referenceCount.I \ profileTimer.I profileTimer.h referenceCount.h \ @@ -36,7 +39,8 @@ config_express.cxx datagram.cxx datagramGenerator.cxx \ datagramInputFile.cxx datagramIterator.cxx \ datagramOutputFile.cxx datagramSink.cxx get_config_path.cxx \ - hashGeneratorBase.cxx hashVal.cxx indent.cxx memoryUsage.cxx \ + hashGeneratorBase.cxx hashVal.cxx indent.cxx \ + memoryInfo.cxx memoryUsage.cxx memoryUsagePointerCounts.cxx \ memoryUsagePointers.cxx multifile.cxx namable.cxx \ nativeNumericData.cxx profileTimer.cxx referenceCount.cxx \ reversedNumericData.cxx trueClock.cxx typeHandle.cxx \ @@ -58,9 +62,11 @@ datagramGenerator.h get_config_path.h \ hashGeneratorBase.I hashGeneratorBase.h \ hashVal.I hashVal.h \ - indent.I indent.h littleEndian.h \ - memoryUsage.I memoryUsage.h memoryUsagePointers.I \ - memoryUsagePointers.h multifile.I multifile.h \ + indent.I indent.h littleEndian.h \ + memoryInfo.I memoryInfo.h \ + memoryUsage.I memoryUsage.h \ + memoryUsagePointerCounts.I memoryUsagePointerCounts.h \ + memoryUsagePointers.I memoryUsagePointers.h multifile.I multifile.h \ nativeNumericData.I nativeNumericData.h \ numeric_types.h \ pointerTo.I pointerTo.h referenceCount.I referenceCount.h \ diff --git a/panda/src/express/memoryInfo.I b/panda/src/express/memoryInfo.I new file mode 100644 index 0000000000..0dbedb30c3 --- /dev/null +++ b/panda/src/express/memoryInfo.I @@ -0,0 +1,83 @@ +// Filename: memoryInfo.I +// Created by: drose (04Jun01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: MemoryInfo::get_void_ptr +// Access: Public +// Description: Returns the data pointer as a void pointer. This +// should always be non-NULL. +//////////////////////////////////////////////////////////////////// +void *MemoryInfo::get_void_ptr() const { + return _void_ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryInfo::get_ref_ptr +// Access: Public +// Description: Returns the data pointer as a ReferenceCount pointer. +// This may be NULL if the data pointer does not +// represent a ReferenceCount object. +//////////////////////////////////////////////////////////////////// +ReferenceCount *MemoryInfo::get_ref_ptr() const { + return _ref_ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryInfo::get_typed_ptr +// Access: Public +// Description: Returns the data pointer as a TypedObject pointer. +// This may be NULL if the data pointer does not +// represent a pointer to a TypedObject. +//////////////////////////////////////////////////////////////////// +TypedObject *MemoryInfo::get_typed_ptr() const { + return _typed_ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryInfo::is_size_known +// Access: Public +// Description: Returns true if the size of the memory block +// referenced by this pointer is known. Most pointers' +// sizes should be known, but some may not be. +//////////////////////////////////////////////////////////////////// +bool MemoryInfo::is_size_known() const { + return (_flags & F_size_known) != 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryInfo::get_size +// Access: Public +// Description: Returns the size in bytes of the memory block +// referenced by this pointer, if it is known. Returns +// zero if the size is not known. +//////////////////////////////////////////////////////////////////// +size_t MemoryInfo::get_size() const { + return _size; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryInfo::get_time +// Access: Public +// Description: Returns the time in seconds (based on the +// GlobalClock) at which the pointer was allocated. +//////////////////////////////////////////////////////////////////// +double MemoryInfo::get_time() const { + return _time; +} + diff --git a/panda/src/express/memoryInfo.cxx b/panda/src/express/memoryInfo.cxx new file mode 100644 index 0000000000..d551c52b6b --- /dev/null +++ b/panda/src/express/memoryInfo.cxx @@ -0,0 +1,149 @@ +// Filename: memoryInfo.cxx +// Created by: drose (04Jun01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "memoryInfo.h" +#include "typedReferenceCount.h" +#include "typeHandle.h" + +//////////////////////////////////////////////////////////////////// +// Function: MemoryInfo::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +MemoryInfo:: +MemoryInfo() { + _void_ptr = (void *)NULL; + _ref_ptr = (ReferenceCount *)NULL; + _typed_ptr = (TypedObject *)NULL; + _size = 0; + _static_type = TypeHandle::none(); + _dynamic_type = TypeHandle::none(); + + _flags = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryInfo::get_type +// Access: Public +// Description: Returns the best known type, dynamic or static, of +// the pointer. +//////////////////////////////////////////////////////////////////// +TypeHandle MemoryInfo:: +get_type() { + // If we don't want to consider the dynamic type any further, use + // what we've got. + if ((_flags & F_reconsider_dynamic_type) == 0) { + if (_dynamic_type == TypeHandle::none()) { + return _static_type; + } + return _dynamic_type; + } + + // Otherwise, examine the pointer again and make sure it's still the + // best information we have. We have to do this each time because + // if we happen to be examining the pointer from within the + // constructor or destructor, its dynamic type will appear to be + // less-specific than it actually is, so our idea of what type this + // thing is could change from time to time. + determine_dynamic_type(); + + // Now return the more specific of the two. + TypeHandle type = _static_type; + update_type_handle(type, _dynamic_type); + + return type; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryInfo::determine_dynamic_type +// Access: Public +// Description: Tries to determine the actual type of the object to +// which this thing is pointed, if possible. +//////////////////////////////////////////////////////////////////// +void MemoryInfo:: +determine_dynamic_type() { + if ((_flags & F_reconsider_dynamic_type) != 0 && + _static_type != TypeHandle::none()) { + // See if we know enough now to infer the dynamic type from the + // pointer. + + if (_typed_ptr == (TypedObject *)NULL) { + // If our static type is known to inherit from + // TypedReferenceCount, then we can directly downcast to get the + // TypedObject pointer. + if (_static_type.is_derived_from(TypedReferenceCount::get_class_type())) { + _typed_ptr = (TypedReferenceCount *)_ref_ptr; + } + } + + if (_typed_ptr != (TypedObject *)NULL) { + // If we have a TypedObject pointer, we can determine the type. + // This might still not return the exact type, particularly if + // we are being called within the destructor or constructor of + // this object. + TypeHandle got_type = _typed_ptr->get_type(); + + if (got_type == TypeHandle::none()) { + express_cat.warning() + << "Found an unregistered type in a " << _static_type + << " pointer:\n" + << "Check derived types of " << _static_type + << " and make sure that all are being initialized.\n"; + _dynamic_type = _static_type; + _flags &= ~F_reconsider_dynamic_type; + return; + } + + update_type_handle(_dynamic_type, got_type); + } + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: MemoryInfo::update_type_handle +// Access: Public +// Description: Updates the given destination TypeHandle with the +// refined TypeHandle, if it is in fact more specific +// than the original value for the destination. +//////////////////////////////////////////////////////////////////// +void MemoryInfo:: +update_type_handle(TypeHandle &destination, TypeHandle refined) { + if (refined == TypeHandle::none()) { + express_cat.error() + << "Attempt to update type of " << (void *)_ref_ptr + << "(type is " << get_type() + << ") to an undefined type!\n"; + + } else if (destination == refined) { + // Updating with the same type, no problem. + + } else if (destination.is_derived_from(refined)) { + // Updating with a less-specific type, no problem. + + } else if (refined.is_derived_from(destination)) { + // Updating with a more-specific type, no problem. + destination = refined; + + } else { + express_cat.error() + << "Pointer " << (void *)_ref_ptr << " previously indicated as type " + << destination << " is now type " << refined << "!\n"; + destination = refined; + } +} diff --git a/panda/src/express/memoryInfo.h b/panda/src/express/memoryInfo.h new file mode 100644 index 0000000000..306b73bc78 --- /dev/null +++ b/panda/src/express/memoryInfo.h @@ -0,0 +1,88 @@ +// Filename: memoryInfo.h +// Created by: drose (04Jun01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef MEMORYINFO_H +#define MEMORYINFO_H + +#include +#include "typeHandle.h" + +class ReferenceCount; +class TypedObject; + +#ifndef NDEBUG +//////////////////////////////////////////////////////////////////// +// Class : MemoryInfo +// Description : This is a supporting class for MemoryUsage. It +// records the detailed information for a particular +// pointer allocated by Panda code. This record is only +// kept if track-mem-usage is configured #t. +// +// It's not exported from the DLL, and it doesn't even +// exist if we're compiling NDEBUG. +//////////////////////////////////////////////////////////////////// +class MemoryInfo { +public: + MemoryInfo(); + +public: + // Members to view the MemoryInfo structure. + TypeHandle get_type(); + + INLINE void *get_void_ptr() const; + INLINE ReferenceCount *get_ref_ptr() const; + INLINE TypedObject *get_typed_ptr() const; + + INLINE bool is_size_known() const; + INLINE size_t get_size() const; + + INLINE double get_time() const; + +public: + // Members to set up the MemoryInfo structure. + void determine_dynamic_type(); + void update_type_handle(TypeHandle &destination, TypeHandle refined); + +private: + enum Flags { + F_got_ref = 0x0001, + F_got_void = 0x0002, + F_size_known = 0x0004, + F_reconsider_dynamic_type = 0x0008, + }; + + void *_void_ptr; + ReferenceCount *_ref_ptr; + TypedObject *_typed_ptr; + size_t _size; + TypeHandle _static_type; + TypeHandle _dynamic_type; + int _flags; + + double _time; + int _freeze_index; + + friend class MemoryUsage; +}; + +#include "memoryInfo.I" + +#endif // NDEBUG + +#endif + diff --git a/panda/src/express/memoryUsage.I b/panda/src/express/memoryUsage.I index 700a69d02b..ad7534f47d 100644 --- a/panda/src/express/memoryUsage.I +++ b/panda/src/express/memoryUsage.I @@ -115,6 +115,40 @@ remove_pointer(ReferenceCount *ptr) { #endif // __GNUC__ +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::is_tracking +// Access: Public, Static +// Description: Returns true if the MemoryUsage object is currently +// tracking memory (e.g. track-memory-usage is +// configured #t). +//////////////////////////////////////////////////////////////////// +INLINE bool MemoryUsage:: +is_tracking() { + return get_global_ptr()->_track_memory_usage; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::get_allocated_size +// Access: Public, Static +// Description: Returns the total number of bytes of allocated memory +// as counted, excluding the memory previously frozen. +//////////////////////////////////////////////////////////////////// +INLINE size_t MemoryUsage:: +get_allocated_size() { + return get_global_ptr()->ns_get_allocated_size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::get_total_size +// Access: Public, Static +// Description: Returns the total number of bytes of allocated memory +// as counted, including the memory previously frozen. +//////////////////////////////////////////////////////////////////// +INLINE size_t MemoryUsage:: +get_total_size() { + return get_global_ptr()->ns_get_total_size(); +} + //////////////////////////////////////////////////////////////////// // Function: MemoryUsage::get_num_pointers // Access: Public, Static diff --git a/panda/src/express/memoryUsage.cxx b/panda/src/express/memoryUsage.cxx index b3c507974f..b2fb07754c 100644 --- a/panda/src/express/memoryUsage.cxx +++ b/panda/src/express/memoryUsage.cxx @@ -49,116 +49,6 @@ double MemoryUsage::AgeHistogram::_cutoff[MemoryUsage::AgeHistogram::num_buckets 60.0, }; -//////////////////////////////////////////////////////////////////// -// Function: MemoryUsage::MemoryInfo::get_type -// Access: Public -// Description: Returns the best known type, dynamic or static, of -// the pointer. -//////////////////////////////////////////////////////////////////// -TypeHandle MemoryUsage::MemoryInfo:: -get_type() { - // If we don't want to consider the dynamic type any further, use - // what we've got. - if (!_reconsider_dynamic_type) { - if (_dynamic_type == TypeHandle::none()) { - return _static_type; - } - return _dynamic_type; - } - - // Otherwise, examine the pointer again and make sure it's still the - // best information we have. We have to do this each time because - // if we happen to be examining the pointer from within the - // constructor or destructor, its dynamic type will appear to be - // less-specific than it actually is, so our idea of what type this - // thing is could change from time to time. - determine_dynamic_type(); - - // Now return the more specific of the two. - TypeHandle type = _static_type; - update_type_handle(type, _dynamic_type); - - return type; -} - -//////////////////////////////////////////////////////////////////// -// Function: MemoryUsage::MemoryInfo::determine_dynamic_type -// Access: Public -// Description: Tries to determine the actual type of the object to -// which this thing is pointed, if possible. -//////////////////////////////////////////////////////////////////// -void MemoryUsage::MemoryInfo:: -determine_dynamic_type() { - if (_reconsider_dynamic_type && _static_type != TypeHandle::none()) { - // See if we know enough now to infer the dynamic type from the - // pointer. - - if (_typed_ptr == (TypedObject *)NULL) { - // If our static type is known to inherit from - // TypedReferenceCount, then we can directly downcast to get the - // TypedObject pointer. - if (_static_type.is_derived_from(TypedReferenceCount::get_class_type())) { - _typed_ptr = (TypedReferenceCount *)_ptr; - } - } - - if (_typed_ptr != (TypedObject *)NULL) { - // If we have a TypedObject pointer, we can determine the type. - // This might still not return the exact type, particularly if - // we are being called within the destructor or constructor of - // this object. - TypeHandle got_type = _typed_ptr->get_type(); - - if (got_type == TypeHandle::none()) { - express_cat.warning() - << "Found an unregistered type in a " << _static_type - << " pointer:\n" - << "Check derived types of " << _static_type - << " and make sure that all are being initialized.\n"; - _dynamic_type = _static_type; - _reconsider_dynamic_type = false; - return; - } - - update_type_handle(_dynamic_type, got_type); - } - } -} - - -//////////////////////////////////////////////////////////////////// -// Function: MemoryUsage::MemoryInfo::update_type_handle -// Access: Public -// Description: Updates the given destination TypeHandle with the -// refined TypeHandle, if it is in fact more specific -// than the original value for the destination. -//////////////////////////////////////////////////////////////////// -void MemoryUsage::MemoryInfo:: -update_type_handle(TypeHandle &destination, TypeHandle refined) { - if (refined == TypeHandle::none()) { - express_cat.error() - << "Attempt to update type of " << (void *)_ptr - << "(type is " << get_type() - << ") to an undefined type!\n"; - - } else if (destination == refined) { - // Updating with the same type, no problem. - - } else if (destination.is_derived_from(refined)) { - // Updating with a less-specific type, no problem. - - } else if (refined.is_derived_from(destination)) { - // Updating with a more-specific type, no problem. - destination = refined; - - } else { - express_cat.error() - << "Pointer " << (void *)_ptr << " previously indicated as type " - << destination << " is now type " << refined << "!\n"; - destination = refined; - } -} - //////////////////////////////////////////////////////////////////// // Function: MemoryUsage::TypeHistogram::add_info @@ -166,8 +56,8 @@ update_type_handle(TypeHandle &destination, TypeHandle refined) { // Description: Adds a single entry to the histogram. //////////////////////////////////////////////////////////////////// void MemoryUsage::TypeHistogram:: -add_info(TypeHandle type) { - _counts[type]++; +add_info(TypeHandle type, MemoryInfo &info) { + _counts[type].add_info(info); } @@ -175,14 +65,16 @@ add_info(TypeHandle type) { // below, to sort the types in descending order by counts. class TypeHistogramCountSorter { public: - TypeHistogramCountSorter(int count, TypeHandle type) { - _count = count; - _type = type; + TypeHistogramCountSorter(const MemoryUsagePointerCounts &count, + TypeHandle type) : + _count(count), + _type(type) + { } bool operator < (const TypeHistogramCountSorter &other) const { return _count > other._count; } - int _count; + MemoryUsagePointerCounts _count; TypeHandle _type; }; @@ -206,7 +98,13 @@ show() const { vector::const_iterator vi; for (vi = count_sorter.begin(); vi != count_sorter.end(); ++vi) { - nout << (*vi)._type << " : " << (*vi)._count << " pointers.\n"; + TypeHandle type = (*vi)._type; + if (type == TypeHandle::none()) { + nout << "unknown"; + } else { + nout << type; + } + nout << " : " << (*vi)._count << "\n"; } } @@ -236,10 +134,10 @@ AgeHistogram() { // Description: Adds a single entry to the histogram. //////////////////////////////////////////////////////////////////// void MemoryUsage::AgeHistogram:: -add_info(double age) { +add_info(double age, MemoryInfo &info) { int bucket = choose_bucket(age); nassertv(bucket >= 0 && bucket < num_buckets); - _counts[bucket]++; + _counts[bucket].add_info(info); } //////////////////////////////////////////////////////////////////// @@ -250,11 +148,13 @@ add_info(double age) { void MemoryUsage::AgeHistogram:: show() const { for (int i = 0; i < num_buckets - 1; i++) { - nout << _cutoff[i] << " to " << _cutoff[i + 1] << " seconds old : " - << _counts[i] << " pointers.\n"; + nout << _cutoff[i] << " to " << _cutoff[i + 1] << " seconds old : "; + _counts[i].output(nout); + nout << "\n"; } - nout << _cutoff[num_buckets - 1] << " seconds old and up : " - << _counts[num_buckets - 1] << " pointers.\n"; + nout << _cutoff[num_buckets - 1] << " seconds old and up : "; + _counts[num_buckets - 1].output(nout); + nout << "\n"; } //////////////////////////////////////////////////////////////////// @@ -265,7 +165,7 @@ show() const { void MemoryUsage::AgeHistogram:: clear() { for (int i = 0; i < num_buckets; i++) { - _counts[i] = 0; + _counts[i].clear(); } } @@ -341,6 +241,37 @@ remove_pointer(ReferenceCount *ptr) { #endif // __GNUC__ && !NDEBUG +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::operator_new_handler +// Access: Public, Static +// Description: This is set up as a global handler function (by +// redefining a function pointer in Dtool) for the +// operator new function. If track-memory-usage is +// enabled, this function will be called whenever any +// new operator within the Panda source is invoked. +//////////////////////////////////////////////////////////////////// +void *MemoryUsage:: +operator_new_handler(size_t size) { + void *ptr = default_operator_new(size); + get_global_ptr()->ns_record_void_pointer(ptr, size); + return ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::operator_delete_handler +// Access: Public, Static +// Description: This is set up as a global handler function (by +// redefining a function pointer in Dtool) for the +// operator delete function. If track-memory-usage is +// enabled, this function will be called whenever any +// delete operator within the Panda source is invoked. +//////////////////////////////////////////////////////////////////// +void MemoryUsage:: +operator_delete_handler(void *ptr) { + get_global_ptr()->ns_remove_void_pointer(ptr); + return default_operator_delete(ptr); +} + //////////////////////////////////////////////////////////////////// // Function: MemoryUsage::Constructor // Access: Private @@ -354,8 +285,18 @@ MemoryUsage() { _track_memory_usage = config_express.GetBool("track-memory-usage", false); + if (_track_memory_usage) { + // Redefine the global pointers for operator new and operator + // delete (these pointers are defined up in DTOOL) to vector into + // this class. + global_operator_new = &operator_new_handler; + global_operator_delete = &operator_delete_handler; + } + _freeze_index = 0; _count = 0; + _allocated_size = 0; + _total_size = 0; } //////////////////////////////////////////////////////////////////// @@ -382,24 +323,31 @@ get_global_ptr() { void MemoryUsage:: ns_record_pointer(ReferenceCount *ptr) { if (_track_memory_usage) { - MemoryInfo info; - info._ptr = ptr; - info._typed_ptr = (TypedObject *)NULL; + pair insert_result = + _table.insert(Table::value_type((void *)ptr, MemoryInfo())); + + // This shouldn't fail. + assert(insert_result.first != _table.end()); + + if (insert_result.second) { + _count++; + } + + MemoryInfo &info = (*insert_result.first).second; + + // We shouldn't already have a ReferenceCount pointer. + if ((info._flags & MemoryInfo::F_got_ref) != 0) { + express_cat.error() + << "Pointer " << (void *)ptr << " recorded twice!\n"; + } + + info._void_ptr = (void *)ptr; + info._ref_ptr = ptr; info._static_type = ReferenceCount::get_class_type(); info._dynamic_type = ReferenceCount::get_class_type(); info._time = TrueClock::get_ptr()->get_real_time(); info._freeze_index = _freeze_index; - info._reconsider_dynamic_type = true; - - Table::iterator ti; - ti = _table.find(ptr); - if (ti != _table.end()) { - express_cat.error() << "Pointer " << (void *)ptr << " recorded twice!\n"; - (*ti).second = info; - } else { - _table[ptr] = info; - _count++; - } + info._flags |= (MemoryInfo::F_reconsider_dynamic_type | MemoryInfo::F_got_ref); } } @@ -427,6 +375,8 @@ ns_update_type(ReferenceCount *ptr, TypeHandle type) { MemoryInfo &info = (*ti).second; info.update_type_handle(info._static_type, type); info.determine_dynamic_type(); + + consolidate_void_ptr(info); } } @@ -456,6 +406,8 @@ ns_update_type(ReferenceCount *ptr, TypedObject *typed_ptr) { MemoryInfo &info = (*ti).second; info._typed_ptr = typed_ptr; info.determine_dynamic_type(); + + consolidate_void_ptr(info); } } @@ -481,6 +433,14 @@ ns_remove_pointer(ReferenceCount *ptr) { MemoryInfo &info = (*ti).second; + if ((info._flags & MemoryInfo::F_got_ref) == 0) { + express_cat.error() + << "Pointer " << (void *)ptr << " deleted twice!\n"; + return; + } + + info._flags &= ~MemoryInfo::F_got_ref; + // Since the pointer has been destructed, we can't safely call its // TypedObject virtual methods any more. Better clear out the // typed_ptr for good measure. @@ -488,15 +448,143 @@ ns_remove_pointer(ReferenceCount *ptr) { if (info._freeze_index == _freeze_index) { double now = TrueClock::get_ptr()->get_real_time(); - _count--; - _trend_types.add_info(info.get_type()); - _trend_ages.add_info(now - info._time); + _trend_types.add_info(info.get_type(), info); + _trend_ages.add_info(now - info._time, info); } - _table.erase(ti); + if ((info._flags & (MemoryInfo::F_got_ref | MemoryInfo::F_got_void)) == 0) { + // If we don't expect to call any more remove_*_pointer on this + // pointer, remove it from the table. + if (info._freeze_index == _freeze_index) { + _count--; + _allocated_size -= info._size; + } + _total_size -= info._size; + + _table.erase(ti); + } } } +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::ns_record_void_pointer +// Access: Private +// Description: Records a pointer that's not even necessarily a +// ReferenceCount object (but for which we know the size +// of the allocated structure). +//////////////////////////////////////////////////////////////////// +void MemoryUsage:: +ns_record_void_pointer(void *ptr, size_t size) { + if (_track_memory_usage) { + pair insert_result = + _table.insert(Table::value_type((void *)ptr, MemoryInfo())); + + // This shouldn't fail. + assert(insert_result.first != _table.end()); + + if (insert_result.second) { + _count++; + } + + MemoryInfo &info = (*insert_result.first).second; + + // We shouldn't already have a void pointer. + if ((info._flags & MemoryInfo::F_got_void) != 0) { + express_cat.error() + << "Pointer " << (void *)ptr << " recorded twice!\n"; + } + + if (info._freeze_index == _freeze_index) { + _allocated_size += size - info._size; + } else { + _allocated_size += size; + } + _total_size += size - info._size; + + info._void_ptr = ptr; + info._size = size; + info._time = TrueClock::get_ptr()->get_real_time(); + info._freeze_index = _freeze_index; + info._flags |= (MemoryInfo::F_got_void | MemoryInfo::F_size_known); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::ns_remove_void_pointer +// Access: Private +// Description: Removes a pointer previously recorded via +// record_void_pointer. +//////////////////////////////////////////////////////////////////// +void MemoryUsage:: +ns_remove_void_pointer(void *ptr) { + if (_track_memory_usage) { + Table::iterator ti; + ti = _table.find(ptr); + if (ti == _table.end()) { + // The pointer we tried to delete was not recorded in the table. + + // We can't report this as an error, because (a) we might have + // removed the void pointer entry already when we consolidated, + // and (b) a few objects might have been created during static + // init time, before we grabbed the operator new/delete function + // handlers. + return; + } + + MemoryInfo &info = (*ti).second; + + if ((info._flags & MemoryInfo::F_got_void) == 0) { + express_cat.error() + << "Pointer " << (void *)ptr << " deleted twice!\n"; + return; + } + + if ((info._flags & MemoryInfo::F_got_ref) != 0) { + express_cat.error() + << "Pointer " << (void *)ptr << " did not destruct before being deleted!\n"; + } + + info._flags &= ~MemoryInfo::F_got_void; + + if ((info._flags & (MemoryInfo::F_got_ref | MemoryInfo::F_got_void)) == 0) { + // If we don't expect to call any more remove_*_pointer on this + // pointer, remove it from the table. + + if (info._freeze_index == _freeze_index) { + _count--; + _allocated_size -= info._size; + } + _total_size -= info._size; + + _table.erase(ti); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::ns_get_allocated_size +// Access: Private +// Description: Returns the total number of bytes of allocated memory +// as counted, excluding the memory previously frozen. +//////////////////////////////////////////////////////////////////// +size_t MemoryUsage:: +ns_get_allocated_size() { + nassertr(_track_memory_usage, 0); + return _allocated_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::ns_get_total_size +// Access: Private +// Description: Returns the total number of bytes of allocated memory +// as counted, including the memory previously frozen. +//////////////////////////////////////////////////////////////////// +size_t MemoryUsage:: +ns_get_total_size() { + nassertr(_track_memory_usage, 0); + return _total_size; +} + //////////////////////////////////////////////////////////////////// // Function: MemoryUsage::ns_get_num_pointers // Access: Private @@ -523,8 +611,9 @@ ns_get_pointers(MemoryUsagePointers &result) { Table::iterator ti; for (ti = _table.begin(); ti != _table.end(); ++ti) { MemoryInfo &info = (*ti).second; - if (info._freeze_index == _freeze_index) { - result.add_entry(info._ptr, info._typed_ptr, info.get_type(), + if (info._freeze_index == _freeze_index && + info._ref_ptr != (ReferenceCount *)NULL) { + result.add_entry(info._ref_ptr, info._typed_ptr, info.get_type(), now - info._time); } } @@ -546,11 +635,12 @@ ns_get_pointers_of_type(MemoryUsagePointers &result, TypeHandle type) { Table::iterator ti; for (ti = _table.begin(); ti != _table.end(); ++ti) { MemoryInfo &info = (*ti).second; - if (info._freeze_index == _freeze_index) { + if (info._freeze_index == _freeze_index && + info._ref_ptr != (ReferenceCount *)NULL) { TypeHandle info_type = info.get_type(); if (info_type != TypeHandle::none() && info_type.is_derived_from(type)) { - result.add_entry(info._ptr, info._typed_ptr, info_type, + result.add_entry(info._ref_ptr, info._typed_ptr, info_type, now - info._time); } } @@ -574,11 +664,12 @@ ns_get_pointers_of_age(MemoryUsagePointers &result, Table::iterator ti; for (ti = _table.begin(); ti != _table.end(); ++ti) { MemoryInfo &info = (*ti).second; - if (info._freeze_index == _freeze_index) { + if (info._freeze_index == _freeze_index && + info._ref_ptr != (ReferenceCount *)NULL) { double age = now - info._time; if ((age >= from && age <= to) || (age >= to && age <= from)) { - result.add_entry(info._ptr, info._typed_ptr, info.get_type(), age); + result.add_entry(info._ref_ptr, info._typed_ptr, info.get_type(), age); } } } @@ -616,10 +707,11 @@ ns_get_pointers_with_zero_count(MemoryUsagePointers &result) { Table::iterator ti; for (ti = _table.begin(); ti != _table.end(); ++ti) { MemoryInfo &info = (*ti).second; - if (info._freeze_index == _freeze_index) { - if ((*ti).first->get_ref_count() == 0) { - (*ti).first->ref(); - result.add_entry(info._ptr, info._typed_ptr, info.get_type(), + if (info._freeze_index == _freeze_index && + info._ref_ptr != (ReferenceCount *)NULL) { + if (info._ref_ptr->get_ref_count() == 0) { + info._ref_ptr->ref(); + result.add_entry(info._ref_ptr, info._typed_ptr, info.get_type(), now - info._time); } } @@ -639,6 +731,7 @@ ns_get_pointers_with_zero_count(MemoryUsagePointers &result) { void MemoryUsage:: ns_freeze() { _count = 0; + _allocated_size = 0; _trend_types.clear(); _trend_ages.clear(); _freeze_index++; @@ -658,7 +751,7 @@ ns_show_current_types() { for (ti = _table.begin(); ti != _table.end(); ++ti) { MemoryInfo &info = (*ti).second; if (info._freeze_index == _freeze_index) { - hist.add_info(info.get_type()); + hist.add_info(info.get_type(), info); } } @@ -692,7 +785,7 @@ ns_show_current_ages() { for (ti = _table.begin(); ti != _table.end(); ++ti) { MemoryInfo &info = (*ti).second; if (info._freeze_index == _freeze_index) { - hist.add_info(now - info._time); + hist.add_info(now - info._time, info); } } @@ -711,6 +804,68 @@ ns_show_trend_ages() { _trend_ages.show(); } +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsage::consolidate_void_ptr +// Access: Private +// Description: If the size information has not yet been determined +// for this pointer, checks to see if it has possible +// been recorded under the TypedObject pointer (this +// will happen when the class inherits from TypedObject +// before ReferenceCount, e.g. TypedReferenceCount). +//////////////////////////////////////////////////////////////////// +void MemoryUsage:: +consolidate_void_ptr(MemoryInfo &info) { + if (info.is_size_known()) { + // We already know the size, so no sweat. + return; + } + + if (info.get_typed_ptr() == (TypedObject *)NULL) { + // We don't have a typed pointer for this thing yet. + return; + } + + void *typed_ptr = (void *)info.get_typed_ptr(); + + if (typed_ptr == (void *)info.get_ref_ptr()) { + // The TypedObject pointer is the same pointer as the + // ReferenceCount pointer, so there's no point in looking it up + // separately. Actually, this really shouldn't even be possible. + return; + } + + Table::iterator ti; + ti = _table.find(typed_ptr); + if (ti == _table.end()) { + // No entry for the typed pointer, either. + return; + } + + // We do have an entry! Copy over the relevant pieces. + MemoryInfo &typed_info = (*ti).second; + + if (typed_info.is_size_known()) { + info._size = typed_info.get_size(); + info._flags |= MemoryInfo::F_size_known; + if (typed_info._freeze_index == _freeze_index) { + _allocated_size += info._size; + } + } + + // The typed_ptr is clearly the more accurate pointer to the + // beginning of the structure. + info._void_ptr = typed_ptr; + + // Now that we've consolidated the pointers, remove the void pointer + // entry. + if (info._freeze_index == _freeze_index) { + _count--; + _allocated_size -= info._size; + } + + + _table.erase(ti); +} #endif // NDEBUG diff --git a/panda/src/express/memoryUsage.h b/panda/src/express/memoryUsage.h index f27e2fe077..bfa3d96730 100644 --- a/panda/src/express/memoryUsage.h +++ b/panda/src/express/memoryUsage.h @@ -22,6 +22,8 @@ #include #include "typedObject.h" +#include "memoryInfo.h" +#include "memoryUsagePointerCounts.h" #include @@ -42,7 +44,6 @@ class EXPCL_PANDAEXPRESS MemoryUsage { public: INLINE static bool get_track_memory_usage(); -PUBLISHED: #if defined(__GNUC__) && !defined(NDEBUG) // There seems to be a problem with egcs-2.91.66: it gets confused // with too many nested inline functions, and sets the wrong pointer @@ -62,7 +63,20 @@ PUBLISHED: INLINE static void remove_pointer(ReferenceCount *ptr); #endif // __GNUC__ && !NDEBUG -#ifndef NDEBUG +#ifdef NDEBUG +public: + INLINE static bool is_tracking() { return false; } + INLINE static size_t get_allocated_size() { return 0; } + +#else // NDEBUG +public: + static void *operator_new_handler(size_t size); + static void operator_delete_handler(void *ptr); + +PUBLISHED: + INLINE static bool is_tracking(); + INLINE static size_t get_allocated_size(); + INLINE static size_t get_total_size(); INLINE static int get_num_pointers(); INLINE static void get_pointers(MemoryUsagePointers &result); INLINE static void get_pointers_of_type(MemoryUsagePointers &result, @@ -87,6 +101,11 @@ private: void ns_update_type(ReferenceCount *ptr, TypedObject *typed_ptr); void ns_remove_pointer(ReferenceCount *ptr); + void ns_record_void_pointer(void *ptr, size_t size); + void ns_remove_void_pointer(void *ptr); + + size_t ns_get_allocated_size(); + size_t ns_get_total_size(); int ns_get_num_pointers(); void ns_get_pointers(MemoryUsagePointers &result); void ns_get_pointers_of_type(MemoryUsagePointers &result, @@ -101,38 +120,25 @@ private: void ns_show_current_ages(); void ns_show_trend_ages(); + void consolidate_void_ptr(MemoryInfo &info); + static MemoryUsage *_global_ptr; - class MemoryInfo { - public: - TypeHandle get_type(); - void determine_dynamic_type(); - void update_type_handle(TypeHandle &destination, TypeHandle refined); - - ReferenceCount *_ptr; - TypedObject *_typed_ptr; - TypeHandle _static_type; - TypeHandle _dynamic_type; - - double _time; - int _freeze_index; - bool _reconsider_dynamic_type; - }; - - typedef map Table; + typedef map Table; Table _table; int _freeze_index; int _count; - + size_t _allocated_size; + size_t _total_size; class TypeHistogram { public: - void add_info(TypeHandle type); + void add_info(TypeHandle type, MemoryInfo &info); void show() const; void clear(); private: - typedef map Counts; + typedef map Counts; Counts _counts; }; TypeHistogram _trend_types; @@ -140,7 +146,7 @@ private: class AgeHistogram { public: AgeHistogram(); - void add_info(double age); + void add_info(double age, MemoryInfo &info); void show() const; void clear(); @@ -148,7 +154,7 @@ private: int choose_bucket(double age) const; enum { num_buckets = 5 }; - int _counts[num_buckets]; + MemoryUsagePointerCounts _counts[num_buckets]; static double _cutoff[num_buckets]; }; AgeHistogram _trend_ages; @@ -156,7 +162,7 @@ private: bool _track_memory_usage; -#endif +#endif // NDEBUG }; #include "memoryUsage.I" diff --git a/panda/src/express/memoryUsagePointerCounts.I b/panda/src/express/memoryUsagePointerCounts.I new file mode 100644 index 0000000000..1080c5e31d --- /dev/null +++ b/panda/src/express/memoryUsagePointerCounts.I @@ -0,0 +1,128 @@ +// Filename: memoryUsagePointerCounts.I +// Created by: drose (04Jun01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointerCounts::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE MemoryUsagePointerCounts:: +MemoryUsagePointerCounts() { + _count = 0; + _unknown_size_count = 0; + _size = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointerCounts::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE MemoryUsagePointerCounts:: +MemoryUsagePointerCounts(const MemoryUsagePointerCounts ©) : + _count(copy._count), + _unknown_size_count(copy._unknown_size_count), + _size(copy._size) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointerCounts::Copy Assignment +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void MemoryUsagePointerCounts:: +operator = (const MemoryUsagePointerCounts ©) { + _count = copy._count; + _unknown_size_count = copy._unknown_size_count; + _size = copy._size; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointerCounts::clear +// Access: Public +// Description: Resets the counter to empty. +//////////////////////////////////////////////////////////////////// +INLINE void MemoryUsagePointerCounts:: +clear() { + _count = 0; + _unknown_size_count = 0; + _size = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointerCounts::is_size_unknown +// Access: Public +// Description: Returns true if none of the pointers in the count +// have a known size, or false if at least one of them +// does. +//////////////////////////////////////////////////////////////////// +INLINE bool MemoryUsagePointerCounts:: +is_size_unknown() const { + return _unknown_size_count == _count; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointerCounts::get_size +// Access: Public +// Description: Returns the total allocated size of all pointers in +// the count whose size is known. +//////////////////////////////////////////////////////////////////// +INLINE size_t MemoryUsagePointerCounts:: +get_size() const { + return _size; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointerCounts::get_count +// Access: Public +// Description: Returns the total number of pointers in the count. +//////////////////////////////////////////////////////////////////// +INLINE int MemoryUsagePointerCounts:: +get_count() const { + return _count; +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointerCounts::Ordering Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool MemoryUsagePointerCounts:: +operator < (const MemoryUsagePointerCounts &other) const { + if (is_size_unknown() != other.is_size_unknown()) { + return is_size_unknown() > other.is_size_unknown(); + } + + if (get_size() != other.get_size()) { + return get_size() < other.get_size(); + } + + if (get_count() != other.get_count()) { + return get_count() != other.get_count(); + } + + return false; +} + +INLINE ostream & +operator << (ostream &out, const MemoryUsagePointerCounts &c) { + c.output(out); + return out; +} diff --git a/panda/src/express/memoryUsagePointerCounts.cxx b/panda/src/express/memoryUsagePointerCounts.cxx new file mode 100644 index 0000000000..b9b1079865 --- /dev/null +++ b/panda/src/express/memoryUsagePointerCounts.cxx @@ -0,0 +1,77 @@ +// Filename: memoryUsagePointerCounts.cxx +// Created by: drose (04Jun01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +#include "memoryUsagePointerCounts.h" +#include "memoryInfo.h" + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointerCounts::add_info +// Access: Public +// Description: Adds a pointer definition to the counter. +//////////////////////////////////////////////////////////////////// +void MemoryUsagePointerCounts:: +add_info(MemoryInfo &info) { + _count++; + + if (info.is_size_known()) { + _size += info.get_size(); + } else { + _unknown_size_count++; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointerCounts::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void MemoryUsagePointerCounts:: +output(ostream &out) const { + out << _count << " pointers"; + if (_unknown_size_count < _count) { + out << ", "; + output_bytes(out, _size); + out << ", "; + output_bytes(out, _size / (_count - _unknown_size_count)); + out << " each"; + + if (_unknown_size_count != 0) { + out << " (" << _unknown_size_count << " of unknown size)"; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MemoryUsagePointerCounts::output_bytes +// Access: Private, Static +// Description: Formats a size in bytes in a meaningful and concise +// way for output, with units. +//////////////////////////////////////////////////////////////////// +void MemoryUsagePointerCounts:: +output_bytes(ostream &out, size_t size) { + if (size < 4 * 1024) { + out << size << " bytes"; + + } else if (size < 4 * 1024 * 1024) { + out << size / 1024 << " Kb"; + + } else { + out << size / (1024 * 1024) << " Mb"; + } +} diff --git a/panda/src/express/memoryUsagePointerCounts.h b/panda/src/express/memoryUsagePointerCounts.h new file mode 100644 index 0000000000..4e98c44c1e --- /dev/null +++ b/panda/src/express/memoryUsagePointerCounts.h @@ -0,0 +1,67 @@ +// Filename: memoryUsagePointerCounts.h +// Created by: drose (04Jun01) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef MEMORYUSAGEPOINTERCOUNTS_H +#define MEMORYUSAGEPOINTERCOUNTS_H + +#include + +class MemoryInfo; + +#ifndef NDEBUG +//////////////////////////////////////////////////////////////////// +// Class : MemoryUsagePointerCounts +// Description : This is a supporting class for MemoryUsage. It +// tracks the relative counts of a number of pointers of +// some type (or age), for use by TypeHistogram and +// AgeHistogram. It's not exported from the DLL, and it +// doesn't even exist if we're compiling NDEBUG. +//////////////////////////////////////////////////////////////////// +class MemoryUsagePointerCounts { +public: + INLINE MemoryUsagePointerCounts(); + INLINE MemoryUsagePointerCounts(const MemoryUsagePointerCounts ©); + INLINE void operator = (const MemoryUsagePointerCounts ©); + + INLINE void clear(); + void add_info(MemoryInfo &info); + void output(ostream &out) const; + + INLINE bool is_size_unknown() const; + INLINE size_t get_size() const; + INLINE int get_count() const; + + INLINE bool operator < (const MemoryUsagePointerCounts &other) const; + +private: + static void output_bytes(ostream &out, size_t size); + +private: + int _count; + int _unknown_size_count; + size_t _size; +}; + +INLINE ostream &operator << (ostream &out, const MemoryUsagePointerCounts &c); + +#include "memoryUsagePointerCounts.I" + +#endif // NDEBUG + +#endif + diff --git a/panda/src/express/memoryUsagePointers.I b/panda/src/express/memoryUsagePointers.I index 3211db744a..bfbdd8c859 100644 --- a/panda/src/express/memoryUsagePointers.I +++ b/panda/src/express/memoryUsagePointers.I @@ -22,14 +22,14 @@ // Description: //////////////////////////////////////////////////////////////////// INLINE MemoryUsagePointers::Entry:: -Entry(ReferenceCount *ptr, TypedObject *typed_ptr, +Entry(ReferenceCount *ref_ptr, TypedObject *typed_ptr, TypeHandle type, double age) : - _ptr(ptr), + _ref_ptr(ref_ptr), _typed_ptr(typed_ptr), _type(type), _age(age) { - _ptr->ref(); + _ref_ptr->ref(); } //////////////////////////////////////////////////////////////////// @@ -39,12 +39,12 @@ Entry(ReferenceCount *ptr, TypedObject *typed_ptr, //////////////////////////////////////////////////////////////////// INLINE MemoryUsagePointers::Entry:: Entry(const Entry ©) : - _ptr(copy._ptr), + _ref_ptr(copy._ref_ptr), _typed_ptr(copy._typed_ptr), _type(copy._type), _age(copy._age) { - _ptr->ref(); + _ref_ptr->ref(); } //////////////////////////////////////////////////////////////////// @@ -54,12 +54,12 @@ Entry(const Entry ©) : //////////////////////////////////////////////////////////////////// INLINE void MemoryUsagePointers::Entry:: operator = (const Entry ©) { - if (_ptr != copy._ptr) { - _ptr->unref(); - _ptr = copy._ptr; + if (_ref_ptr != copy._ref_ptr) { + _ref_ptr->unref(); + _ref_ptr = copy._ref_ptr; // We can't call unref_delete(), because we don't know what kind // of pointer it is precisely. Potential leak. - _ptr->ref(); + _ref_ptr->ref(); } _typed_ptr = copy._typed_ptr; _type = copy._type; @@ -75,6 +75,6 @@ INLINE MemoryUsagePointers::Entry:: ~Entry() { // We can't call unref_delete(), because we don't know what kind // of pointer it is precisely. Potential leak. - _ptr->unref(); + _ref_ptr->unref(); } diff --git a/panda/src/express/memoryUsagePointers.cxx b/panda/src/express/memoryUsagePointers.cxx index 218bd4695b..f091848031 100644 --- a/panda/src/express/memoryUsagePointers.cxx +++ b/panda/src/express/memoryUsagePointers.cxx @@ -78,7 +78,7 @@ get_num_pointers() const { ReferenceCount *MemoryUsagePointers:: get_pointer(int n) const { nassertr(n >= 0 && n < get_num_pointers(), NULL); - return _entries[n]._ptr; + return _entries[n]._ref_ptr; } //////////////////////////////////////////////////////////////////// @@ -98,7 +98,7 @@ get_typed_pointer(int n) const { return typed_ptr; } - ReferenceCount *ptr = _entries[n]._ptr; + ReferenceCount *ref_ptr = _entries[n]._ref_ptr; TypeHandle type = _entries[n]._type; @@ -116,7 +116,7 @@ get_typed_pointer(int n) const { if (type != TypeHandle::none() && type.is_derived_from(TypedReferenceCount::get_class_type())) { - return (TypedReferenceCount *)ptr; + return (TypedReferenceCount *)ref_ptr; } return NULL; } @@ -176,13 +176,13 @@ clear() { // only by MemoryUsage. //////////////////////////////////////////////////////////////////// void MemoryUsagePointers:: -add_entry(ReferenceCount *ptr, TypedObject *typed_ptr, +add_entry(ReferenceCount *ref_ptr, TypedObject *typed_ptr, TypeHandle type, double age) { // We can't safly add pointers with a zero reference count. They // might be statically-allocated or something, and if we try to add // them they'll try to destruct when the PointerTo later goes away. - if (ptr->get_ref_count() != 0) { - _entries.push_back(Entry(ptr, typed_ptr, type, age)); + if (ref_ptr->get_ref_count() != 0) { + _entries.push_back(Entry(ref_ptr, typed_ptr, type, age)); } } diff --git a/panda/src/express/memoryUsagePointers.h b/panda/src/express/memoryUsagePointers.h index 02936e9eac..58b57cf99c 100644 --- a/panda/src/express/memoryUsagePointers.h +++ b/panda/src/express/memoryUsagePointers.h @@ -64,12 +64,12 @@ PUBLISHED: void clear(); private: - void add_entry(ReferenceCount *ptr, TypedObject *typed_ptr, + void add_entry(ReferenceCount *ref_ptr, TypedObject *typed_ptr, TypeHandle type, double age); class Entry { public: - INLINE Entry(ReferenceCount *ptr, TypedObject *typed_ptr, + INLINE Entry(ReferenceCount *ref_ptr, TypedObject *typed_ptr, TypeHandle type, double age); INLINE Entry(const Entry ©); INLINE void operator = (const Entry ©); @@ -80,7 +80,7 @@ private: // (since ReferenceCount has no public destructor). If we can't // delete it, we can't make a PointerTo it, since PointerTo wants // to be able to delete things. - ReferenceCount *_ptr; + ReferenceCount *_ref_ptr; TypedObject *_typed_ptr; TypeHandle _type; double _age; diff --git a/panda/src/express/referenceCount.h b/panda/src/express/referenceCount.h index e9c3f5096b..f8a9fce2b6 100644 --- a/panda/src/express/referenceCount.h +++ b/panda/src/express/referenceCount.h @@ -118,7 +118,7 @@ private: // only works when the base type is, in fact, a class. //////////////////////////////////////////////////////////////////// template -class EXPCL_PANDAEXPRESS RefCountObj : public Base, public ReferenceCount { +class EXPCL_PANDAEXPRESS RefCountObj : public ReferenceCount, public Base { public: INLINE RefCountObj(); INLINE RefCountObj(const Base ©); diff --git a/panda/src/glgsg/glGraphicsStateGuardian.cxx b/panda/src/glgsg/glGraphicsStateGuardian.cxx index 3b1eb1c89a..de4c4e612c 100644 --- a/panda/src/glgsg/glGraphicsStateGuardian.cxx +++ b/panda/src/glgsg/glGraphicsStateGuardian.cxx @@ -495,9 +495,7 @@ render_frame(const AllAttributesWrapper &initial_state) { // texture state has changed, we have to be sure to clear the // current texture state now. A bit unfortunate, but probably not // measurably expensive. - NodeAttributes state; - state.set_attribute(TextureTransition::get_class_type(), new TextureAttribute); - set_state(state, false); + clear_attribute(TextureTransition::get_class_type()); #endif if (_clear_buffer_type != 0) { @@ -558,9 +556,7 @@ render_frame(const AllAttributesWrapper &initial_state) { // Also force the lighting state to unlit, so that issue_light() // will be guaranteed to be called next frame even if we have the // same set of light pointers we had this frame. - NodeAttributes state; - state.set_attribute(LightTransition::get_class_type(), new LightAttribute); - set_state(state, false); + clear_attribute(LightTransition::get_class_type()); // All this work to undo the lighting state each frame doesn't seem // ideal--there may be a better way. Maybe if the lights were just @@ -1997,15 +1993,11 @@ copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr) { // activate(); set_pack_alignment(1); - NodeAttributes state; - // Bug fix for RE, RE2, and VTX - need to disable texturing in order // for glReadPixels() to work // NOTE: reading the depth buffer is *much* slower than reading the // color buffer - state.set_attribute(TextureTransition::get_class_type(), - new TextureAttribute); - set_state(state, false); + clear_attribute(TextureTransition::get_class_type()); int xo, yo, w, h; dr->get_region_pixels(xo, yo, w, h); diff --git a/panda/src/graph/nodeAttributes.I b/panda/src/graph/nodeAttributes.I index cf73e24dcc..16ba260ed8 100644 --- a/panda/src/graph/nodeAttributes.I +++ b/panda/src/graph/nodeAttributes.I @@ -77,6 +77,16 @@ insert(NodeAttributes::iterator position, return _attributes.insert(position, x); } +//////////////////////////////////////////////////////////////////// +// Function: NodeAttributes::find +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE_GRAPH NodeAttributes::iterator NodeAttributes:: +find(const key_type &k) { + return _attributes.find(k); +} + //////////////////////////////////////////////////////////////////// // Function: NodeAttributes::erase // Access: Public diff --git a/panda/src/graph/nodeAttributes.h b/panda/src/graph/nodeAttributes.h index 93d0cecd73..18e645fa82 100644 --- a/panda/src/graph/nodeAttributes.h +++ b/panda/src/graph/nodeAttributes.h @@ -64,6 +64,7 @@ public: // of PANDA.DLL. typedef Attributes::iterator iterator; typedef Attributes::const_iterator const_iterator; + typedef Attributes::key_type key_type; typedef Attributes::value_type value_type; typedef Attributes::size_type size_type; @@ -73,6 +74,7 @@ public: INLINE_GRAPH const_iterator begin() const; INLINE_GRAPH const_iterator end() const; INLINE_GRAPH iterator insert(iterator position, const value_type &x); + INLINE_GRAPH iterator find(const key_type &k); INLINE_GRAPH void erase(iterator position); public: diff --git a/panda/src/graph/nodeRelation.I b/panda/src/graph/nodeRelation.I index fc26edbf73..f07555907a 100644 --- a/panda/src/graph/nodeRelation.I +++ b/panda/src/graph/nodeRelation.I @@ -289,6 +289,21 @@ get_last_update() const { return _last_update; } +//////////////////////////////////////////////////////////////////// +// Function: NodeRelation::clear_wrt_cache +// Access: Public +// Description: Blows away the wrt cache information on this arc. +// This forces the wrt to be recomputed next time it is +// asked for. +//////////////////////////////////////////////////////////////////// +INLINE_GRAPH void NodeRelation:: +clear_wrt_cache() { + _net_transitions.clear(); + _top_subtree = (Node *)NULL; + _all_verified = UpdateSeq::initial(); + _last_update = ++last_graph_update(_graph_type); +} + //////////////////////////////////////////////////////////////////// // Function: NodeRelation::create_typed_arc // Access: Public, Static diff --git a/panda/src/graph/nodeRelation.h b/panda/src/graph/nodeRelation.h index 7f6084b237..6cdfffdc40 100644 --- a/panda/src/graph/nodeRelation.h +++ b/panda/src/graph/nodeRelation.h @@ -122,6 +122,7 @@ PUBLISHED: INLINE_GRAPH int compare_transitions_to(const NodeRelation *arc) const; INLINE_GRAPH UpdateSeq get_last_update() const; + INLINE_GRAPH void clear_wrt_cache(); public: bool sub_render_trans(const AllAttributesWrapper &attrib, diff --git a/panda/src/pstatclient/pStatClient.cxx b/panda/src/pstatclient/pStatClient.cxx index ea0a94a32d..aed24bc2b0 100644 --- a/panda/src/pstatclient/pStatClient.cxx +++ b/panda/src/pstatclient/pStatClient.cxx @@ -39,6 +39,10 @@ PStatClient *PStatClient::_global_pstats = NULL; +#ifndef CPPPARSER +PStatCollector _memory_usage_pcollector("Panda memory usage"); +#endif + //////////////////////////////////////////////////////////////////// // Function: PStatClient::PerThreadData::Constructor // Access: Public @@ -363,6 +367,14 @@ make_thread(const string &name) { //////////////////////////////////////////////////////////////////// void PStatClient:: main_tick() { + // We have code here to report the memory usage. We can't put this + // code inside the MemoryUsage class, where it fits a little better, + // simply because MemoryUsage is a very low-level class that doesn't + // know about PStatClient. + if (MemoryUsage::is_tracking()) { + _memory_usage_pcollector.set_level(MemoryUsage::get_total_size()); + } + get_global_pstats()->get_main_thread().new_frame(); } diff --git a/panda/src/pstatclient/pStatProperties.cxx b/panda/src/pstatclient/pStatProperties.cxx index 850aa7b6ed..ac298f097f 100644 --- a/panda/src/pstatclient/pStatProperties.cxx +++ b/panda/src/pstatclient/pStatProperties.cxx @@ -147,6 +147,7 @@ static LevelCollectorProperties level_properties[] = { { 1, "State changes", { 1.0, 0.5, 0.2 }, "", 500.0 }, { 1, "State changes:Transforms", { 0.2, 0.2, 0.8 }, }, { 1, "State changes:Textures", { 0.8, 0.2, 0.2 }, }, + { 1, "Panda memory usage", { 0.8, 0.2, 0.5 }, "MB", 64, 1048576 }, { 0, NULL } }; diff --git a/panda/src/putil/pointerToArray.I b/panda/src/putil/pointerToArray.I index aa133758d3..4a1486ce71 100644 --- a/panda/src/putil/pointerToArray.I +++ b/panda/src/putil/pointerToArray.I @@ -17,10 +17,10 @@ //////////////////////////////////////////////////////////////////// template -vector PointerToArray::_empty_array; +pvector PointerToArray::_empty_array; template -vector ConstPointerToArray::_empty_array; +pvector ConstPointerToArray::_empty_array; //////////////////////////////////////////////////////////////////// // Function: PointerToArray::Constructor @@ -30,7 +30,7 @@ vector ConstPointerToArray::_empty_array; template INLINE PointerToArray:: PointerToArray() : - PointerToBase > >((RefCountObj > *)NULL) + PointerToBase > >((RefCountObj > *)NULL) { } @@ -42,7 +42,7 @@ PointerToArray() : template INLINE PointerToArray:: PointerToArray(size_type n) : - PointerToBase > >(new RefCountObj >) { + PointerToBase > >(new RefCountObj >) { _ptr->reserve(n); insert(begin(), n, Element()); } @@ -55,7 +55,7 @@ PointerToArray(size_type n) : template INLINE PointerToArray:: PointerToArray(size_type n, const Element &value) : - PointerToBase > >(new RefCountObj >) { + PointerToBase > >(new RefCountObj >) { _ptr->reserve(n); insert(begin(), n, value); } @@ -68,7 +68,7 @@ PointerToArray(size_type n, const Element &value) : template INLINE PointerToArray:: PointerToArray(const PointerToArray ©) : - PointerToBase > >(copy) + PointerToBase > >(copy) { } @@ -148,7 +148,7 @@ template INLINE PointerToArray::size_type PointerToArray:: max_size() const { nassertd(_ptr != NULL) { - ((PointerToArray *)this)->reassign(new RefCountObj >); + ((PointerToArray *)this)->reassign(new RefCountObj >); } return _ptr->max_size(); } @@ -173,7 +173,7 @@ template INLINE void PointerToArray:: reserve(PointerToArray::size_type n) { if (_ptr == NULL) { - reassign(new RefCountObj >); + reassign(new RefCountObj >); } _ptr->reserve(n); } @@ -199,7 +199,7 @@ template INLINE PointerToArray::reference PointerToArray:: front() const { nassertd(_ptr != NULL) { - ((PointerToArray *)this)->reassign(new RefCountObj >); + ((PointerToArray *)this)->reassign(new RefCountObj >); } nassertd(!_ptr->empty()) { _ptr->push_back(Element()); @@ -216,7 +216,7 @@ template INLINE PointerToArray::reference PointerToArray:: back() const { nassertd(_ptr != NULL) { - ((PointerToArray *)this)->reassign(new RefCountObj >); + ((PointerToArray *)this)->reassign(new RefCountObj >); } nassertd(!_ptr->empty()) { _ptr->push_back(Element()); @@ -261,7 +261,7 @@ template INLINE void PointerToArray:: erase(iterator position) const { nassertd(_ptr != NULL) { - ((PointerToArray *)this)->reassign(new RefCountObj >); + ((PointerToArray *)this)->reassign(new RefCountObj >); } nassertv(position >= _ptr->begin() && position <= _ptr->end()); @@ -277,7 +277,7 @@ template INLINE void PointerToArray:: erase(iterator first, iterator last) const { nassertd(_ptr != NULL) { - ((PointerToArray *)this)->reassign(new RefCountObj >); + ((PointerToArray *)this)->reassign(new RefCountObj >); } nassertv(first >= _ptr->begin() && first <= _ptr->end()); nassertv(last >= _ptr->begin() && last <= _ptr->end()); @@ -294,7 +294,7 @@ template INLINE PointerToArray::reference PointerToArray:: operator [](size_type n) const { nassertd(_ptr != NULL) { - ((PointerToArray *)this)->reassign(new RefCountObj >); + ((PointerToArray *)this)->reassign(new RefCountObj >); } nassertd(!_ptr->empty()) { _ptr->push_back(Element()); @@ -313,7 +313,7 @@ template INLINE void PointerToArray:: push_back(const Element &x) { if (_ptr == NULL) { - reassign(new RefCountObj >); + reassign(new RefCountObj >); } _ptr->push_back(x); } @@ -327,7 +327,7 @@ template INLINE void PointerToArray:: pop_back() { nassertd(_ptr != NULL) { - ((PointerToArray *)this)->reassign(new RefCountObj >); + ((PointerToArray *)this)->reassign(new RefCountObj >); } nassertv(!_ptr->empty()); _ptr->pop_back(); @@ -344,7 +344,7 @@ template INLINE void PointerToArray:: make_empty() { nassertd(_ptr != NULL) { - ((PointerToArray *)this)->reassign(new RefCountObj >); + ((PointerToArray *)this)->reassign(new RefCountObj >); } nassertv(!_ptr->empty()); _ptr->clear(); @@ -385,10 +385,10 @@ p() const { // with some of the vector's esoteric functionality. //////////////////////////////////////////////////////////////////// template -INLINE vector &PointerToArray:: +INLINE pvector &PointerToArray:: v() const { nassertd(_ptr != NULL) { - ((PointerToArray *)this)->reassign(new RefCountObj >); + ((PointerToArray *)this)->reassign(new RefCountObj >); } return *_ptr; } @@ -403,7 +403,7 @@ template INLINE void* PointerToArray:: get_void_ptr() const { - return PointerToBase > >::_ptr; + return PointerToBase > >::_ptr; } //////////////////////////////////////////////////////////////////// @@ -415,7 +415,7 @@ template INLINE void PointerToArray:: set_void_ptr(void* p) { - reassign((RefCountObj > *)p); + reassign((RefCountObj > *)p); } //////////////////////////////////////////////////////////////////// // Function: PointerToArray::get_ref_count @@ -435,7 +435,7 @@ get_ref_count() const { //////////////////////////////////////////////////////////////////// template INLINE PointerToArray &PointerToArray:: -operator = (RefCountObj > *ptr) { +operator = (RefCountObj > *ptr) { reassign(ptr); return *this; } @@ -462,7 +462,7 @@ operator = (const PointerToArray ©) { template INLINE void PointerToArray:: clear() { - reassign((RefCountObj > *)NULL); + reassign((RefCountObj > *)NULL); } @@ -475,7 +475,7 @@ clear() { template INLINE ConstPointerToArray:: ConstPointerToArray() : - PointerToBase > >((RefCountObj > *)NULL) + PointerToBase > >((RefCountObj > *)NULL) { } @@ -487,7 +487,7 @@ ConstPointerToArray() : template INLINE ConstPointerToArray:: ConstPointerToArray(const PointerToArray ©) : - PointerToBase > >(copy) + PointerToBase > >(copy) { } @@ -499,7 +499,7 @@ ConstPointerToArray(const PointerToArray ©) : template INLINE ConstPointerToArray:: ConstPointerToArray(const ConstPointerToArray ©) : - PointerToBase > >(copy) + PointerToBase > >(copy) { } @@ -579,7 +579,7 @@ template INLINE ConstPointerToArray::size_type ConstPointerToArray:: max_size() const { nassertd(_ptr != NULL) { - ((ConstPointerToArray *)this)->reassign(new RefCountObj >); + ((ConstPointerToArray *)this)->reassign(new RefCountObj >); } return _ptr->max_size(); } @@ -604,7 +604,7 @@ template INLINE ConstPointerToArray::size_type ConstPointerToArray:: capacity() const { nassertd(_ptr != NULL) { - ((ConstPointerToArray *)this)->reassign(new RefCountObj >); + ((ConstPointerToArray *)this)->reassign(new RefCountObj >); } return _ptr->capacity(); } @@ -619,7 +619,7 @@ template INLINE ConstPointerToArray::reference ConstPointerToArray:: operator[](size_type n) const { nassertd(_ptr != NULL) { - ((ConstPointerToArray *)this)->reassign(new RefCountObj >); + ((ConstPointerToArray *)this)->reassign(new RefCountObj >); } nassertd(!_ptr->empty()) { _ptr->push_back(Element()); @@ -638,7 +638,7 @@ template INLINE ConstPointerToArray::reference ConstPointerToArray:: front() const { nassertd(_ptr != NULL) { - ((ConstPointerToArray *)this)->reassign(new RefCountObj >); + ((ConstPointerToArray *)this)->reassign(new RefCountObj >); } nassertd(!_ptr->empty()) { _ptr->push_back(Element()); @@ -655,7 +655,7 @@ template INLINE ConstPointerToArray::reference ConstPointerToArray:: back() const { nassertd(_ptr != NULL) { - ((ConstPointerToArray *)this)->reassign(new RefCountObj >); + ((ConstPointerToArray *)this)->reassign(new RefCountObj >); } nassertd(!_ptr->empty()) { _ptr->push_back(Element()); @@ -698,10 +698,10 @@ p() const { // with some of the vector's esoteric functionality. //////////////////////////////////////////////////////////////////// template -INLINE const vector &ConstPointerToArray:: +INLINE const pvector &ConstPointerToArray:: v() const { nassertd(_ptr != NULL) { - ((ConstPointerToArray *)this)->reassign(new RefCountObj >); + ((ConstPointerToArray *)this)->reassign(new RefCountObj >); } return *_ptr; } @@ -724,7 +724,7 @@ get_ref_count() const { //////////////////////////////////////////////////////////////////// template INLINE ConstPointerToArray &ConstPointerToArray:: -operator = (RefCountObj > *ptr) { +operator = (RefCountObj > *ptr) { reassign(ptr); return *this; } @@ -763,6 +763,6 @@ operator = (const ConstPointerToArray ©) { template INLINE void ConstPointerToArray:: clear() { - reassign((RefCountObj > *)NULL); + reassign((RefCountObj > *)NULL); } diff --git a/panda/src/putil/pointerToArray.h b/panda/src/putil/pointerToArray.h index 193d12fde3..1bcf0254c2 100644 --- a/panda/src/putil/pointerToArray.h +++ b/panda/src/putil/pointerToArray.h @@ -72,8 +72,7 @@ #include "referenceCount.h" #include "pointerTo.h" - -#include +#include "pvector.h" //////////////////////////////////////////////////////////////////// // Class : PointerToArray @@ -84,17 +83,17 @@ // with a reference count. //////////////////////////////////////////////////////////////////// template -class PointerToArray : public PointerToBase > > { +class PointerToArray : public PointerToBase > > { public: - typedef vector::value_type value_type; - typedef vector::reference reference; - typedef vector::const_reference const_reference; - typedef vector::iterator iterator; - typedef vector::const_iterator const_iterator; - typedef vector::reverse_iterator reverse_iterator; - typedef vector::const_reverse_iterator const_reverse_iterator; - typedef vector::difference_type difference_type; - typedef vector::size_type size_type; + typedef pvector::value_type value_type; + typedef pvector::reference reference; + typedef pvector::const_reference const_reference; + typedef pvector::iterator iterator; + typedef pvector::const_iterator const_iterator; + typedef pvector::reverse_iterator reverse_iterator; + typedef pvector::const_reverse_iterator const_reverse_iterator; + typedef pvector::difference_type difference_type; + typedef pvector::size_type size_type; PUBLISHED: INLINE PointerToArray(); @@ -153,7 +152,7 @@ public: INLINE operator Element *() const; INLINE Element *p() const; - INLINE vector &v() const; + INLINE pvector &v() const; //These functions are only to be used in Reading through BamReader. //They are designed to work in pairs, so that you register what is @@ -168,7 +167,7 @@ public: // Reassignment is by pointer, not memberwise as with a vector. INLINE PointerToArray & - operator = (RefCountObj > *ptr); + operator = (RefCountObj > *ptr); INLINE PointerToArray & operator = (const PointerToArray ©); INLINE void clear(); @@ -179,7 +178,7 @@ private: // NULL pointer. It might not be shared properly between different // .so's, since it's a static member of a template class, but we // don't really care. - static vector _empty_array; + static pvector _empty_array; }; //////////////////////////////////////////////////////////////////// @@ -188,22 +187,22 @@ private: // may not be modified. //////////////////////////////////////////////////////////////////// template -class ConstPointerToArray : public PointerToBase > > { +class ConstPointerToArray : public PointerToBase > > { public: - typedef vector::value_type value_type; - typedef vector::const_reference reference; - typedef vector::const_reference const_reference; - typedef vector::const_iterator iterator; - typedef vector::const_iterator const_iterator; + typedef pvector::value_type value_type; + typedef pvector::const_reference reference; + typedef pvector::const_reference const_reference; + typedef pvector::const_iterator iterator; + typedef pvector::const_iterator const_iterator; #ifdef WIN32_VC // VC++ seems to break the const_reverse_iterator definition somehow. - typedef vector::reverse_iterator reverse_iterator; + typedef pvector::reverse_iterator reverse_iterator; #else - typedef vector::const_reverse_iterator reverse_iterator; + typedef pvector::const_reverse_iterator reverse_iterator; #endif - typedef vector::const_reverse_iterator const_reverse_iterator; - typedef vector::difference_type difference_type; - typedef vector::size_type size_type; + typedef pvector::const_reverse_iterator const_reverse_iterator; + typedef pvector::difference_type difference_type; + typedef pvector::size_type size_type; INLINE ConstPointerToArray(); INLINE ConstPointerToArray(const PointerToArray ©); @@ -233,13 +232,13 @@ public: INLINE operator const Element *() const; INLINE const Element *p() const; - INLINE const vector &v() const; + INLINE const pvector &v() const; INLINE int get_ref_count() const; // Reassignment is by pointer, not memberwise as with a vector. INLINE ConstPointerToArray & - operator = (RefCountObj > *ptr); + operator = (RefCountObj > *ptr); INLINE ConstPointerToArray & operator = (const PointerToArray ©); INLINE ConstPointerToArray & @@ -252,7 +251,7 @@ private: // NULL pointer. It might not be shared properly between different // .so's, since it's a static member of a template class, but we // don't really care. - static vector _empty_array; + static pvector _empty_array; }; diff --git a/panda/src/sgmanip/nodePath.I b/panda/src/sgmanip/nodePath.I index 9efbb72aa9..e3cee89a26 100644 --- a/panda/src/sgmanip/nodePath.I +++ b/panda/src/sgmanip/nodePath.I @@ -1446,12 +1446,14 @@ hide_collision_solids() { //////////////////////////////////////////////////////////////////// // Function: NodePath::is_hidden // Access: Public -// Description: Returns true if some arc above this bottom node has -// been set to 'hide', false if it should be visible. +// Description: Returns true if the bottom arc has been hidden, +// false otherwise. The bottom node may still be +// invisible due to a higher ancestor having been +// hidden; use get_hidden_ancestor() to check this. //////////////////////////////////////////////////////////////////// INLINE bool NodePath:: is_hidden() const { - return !get_hidden_ancestor().is_empty(); + return (arc()->has_transition(PruneTransition::get_class_type())); } //////////////////////////////////////////////////////////////////// @@ -1489,10 +1491,26 @@ unstash() { //////////////////////////////////////////////////////////////////// // Function: NodePath::is_stashed // Access: Public -// Description: Returns true if some arc above this bottom node has -// been set to 'stash', false if it should be visible. +// Description: Returns true if the bottom arc has been 'stashed', +// false otherwise. //////////////////////////////////////////////////////////////////// INLINE bool NodePath:: is_stashed() const { - return !get_stashed_ancestor().is_empty(); + return (arc()->get_graph_type() == NodeRelation::get_stashed_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::clear_wrt_cache +// Access: Public +// Description: Recursively calls clear_wrt_cache() on every arc +// beginning at the bottom arc and below. This wipes +// out the cached wrt information, which will make the +// next call to wrt() more expensive, but may reclaim +// some memory and free up some otherwise unused +// pointers. +//////////////////////////////////////////////////////////////////// +INLINE void NodePath:: +clear_wrt_cache() { + nassertv_always(!is_empty()); + r_clear_wrt_cache(arc()); } diff --git a/panda/src/sgmanip/nodePath.cxx b/panda/src/sgmanip/nodePath.cxx index d6c3bb7c93..770eb5e344 100644 --- a/panda/src/sgmanip/nodePath.cxx +++ b/panda/src/sgmanip/nodePath.cxx @@ -3147,3 +3147,23 @@ r_adjust_all_priorities(NodeRelation *arc, int adjustment) { r_adjust_all_priorities(child_arc, adjustment); } } + +//////////////////////////////////////////////////////////////////// +// Function: NodePath::r_clear_wrt_cache +// Access: Private +// Description: The recursive implementation of +// clear_wrt_cache(). This walks through the +// subgraph defined by the indicated arc and below. +//////////////////////////////////////////////////////////////////// +void NodePath:: +r_clear_wrt_cache(NodeRelation *arc) { + arc->clear_wrt_cache(); + + Node *dnode = arc->get_child(); + + int num_children = dnode->get_num_children(_graph_type); + for (int i = 0; i < num_children; i++) { + NodeRelation *child_arc = dnode->get_child(_graph_type, i); + r_clear_wrt_cache(child_arc); + } +} diff --git a/panda/src/sgmanip/nodePath.h b/panda/src/sgmanip/nodePath.h index fe829bbb3b..806eac2d97 100644 --- a/panda/src/sgmanip/nodePath.h +++ b/panda/src/sgmanip/nodePath.h @@ -510,6 +510,7 @@ PUBLISHED: NodePath get_stashed_ancestor() const; void prepare_scene(GraphicsStateGuardianBase *gsg); + INLINE void clear_wrt_cache(); void show_bounds(); void hide_bounds(); @@ -542,6 +543,7 @@ private: void r_list_transitions(ostream &out, int indent_level) const; void r_adjust_all_priorities(NodeRelation *arc, int adjustment); + void r_clear_wrt_cache(NodeRelation *arc); // It's important that there are no data members in this class. Put // them in NodePathBase instead.