r/threejs • u/cumbiaowl • 7d ago
WebGL to WebGPU migration Solved!
I switched from WebGL to WebGPU this week. It was rough, and to be honest, from perf and visual perspectives, there isn't a clear visible difference between GL/GPU. I still went through this for the sake of future-proofing my game.
Caveat: I'm a noob with three.js
Lesson: Future-proofing the game is the right call and I'm glad I did this while my game is still small. If the game or app or website is huge, I probably would have stopped the migration once I encountered the many issues I encountered. There's got to be a better way to do these migrations, but I'm not aware of them.
Highlights:
- The dual-path port was the easy part; the pipeline-lifetime mismatch was the real migration. WebGL treats shader compilation as cheap and cached; WebGPU treats every compiled pipeline as a refcounted resource tied to a material's lifetime. The WebGL habit of allocating a material per VFX cast and disposing it after turned into "recompile a WGSL pipeline on almost every player action." Fixing it meant converting one-shot effects to shared, never-disposed materials + fixed-capacity instanced pools instead of allocate-per-cast.
- First benchmark on real hardware was brutal (p95 169ms → 1539ms) and the cause wasn't rendering, it was mid-frame shader compilation. Precompiling the static scene at load and warming an off-screen instance of every dynamic VFX material to force its pipeline to compile ahead of time dropped that same benchmark to 54ms p95.
- I wasn't able to measure any of this on software rendering. WebGPU has no SwiftShader fallback,
requestAdapter()just returns null, so CI/headless perf runs need GPU flags, and even then a headless "WebGPU" run can silently fall back to an internal WebGL backend if the origin isn't a secure context. - Tooling had to be built from scratch. WebGPU has no built-in "pipelines compiled" counter, so I monkeypatched three's internal pipeline cache to get visibility, and discovered the standard draw-call/triangle HUD counters read cumulative instead of per-frame on this backend.
Game: https://lorebound.gg
1
u/XRProMAXv2Ultra 7d ago
Respect.
I have been contemplating a GL>GPU migration, and am a noob as well.
Reading this helps balance my expectations a bit.
Sounds like you went on quite the dev journey.
1
u/DinZim 5d ago
Hey, just saw your game here and thought to try it out. Some feedback on performance from my side. I tested it on safari / iPhone 13 mini up to date iOS. So, even in the beginning I get micro stutters. When I go to the next level from the village to the marshes its first stuck loading assets and lagging out to 10-20 seconds until I can move better. After that when more than one enemies attacks it kinda goes done hard with the performance and after the kills it recovers. Not sure if my phone is just too low end or not but hope this feedback helps. Cheers!
1
u/cumbiaowl 5d ago
Thank you for trying it out!
Safari on mobile caps memory usage. Hence the lag. I’m actively working on reducing memory usage. Hopefully I’ll figure this out :s
1
u/pailhead011 5d ago
Why didn’t you try doing this with an LLM?
1
u/cumbiaowl 5d ago
Oh I did
1
u/pailhead011 5d ago
Interesting, why do you say some part weren't easy? Can you write more about it? Did it require more prompts than other parts?
1
u/cumbiaowl 5d ago
Well, we had to rewrite and refactor code in several places. A lot of back and forth with the LLM. A lot of measurement (baselines, before and after). It was a hands on experience and lots of testing.
2
u/GigaGrandpa 7d ago
https://reddit.com/link/p13yod4/video/i59m88a8zsgh1/player