How to free the VRAM of destroyed entities


#1

Hey guys,

I am working on a small test project to compare per vertex and per pixel shaded meshes, because I was interested if there are some use cases in which one technique has advantages over the other. For example, regarding the download size of the assets.

So I build a small test scene and swap out the models in the scene to compare things like the size of the models/textures, VRAM usage, render time etc. But I have some troubles to figure out, how to free the VRAM again. As I don’t want to create a new scene for every mesh I thought, that I just spawn the different models programmatically. Check the stats from the debugger, switch to the next model and delete the first entity.

But the VRAM usage stays the same after I destroy the entity with .destroy()
After a while I figured out, that I can reduce the VRAM usage, if I also destroy the applied textures manually like this:

// Get the material of the model
var material = entity.model.model.meshInstances[0].material;
// and its diffuse, to free the used resources
var diffuse = material.diffuseMap;
if(diffuse !== null) diffuse.destroy();

But this causes, that the material stays black, if I create the entity again. The diffuse texture is still applied to the material and I also tried to upload and reassign the texture to the material, or to update the material itself, but the result stays the same.

Another thing that I noticed is, that the profiler displays a negative VRAM usage if I call the destroy function multiple times on the texture. Any ideas how I can correctly free the VRAM, if I want to swap between multiple objects in the scene, while just seeing the stats of the currently enabled/existing object in the scene?

Greetings,
Martin


#2

Thanks @swatty,

Calling texture.destroy() should free up the VRAM usage. We don’t currently do this in any automated way and you have to handle it yourself as you’ve discovered.

In your code I think you will need to add the lines:

material.diffuseMap = null;
material.update();

at the end to ensure that the material isn’t referencing the destroyed texture. That should prevent the black material.

It looks like destroying a texture multiple times leads to a bug as we’re not checking that it has already been deleted. I’ve created a github issue for this. You can watch the issue to be notified when we’ve fixed it! :smile:


#3

Hey dave,

as usual, thanks for the quick reply and creating the github issue for me. So the guy called
frank can make it up, that he broke the shader chunks a while ago :stuck_out_tongue_winking_eye:

I quickly added your code and checked, if that is resolving the issue with the black material, but that seems to not be the case. If I assign a different texture to the same material it renders proper, but I cannot reassign the destroyed texture again.

Also not, if I try to manually push it into the graphics memory like this:

material.diffuseMap = app.assets.get( this.targetTexture[0]).resource;  
material.diffuseMap.upload();
material.update();

Can it be, that the upload function is broken, or that I simple use it in the wrong way?

I am also not sure how to do same thing with the vertex and index buffer. I saw in the documentation that they both have a destroy function, but I am not sure how to access it correctly.

I tried it this way:

var entity = app.root.findByName(this.targetName);
entity.model.model.meshInstances[0].mesh.indexBuffer[0].destroy();
entity.model.model.meshInstances[0].mesh.vertexBuffer.destroy();

In that case the model ends up like this after the first call:

Although it was a model of a star upfront, this was not the result I expected to get :grin:
After the second call it completely disappears, but the debugger shows just a slight decrease of a few kb in the VRAM usage, though I would expect it to go near zero, as this is the only model in the scene.

If you like I could create a separate test project tomorrow, might be a bit easier to show the problem.

Greetings,

Martin


#4

Maybe I mis-understood.

This code:

// Get the material of the model
var material = entity.model.model.meshInstances[0].material;
// and its diffuse, to free the used resources
var diffuse = material.diffuseMap;
if(diffuse !== null) diffuse.destroy();
material.diffuseMap = null;
material.update();

will remove the diffuse map from the material and destroy the texture. You can’t re-use the texture after destroying it.

Getting the same texture back after destroying it is a little complicated. We need to do a bit more work to make this easier. What you probably have to do at the moment is something like this.

var material = this.entity.model.material;
var texture = material.diffuseMap;

// remove texture from material;
material.diffuseMap = null;
material.update();

// destroy texture webgl resource and unload asset
texture.destroy(); // destroy texture 
var url = asset.file.url + "?t=" + asset.hash; 
app.loader.clearCache(url, asset.type); // clear old texture from loader cache
asset.unload(); // reset asset storage

app.assets.load(asset); // reload and create texture
asset.ready(function (asset) {
    // add texture back to material
    material.diffuseMap = asset.resource;
    material.update();
});

#5

Hey dave,

I basically just want to switch between two different meshes in a scene, while removing all data from the VRAM, each time the models are swapped. So I can compare the different meshes better.

The reason behind this is, that I noticed vertex shaded meshes can be a lot smaller regarding their asset sizes in certain situations. For example, with the low-poly style, which I used on our playhack game. I was interested in what use case it would be beneficial to color meshes via vertex color, instead of pixel color.

I tested to re-assign the texture, the way you mentioned, but I still cannot get the texture to render again. I quickly created a separate test project, would be awesome, if you could have a look at it.

Play link: http://playcanv.as/p/J2KIR9BR
Editor link: https://playcanvas.com/editor/scene/424636

Just press space to spawn/delete the meshes. If you press space 3 times (first time to re-spawn the pixel shaded mesh) you will see the problem. I guess I am still doing something wrong, but thank you anyway for the help so far :smiley:

Greetings,

Martin


#6

Hi,

Just wanted to comment on this thread. It seems to me that the textures are not being cached.
I’ve added similar code in my game and when I call the app.loader.getFromCache function before the app.loader.clearCache (using the same url and type as arguments), the getFromCache returns undefined. Therefore the clearCache is doing nothing to help clear memory.

I also noticed that the code that appends to the texture url, that is,

  • “t=” + this.modelmaterialTextureAsset.hash;

returns undefined. Regardless, with or without the t argument it seem that the texture asset isn’t cached anyway

One good thing is that clearCache for models world so at least some memory is working but not sure if the texture url is correct? Does the browser save the texture in cache?


#7

Just to be sure, when we say cache, are we talking about browser cache?


#8

I’m assuming it’s browser cache


#9

Browser cache should ‘just work’ without needing any extra work from the developer. (As in the engine would just load from cache if it exists in the browser cache).

Eg https://playcanv.as/p/2OlkUaxF/

On first load:

On next load, you can see a lot of assets are loaded from disks and memory


#10

Try to call app.loader.getFromCache on a texture after preloaded texture asset is shown on your screen. You’ll notice it returns undefined


#11

Why/How are you calling app.loader.getFromCache?