Wait for compute shaders to be ready

Sorry this is my third post here in the last few weeks, I’ve been experimenting with WebGPU and compute shaders but I’ve been running into certain intermittent issues which I think I’ve tracked down to the scene starting before all the compute shaders are ready.

In my project I need to add

// Wait for compute shader to be ready
while (!bodiesComputeShader.isReady()) {
  await new Promise((resolve) => setTimeout(resolve, 100));
}

before my engine.runRenderLoop for the scene to run correctly. Without it, 50% of the time it seems the compute is running before its ready, as the output buffer seems to remain empty.

I’ve tried using await scene.whenReadyAsync(); but it doesn’t seem to work.

Is there something else I need to do, or an easy way to wait for everything to be ready?

It seems to run fine in the playground, but locally I need to uncomment lines 125-128 for it to work.

Playground link: https://playground.babylonjs.com/#CJGZQM#1

EDIT: I’ve just tried moving my compute shader from a .fx file and passing it directly using
{ computeSource: shaderSource } and it seems to fix the issues, so not sure if that’s related to it.

EDIT2: This may also be related and may be a bug or just my misunderstanding, in the “Boids” compute shader example the simulation freezes after 15-20 sec, but changing the dispatch() to a dispatchWhenReady() call fixes this and lets the simulation run continuously.

On a side note, are there any plans to allow us to store our webgpu shaders in .wgsl files directly?

Thanks

Yes, for now you should do something along those lines. You can also use dispatchWhenReady instead of dispatch, to make sure that the dispatch will actually do something (but internally this is like using a setTimeout as you do in your snippet).

What can happen is that the first dispatch will actually be postponed to the next frame because the effect is not ready yet, and the storage buffers bound to the shader will not be in the right order, they will have been swapped: you will have the empty buffer bound as input.

What would happen is simulated in this PG :

I also reproduce this behavior. However, it works as expected in Canary without using dispatchWhenReady, so it’s a bit strange… I’ll have to dig a little deeper.

It can already work, see GitHub - Popov72/OceanDemo: Ocean demo in WebGPU with Babylon.js for eg (in https://github.com/Popov72/OceanDemo/tree/main/src/assets/ocean).

Thanks, that makes a lot of sense.

Thank you for the ocean demo I’ll look at changing my files to .wgsl.

Would it be worth implementing something like whenReadyAsync() for compute shaders or even adding the checks to scene.whenReadyAsync() so then dispatches can just be called with a plain dispatch

I feel like it would be nice ideally to be able at some point during scene setup, you could have an await engine.whenAllComputeReadyAsync() and then be able to just use plain ‘dispatch’ calls from then on.

I’m happy to look into this and raise a PR if you think its a good idea or might be worth it.

EDIT: Importing my shaders directly from a .wgsl file seems to fix my original issue as well. The timing must be just different enough with Babylon’s loading method from the .fx files that it causes the first render to occasionally skip the first dispatchWhenReady.

I think this is a good idea and PR is welcome!

However, I don’t know the right way to do it… The compute shaders are not linked to a scene (we don’t pass a scene to the constructor), but to an engine. So we would need to track the list of compute shader instances somewhere but not in the scene, so it can only be in the engine itself, which I don’t like very much…

cc @sebavan for his opinion. Maybe we should pass a scene to the constructor just to be able to handle them at the scene level, like we do with materials, meshes, etc…

I noticed there was already an engine.areAllComputeEffectsReady() so I just added an async version which can be awaited.

Not sure if this is the best way to do it but seems like it works!

I’ve created a PR here: WebGPU/Compute: Added function that resolves when all compute effects are ready by jtsorlinis · Pull Request #13867 · BabylonJS/Babylon.js · GitHub

In the general case, waiting for the compute effects to be ready is not enough, you have to wait for all the ComputeShader instances to be ready because when the shader uses textures, it also checks that the textures are ready. If you don’t use texture, however, it’s equivalent.

Agree it is a bit of a stretch but it is nice to have :slight_smile: That said, the features relying on compute should be attach to a scene to ensure proper readyness as you mentioned. I think the computeEffects are more like effects and could be shared across to save on compilation time.