[SOLVED] Application will stutter in the Intro Animation

Hello everyone,

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.



Thanks in advance!

There could be various factors, but probably the shader is compiling? Try this suggestion:

First guess is shaders compiling too. Especially if it’s the first frame that that a particular set of materials is shown

Can you share a published link for people to look at?

Thanks for your replies!

@LeXXik Will definetely check out that thread and see if it leads me to a solution!

@yaustar Yes, here you go: Stuttering Scene - PLAYCANVAS

It does look shader compilation related.

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?

Briefly show the materials in frame for single frame at the start. They need to render to be compiled.

I think as a quick test, you can disable frustum culling on the camera for this to see if it still stutters.

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.

Alright, awesome, this helps a lot and thanks for explaining!

I’ll leave this open, if any follow-up questions occur but for now I got something to work with.

As a sanity check, if you disable shadows under Project Settings → Rendering → Clustered Lighting, does the stutter go away? Also are you on Windows?

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.

Yes, I’m on Windows 11. And if that matters - I’m using an RTX 3080

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 :thinking:

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.

image

What I just figured out:

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

What do you have in the preloader script?

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