547 karma · joined August 16, 2020
_Vendors_ did it poorly, which is why everyone wanted to get away from them. You could argue they over-corrected and left _too_ much to the user, but better safe than sorry.
Sure users can write bad code as well, but at least it's _consistently_ bad, and fully under their control. There are so many fewer problems nowadays like "oh it works on my desktop, but on this older intel GPU for a user with outdated drivers it segfaults in the driver, and there's nothing we can do to fix it ourselves".
The solution Vulkan came up with was "make everything as explicit and user-controlled as possible", and leave "implementing a more user friendly driver" up to user-space, so that it could be tweaked by users, wouldn't be tied to driver updates users would never do, would be consistent across platforms, etc.
Except that never really happened. There are some graphics-driver-as-a-library projects (daxa, nabla, etc), but none with a large amount of community support.
Meanwhile GPU's evolved over time (AMD RDNA1, Nvidia Turing, Intel Arc, Apple Silicon), and it turns out that some of the choices Vulkan 1.0 bet on weren't great (explicit extensive pipeline state, binding model, render passes).
Vulkan 1.2/1.3 cleaned up a lot of this (dynamic state, descriptor indexing, sync v2), which also happens to improve ergonomics a lot to the point that it's much less painful to use Vulkan directly nowadays, as long as you're ok dropping support for older platforms.
The CPU feeds commands (CommandBuffers) telling the GPU what to do over a Queue.
WebGPU/wgpu/dawn only have a single general purpose queue. Meaning any data upload commands (copyBufferToBuffer) you send on the queue block rendering commands from starting.
The solution is multiple queues. Modern GPUs have a dedicated transfer/copy queue separate from the main general purpose queue.
WebGPU/wgpu/dawn would need to add support for additional queues: https://github.com/gpuweb/gpuweb/issues?q=is%3Aopen+is%3Aiss...
There's also ReBAR/SMA, and unified memory (UMA) platforms to consider, but that gets even more complex.
The first part is true, but the second part is not. Allocation and synchronization is automatic.
WebGPU is pretty far behind what AAA games are using even as of 6 years ago. There's extra overhead and security in the WebGPU spec that AAA games do not want. Browsers do not lend themselves to downloading 300gb of assets.
Additionally, indie devs aren't using Steam for the technical capabilities. It's purely about marketshare. Video games are a highly saturated market. The users are all on Steam, getting their recommendations from Steam, and buying games in Steam sales. Hence all the indie developers publish to Steam. I don't see a web browser being appealing as a platform, because there's no way for developers to advertise to users.
That's also only indie games. AAA games use their own launchers, because they don't _need_ the discoverability from being on Steam. So they don't, and avoid the fees. If anything users _want_ the Steam monopoly, because they like the platform, and hate the walled garden launchers from AAA companies.
EDIT: As a concrete example of the type of problem's WASM for games face, see this issue we discovered (can't unload memory after you've loaded it, meaning you can never save memory by dropping the asset data after uploading assets to the GPU, unless you load your assets in a very specific, otherwise suboptimal sequence): https://github.com/bevyengine/bevy/issues/12057#issuecomment...
(I work on high end rendering features for the Bevy game engine https://bevyengine.org, and have extensive experience with WebGPU)
Lumen is a fully _dynamic_ GI system. No slow offline baking, and much more flexible (and therefore realistic) lighting, especially reflections. The downside is it's much, much slower (but still barely fast enough to be realtime 60fps), and requires a semi-recent graphics card that support hardware-accelerated raytracing.
As for whether meshlets or realtime dynamic GI is harder, well, I'd probably lean towards GI right now. I haven't yet started the (very complex) LOD building system for meshlets though, so maybe I'll change my mind in the future :)
Solari is _extremely_ experimental. I'm basically entirely scrapping my previous version[1] based on Radiance Cascades[2], and starting over with ReSTIR[3] techniques. Don't expect it anytime soon, but I'm definitely having fun working on it! The real technical blockers to Solari is that wgpu, our graphics library, does not have upstreamed raytracing support. I've been using a semi-buggy fork[4], but we'll need to get it merged before Bevy can merge any Solari code.
Meshlets (no cool product name like Solari) on the other hand are much less experimental. I already have a very functional and fairly fast GPU-driven renderer based on meshlets implemented, and mostly reviewed, and it should hopefully get merged into Bevy's main branch within a few weeks. Check the meshlet section of the "What Comes Next" section at the bottom of this post to see an image. I'm planning to do a livestreamed review session to get the PR pushed through since it's such a complex PR (thousands of lines). However, what I have so far is just the basics. Memory usage is a big issue, and lack of hierarchical LODs (the main selling point of Nanite) are not yet implemented. Further more, I'd really like to have both mesh shaders (for large triangles), and software raster (for small triangles) implemented to improve the visibility buffer raster speed. Mesh shaders are sadly not supported in wgpu, nor has anyone started a fork to implement them. For software raster, we need support for 64bit integers and texture atomics in wgpu, but that's something we're working on[5].
So yeah, lots of experimental work that's going to take a while to be production-ready and available to users, part of which will need to be working with wgpu to implement more modern features, as wgpu's current priorities are understandably focused on the WebGPU spec, which lacks the newer features from 2020+ that started shipping in the last 2-3 GPU generations. I'm having a lot of fun though, and making good progress!
[1]: https://github.com/bevyengine/bevy/pull/10000
[2]: https://drive.google.com/file/d/1L6v1_7HY2X-LV3Ofb6oyTIxgEaP...
[3]: https://interplayoflight.wordpress.com/2023/12/17/a-gentler-...
[4]: https://github.com/gfx-rs/wgpu/pull/3631
[5]: https://github.com/gfx-rs/wgpu/pull/5123, https://github.com/gfx-rs/wgpu/pull/5154
* Subgroup operations
* Push constants (available in wgpu, but not WebGPU the spec/web impl)
* u64 + atomic image ops
* Mesh shaders
* Raytracing
* Binding arrays / descriptor indexing + device buffer addresses
Best of luck though, and always glad to see more people work on the WebGPU spec! Maybe you can help push for raytracing and bindless extensions :)
I have a PR that does a lot of the same things (meshlets, visbuffer, material depth, two pass occlusion culling) open for Bevy https://github.com/bevyengine/bevy/pull/10164 that I've been working on, which uses WebGPU.
WebGPU is actually a pretty good API imo. It's missing some advanced features like raytracing, mesh shaders, and subgroup operations (coming soon!), but it can still do a lot.
The much bigger missing feature is "bindless" support (non-uniform arrays of bound resources). BindGroup overhead (and ergonomics) is a significant downside.
Tiny glade is a game that uses bevy_ecs, but doesn't use bevy's rendering crates at all. Personally I have several WIP PRs that implement alternative rendering paths (raytracing, GPU-driven meshlet + visibility buffer rendering) and nothing internal to Bevy really changes. For users, again nothing really changes, they just add a FeatureFooPlugin to their app and then use FooComponent or whatever and it all works.
While individual crates/plugins might get bloated, Bevy as a whole will be fine. Users can choose to ignore those plugins, and use something else that works better for them.
I would also like to point out that this is a game engine - by definition, they're bloated and complex beasts, and absorb every feature under the sun because _someone_ will have a use case for some kind of game. The key is how the feature bloat affects ergonomics. It's totally fine to have 1000 features, as long as you don't _need_ to interact with most of them unless you want to.
There's been no real demand or contributors to improve it, but it's open source so there's no reason you or anyone else could stamp out the bugs https://github.com/gfx-rs/wgpu#supported-platforms.
CUDA does things like depend on specific scheduler behavior for GPU threads in order to guarantee forward progress, allowing more efficient single-pass computation routines. Or allowing CUDA kernels to launch additional kernels or allocate GPU memory from within the kernel, without host communication. Or checking the GPU architecture and performing specific micro-optimizations designed around that hardware's internal design.
GPU's are nothing like CPU's, in that the x86 architecture is fairly stable. There's not a _ton_ of difference between different CPUs. Sure, performance characteristics and cache sizes might be different, but generally they have the same instruction set. Each GPU generation is basically a completely different architecture, much less between GPU companies. That's (partly) why Vulkan is so complicated - it tries to support the lowest spec 2013 mobile GPU, and the highest spec 2023 desktop GPU in one single API.
Coincidentally, I help develop the Bevy game engine, and I was just talking to people about working on a bevy_usd library. From what they told me, USD is extremely complicated, and the "spec" is basically just the C++ source code. I was warned not to bother. Imo it would be better if glTF just got more support and extensions.
- bevy_pbr: Bevy's standard physically-based renderer. Provides PBR shaders, user-extensible materials, as well as APIs for organizing draw calls and rendering.
- bevy_core_pipeline: Defines some standard rendering setups like "2d" and "3d - Opaque, transparent, etc", with the goal that you could plug your own wgpu-based renderer into this layer. Also currently holds all of our post-processing effects (TAA, tonemapping, bloom, etc).
- bevy_render: One part wrapper/helpers around wgpu, one part generic renderer utilities like a render node graph and 3D camera types.
There's also some extra crates I didn't cover like bevy_sprite for 2D, bevy_ui for UI, etc.
If you want to build a wgpu-based renderer, you have a couple of options depending on what kind of rendering you need. You can plug in your own custom renderer instead of bevy_pbr using bevy_core_pipeline. You can still use bevy_pbr and just add your own render nodes on top of it. Or, you can ditch wgpu/bevy_render completely and build everything from scratch (at the cost of losing UI/2D support).
Anything you do would take the form of a custom plugin. Bevy is extremely modular - plugins are how we organize and develop the engine itself, so removing certain rendering plugins and adding your own is easy.
EDIT: I encourage you to join Bevy's discord channel and discuss your project in the #rendering channel, it's much easier to provide help and give info than over a forum.
We simply don't have the contributors to create and maintain such a renderer, at least at the moment.
Pros:
- Compared to OpenGL, WebGPU is wayyy better in every way
- Compared to Vulkan/DirectX 12/Metal, WebGPU is 0.5-1 steps higher level (easier to use), and covers macOS/iOS without needing a separate Metal backend
- We get WebGPU (browser) support for free, as mentioned in this post :). wgpu also gives us WebGL2 for free (provided you don't use certain features) which we're using to target browsers until WebGPU is more fully supported.
- WGSL (the shader language) is actually fairly nice coming from Rust, compared to the more C-style GLSL/HLSL
Cons:
- We're leaving performance on the table due to WebGPU validation / extra work it needs to do to abstract differences between platforms and be higher level than the APIs it wraps. Not the worst thing in the world, but it's a downside. Probably can be alleviated on non-browser platforms with opt-in relaxed validation and lower level APIs (wpgu-hal).
- Library/tooling maturity is much worse. Wgpu/naga (the WGSL shader compiler) frequently have bugs (less now than they used to), and error messages leave much to be desired. Shader tooling is much worse - we've had to build our own shader import system, syntax highlighting is provided by a non-bevy VSCode extension someone on the internet kindly maintains, GPU debugging tools don't have source-level info for WGSl shaders, etc. Again this will get better over time.
- We can't access all the latest features. Things like raytracing, subgroup/warp/wave operations, binding arrays, etc. That said, wgpu does provide native-only extensions for some of these. Support inevitably trails behind Vulkan/DirectX 12 though. Not really a huge deal for the most part, but I personally do miss this. Again will get better as wgpu/webgpu finalize and more time can be spent extending the spec with newer features.
- I've left this for last, but the explicit binding model sucks. WebGPU is fairly high level, but keeping explicit binding instead of requiring binding arrays is awful. Every time you want to add a texture/buffer/etc to your shader, you need to modify the bind group layout, modify the bind group, and then modify the shader, and keep those definitions in sync. When you have multiple pipelines, bind groups, shaders, and modular systems that only need certain resources under different conditions or certain parts of different shaders, it's just awful to keep track of. The ergonomics are terrible. I wish WebGPU would've required binding arrays and just accepted losing support for some older devices. In Bevy we plan to write an abstraction that acts more like binding arrays and falls back to explicit bindings where needed, but that's still a lot of work.
Overall, I generally think wgpu was/is the right choice for Bevy. The ergonomics could still use work, we leave performance on the table, we don't get all the advanced and new features, and the growing pains were (and still are, to a lesser degree) real. But we still get better ergonomics compared to other options, WebGPU support for browsers, 1 rendering backend vs at least 3 (Vulkan/Metal/WebGL2), and the rest of the issues are fixable over time and with more work done on both Bevy's side and the library and tooling side.
Sometimes I wish we went with just Vulkan+Metal and could drop down to lower level stuff, get access to new features, and just generally not have to deal with extra layers and immature tooling. But I still think it's the right choice for the project overall. Discalimer: just my own opinion, I don't represent the project.
> Also, have you thought of teaming up with anyone who is explicitly teaching webgpu / wgsl? Seeing someone create a rust perspective course on learning webgpu could be nice.
Learn wgpu (https://sotrh.github.io/learn-wgpu) provides a nice intro to WebGPU/WGSL. I don't think there's really anything Bevy specifically would be able to provide. Once you wrap your head around the APIs themselves, generally the hard part of graphics programming is dealing with the boilerplate, and the actual 3D rendering techniques themselves irrespective of the API you write in.
That said, there's a few improvements I've though about contributing to learn wgpu to cover the more advanced side of things (compute shaders, storage resources, alignment rules, etc), but I haven't had time lately.
What is your use case for needing webgl1?
Bevy is also extremely modular. If you absolutely need webgl1 support, you can implement it yourself as a plugin without losing the rest of the non-rendering parts of bevy like audio, input, the core ECS of course, etc.
The benefits WASM brings are:
1. Sandboxing - security 2. Isolation - if a plugin crashes, it won't bring down the whole program 3. Interoperability - write a plugin in lots of different languages, not just lua or js 4. Speed
I'm hoping to write my thesis for my master's degree on this topic this year. I'm also in the process of writing a game like screeps, where users provide a WASM script to control units for an RTS-style game (without combat though) https://github.com/JMS55/botnet.
It's amazing how simple it is to constrain memory usage, runtime duration, and secure exported functions to a WASM VM. Performance is also great - currently about ~6 microseconds per tick per unit, up to ~200 microseconds when doing expensive pathfinding. All that, while letting you program your units in Rust - the same language as the server is written in, while being able to share code with the server, and not having to use something more script-y like lua.
It would make the replay system I'm writing much more difficult though xD
Here's an example bot script: https://github.com/JMS55/botnet/blob/master/example_bot/src/...
The basic infrastructure of the project is more or less in place, besides a visual replay viewer which I'm working on right now. What's needed is a bunch of work in designing game mechanics and APIs. I don't actually have any plans at the moment beyond bots running around and harvesting randomly generated resources. Feel free to open a discussion on the github page if you're interested in Rust, WebAssembly, and video games.
Until then, you can either avoid UI files, write them by hand, or my favorite option: use blueprints[1], a file format that compiles to UI files. For instance, this file[2] describes a form with a folding advanced section, complete with functions to call when buttons are pressed, and CSS class names to apply to widgets.
[1]: https://jwestman.pages.gitlab.gnome.org/blueprint-compiler/ [2]: https://github.com/JMS55/whatthefn/blob/master/res/blp/profi...