Hi everyone! ![]()
First I just want to say - thank you so much for Babylon.js. It is a genuinely amazing engine, very robust, and I really enjoy working with it every day. The new FrameGraph addition in particular is something special - having the ability to construct exactly the pipeline I need, task by task, has been so much fun, and is extremely useful. Especially combining effects that previously were hard or impossible to assemble together. So thank you so much to the whole team, and especially to @Evgeni_Popov for the incredible work on FrameGraph!
I wanted to share something I found while building my production app, in case it helps anyone who runs into the same thing. I donโt have a minimal Playground yet since the issue is tied to my production scene geometry - I would love to add one when I get a chance, but I hope the workaround and general investigation information is useful in the meantime. Because I dont have a playground reproduction, adding this to questions. Please let me know if this is useful and if the questions is the right place for it
The difficulty
On mobile WebGPU (confirmed on iPad Safari and Android Chrome), when SSR and TAA are both active in a FrameGraph pipeline, reflective surfaces start producing green/black dot artifacts. These dots accumulate over time โ usually when camera is rotated/moved
The same setup works perfectly on desktop WebGPU. Disabling SSR makes the artifacts disappear completely.
It looks like SSR is producing NaN or Inf pixel values on mobile WebGPU, which then accumulate in the TAA history buffer and never get cleared. I cannot confirm exactly why this happens at the moment but looks like this is a good hypotesis, since workaround fixes the issue
Environment:
-
Babylon.js 9.0.0
-
WebGPU engine (FrameGraph v1.0)
-
Confirmed broken: iPad Safari, Android Chrome
-
Confirmed working: desktop Chrome/Edge WebGPU
Working workaround - SSR NaN Sanitizer Task
The fix is to insert a FrameGraphPostProcessTask between the SSR output and the TAA input. It runs a full-screen shader that checks every pixel for NaN/Inf and replaces bad values with opaque black vec4(0,0,0,1) before they ever enter the TAA history buffer.
// WGSL โ WebGPU
// Register in Effect.ShadersStoreWGSL["ssrNaNSanitizerFragmentShader"]
var textureSampler: texture_2d<f32>;
var textureSamplerSampler: sampler;
@fragment
fn main(input: FragmentInputs) -> FragmentOutputs {
let c = textureSample(textureSampler, textureSamplerSampler, input.vUV);
let bad = any(c != c)
|| any(c > vec4f(65504.0))
|| any(c < vec4f(-65504.0));
fragmentOutputs.color = select(c, vec4f(0.0, 0.0, 0.0, 1.0), bad);
}
Wire it into the FrameGraph between SSR and TAA:
javascript
const sanitizerTask = new FrameGraphPostProcessTask(
"ssrNaNSanitizer", frameGraph, sanitizerEffectWrapper
);
sanitizerTask.sourceTexture = ssrTask.outputTexture;
sanitizerTask.dependencies = new Set([ssrTask.outputTexture]);
frameGraph.addTask(sanitizerTask);
// feed sanitized output into TAA instead of raw SSR output
taaTask.sourceTexture = sanitizerTask.outputTexture;
resulting FrameGraph pipeline:
Resulting FrameGraph pipeline:
SSR (half-float output)
โ
โผ
NaN Sanitizer Task โโโ inserted here
(full-screen pass, checks every pixel)
โ
โผ
TAA
โ
โผ
Image Processing (tone map + exposure)
โ
โผ
Backbuffer
Hope this could be helpful in case someone encounters the same issue!




