WebGL support is missing a lot of improvements

Hi, I wish the following Webgl features were added:

  • Continue working with Google by providing what they asked to fix shader compiling stutter bug at https://issues.chromium.org/issues/40685460 - this is unfixed because of Unity not responding to request since Dec 16, 2020 11:19PM
  • Help browser developers by participating in fixing other shader compiling stutter issues:
    https://bugzilla.mozilla.org/show_bug.cgi?id=918941
    Chromium issues 40508951, 40814782
  • VAO - vertex array objects - this will reduce amount of slow webgl commands issued when changing vertex streams formats
  • Sampler objects - the same for texture sampling modes (ancient UE4 webgl export did that already years ago)
  • UBO for all renderers - BIRP, URP, UI renderers, particles, etc - this will get rid of multiple slow small set uniform calls (ancient UE4 webgl export did that already years ago for some parts of renderer)
  • Unlock instancing and batching limits - currently there is a limit of amount of vertices per batch, and limit of instances in instancing - provide configurable options and make limits higher
  • Use WEBGL_multi_draw extension - this will allow drawing many models with many lods with many materials in 1 draw call, if all geometry is in one big array buffer, using the same shader and all textures are in texture arrays. (According to Khronos Group presentations for Webgl this gives 10-60x performance increases).
  • Ability to remove contents of packaged builds by providing textual lists of files to cut (as in UE)
  • InEditor support for building DXT and ASTC packages with textures only, while models and sounds are in “commondata” package (already supported partially in UE4 github branches)
  • Add an API to allow developing own graphic backends if you plan to continue keeping making small efforts on WebGL
  • Merge changes from Chinese branch of Unity 2022 Tuanjie, which adds new c# .net version support, tools to minimize builds, memory and asset optimizations.
  • Ability to remove from the build: global-metadata.dat (starts from 3426kb), unity default resources (1590kb depends on build configuration), URP XR AR VR stuff
  • Add ability to unpack the builds and see which garbage got inside.
  • Add ability to specify additional emscripten flags like -o2, -fno-rtti (add this as default)
  • Contact emscripten and binaryen developers to inform them that in some compiling and optimization passes their tools work only in single-threaded mode, especially in LTO and wasmopt stages
  • Fix the loading bar on brotli and gzip by hardcoding downloaded data sizes in the JS/HTML so loading bar works on every build configuration correctly by calculating against correct amount of downloaded/total data
  • Cut everything XR AR VR related from shaders and resources.
  • Make .mp3 as the only and default option, play it directly with web audio api and don’t decode (this format is the most compatible among all devices), AAC is not.
  • Fix the video player to also support HTML5 sources, thoroughly test against all supported formats and fill bug reports to browser vendors as needed (hanging .mp4 playback in Chrome)
  • Fix input by providing an option to auto summon keyboard on tapping the edit field AND add virtual keyboard example implementation, fix long standing bugs
  • Add support for Web worker based multithreading for those HTML5 gaming platforms which forbid using SharedArrayBuffer due to its vulnerabilities, or due to inability to develop correct CORS policies for isolated ad frames and payment system frames (currently everything is based on emscripten’s pthread library, which needs SharedArrayBuffer)
  • Add new C# backends + .net 10 + NativeAOT (2 new compilers emerged)
  • Make stripping of WASM automatic and efficient, current 4-6mb brotli sizes are much higher than download sizes of modern WASM engines.
  • Add modern 2d and 3d physics engines, not the old and buggy PhysX, or at least upgrade to Nvidia Multiverse Physics.
  • Dedicate 1 WebGL developer to visiting Khronos Group events to keep the engine up-to-date with modern features.
  • Make internal JSON de-(se-)rializer exposed and make it compatible with all Unity types, so we do not need external packages for this because of their build sizes.
  • Ensure that both data and wasm files in all compression modes (none, gzip, brotli) and in all configurations with both decompression fallback on/off are cached in various browsers starting from at least Chrome 100.
  • Make shader graph shaders smaller - simple lit shader inside .data file is 200kb.
  • Make BIRP PostProcess shader smaller - it is 1 mb now.

Regards!

excellent interrogation, we are waiting for your answer Unity

I don’t agree with all the points but some are definitely worth an investigation IMHO.

That said, if Unity is ressource constrained, I would prefer them prioritize WebGPU vs WebGL.

Hi!

Thanks for the detailed feedback. I can give answers to some of them.

The global-metadata.dat is necessary for the IL2CPP .Net runtime. It contains all information necessary for reflection. So it is not possible to straight up remove that file entirely without completly disabling reflection support. However, we are making efforts the reduce the overal code size and meta data size through better code stripping.

It’s possible to set additional linker args through PlayerSettings.WebGL.emscriptenArgs. For the settings you want to add: the optimization level is via the Build Settings “Development Build” and “Code Optimization”.

  • Development Build: true → -O1
  • Code Optimization: Shorter Build Time → -O2
  • Code Optimization: Runtime Speed → -O3
  • Code Optimization: Disk Size → -Os
    The option -fno-rtti is already used.

Thats a great idea and I raised it with the team.

On what platforms and browsers are you having issues with AAC support? As far as I am aware only Linux does not have out-of-the-box AAC support without installing the required packages. In all other browsers and platforms it works without issues.
Regarding the “don’t decode” you can control if a audio clip is decoded when loaded or on the fly during playback using the AudioClipLoadType.

We do have support for the system virtual keyboard on mobile browsers. Do you have issues with that feature? Which specific bugs do you refer to?

The emscripten pthread implementation also makes use of the Web Workers internally. If you want to utilize them in your game directly you can write a JS plugin to perform certain tasks in a Web Worker. It would be hard for us to add support for the message-passing type of multithreading to the C# API since it would be a Web Platform specific feature.

Can you send us steps to reproduce your issue? We currently don’t have any open bugs regarding the caching of the files of a Web Build.

Hei. Plenty to unpack here so it will take a while from us to answer to everything, but for this, check out GitHub - Unity-Technologies/UnityDataTools: Experimental tools and libraries for reading and analyzing Unity data files.

This will be part of the engine-wide transition to Core CLR (probably Unity 7-ish).

Did you enable LTO (link time optimization) favoring disk size, not performance, and Managed Stripping Level set to High?

You can get a blank WebGL to below 4 MB if you tune it aggressively. Lots of ideas and tests in this thread.

Specifically about the removal of the splashscreen, you first have to uncheck “show logo” and then “disable splashscreen”, otherwise the “show logo” checkbox remains checked and the logo image included in builds (ca. 100 Kb).

That would be an engine-level change, not just for web platform. The 2D physics engine is based on Box2D, not PhysX.

Once Entities is well supported on mobile you’d also get to use Unity Physics on the web, and presumably Havok too.

Again, that would have to be an engine-wide addition/change.

Unity has JsonUtility which has some limitations, which shouldn’t pose an issue unless you need to load from externally created json files. Otherwise there’s NewtonSoft.Json package (no need to embed custom Newtonsoft), but it doesn’t “understand” Unity types.

Finally, there’s the Serialization package where you can handcraft your json (or binary) serialization.

BIRP is legacy since 6.0. Don’t expect any work going into that anymore.

I think the fundamental concern here is that we want WebGL builds to be smaller and faster. I agree! It’s definitely being worked on from multiple fronts. I, and everyone else working on the web, really appreciate the notes and wanting to make the web even better. Even if all the notes aren’t actionable, or without significant side effects, they are appreciated and taken seriously.

I can speak on the graphics concerns. We are investing in WebGPU, but WebGL still has a valuable place and isn’t being neglected.

  • VAO can certainly reduce the amount of GL commands to a degree, at other costs. We do this for platforms where the capabilities are there to make it work well, which isn’t necessarily always true for WebGL. I’m always looking for ways to improve performance, so it’s definitely something I’ll look into further if we can improve how they’re used for WebGL.

  • Sampler objects can be useful and Unity uses them in several cases. I’ll make a note to make sure WebGL isn’t unnecessarily not using them.

  • UBOs vs individual uniforms is much more of a pro/con situation. You’re trading GL command calls for buffer management and memory overhead. We use UBOs with GLES/WebGL in places where it makes sense, but in many cases the individual GL uniform calls actually have less overhead. Always good to make a note to investigate if we can improve how/when they’re used without sacrificing memory or performance.

  • Instancing and batch limits are there for performance reasons. Increasing the limit can actually have negative effects. Should it be configurable? That would add significant complexity that wouldn’t necessarily have a positive impact. I’ll make a note of it as something we can investigate further though.

  • WEBGL_multi_draw is useful. It’s not supported by Firefox. It’s not trivial to integrate either, but definitely keeping an eye on ways we might be able to utilize it. The demo from the Khronos presentation isn’t a straight forward “turn this on and you’ll get 10-60x performance increase”. It was manufactured to exploit its effectiveness. But if we can find ways to use it effectively, we definitely will.

  • Adding an API to implement custom graphics back-ends unfortunately isn’t in the cards. I can assure you we’re not neglecting WebGL, even as we work on WebGPU. We don’t have infinite resources but we all care deeply about the web and working toward makin it the best we can.

I could go on, it’s interesting going through these notes, but have to get back to work. One other note, we do work very closely with Google, Mozilla, and Apple on WebGL, WebGPU, WebAssembly, as well as directly contributing to Emscripten. Not always publicly, but we are actively involved. The web is an amazing platform, there’s a lot of room for improvement, and along with developers like you we can keep working to make that happen.

Is there any chance to disable reflection if developer doesn’t use this feature, or is it needed by the engine and plugins themselves? Are there any methods to keep only the metadata needed by engine, and only for the required c# reflection-related features per-function/feature/reflection API call/per object/struct/method/assembly instead of full blown metadata for everything just in case?

  1. According to "aac" | Can I use... Support tables for HTML5, CSS3, etc Firefox is marked as “partial support” requiring installing codecs manually.
  2. Also some previous mobile versions of Yan dex Bro wser (Chromium-based) had no sound with AAC, that’s still happen sometimes as users tend not to update browser versions.
  3. Not AAC support-related, but I’ve just imported mp3 song 1529kb in size and on 100 quality it says about imported size 5.6 mb, but with quality level 1 it shows 2.8 mb, which is still 190.43% higher than precompressed optimized song in Audacity (mp3) That’s why I also ask for keeping mp3 files untouched by build/import/convert process. Force to Mono doesn’t help (it is already mono 52 kbps, 3 min 57 sec).
  1. Unfortunately I’m not using “favoring disk size” due to 40% decrease of FPS on my Android device from 2015, so I consider this mode unusable for my games, and managed stripping level is always at High for my builds. Some engines (names start from “D” or “C”) have smaller build size. My uncompressed .wasm size is never smaller than 17 mb, that’s 3 times better than of a custom stripped down “U” engine, but still community is desperately awaiting radical improvements, and some are transitioning to “D” and “C” engines in the meantime due to Po ki and Cra zy Gam es platforms favoring engines with smaller sizes and lower startup times.
  2. Analysis using weave tool shows that the most size oppressors in a compact build are:
  • Full blown physx 2.2 mb - presumably not modularized
  • std 0.4 mb
  • il2cpp 0.3 mb
  • tetgenmesh 0.27 mb
  • updaterenderdatapure 0.15 mb
  • physicscommands 0.13 mb

Thanks for the links! Unfortunately that package adds approx 6-8 mb of dependencies to the uncompressed build, so I had to cram in the System.Text.Json with its dependencies, which also has higher footprint than JsonUtility, but is smaller than NewtonSoft.Json. My use case is to make a save game system by serializing a GameState object and putting it to the browser’s Local Storage or cloud save provider and back when loading and to work with JSON responses from leaderboard services.

Thank you very much!

Hello! One of the issues I’m having is that the keyboard doesn’t work in full-screen mode. The bug is marked as “Won’t Fix” in the Issue Tracker.

Additionally I’ve just discovered, than SRP batcher doesn’t merge draw calls on WebGL. Disappointing, the Dynamic batcher now is hidden by default, but it does merging. And definitely there are the same calls to vertexAttribPointer (without VAO) looking like this - even not cached.

uniform1iv: WebGLUniformLocation - ID: 14, [..(1)..]
bindBuffer: ARRAY_BUFFER, WebGLBuffer - ID: 966
vertexAttribPointer: 0, 3, FLOAT, false, 32, 0
vertexAttribPointer: 1, 3, FLOAT, false, 32, 12
vertexAttribPointer: 2, 2, FLOAT, false, 32, 24
bindBuffer: ELEMENT_ARRAY_BUFFER, WebGLBuffer - ID: 967
drawElementsInstanced: TRIANGLES, 618 indices, UNSIGNED_SHORT, 0, 5VertexFragment
uniform1iv: WebGLUniformLocation - ID: 14, [..(1)..]
bindBuffer: ARRAY_BUFFER, WebGLBuffer - ID: 826
vertexAttribPointer: 0, 3, FLOAT, false, 32, 0
vertexAttribPointer: 1, 3, FLOAT, false, 32, 12
vertexAttribPointer: 2, 2, FLOAT, false, 32, 24
bindBuffer: ELEMENT_ARRAY_BUFFER, WebGLBuffer - ID: 827
drawElements: TRIANGLES, 948 indices, UNSIGNED_SHORT, 0VertexFragment
uniform1iv: WebGLUniformLocation - ID: 14, [..(1)..]
bindBuffer: ARRAY_BUFFER, WebGLBuffer - ID: 782
vertexAttribPointer: 0, 3, FLOAT, false, 32, 0
vertexAttribPointer: 1, 3, FLOAT, false, 32, 12
vertexAttribPointer: 2, 2, FLOAT, false, 32, 24
bindBuffer: ELEMENT_ARRAY_BUFFER, WebGLBuffer - ID: 783

Test results:
SRP batcher only:
clear: 3
draw: 1126
total: 3750

Dynamic only:
clear: 3
draw: 382
total: 2845

Instancing only:
clear: 3
draw: 400
total: 2410

Static only 12mb->37mb:
clear: 3
draw: 673
total: 1146

It is disappointing, that for WebGL Emscripten is used instead of a native JS renderer written optimally from scratch.

Also, if SRP batcher is enabled, nothing works: neither dynamic, nor instancing. dynamic+instancing enabled prefers instancing even if draw calls are higher. And all checkboxes are spread everywhere: SRP in URP asset, dynamic is hidden behind graphics settings and shows up in URP asset, static is in the build profile, and instancing is per material.

We are waiting for new answers from Unity developers. Such posts with such detailed issues are rare.

I’m wondering if an emscripten update is planned for the near future or not. The current version is from 2023 and since then a significant number of updates and optimizations have taken place!

I suspect that will be a very hard and dangerous update no one will take with seemingly such limited resources Unity WebGL team has + compiling performance might got worse (at least that happened for the UE4 fork with the latest Emscripten) + this will require full retest of BIRP (which is obsolete), SRP and URP as Emscripten handles the GLES → WebGL translation itself, not only the C++ → WASM.