Passing a primitive variable to a compute shader that runs multiple times per frame

I’ve just started experimenting with WebGPU and one of my compute shaders needs to be dispatched multiple times per frame with a different int value passed in.

If I do something like the following, If the uniform buffer I’m updating is dynamic then each dispatch will always get the latest value, which makes sense because I’m assuming the shader will take the value when its run, and not a snapshot of when the dispatch was called.

for (let i = 0; i < 10; i++) {
  uBuffer.updateUInt("divider", i);
  cShader.dispatch(Math.ceil(totalCells / 256), 1, 1);
}

    divider.update();

The only way I could make it work was by making 10 uniform buffers with length 1 but this seems like a bit of a hacky workaround.

const dividers: UniformBuffer[] = [];
for (let i = 0; i < 10; i++) {
  const divider = new UniformBuffer(
    engine,
    undefined,
    false,
    `dividerBuffer${i}`
  );
  divider.updateUInt("divider", i);
  divider.update();
  dividers.push(divider);
}

for (let i = 0; i < 10; i++) {
  cShader.setUniformBuffer("divider", dividers[i]);
  cShader.dispatch(Math.ceil(totalCells / 256), 1, 1);
}

Is there something obvious that I’m missing?

Welcome aboard!

Actually, the uniform buffer class should take care of this for you, it will use additional buffers in case you need to update the buffer several times in a frame.

However, you need to call the udpate method to update the GPU buffer, I think it’s what you missed:

for (let i = 0; i < 10; i++) {
  uBuffer.updateUInt("divider", i);
  uBuffer.update();
  cShader.dispatch(Math.ceil(totalCells / 256), 1, 1);
}

That’s what our ocean demo is doing: see around lines 2552-2564

1 Like

Thanks for the prompt reply!

I did actually have the update() function in my original code I just forgot to put it in my post sorry!.

I did some more investigating and made a reproducible example in the playground. I’ve noticed the issue only occurs when there is another compute dispatch beforehand.

In my playground example I have a compute shader that first sets the buffers back to all 0s and then the second shader is the one that sets the values accordingly. I’ve adjusted the second shader so it will just output 0,1,2,2,4,4,4,4,8,8,8 etc for debugging and it works correctly on its own.

When there is another shader run beforehand though (line 104), something seems to go amiss and the output changes to 0,0,2,2,4,4,4,4 etc. If you comment out line 104 the values work as expected.

It looks like sometimes the context isn’t getting marked as dirty, if I add an explicit this._context.clear() in @babylonjs/core/Compute/computeShader.js (line 222) just before the dispatch then the issue goes away.

Not sure if there’s something obvious I’m missing or doing something wrong but any assistance would be greatly appreciated.

There’s a bug indeed, here’s the fix:

Oh wow that was quick! And there’s already a release out too!

Thank you so much