Memory allocation and performance

Hi

Entering the final phase of the project and during testing we’ve started getting some users seeing a very large memory allocation on their desktop computers just by glancing at the values in task manager for the Chrome tab in question. (Is that a good way to measure?)

Values between 600-800MB and their computers became unresponsive while loading the site. (specs en i5a@2,6GHz med 8GB ram).

Some stats from profiler using launch in debug mode:

Triangles: 182 000
Draw calls: 253
Total VRAM: 43MB

I’d love to get some insight on where I should start. We’ve tested on mobile safari aswell (iPhone X) and we’re getting good values there. Frames render at 16.7ms and seem very responsive.

Best regards

  • Björn

Have a look at the Memory tab in Chrome - that should give you pretty detailed into about JS memory use. Based on your stats, it seems it’s not video memory causing the problems.

Hi! Thanks for prompt answer! That’s a really good tip. I tried recording a snapshot and got this result:

Withour knowing exactly what I should be worried about, the number of DOM nodes seems slightly excessive?

But to be fair, I just tried to create an empty project based on the Model viever template and got similar numbers on the DOM Nodes. So perhaps that’s not the culprit here.


@Leonidas would you have any insight on this to point me in the right direction? I’ve read a few threads where it seemed like you had a clear view of performance issues from different aspects. The weird thing here is, it runs fine on for example iPhone 6, and my monster laptop, but not in the stuff in between. The “excel”-computers which I call them :wink: thin i5 laptops with integrated graphics and 8GB ram specs for example.

Not sure what happened there but when I tried measuring again, reopening the tab the numbers are WAY lower.

What’s reasonable?

And to further confuse myself, I managed to measure these low numbers on DOM numbers on my project, but this time not from the launch menu but instead a built version hosted on Playcanv.as

what gives? :man_shrugging:

Hi @bjorn.syse,

I don’t have a point of view on the number of DOM nodes allocated, I think it doesn’t really matter for most Playcanvas apps. Most Playcanvas apps and games won’t allocate many DOM nodes, especially if they are using the Playcanvas UI.

You are mostly concerned of two numbers when debugging browser crashes which 95% of the time happen when there is no more memory available by the OS:

  1. JS Heap size, regular RAM.
  2. Video memory (VRAM), which isn’t reported in the browser dev tools as far as I know. But you can find it in the Playcanvas profiler.

In systems, usually desktop or laptops, with a discreet GPU VRAM is separate from the main system memory (RAM) and unless you are loading really high res textures you can be sure that you will have plenty.

In systems with an embedded GPU, many laptops and almost all mobile devices, the OS allocates either explicitly (fixed amount) or dynamically (non fixed amount) VRAM as part of the system main memory. This means that the total amount of memory your app is trying to allocate isn’t only the JS Heap size as reported by the browser dev tools, but you have to add whatever VRAM you are allocating to that.

So what is a safe border to not cross on those devices? This really depends on a combination of two things from my experience:

  1. Total RAM available, older devices like the iPhone 6 have only 1GB of RAM.
  2. Amount of free RAM available at the time the user is trying to launch this webpage. If he has many apps in the background running, quite a common scenario, the OS will lower the total amount of memory it will allow for this new browser tab to allocate.

I’d say that if you are targeting older devices a good limit is 150MB, though potentially if point 2. is true you can still crash on older devices.

All this won’t really affect performance / frame rate. This is only to have your app boot without crashing the browser tab.

Thanks, Leonidas for taking the time to speculate on this! Really appreciated.

Ok, that’s comforting, perhaps I shouldn’t stare myself blind at the DOM nodes then. But more on the total amount of memory allocated. That being said, what in the world could allocate that many DOM nodes in an app like this? care to speculate?

It’s funny I got it running just fine on an iPhone 6 but perhaps these reports about computers freezing for a short period while loading came from people having 100 tabs open in the browser with 8GB shared memory. I don’t know, and hard to take precautions for. They did report the performance itself was fine when it had finished loading.

When you talk about memory, we’ve been keeping our eyes on the VRAM allocated reported by the Playcanvas profiler and we’ve been circling around 40-80 MB in total VRAM, and hope not to go above that higher number. Still working on the final textures so I can’t say for sure.

Apart from VRAM you mention the JS heap size on top of this number. Right now it is reporting pretty stable numbers around 200MB (+/-20MB), so adding the VRAM to that put’s us at 250ish MB.

Perhaps that’s high. But I figured the older mobiles would be the bottleneck here, so that’s why it surprised me the reports came from laptops.

What affects this JS head size allocation, would that be all the assets in the scene? Looking at the total size of the app in the Chrom dev console we’re looking at 32MB downloaded and 92MB uncompressed.

image

What’s our main goto strategies if we want to try and decrease this JS Heap memory allocation. Batching of meshes? reducing materials? or are we talking VRAM when it comes to the graphics?

Regards

  • Björn

Hmm, no idea about the DOM nodes, I’ve seen those numbers go up/down on the same app quite regularly.

So, it’s not easy to tell without inspecting the app in question, but one thing you should be looking at is the memory allocation graph in the Performance browser dev tab.

If you press the Record button (top left) while your app starts loading and leave it record for 30-60 seconds after the app starts running, you will be presented with a much cleaner view of what’s going on.

Initially, at the first frame, your app may allocate larger amounts of memory. You can estimate that from the download size since the asset potentially it can get uncompressed when loaded in memory, most of the time when the engine is preparing the assets to be sent to the GPU. For example .json model assets need to parsed as JSON text which requires a lot of memory potentially. Using .glb models will reduce both memory required and parse time.

The first frames are usually when the app consumes most of the memory, after a while the garbage collector will kick in free an amount of memory. That memory spike though can potentially lead to a memory crash.

One good old strategy is to start disabling parts of your app (both entities and assets) while watching that memory graph. That way you will get a better understanding of what’s the impact of each one.

Hope I’ve been of help!

2 Likes

wow, thanks! You’ve helped a lot @Leonidas!

Memory allocation graph, looking at it now. I’ll be sure to keep a close eye on that one as we start finalizing the app with the final assets during the day.

Exactly, good and simple strategy. And I could do that in runtime/launch mode perhaps, and glancing at the pane below showing the JS Heap size in the meantime right? (without recording) this one:

The GLB path you mentioned sound hyperinteresting. I wasn’t aware support for GLB was implemeted yet. I’ll look into it right away!

Hi again

I tried converting one of our FBX files to GLB, importing both and these are the numbers concerning size, which should affect memory allocation in the end as well then I guess? Unpacked size is about half with glb compared to json. am I reading that correctly?

(I used the facebook GLB converter https://developers.facebook.com/docs/sharing/3d-posts/glb-tutorials/, very useful tool, together with the GLB loading scripts found in this thread by @yaustar: Visual difference between "Playcanvas GLB Viewer" and "Loading/Showing GLB in a basic project" thanks for that!)

Yes, glb will be both faster and require less memory to get uploaded in the GPU.

There is currently fbx to glb import support in code beta in the editor that can help with this.

1 Like

Sounds VERY promising! we’re evaluating this path right now.

What’s that support you’re mentioning. Right now I’m using some manual import scripts I found in that tread I linked to but there is not visual representation of the models in the viewport and I have to add/change materials programatically by searching the meshes. Is there something else I should be doing that would aid my in the pipeline?

@yaustar can add you in the closed beta, if possible, then you can re upload your fbx source files and you will get as a result .glb model assets.

Those can be used normal in editor as the .json regular ones.

1 Like

That sounds like it would be a life saver right now. We have our deadline monday 8 am. @yaustar is that possible to arrange? Does it come with any drawbacks? (we’re three users in our team), I am the owner of the project though.

Seems right now, I can’t even traverse the meshInstances of the model using this code:

var LoadGlb = pc.createScript('loadGlb');

LoadGlb.attributes.add('glbAsset', { type: 'asset', assetType: 'binary'});
LoadGlb.attributes.add('material1replaceName', { type: 'string', title: 'Material 1 name', description: "The name of the material in the GLB to replace"});
LoadGlb.attributes.add('material1', { type: 'asset', assetType: 'material'});

// initialize code called once per entity
LoadGlb.prototype.initialize = function() {
    var self = this;
    utils.loadGlbContainerFromAsset(this.glbAsset, null, this.glbAsset.name, function (err, asset) {
        self.entity.model.asset = asset.resource.model;
        // self.app.root.findByName('Camera').script.orbitCamera.focus(self.entity);
    });
    
    // List materials and meshes:addEventListener
    
     console.log(self.entity.model.meshInstances);
};

Last line reports null. Could be something else I guess, or do I need to be in the beta to be able to do those kind of manipulations? mainly I would like to replace materials.

It takes time for the GLB to be loaded via loadGlbContainerFromAsset function. You will have to wait till the callback is called before you can work with the model asset.

1 Like

Ah! obviously. So I just moved that line up here, is that the right way to do that?

// initialize code called once per entity
LoadGlb.prototype.initialize = function() {
    var self = this;
    utils.loadGlbContainerFromAsset(this.glbAsset, null, this.glbAsset.name, function (err, asset) {
        self.entity.model.asset = asset.resource.model;
        // self.app.root.findByName('Camera').script.orbitCamera.focus(self.entity);
        
        console.log(self.entity.model.meshInstances);
    });
    
    // List materials and meshes:addEventListener

It should do. There may be a frame of process of the model asset to the mode component.