SSR + WebGPU on mobile: NaN/Inf artifacts accumulate in TAA history over time. Analysis + working workaround [no Playground yet]

Hi everyone! :waving_hand:

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!

1 Like

Thanks for reporting!

In an effort to fix these artifacts in the first place, can you test the monkey-patching we did in this PG:

If this resolves the artifacts in your project, we will merge them into the repository.

Thank you in advance!

1 Like

Thank you so much, @Evgeni_Popov for such a quick response! And thank you for the playground scene with monkey-patching

Verified this in following order:

  • Disabled sanitizer workaround
  • Reproduced the issue on the same scene - got the artifacts on mobile:

  • Applied your fix from the playground
  • Checked on the same scene with the same reproduction steps

See that the amount of artifacts decreased substantionally, but some of them remain:

Hope this is helpful, I will do my best to localize this in the playground scene, but will need a bit of time to simplify it as much as possible to smallest example. Also need to setup more convenient development process, as my main fast iteration loop is local desktop, mobile branch is very slow to iterate at the moment

Itโ€™s better!

Can you try this new one, additional guards have been added:

1 Like

Thank you so much, @Evgeni_Popov ! :folded_hands:

Iโ€™ve localized the setup to babylon playground scenes and got this sequence:

  • Baseline difficulty reproduction:

    No monkey-patching, sanitizer turned off, reproduced on this scene on mobile, no issues on desktop as before

In order to reproduce it, kindly move the camera a bit and wait until TAA accumulates and produce the dots near the car on the left:

  • Same scene plus monkey-patching from your latest example:

Green dots looks resolved but black ones still there

Interesting discovery from latest verification:

I could not reproduce the original issue on playground untill I mirrored auto-exposure logic from local pipeline

So looks like auto exposure logic either amplify or could be producer of the artifacts

Since it works without issues on desktop and reproduces only on mobile/tablets, hopefully this can be an indication of the underlying reason and could be helpful in localization

Thank you again for you super fast responses and all of the examples! Please let me know if there anything I can help with

p.s: also prepared the PG with sanitizer workaround in case this could be helpful for localization and to remove ambiguity about the original setup:

Thanks for the repro!

I was able to repro on my iPad, and came up with a fix:

Can you check if it fixes it for you?

[EDIT] I created a PR for it:

1 Like

Thank you, @Evgeni_Popov :folded_hands:

Confirmed - verified this on your playground with new monkey-patch - fixed

Applied same monkey-patch to my local project - fixed there as well! :tada:

Thank you for the fix and for your super prompt replies!

2 Likes