Optimize: Effect/Shader output cache?

I’m looking to optimize a large mesh that the material is generated by a shader. Even though the material freeze() is set, it still calls the binding and renders the effect every frame… The material of the shader is not effected by the lighting or camera.

I’m getting poor FPS on some devices and it seems reducing this potentially redundant rendering could subtract ~50ms per frame

Material does not have any alpha

Any thoughts? Questions?

@artfiedler

You need to pre-compile your shader. Here’s a thorough explanation of the issue and possible solution from the old forum.

I initially thought that this could be accomplished as it is done in DirectX. Moreso, it is not a simple task(as it is in DirectX) in OpenGL derivatives such as WebGL. However, I did find a script to do this Here’s the code:

And here’s an explanation of the process:
http://toji.github.io/shader-perf/

Galen

After reviewing, it does not seem that compiling the shader is a problem, it seems it’s compiled only once. The shader is just called 1billion :wink: times aka X times per second… my point is the output of the shader each time it is called is going to end up being the same everytime its called, so instead of evaluating every pixel over and over maybe babylon can do some kinda output caching or maybe there is a way I can add it…

In a 2D environment I believe they would call this bitmap caching. Obviously in a 3D environment angles and perspectives come into account and you cant just screenshot the canvas and save that as a layer because the camera will move… but maybe the shader could output a material into memory and use that, similar to the DynamicTexture… maybe need a shader to output to a dynamic texture :smiley:

@artfiedler

We ought to be able to compile a shader into a VAO with a function such as createVAOFromBufferInfo. Then call the VAO at render. This is already available in WebGL, but I’m not certain of all the functions necessary to do so.

@Deltakosh… who would be the best Dev to comment on this?

Galen

You can maybe use renderTargetTexture to capture the output of your shaders and store it in a texture as long as it does not change?

Render to texture example:
https://www.babylonjs-playground.com/#CJWDJR#0

Thanks @Deltakosh this does seem to be similar to what I’m looking for, I’ll mess around with it, size might be an issue but maybe I can massage it a bit.

Congrats on 4.0!

1 Like

Thanks a lot :smiley:

@Deltakosh

I need a hand here, I’ve spent entirely too much time on trying to squeeze the performance out of babylonjs + webgl for a 3d map, that I expect to run on desktop, laptop, tablet, and mobile. I created the dumbest version that I could, works fine on a desktop, but the next step down on a laptop horrid frame rate.

The biggest issue is there are some meshes in the scene that have some animation, like water, and the rest of the meshes unless you move the map it should be identical to the previous frame(think bitmap caching here)… sure I can stop the water but STILL I can’t get 60 frames even with the static non-moving meshes and sprites!

Here comes my idea… I went and modified babylonjs to pass a flag to the scene.render function called RenderPipelineOptions which simply contains an array of boolean dirty flags for each render group index. In the RenderManager, if the rendergroup is not flagged dirty I completely skip rendering everything in the render group, assuming the layer has been previously rendered and is now in a “bitmap cached” state… I’ve turned off autoClear everywhere I could on the depth, stencils, etc on the scene… I’m able to achieve massive performance boosts by only rendering those when needed… however those render groups STILL get cleared… I went and even modified engine._gl.clear functions to not call and clear as well, and they still ended up cleared…

So here’s where I need a hand…
I decided to implement a cache feature into babylonjs which I think will solve my performance issues… but I’m definitely not a pro at WebGL and camera perspectives etc…

This is what I would like to do pseudo… >

if (renderGroup.cache) {
if(renderGroup.cache.isDirty === true || !renderGroup.cache.texture) {
// creates or updates the cache render target texture that is the smallest texture size for the
// render groups canvas area
renderGroup.renderToCache(renderGroup.cache, …);
}
// draws the cached render target texture to the webgl canvas in the specific rectangle area
// with transparency that the renderGroup covers
renderGroup.renderCache(renderGroup.cache, …)
}
else {
// render normally no cache
renderGroup.render(…);
}

I’m able to render a specific mesh to a render target texture and that on a plane however aspect of the rendering is off, and where specifically to position it etc… able to give me a hand here, I basically want to render to a frame buffer it sounds like, then copy that framebuffer onto the canvas but it’s not so simple as that.

I believe we have to

  1. Determine size of render group
  2. Generate the render target texture at the power of two(I’m assuming) to contain the render group
  3. Generate a plane in the render groups position relative to the camera / canvas space
  4. Use a shader to draw the render target texture

Seems this would be a great improvement on performance of babylon, thoughts? Did I miss something that already does this?!

Also should note, I didn’t just jump to creating 4 or 5 overlaying webgl canvas is because I assume it would be much more performant to do it in a single instance, instead of having 4 or 5 scenes, engines, cameras, post fx and then the browser would have to composite it, which could become and issue I assume.

hey
how are you :slight_smile:

i just talk about my experience so may be this is wrong at all :slight_smile:

i think the Vertex + Fragment Shader is optimized way for show 3d scene in any device
that is lower possibility of call of main per sec but you most make balance between cpu and GPU

@nasimiasl I believe this is what I have, I have very few meshes, and the a shader is used to render/mix some textures, and I have lots of sprites for trees

@nasimiasl See I believe my method is the way to go, because I was even able to use a more complex mesh with higher detail where I was “skipping” rendering and clearing, but I think it was when I moved the mouse and the highlight layer/selection mesh moved then it cleared out canvas… if it didn’t do that I’m able to achieve 45-60fps on my laptop with max quality, but if it leave the scene to render the default method using the render loop I get about 8fps

i am sure you can be solve it just make PG we can help you on code

When trying to find any other example on the net, I ran across easeljs, check out their example here… https://www.createjs.com/demos/easeljs/cache low fps until you hit the cache check, their code EaselJS/StageGL.js at master · CreateJS/EaselJS · GitHub seems to be doing something like what I’m talking about, seems they are rendering to a texture then drawing the texture using some shader somewhere… their code is MIT licensed and not very similar structure to the way babylon’s render pipeline works it seems.

I might have found a solution… and its basically already built in to Babylon… Reusing PassPostProcess and directly rendering it when the render group is dirty, store it, then when the rendergroup is not dirty just draw that texture back using the PostProcess methods… I might have to do something with the depth because even though I’m rendering the render target texture at the time the render group is drawn it’s still ending up on top of everything

Please let me know if I can add something to help in the engine :slight_smile:

@Deltakosh I’m modifying the Babylon code in intent for you to review/finalize it, but if you got time you could probably knock this out faster then me… lol

I’m in the renderingManager.ts render function, when I replace this line… renderingGroup.render(customRenderFunction, renderSprites, renderParticles, activeMeshes);
with the cached texture when its cached, I call something like this…

var pp = myRenderGroupCachePostProcess;

pp.onApply = (effect) => {
effect._bindTexture(“textureSampler”,cachedRenderTargetTexture.getInternalTexture());
};
postProcessManager.directRender([pp]);

problem is its rendering ontop of all other rendering groups that are suppose to be above it… I’m assuming because the post process uses 2d coordinates… do I need to get a depth texture from the rendering group as well? What’s the best way, I assume I could then mix the depth texture with 2d image in the shader… any tips?

LOL :smiley:

I was laughing as well

1 Like

Do you think you could hack something simple in the playground? I could definitely have a look to see where you are and how I can help

I like your idea and I think it could help a lot of users