Versions: 9.15.0 and 9.20.0 (both reproduce) · Chrome 151 / Windows / NVIDIA RTX 3060 Ti · WebGL2
Symptom (as seen in production): on cold load, every PBRMaterial renders black/invisible while StandardMaterial and ShaderMaterial render fine. No console errors. Refresh usually fixes it.
Cause (traced to the WEBGL2 pipeline cache):cachedPipelines registers a pipeline before its program finishes parallel-compiling, and cached pipelines are shared across Effect instances with no reference tracking. Two concrete defects:
An Effect constructed with the same key while the first is still compiling takes the cache-hit branch and reports isReady() === true while the driver still reports COMPLETION_STATUS_KHR === false — and it is never polled.
Disposing that second Effect while the shared program is still compiling calls resetCachedPipeline + _deletePipelineContext, destroying the pipeline under the surviving Effect. The survivor is permanently isReady() === false with getCompilationError() === "", its retry poll spins silently for 120s, and executeWhenCompiled never fires. Since the whole .env decode hangs on executeWhenCompiled of a rgbdDecode postprocess effect, scene.environmentTexture never becomes ready → all PBR invisible, zero errors.
Repro: a single self-contained HTML file — no build step, no external assets (the .env is generated in-page via CreateEnvTextureAsync). It instruments driver state vs. Babylon state per 250ms sample and prints per-scenario hit rates. Both defects reproduce 2/2 per run, on 9.15.0 and 9.20.0.
Readable source (Babylon pinned from CDN; run from any static server):
Live demo, 9.20.0 (runs the suite in your browser; results render on the page, ~2 min): https://pub.hyperagent.com/p/lxG_hGCuCJwnqGr80Q-Ms1qMgoIp9vVZ0JYQHSoFb3k?delay=700&iters=2
Add &fix=1 to see a proposed fix (refcounted shared pipelines + deferred cache-hit readiness) bring every scenario to 0/2 with the healthy baseline unchanged.
(The live-demo copy has the Babylon bundle inlined because that host blocks CDN scripts — the gist is the canonical readable version.)
Not a playground repro because the instrumentation must install before Engine creation, which the playground doesn’t allow.
I have a patch ready (4 files, 8 hunks, applies cleanly to both 9.15.0 and 9.20.0) plus instrumented JSON results — happy to open an issue + PR if this is confirmed.
Not sure if you had a chance yet @sebavan, but the patch I sent fixes it in my real app- i’m loading that patch on top of 9.20.0. My surfaces aren’t going black anymore. Along the way I hit a second issue: the environment BRDF texture can come back completely black while still reporting ready, which also blacks out all PBR materials with no error. I’m working around it by loading my own BRDF texture and letting the shader decode it directly, instead of Babylon’s built-in expansion step. If you want to dig into it I can file a separate issue with a playground repro.
Sorry, it took me a minute to look through notes of how I got to where we’re at.
The short version is- the repro just uses it as the shortest way to force the bugged state. My real app never touches Effect, and everything in the failing scene was created via createEffect.
In my app, every PBR surface in the room would half the time render black or skipped (invisible), while StandardMaterial and ShaderMaterial surfaces rendered ok. No errors, and a refresh usually fixed it, so it took a while to track it down. Once I got it into the bugged state, I ran a few checks in the devtools console.
Babylon’s internal postprocess+rgbdDecode Effect was stuck at isReady() === false, but checking its WebGL program in devtools showed both LINK_STATUS and COMPLETION_STATUS_KHR as true. Calling engine._isRenderingStateCompiled(effect._pipelineContext) once flipped the effect ready and the whole room appeared / black walls colorized etc.
Calling _isRenderingStateCompiled was not just checking the status. That method also finalizes a completed pipeline and fires its compile callback. One call from devtools made Babylon recognize the already-finished program, after which the Effect became ready and the room rendered normally.
I never caught the exact trigger, which is why the repro forces the same “type” of bug instead of replaying my scene. With the patch I sent, one Effect can no longer dispose a pipeline that another Effect still uses.
It would be amazing if you have a repro of your app somewhere as it seems that smthg is really buggy in the way we managed our rdbd texture or pbr material. I would really like to patch the root cause vs the current effect it has on your app.
The code path relying on the pipeline key should normally only be used in a prebuild scenario not similar to what would be addressed here.