Memory Cleanups

russellbartley
Posts: 9
Joined: Thu Sep 04, 2008 4:17 am

Memory Cleanups

Post by russellbartley »

Hi,

I have made a few changes to the way bullet handles its memory, and I'd like to offer them as options to be merged into the next release (so I have less merges :)) I'd like some feedback if possible on these suggestions too.

inside of btHashesdOverlappingPairCache I found it did quite a few allocs/reallocs as the number of objects increased. This fragmented my memory quite a bit, so I added a consructor that allocates the hash table to an initial size, so it doesnt need to do all those small allocs/reallocs as it expands heres what I added:

> btHashedOverlappingPairCache::btHashedOverlappingPairCache(int initialAllocatedSize):
> m_overlapFilterCallback(0),
> m_blockedForChanges(false)
> {
> m_overlappingPairArray.reserve(initialAllocatedSize);
> growTables();
> }
>


I also changed the call to this constructor in btAxisSweep3.h:

< m_pairCache = new(ptr) btHashedOverlappingPairCache();
---
> m_pairCache = new(ptr) btHashedOverlappingPairCache(userMaxHandles);

I added code to the constraint solver: btSequentialImpulseConstraintSolver::reset() to free up any memory it was using, so I could free up all the memory it was using without shutting down bullet. This allowed me to free this memory if I was destroying the last scene I was using:


void btSequentialImpulseConstraintSolver::reset()
{
m_btSeed2 = 0;
m_tmpSolverBodyPool.clear();
m_tmpSolverConstraintPool.clear();
m_tmpSolverFrictionConstraintPool.clear();
m_orderTmpConstraintPool.clear();
m_orderFrictionConstraintPool.clear();
}

Thanks

Russ
rusty
Posts: 25
Joined: Fri Sep 19, 2008 10:23 am

Re: Memory Cleanups

Post by rusty »

Hey Russ, I see you've switched from ODE to Bullet then!

It's a neat change, especially if you're working on console. Where does your code pull the initial allocation size for the colliding pairs cache? Is this maybe something that should be put into btCollisionConfiguration?
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Re: Memory Cleanups

Post by Erwin Coumans »

Good points.

Code: Select all

> btHashedOverlappingPairCache::btHashedOverlappingPairCache(int initialAllocatedSize):
[...]
and

Code: Select all

void	btSequentialImpulseConstraintSolver::reset()
[...]
Will be accepted, but changing btAxisSweep3 isn't necessary: the btAxisSweep3 accepts an optional argument to pass in the pair cache, so it would be better to construct your pair cache, and pass it in the constructor:

Code: Select all

int initialMaxPairs = 32768;//reserve 2 pairs per object
int maxHandles = 16384;
btHashesdOverlappingPairCache * pairCache = new btHashesdOverlappingPairCache(initialMaxPairs);
broadphase = new btAxisSweep3(worldMin,worldMax,maxHandles,pairCache);
Is this ok with you?
Thanks,
Erwin
russellbartley
Posts: 9
Joined: Thu Sep 04, 2008 4:17 am

Re: Memory Cleanups

Post by russellbartley »

Cool,

thanks for the quick response erwin. (you too rusty).

I've modified my project to take responsibility for the btHashedOverlappingPairCache, and reverted the btAxisSweep change.

I'm currently wrestling with a serialize/deserializeInPlace bug in CodeWarrior, as CodeWarrior loves to put the vtbl at the front of a class before the data members, while all other configs put it at the end. When I do a serialize on the PC, then later at runtime do a deserialiseInPlace in CodeWarrior, codeWarrior gets the deserialize wrong. When I have a solution I'll post it up for review.

Thanks
rusty
Posts: 25
Joined: Fri Sep 19, 2008 10:23 am

Re: Memory Cleanups

Post by rusty »

Russ, just a thought here. What platform are you using Codewarrior/Radix for? If it's the Wii, surely you're going to run into endian issues by just reading in seriailzed data that's just blindly written out on the PC?
russellbartley
Posts: 9
Joined: Thu Sep 04, 2008 4:17 am

Re: Memory Cleanups

Post by russellbartley »

Yep, already handling the endianness ok, just the virtuals are a headache.

currently adding a fix that saves it out as a c-struct, then rebuilding from that.

Any c++ simple solution would be appreciated :)
rusty
Posts: 25
Joined: Fri Sep 19, 2008 10:23 am

Re: Memory Cleanups

Post by rusty »

russellbartley wrote:Yep, already handling the endianness ok, just the virtuals are a headache.

currently adding a fix that saves it out as a c-struct, then rebuilding from that.

Any c++ simple solution would be appreciated :)
I don't think that there IS an elegant solution to that issues, atleast, not one that involves adding big changes to your tool that writes out the data and adding support for a "flat" format of the data in the game app.

Anyhow...don't want to derail this thread any more than I have. I'm sure "she'll be right" once you've gotten it all to work :)
sfichele
Posts: 1
Joined: Thu Oct 09, 2008 5:22 pm

Re: Memory Cleanups

Post by sfichele »

what about doing a memmove on the class data by 4 bytes?

For example in the btOptimizedBvh::deserialize

btOptimizedBvh *targetBvh = (btOptimizedBvh *)o_alignedDataBuffer;

#if PLATFORM_WII
memmove((unsigned char*)targetBvh + 4, (u8*)targetBvh, sizeof(btOptimizedBvh) - 4); // would (hopefully) place the data in the right place
#endif

// construct the class so the virtual function table, etc will be set up
// Also, m_leafNodes and m_quantizedLeafNodes will be initialized to default values by the constructor
new (targetBvh) btOptimizedBvh;

----
OR
----

All the serialisation code could place 4 bytes before any output. Then when deserialising, if its the wii then deserialise straight from the buffer, otherwise for other compilers add 4 bytes to the buffer before deserialising. Which avoids costly memmoves.

Stan...
Sam Hanke
Posts: 1
Joined: Fri Jan 09, 2009 10:43 am

Re: Memory Cleanups

Post by Sam Hanke »

We've run into this exact same issue.

I've watched the memory whilst serializing/de-serializing on both platforms and on PC the address of the first member of the btQuantizedBvh compared to the address of the btQuantizedBvh class itself is offset by 16 bytes. This could possibly be accounted for by the vfptr if proceeded directly by the virtual function table (however, as there are only 2 virtual functions in that class, that still leaves 4 bytes that I can't explain).

On Wii the address of the first member is the same as the address of the structure, so if the vfptr and table were located elsewhere on this platform then that would fit nicely with the explanation above.

As a quick hack, I tried de-serialising on Wii from the 16th byte in the buffer and it returned a pointer to what looked like a valid bvh. However, it still doesn't work and nothing collides as the same problem would have cropped up whilst initialising the subtree headers from the buffer.

I guess that's what you get when trying to stream out/in a class as raw data like this. So basically, the current serialize/de-serialize implementations aren't going to work cross platform. The c-struct method could work but it'd be messy as there are lots of classes that would need duplicating as c-structs to get this to work. Perhaps alternatively, if Bullet could be given file streaming capability by assigning pointers to functions for streaming in/out arrays of single bytes (kinda like how the debug drawer requires the registration of an external line drawing function) then the serialize/de-serialize functions could be tweaked to use those functions to stream each member of the bvh seperately... just a thought.

Else, there is one remaining (less than ideal) option which would involve building the cached versions of the rigid bodies on the target platform. The Wii for example has a hio library inside the RVL_SDK that can be used to send data back from NDEV to PC. We already use this to build platform specific stuff (like meshes and mip-mapped texture structures for our renderer). It's not ideal as it's slow and complicates the build process, but at least it'd get the job done.


Edit:

I've just tried building the rigid bodies entirely on Wii. Turns out there is a small caveat in that the last line of the serialize function has to be removed in order for it to work on Wii:

// this wipes the virtual function table pointer at the start of the buffer for the class
*((void**)o_alignedDataBuffer) = NULL;
Otherwise, because the address of the first member is at the address of the bvh structure on Wii, all it does is null bvh->m_bvhAabbMin[0].

Sam
kester
Posts: 27
Joined: Mon Dec 01, 2008 5:08 am

Re: Memory Cleanups

Post by kester »

The hack we use on Codewarrior Wii to put the vtable at the start of the class is to derive from an empty base class:

Code: Select all

///Because CodeWarrior for Wii puts the vtable at the end of the class rather than beginning,
///we need to force its hand so that Bullet's serialisation works.
class btHackEmptyBase
{
public:
	virtual ~btHackEmptyBase() { }
};

///The btQuantizedBvh class stores an AABB tree that can be quickly traversed on CPU and Cell SPU.
///It is used by the btBvhTriangleMeshShape as midphase, and by the btMultiSapBroadphase.
///It is recommended to use quantization for better performance and lower memory requirements.
ATTRIBUTE_ALIGNED16_BEGIN(class) btQuantizedBvh : public btHackEmptyBase
{
public:
The rest of the serialization/deserialization should work properly after that.