unfortunately my application will always stutter for around 290ms once it is done loading and plays the Intro Camera animation. I have tried to figure out what is going on by inspecting with Chrome Dev Tools and watching the Profiler closely.
Sadly I have not been able to gather much information about my issue so far and was hoping anyone could help me out.
The following are screenshots of my Profiler, my Network graph and my performance profile.
If you need any additional information, let me know.
I also noticed that you have about 40-50 lights in the scene. Just as a test, can you disable all but one and see if it still shutters? I wonder if this is culling related too
Alright, tried disabling all but one light, unfortunately, that didn’t change anything at all.
So I guess it might really be shaders compiling.
In the meantime I checked out the thread @LeXXik linked. I also took a deeper look into the “Load multiple assets at runtime” demo. Now I am wondering how to apply the script for materials as these can’t really be enabled/disabled. Is there a way to achieve something similar for materials?
The particular part that LeXXik was referring to was about the reply on shader compiling:
Compiling shaders is always a blocking operation for the browser. Usually what developers do is load/enable all their materials when the app starts,for a single frame. That will force all materials to compile their shaders at the same time. Since it will happen as part of the app loading it won’t be that noticable. Later on any model loading that uses those materials won’t lag/block the main thread.
Basically you would have all the materials in view on the very first frame so that the renderer compiles the shader.
Oh okay, I see. So I’m assuming you’re saying I would want to briefly load all the materials during the loading screen so they are compiled once the app starts?
Ah, I think I get it now, sorry for the inconvenience.
Yeah, tried that, reduced the duration of the stutter by half and it stutters a little earlier but it’s a lot better. Can you quickly go a bit into detail as to why that is the case?
The first time any material is in view, it will need to be compiled. Our Standard Material does optimisation to reduce the shader size based on what features are used (eg does it have opacity, etc) making it more performant.
If the combination of features have not been compiled before, it will need to do a new compile otherwise it just retrieves it from cache.
At this stage, what I would normally do is disable Entities in the scene until the stutter goes away to narrow down what materials or Entities are causing the issue and work from there.
Yes, disabling shadows completely removes the stutter. There still are minor events in the profiler, but those don’t seem to cause noticeable frame drops.
My best guess here is the stutter is due to new lights coming into frame and the renderer updating shadows. I didn’t think it would be that costly though
Well maybe these settings affect it too. Reduced the shadow atlas resulution to 1024 and the Shadow type to PCF 3x3 and it reduced the time of the shaders compiling by a third.
Assigning a preloader script to the scene root, which loads all the assets for one frame and then disables them again, increases the stutter at application start by roughly 50ms. In addition to that though, everything else works fine after that, with a constant frame rate and no issues at all. I may just overlay the stutter with an additional frame of loading screen so it won’t even be noticeable and all my problems are a thing of the past I guess
It is overall the script used in the “Load multiple assets at runtime” demo, only the onAssetsLoaded function is somewhat different. Also I didnt use assets tagged as level but rather my materials tagged as “material”.
var Preloader = pc.createScript('preloader');
// initialize code called once per entity
Preloader.prototype.initialize = function() {
this.loadAssets();
};
Preloader.prototype.loadAssets = function() {
var self = this;
// Find all the assets that have been tagged 'level'
// In this example, they are in the 'assets-to-load' folder
var assets = this.app.assets.findByTag('material');
var assetsLoaded = 0;
var assestTotal = assets.length;
// Callback function when an asset is loaded
var onAssetReady = function() {
assetsLoaded += 1;
// Once we have loaded all the assets
if (assetsLoaded === assestTotal) {
self.onAssetsLoaded();
}
};
// Start loading all the assets
for(var i = 0; i < assets.length; i++) {
if (assets[i].resource) {
onAssetReady();
} else {
assets[i].ready(onAssetReady);
this.app.assets.load(assets[i]);
}
}
if (!assets.length) {
this.onAssetsLoaded();
}
};
Preloader.prototype.onAssetsLoaded = function() {
this.app.root.findByName('materialprerender').enabled=true;
this.app.root.findByName('Gelaende').enabled=true;
this.app.root.findByName('Schriftzug_Neu').enabled=true;
setTimeout(function(){
this.app.root.findByName('materialprerender').enabled=false;
this.app.root.findByName('Gelaende').enabled=false;
this.app.root.findByName('Schriftzug_Neu').enabled=false;
}.bind(this), 1);
};
Ah, you have this where I assume you are rendering the materials in view. That 's the important part as it looks like your material assets in the Editor are all set to preload anyway