Hi everyone,
we have been working with dynamic collision meshes lately and ran into a problem with CollisionComponent / RenderComponent.
I have created a minimal reproducer here:
https://playcanvas.com/project/931187/overview/collision-mesh-leak
We basically create a new Entity from script, assign a Collision-, Render- and Rigidbody Component to it and set the RenderComponent’s asset to a RenderAsset (Editor Attribute) and the CollisionComponent’s renderAsset to the same asset.
This is the code:
this.spawnedEntity = new pc.Entity();
this.spawnedEntity.addComponent("collision", {
type: "mesh"
});
this.spawnedEntity.addComponent("rigidbody", { type: pc.BODYTYPE_STATIC });
this.spawnedEntity.addComponent("render", {type: "asset"});
this.spawnedEntity.render.asset = this.meshAsset;
this.spawnedEntity.render.layers = [this.app.scene.layers.getLayerByName("World").id];
this.spawnedEntity.render.material = this.meshMaterial;
this.spawnedEntity.collision.renderAsset = this.meshAsset;
this.entity.addChild(this.spawnedEntity);
this.spawnedEntity.setPosition(0,0,0);
this.spawnedEntity.setLocalPosition(0,0,0);
We later destroy that spawned entity again by calling Entity.destroy()
.
We would expect the destroy call to clean up all allocated memory accordingly. However, when creating and destroying a lot of entities in this manner, Ammo will report a OOM Exception after a while. In the reproducer we create and destroy 10 000 entities each frame and ammo will run out of memory after about 2 seconds. We used a simple mesh of a box. With more complex meshes, this will happen even faster.
I have dug into the engine code for a couple of hours now but can’t seem to find an easy fix for this.
So I’m now asking for your help.
Am I doing something wrong during entity creation?
Am I supposed to manually clean up any resources when dynamically creating an entity like this?
Some technical details that might be helpful / of interest to you:
By digging through memory snapshots and the engine code, I noticed that when Components get added to an Entity, their data is registered in an internal store
of the corresponding system. I.e. the RigidbodyComponentSystem will store RigidbodyComponentData for each RigidbodyComponent that is created. I noticed that in this scenario the Rigidbody does not contain a body
entry in it’s data at creation. However, the initialization of the CollisionComponent will cause recreatePhysicalShapes
of the CollisionComponentSystem to be called, which will call createBody
for the RigidbodyComponent of the Entity. The created body will be allocated and stored in the RigidbodyComponent, but the RigidbodyComponentData in the System’s store
is never updated. Usually the allocated body will be freed in the onRemove
, when a RigidbodyComponent is removed from an Entity, but since the data does not contain a body entry, the memory allocated by the body is leaked.
I tried to workaround this by manually setting the body information later (see commented line in the reproducer), but that only delayed the OOM error by a couple of seconds:
this.spawnedEntity.rigidbody.system.store[this.spawnedEntity.getGuid()].data.body = this.spawnedEntity.rigidbody.body;