Chug caused by render.setShaderAuto()

Hi all,

I am loading some models asynchronously and it works splendidly. I need to have shadows in my scene and so I’m using Panda3d’s shadowCaster system. This means that I have to use the auto shader: render.setShaderAuto, which also means that even though the model is loaded asynchronously, there will be a noticeable chug when a model is first added into the scene as explained by David:

My question is, is there any way to circumvent this, while still using the auto-shader, since the time David said this? Or is it for the time being something unavoidable?

Thanks in advance.

Yes, you can use render.prepareScene and premungeScene in order to pre-generate the shaders, eg. during a loading screen.

Panda3D 1.11 will feature a special shader generation cache so that this only has to happen the first time the game is launched.

2 Likes

I believe I can confirm that this did indeed eliminate a shader load hitch in a version of Across the Night that had long bothered me.

base.render.prepareScene(GraphicsStateGuardianBase.getDefaultGsg())
base.render.premungeScene(GraphicsStateGuardianBase.getDefaultGsg())

@Thaumaturge specifically the first encounter with a Mechanoid rocketship, just before the shields come up for the first time on that level. I added these lines (with “common.base.render” not just base.render) to Section2.py . I could be fooling myself, but I do believe it worked.

2 Likes

I believe that I know the chug that you mean–it was a bit of a burr in the experience, I fear!

Well, I’m glad that this solution has been found and works, then! :slight_smile:

Remind me, please: are you in a position to make a new build? If so, by all means feel free to make such a build and update the project-page on Itch and the thread here! If not, let me know and I can likely incorporate these lines and produce a build myself.

Could you test it out on your machine and see if it does in fact resolve the chug? I wouldn’t want to go through the trouble if it was a fluke on my end.

Ah, a fair point. Give me some time to do so…

Hmm, on consecutive runs I believe I am still seeing a slight delay. I’m curious to see if you have any luck with it.

Ah, I believe I discovered how I was fooling myself.

If the enemy fighter causes the player’s shields to come up first, as in the Mechanoid hits the player’s shields first, the subsequent Mechanoid shields coming up is already in cache so has no visible chug due to, I imagine, them sharing the same effect.

I apologize for getting ahead of myself there, I should have done more thorough testing before claiming victory. I do tend to get overexcited about this kind of thing. I would very much like a way to eliminate these chugs / hitches / delays for a variety of games.

Aaaah, fair enough! I’ll cancel my own testing, then. (And thank you for the update!)

It’s a pity, but so it goes.

Might you indulge my obsession? I believe this addition to the class ShieldedObject() of GameObject.py has eliminated the shield chug.

class ShieldedObject():
    def __init__(self, shieldRoot, colour, shieldScalar = 1):
        self.shieldRoot = shieldRoot
        self.colour = colour
        self.shieldScalar = shieldScalar

        self.shields = []
        self.shieldDuration = 0.5

        shield = section2Models["shield.egg"].copy_to(self.root)
        tex = shield.findTexture(TextureStage.getDefault())
        tex.setWrapV(Texture.WM_clamp)
        # shield.setScale(self.size*self.shieldScalar)
        # shield.setColorScale(self.colour)
        shield.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne))
        # shield.lookAt(common.base.render,
        #               self.root.getPos(common.base.render) + incomingImpulse)
        shield.setBin("unsorted", 1)
        shield.setDepthWrite(False)
        shield.setTwoSided(True)
        shield.setShaderAuto(1)
        self.shields.append([shield, 0])

    def alterHealth(self, dHealth, incomingImpulse, knockback, flinchValue, overcharge = False):
        if dHealth < 0 and incomingImpulse is not None and self.health > 0:
            shield = section2Models["shield.egg"].copy_to(self.root)
            tex = shield.findTexture(TextureStage.getDefault())
            tex.setWrapV(Texture.WM_clamp)
            shield.setScale(self.size*self.shieldScalar)
            shield.setColorScale(self.colour)
            shield.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne))
            shield.lookAt(common.base.render,
                          self.root.getPos(common.base.render) + incomingImpulse)
            shield.setBin("unsorted", 1)
            shield.setDepthWrite(False)
            shield.setTwoSided(True)
            shield.setShaderAuto(1)
            self.shields.append([shield, 0])

From the console log, this causes the shield to be instantiated well before the first encounter. Visually I believe this has removed the shield delay.

Could you verify? :slight_smile: @Thaumaturge

Ok, just to be clear, the models in question are loaded in as the player is already walking around in the world. The idea is to have them loaded in seamlessly. If I don’t add render.setShaderAuto(), these models are loaded in with zero chugs. However, if I add it, there is a noticeable, brief chug. Adding these 2 commands in the loading screen doesn’t seem to solve this:

render.prepareScene(GraphicsStateGuardianBase.getDefaultGsg())
render.premungeScene(GraphicsStateGuardianBase.getDefaultGsg())

There’s still a chug. I used PSTATS and indeed, the culprit is the shader being created at run time for the newly added model:

drawEr
Spike in draw

prepEr
Caused by spike in prepare

shadEr
Caused by spike in making a new shader

So is this because a new shader has to be made each time a new model is loaded into the scene? Is there a way to have this shader made in another thread? It seems that David even admitted this, at the time:

So what can be done about this?

Alternatively, get a shader dump and apply it directly to the render node.

from panda3d.core import loadPrcFileData
loadPrcFileData("", "dump-generated-shaders 1")
1 Like

I apologize, upon further investigation, that indeed does work, but with one apparent caveat: if you use the cardmaker class to create something like a health bar and put it atop your model, unless you explicitly set off the shaders for it like this:

healthBar.setShaderOff()

it will still cause the chug even with those 2 commands you mentioned. Other than that, it works well, thank you.

Thanks, I tested this and it also seems to work for me.

However, when dynamically adding light sources, the shader must be supplemented by repeating the cycle over them. At the moment, this is rigidly performed by indexes, which, I think, will not be updated in this case. But this is just a theory.

The post from David is from 2011. We have changed this feature since then.

Create the health bar ahead of time and call prepare/premunge on that as well.

Sorry it took so long to get back to you!

I will confess, testing now I’m having trouble spotting the chug in order to compare. I’ve even tried clearing out my Panda cache!

The change that you propose certainly doesn’t appear to hurt, however, and if it removes the chug on your end then I’m happy to have it in!

I have thus committed it now, I believe; if you want to make a build and update the itch-page, feel free to do so! :slight_smile:

1 Like

Ah, thank you for committing my change to your build-repo. Upon testing from there, it looks like the chug fix does indeed persist. It is a subtle thing, but a noticeable improvement nonetheless, I believe!

I will find some time make a new build for the itch page, and I will make a note in the game’s reveal post when that is completed for a mini-update.

1 Like

Excellent then, and thank you! :slight_smile: