Skip to content

GPU: Experimental WebGPU SDLGPU Backend - #16020

Draft
TheeStickmahn wants to merge 91 commits into
libsdl-org:mainfrom
TheeStickmahn:main
Draft

GPU: Experimental WebGPU SDLGPU Backend#16020
TheeStickmahn wants to merge 91 commits into
libsdl-org:mainfrom
TheeStickmahn:main

Conversation

@TheeStickmahn

@TheeStickmahn TheeStickmahn commented Jul 18, 2026

Copy link
Copy Markdown
  • I confirm that I am the author of this code and release it to the SDL project under the Zlib license. This contribution does not contain code from other sources, including code generated by a Large Language Model ("AI").

Description

This is my SDLGPU WebGPU backend. I've been working on it for about 1 and a half months, and it's reached a level of "finishedness" where I need feedback on how it works.
As of right now it can run 34 out of the 34 SDLGPU examples, and I've got it running on Windows, Linux, and the Web.

It currently supports most of the SDLGPU standard, however I'm making it a draft PR for a few reasons.

  • 1: It's not finished.
  • 2: Some parts of the backend are shall we say..... oddly designed.
  • 3: I've seen ancient Egyptian hieroglyphics more legible than this code.
  • 4: This codebase is filled with some of the most deranged & demented code I've ever written.
    • 4.5: WebGPU drove me insane, and some of that insanity spilled over into the code comments. They're not very professional.

This is the first time I've ever contributed to any project that's not my own (my hubris knows no bounds), so please don't murder me when I do something stupid.
I'll be happy to help with any issues that'll inevitably be found.
(Note that I'm Swedish, so if you're in the Americas I'll be at least ~6 hours behind you.)
(Also, read the README-WEBGPU.md file. Please.)

(TLDR: WebGPU has tortured me and I will further inflict this pain upon others by forcing them to review my code.)

Before this can actually be considered for merging into SDL, we'll need to do a lot of things.

  • Remake CMake shenanigans.
    • The CMake has now been remade in order to make the build process easier and more intuitive. If compiling natively, it'll search for a static Dawn library in the CMake source directory. On Emscripten, it'll automatically link Emdawnwebgpu through Emscripten's official remote port.
      • (This behaviour can be toggled through the SDL_WEBGPU_USE_PORTS CMake option, by default on)
  • Make sure it works on all platforms.
    • Linux
    • Web. (see the web demo: https://thestickmahn.gitlab.io/ihatenamingthings/)
      • Chrome & Chromium derived.
      • Firefox*
        • Works, but has a bug when it comes to switching examples. Firefox dislikes it when you destroy a window before it's been presented (not 100% sure but let's pretend I know what I'm doing), which means that if you switch examples too fast, it'll freeze until you switch to another tab and then go back to the examples. NOTE: This bug is seemingly fixed in Firefox Beta 154.
      • Safari
    • Windows
    • MacOS (Haven't added video support yet. Objective-C is a horrifying language.)
    • iOS (Also no video support)
    • Android (Don't have an Android phone.) (Also, no video support)
  • Implement unimplemented features
    • Buffer cycling.
    • Texture cycling.
    • Mipmap generation.
    • Pretty much none of the backend's code is thread safe. We should make it thread safe.
    • Numerous other things I've certainly forgotten.
  • Add runtime error handling & validation. The backend currently has very sparse error handling, and allows you to shoot yourself in the foot with ease.
  • WebGPU features. (Sorta half-done, there's no way to configure the feature set.)
  • Bug fixes.
    • Downloading from buffers didn't work, because I hadn't implemented SDL_DownloadFromGPUBuffer... My genius frightens me.
    • Bunch of memory leaks.
      • UPDATE (2026-08-06): Most memory leaks are fixed, however there are still some leaks left. After running all 34 examples, ASan reported back 246305 bytes of leaked memory.
    • The shader parser doesn't support all valid WGSL.
      • This is largely due to the parser's crude nature. It doesn't understand some key words, and features such as binding a resource to a struct are unsupported entirely.
    • Stencils were broken. They're fixed now :)
    • Issues with bindings and bind groups.
      • Textures with non-filterable texture types are implemented horribly. You have to put //!SDLGPU_COMPAT_F32_UNFILTERABLE somewhere within your shader code to use non-filterable texture types. This is bad, as not only does it force unfilterable sampling for ALL samplers regardless of type, it is also confusing, as this is also needed for sampling depth textures.
        • UPDATE (2026-07-25): The WebGPU feature which allows for filterable float32 textures is now automatically requested. It's optional, since there's only about 90% support for the feature, but on most devices it should just work OOTB. We should still figure out a solution for this though, as there's still the issue of sampling depth textures.
    • Remove support for wgpu-native and switch fully to Dawn.
      • As of Aug. 11th, 2026, wgpu-native is no longer supported in the WebGPU backend. It is suggested you switch to Dawn instead.
  • Remove redundant code.
  • Numerous other things I've certainly forgotten.
  • Not necessarily something that's a must, but we should add SPIRV -> WGSL support to SDL_shadercross.

Anyways, I'm gonna go play CloverPit now. Have fun!
(Or don't; I'm not your dad.)

Existing Issue(s)

Resolves issue 10768.

image

Currently only supports Wayland, and it's quite..... bad

This is a DRAFT! Just a small proof-of-concept.

I'll submit a draft PR to the SDL project to see if I should continue
development.

If this is greenlit, I might making consider a WGPU SDL_GPU backend if
wgpu-native is in a good enough state (maybe Dawn if it isn't?)

No promises though! Don't get angry at me if I don't!

Current known issues:
1: Error handling is sparse.
2: It only supports the shared library version of wgpu-native, and it
does that by just loading it whenever it needs to do something
3: wgpu-native is bad and doesn't implement wgpuGetProcAddress so we're
just using SDL_LoadFunction instead. I'd much prefer using the native
wgpuGetProcAddress but alas....
Removed webgpu.h and instead we're defining the types ourselves

Thank you @HamdyElzanqali for pointing out my stupidity lol
wgpu_native really seems to like statically linking itself
(as shown by it not implementing the functionality for dynamic linking),
so I added an option to statically link it alongside SDL.

How I implemented this almost certainly goes against some convention or
guideline in SDL but I'm shooting blind here. I have no idea what to do.
Added Windows support for WGPU, although it is untested since I don't
have a Windows machine to test it on.
Added support for Google's Dawn WebGPU implementation.
You control which one's used via the option "SDL_WGPU_LIB" where it's
either "wgpu-native" or "dawn".
Congratulations; you can now use WebGPU on the Web.

I'll make an example eventually, but it's midnight in Sweden and I gotta
go to bed.
I'm calling it that simply because it's quicker.
Oh my god, there's SO MUCH STUFF TO DO
Keep on buffering Denji

I'm losing my mind
I am so confused here.

This backend is effectively a port of the Vulkan backend, but just with
WebGPU instead, but that means that I'm often including redundant
functionality or attempt to create things that just don't work in WebGPU

This backend will have to be polished, as right now it's about as smooth
as 1 grit sandpaper.
Since WebGPU shader modules can have multiple types of stages
(one shader can have a vertex, fragment, and compute stage in it), I had
to make a workaround.
I'm actually sorta proud of my solution! How it works is that every
"SDL_GPUShader" is actually a "WebGPUShaderReference", which stores the
entrypoint, what stage it is, and a hash of the shader source code.

That hash is used as the key into a hash table in the renderer, which
contains the "actual" shader.

I'm bad at explaining things, so I'd just read the code. It's only two
functions and I hope I made it easy to read.
YOU WILL GET A 200 LINE FUNCTION AND YOU WILL MERGE IT
Or, I mean; I actually started work on this three days ago.
I just hate WebGPU bind groups so much that I've actively procrastinated
on implementing them.

I will be the first to say this: My implementation of bindings in WebGPU
SUCKS. I will not deny that.

I made this quickly, and badly, since if this is not done; I will be
permanently stuck in a state of procrastination, since I cannot work on
multiple things at once.

Something something neurodivergence, something something ADHD.
Sorry for the lack of commits in the past few days.
Uniforms'll require some extra work since WebGPU expects to receive a
buffer while SDL_GPU only provides raw data on the CPU side
I've got it compiling, now I just gotta fix why it crashes.
Why's nothing showing up on the screen 😭
@TheeStickmahn

Copy link
Copy Markdown
Author

Alright; I've managed to get the backend running 33 out of 34 examples.
The only holdout is GenerateMipmaps, and that's because I haven't implemented SDL_GenerateMipmapsForGPUTexture yet.

image

34 out of 34 examples work now!
Yipeeeee!
@TheeStickmahn

TheeStickmahn commented Jul 28, 2026

Copy link
Copy Markdown
Author

With my latest commit implementing SDL_GenerateMipmapsForGPUTexture, I am very happy to say that 34/34 SDLGPU examples are now running on the Web. (Atleast, when I tried it on Linux)
https://thestickmahn.gitlab.io/ihatenamingthings/

yipeeee

@slouken

slouken commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Hooray! :)

This fixes some issues with Firefox.

This is also the final nail in the coffin for wgpu-native support, as it
does not implement wgpuInstanceWaitAny.
Next up: Currently the reference count only increases if you use a
function with a "cycle" argument. Binding a texture/buffer won't
increase the reference count.

We'll need some way of counting the references within one command
buffer's scope, as right now we're just zeroing the refcount, which
would break any command buffers that come next.
@gammamuse

Copy link
Copy Markdown

This is very exciting! I'm so ready to learn the new SDL GPU framework and was wondering about the possibility of deploying to web. Thanks so much for your hard work. 🙏

Shit so jank it broke half the damn backend.
They're not implemented very well.
Very gross, like most of this project.
Giant commit because my ADHD won and made me work on a thousand things
at once

Changes:
- 33/34 examples now work on Safari! (MacOS 26 Tahoe, M1 Macbook Air)
  - Only holdout is CompressedTextures, which for some reason is just
super broken. I have no idea why, it's not outputting any errors. I'll
attempt a frame capture using XCode soon.
- Bound resources are now tracked as "used" by in-flight command
buffers, preventing release before they're done.
- Empty bind groups are no longer bound as "NULL" but instead as their
actual bind groups, in order to make Safari stop throwing a fit.
  - This had the side effect of revealing that the entire bind group
system was held together with hopes and dreams.
  - I then proceeded to re-add new hopes and dreams, fixing it, barely.
(see the added FIXME in the SDL_gpu_webgpu.c)
- Release of textures, buffers, samplers, fences, & submitted command
buffers are now done through a queue system instead of instantly.
- Switched fully from callback-based async to future based async to
please Emscripten & Asyncify.
- Fixed multiple segfaults caused by said switch.
- Multiple functions which had use internally but only had SDLGPU
opaques as arguments were made into internal versions which use the
WebGPU backend's types.
- Renamed some functions and variable to reflect their use more
accurately.
- Command Encoders are now owned by the command buffer and not the
renderer.
- Bug fixes here and there.

Current issues that I care about:
(ranked from "this is bad" to "oh shit oh fuck", bottom->top)

- The bind group system is hot garbage that'll break any second now.
- Destroying the WebGPU device leaks 5MiB of memory. Likely some
resource that's not being freed. Also, likely my fault.
- GenerateMipmaps is broken on Safari.
- Memory leaks in countless initialization / resource creation
functions.
  - This results in anywhere from 6-50KiB of leaked memory per example.
  - However, there are no continous leaks. Running the backend for 1
second and 1 day will leak the same amount of RAM. (probably, i haven't
actually left it running for a day)
ASTC textures need to have their height and width be multiples of the
block height & width, so the backend now automagically pads the texture
size for you.

We should add something to the docs about this (alongside everything
else I haven't documented :P)
"What idiot coded this!?" - Idiot who coded this.
Changes:
- Automatically pad buffer sizes to be divisible by 4. Didn't know that
was a restriction, but I do now.
- Pipeline vertex buffer array stride was borked and it hadn't become an
issue until now.
- Added an "ALIGN_VALUE" macro since it was getting *really* annoying
writing the same alignment code over and over again.
- The backend now prevents you from creating a sampler with a negative
lodMinClamp since WebGPU doesn't like that.

These were all targeted fixes for Dear ImGUI's SDLGPU backend, which now
works without having to change anything about Dear ImGUI aside from
giving it new shaders!
Mapping buffers in WebGPU is insanely slow. Like.... really slow.
So, we're not doing that anymore! Now, any "mapped" upload buffer is
pretty much an IOU. You're given a piece of malloc'd memory and when you
run say UploadToGPUBuffer, it then calls wgpuQueueWriteBuffer for you,
making it so that uploading to buffers can be done before the heat death
of the universe.

The original code is still there though. It's just inaccessible since
it'll always choose to pseudo-map because it's much much faster.

TLDR: WebGPU can upload the data better than you can. We're gonna let
it.
@jsoulier jsoulier mentioned this pull request Aug 10, 2026
6 tasks
…ove old README's.

Changes:
- Rewrote my CMake code in order to make it less painful to build this
project.
On Emscripten it's seamless as it'll automatically link with
Emdawnwebgpu. (unless you tell it not to)
On native, it'll look for a Dawn library in the CMake source directory,
and in /usr/lib if they're on a *nix system.
- With the CMake rewrite, I've removed support for wgpu-native, as it
doesn't provide any benefits by keeping it in.
- I've removed the old README's, since they're old and would likely
cause more harm than good.
I'll probably make some new ones, but for now it's easy enough to build
this backend, and there aren't any HUGE bugs I know of.
…gotten to push it.

Sometimes my genius frightens me.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants