Version 7.32.2 - Endless loop in CreateScreenshot Using RenderTarget

Hi everybody,

just noticed: the Version 7.32.2 makes the function BABYLON.Tools.CreateScreenshotUsingRenderTargetAsync never resolve the promise and the function CreateScreenshotUsingRenderTarget never calling the successCallback()
It seems like the internal texture-readyness-check (setTimeout()) never gets true - stuck in endless loop.
I cannot reproduce this in PG unfortunately - i am a bit lost.

Version 7.32.0 was working - i guess this has something to do with the big Frame graph PR?

Do you have any ideas where do look next to debug this further?

Thank you very much in advance!
Klaus

This seems weird but let me add @Evgeni_Popov

Can you test with the latest version (7.35.1), as other fixes may have been merged since then?

1 Like

Version 7.35.1 is broken for me, tracked it back to the last working Version 7.32.0.

This is part of a more complex project, the default Playgrounds from the docs all seem to work, so I struggle to reproduce this…
Using WebGL2, nothing too fancy for the engine/scene setups, removed all postprocesses …
BJS - [20:43:27]: Babylon.js v7.35.1 - WebGL2 - Parallel shader compilation
Electron 33 / Chrome 130

This functions generates preview images of items to be saved to disk. The call to BABYLON.Tools.CreateScreenshotUsingRenderTargetAsync resizes the engine … and hangs … not resolving promise.

... new BABYLON.Engine(this.canvas, false)
..  this.scene = new BABYLON.Scene(this.engine)
...
 let engine = this.scene.getEngine() as BABYLON.Engine
 let camera = this.camera_item.camera as BABYLON.ArcRotateCamera
...
 let data = await BABYLON.Tools.CreateScreenshotUsingRenderTargetAsync(
      engine,
      camera,
      {
        precision: 1.0,
        width: 1024,
        height: 1024,
      },
      'image/png'
}

Can you repro here https://playground.babylonjs.com/ ? I would like to address it ASAP as it sounds like a regression.

Sorry I was meaning to ask outside of the playground… Can you repro on a github project maybe ?

If you request a width/height for the screenshot which is different from the output (canvas) width/height, can you make a test where the requested width/height is the same as the canvas? It’s a different code path in the code, it may help use narrow down the problem.

Thanks in advance!

Another test: in case you don’t pass a parameter for the “samples” parameter (6th parameter of CreateScreenshotUsingRenderTarget) or if you pass 1, can you pass 4 and see if that helps?

requested width/height is the same as the canvas
→ same result

(6th parameter of CreateScreenshotUsingRenderTarget) or if you pass 1, can you pass 4
→ same result

Thanks for all your support, I’m trying to recreate the situation in this PG:
https://playground.babylonjs.com/#I4GRVO#1

→ Everything works as expected here - my own project is broken since 7.32.2+ …

I deconstruct everything to figure out the differences to my local project … which is dependent on Electron …

Due to the different environment (Electron / browser), you may not be able to reproduce the error in the browser, unfortunately… If you can give us a repro in a separate github project / by sending us a private email if the project is not to be disclosed, we might (hopefully!) find the bug quickly.

1 Like

Thanks again for all your time!
Right now I’m unable to share the project in its current state / reasonable time - I try further deconstruct, shrink it and try to make it available.

Vite → http://localhost → Chrome behaves exactly the same as in Electron. So this may not be a difference.

Differences to Playground are:

  • multiple scenes for 3D and GUI
  • multiple cameras
  • GLB-Models / multiple materials
  • Typescript/Treeshaking/ES6-Module-Imports? (no errors in log)

Any ideas?

It’s hard to say, unfortunately…

Have you been able to perform these tests in your existing code:

If you request a width/height for the screenshot which is different from the output (canvas) width/height, can you make a test where the requested width/height is the same as the canvas? It’s a different code path in the code, it may help use narrow down the problem.

Another test: in case you don’t pass a parameter for the “samples” parameter (6th parameter of CreateScreenshotUsingRenderTarget ) or if you pass 1, can you pass 4 and see if that helps?

This could help us narrow down the problem.

Thank you for your patience!
I tried both your suggestions with no difference in behavior. Noticed that different width/height scaled my entire viewport/canvas. Also samples = undefined, 1, 2 or 4 makes no difference.

Everything in the scene is already loaded and running - screenshot is called on button click.
I debugged to the point where texture.isReadyForRendering() check is false

thanks to Vite the @babylonjs/core file: is chunk-Q7QCB…

function CreateScreenshotUsingRenderTargetAsync()
-> CreateScreenshotUsingRenderTarget()
...
const renderWhenReady = () => {
 if (texture.isReadyForRendering() .... // <= always false
...
 } else {
  setTimeout(renderWhenReady, 16);
 }

=> texture.isReadyForRendering() always is false
So the renderWhenReady never happens and function never returns.
What am I missing in my scene?

Can you debug inside isReadyForRendering? You should end up in ObjectRenderer._checkReadiness at some point (use v7.35.1+). The call to mesh.isReady(true) will probably return false, so you should also step inside this function to see more precisely what’s wrong. Try to get the name of the mesh or the material that is not ready. It’s not the ideal way to debug, but without a repro, I don’t see how to do it.

Thank you again @Evgeni_Popov for encouraging me to debug deeper!
Tracked this down to ParticleSystems “not being ready” aka not having mesh-emitters set
→ already answered in Screenshot and ParticleSystem

_checkReadiness() {
 ...
    const particleSystems = this.particleSystemList || scene.particleSystems;
    for (const particleSystem of particleSystems) {
      if (!particleSystem.isReady()) {
        returnValue = false;
// particlesystem 
  isReady() {
    if (!this.emitter || ... ) {
      return false;
    }

→ happens when emitter is BABYLON.Vector3

Solution/Workaround
Setting dummy-mesh for all (in my case unused) ParticleSystems in scene, before calling BABYLON.ScreenshotTools.CreateScreenshotUsingRenderTargetAsync

scene.particleSystems.forEach((ps) => {
  ps.emitter = dummy_mesh
})

Not quite clear to me, why this happened exactly since v7.32.2 …
Some graceful failing would have also been helpful, not launching infinite setTimeout() …

Thanks again for your help and great work with babylon.js!

@RaananW I think we should use your new function everywhere we check readyness and have a timeout error of some sort to log a message ?

Referring to the setInterval function?

Yup the timeout could help here I guess

1 Like

Sure. we should use this function whenever there is no render loop to use. I can see if i can move it somewhere that is tree-shakable and won’t add too much to a package using the shader precompilation. i’ll add it to my list of stuff-to-do :slight_smile:

2 Likes