Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SDL3 GPU WebGPU Backend #12046

Open
wants to merge 88 commits into
base: main
Choose a base branch
from
Open

SDL3 GPU WebGPU Backend #12046

wants to merge 88 commits into from

Conversation

klukaszek
Copy link

Description

Congrats on shipping SDL 3.20, and officially releasing SDL3!

Now that SDL3 has been released, I have decided to open a PR for my work for the WebGPU backend as suggested by @flibitijibibo.

Attached is a checklist of the API methods, as well as a checklist of working examples. (As of 2025-01-21).

Examples and more info can be found at: https://github.com/klukaszek/SDL3-WebGPU-Examples
(Based on https://github.com/TheSpydog/SDL_gpu_examples/)

A live demo can be found at: https://kylelukaszek.com/SDL3-WebGPU-Examples/.

My fork currently fails to pass the Emscripten pipeline test for some reason that I haven't taken the time to investigate yet. So that will probably have to be resolved before merging with main.

I'm probably gonna get to work on compute pipelines sometime soon if no one ends up working on that by the time I'm free again.

Shaders

This current implementation of the backend expects WGSL shaders since I have only tested on browsers, and browser implementations of WebGPU don't offer support for the SPIRV SType. Once native WGPU support becomes a priority, then this issue can be tackled.

API Checklist

General

  • DestroyDevice
  • SupportsPresentMode
  • ClaimWindow
  • ReleaseWindow

Swapchains

  • SetSwapchainParameters
  • SupportsTextureFormat
  • SupportsSampleCount
  • SupportsSwapchainComposition

Command Buffers and Fences

  • AcquireCommandBuffer
  • AcquireSwapchainTexture
  • GetSwapchainTextureFormat
  • Submit
  • SubmitAndAcquireFence (Should just call Submit)
  • Cancel (Should be no-op for WebGPU)
  • Wait (Should be no-op for WebGPU)
  • WaitForFences (Should be no-op for WebGPU)
  • QueryFence (Should be no-op for WebGPU)
  • ReleaseFence (Should be no-op for WebGPU)

Note: WebGPU has no exposed fence API.

Buffers

  • CreateBuffer
  • ReleaseBuffer
  • SetBufferName
  • CreateTransferBuffer
  • ReleaseTransferBuffer
  • MapTransferBuffer
  • UnmapTransferBuffer
  • UploadToBuffer
  • DownloadFromBuffer
  • CopyBufferToBuffer

Textures

Samplers

  • CreateSampler
  • ReleaseSampler

Debugging

  • InsertDebugLabel
  • PushDebugGroup
  • PopDebugGroup

Graphics Pipelines

  • CreateGraphicsPipeline
  • BindGraphicsPipeline
  • ReleaseGraphicsPipeline

Compute Pipelines

  • CreateComputePipeline
  • BindComputePipeline
  • ReleaseComputePipeline

Shaders

  • CreateShader
  • ReleaseShader

Rendering

  • BeginRenderPass
  • EndRenderPass
  • DrawPrimitivesIndirect
  • DrawPrimitives
  • DrawIndexedPrimitives
  • DrawIndexedPrimitivesIndirect

Copy Passes

  • BeginCopyPass
  • EndCopyPass

Compute Passes

  • BeginComputePass
  • EndComputePass
  • DispatchCompute
  • DispatchComputeIndirect
  • BindComputeSamplers
  • BindComputeStorageTextures
  • BindComputeStorageBuffers
  • PushComputeUniformData

Fragment Stage

  • BindFragmentSamplers
  • BindFragmentStorageTextures
  • BindFragmentStorageBuffers
  • PushFragmentUniformData
    • Needs to be rewritten.

Vertex Stage

  • BindVertexBuffers
  • BindIndexBuffer
  • BindVertexSamplers
  • BindVertexStorageTextures
  • BindVertexStorageBuffers
  • PushVertexUniformData
    • Needs to be rewritten.

Rendering States

  • SetViewport
  • SetScissor
  • SetBlendConstants
  • SetStencilReference

Composition

  • Blit
    • Mostly functional.
    • Bug: Example "Blit2DArray.c" has a sampler issue where the RHS is not downsampled.
    • Bug: Example "TriangleMSAA.c" does not cycle between different sample counts.

Example Checklist

  • ClearScreen.c
  • BasicTriangle.c
  • BasicVertexBuffer.c
  • CullMode.c
  • BasicStencil.c
  • InstancedIndexed.c
  • TexturedQuad.c
  • Texture2DArray.c
  • TexturedAnimatedQuad.c
    • Example loads with no warnings, but nothing draws.
    • Needs to be investigated.
  • Clear3DSlice.c,
  • Blit2DArray.c
    • Sampler issue on right texture with no warnings, but draws.
    • Needs to be investigated.
  • BlitCube.c
  • BlitMirror.c
    • Example loads with no warnings, but nothing draws.
    • Needs to be investigated.
  • Cubemap.c
  • CopyAndReadback.c
  • CopyConsistency.c
  • BasicCompute.c
  • ComputeUniforms.c
  • ToneMapping.c
  • CustomSampling.c
  • DrawIndirect.c
  • ComputeSpriteBatch.c
  • TriangleMSAA.c
    • Draws properly, but no visible change occurs when changing the sample count.
    • Needs to be investigated.
  • WindowResize.c (Resizes browser canvas. Have not tested anything natively.
  • GenerateMipmaps.c

Native WebGPU Support

I have not done any testing with native distributions of WebGPU (WGPU Native / Dawn), though I have implemented Elie Michel's surface selector logic sdl3webgpu.c for when someone wants to give it a test.

Warning:
The preprocessor macros in WebGPU_INTERNAL_CreateSurface() don't seem to work properly, and as a result, I hard coded in a workaround since I'm only testing on the web for the time being.

Existing Issue(s)

#10768

…gh all of the commits were the one I just rebased... Fixed everything back up.
…PU objects aren't being released via the bindings. Might be an actual bug with Emscripten's bindings specifically, need more info.

Working on a solution for uniform functions in SDL3. WebGPU BindGroups make this specific approach tough to handle. Assume uniform struct is stored at group 0 binding 0, contents should be 1 buffer FOR NOW.
…ere is no reason for them to mimic the Vulkan implementation. Added GPU API checklist. Next will be vertex and fragment uniform buffers.

Updated checklist
… crashes, but nothing renders properly. Need to investigate further.
…a bunch of existing bugs with the backend. Still encountering a layerCount issue that I cannot verify. My debugger says the texture and texture view both have 4 layers, but the error says that the texture's array layer count is 1.
…allows views of 1 layer for color attachments...
… pipelines. Now we create internal SDL pipelines and everything is handled nicely. 3D texture example still works.
…gate why the sampler isn't working in the Blit2DArray example.
…no longer needed outside of the frame. Minimizes heap resizing
… more static allocations now. Static allocations only occur on named object creation, and when dealing with PipelineLayouts. Planning on refactoring PipelineLayouts later.
… the emscripten keyboard event handlers when no hint was set.
… configure the surface. Elie Michel's surface configuration logic was added but the macros don't seem to want to work for me. I've added a temporary workaround since I am only testing Emscripten anyways.
@slouken slouken added this to the 3.4.0 milestone Jan 21, 2025
… the emscripten keyboard event handlers when no hint was set.
… configure the surface. Elie Michel's surface configuration logic was added but the macros don't seem to want to work for me. I've added a temporary workaround since I am only testing Emscripten anyways.
…ly implemented yet, but I have worked the existing pooling logic from the other backends to work with WebGPU. Makes use of mutexes and attempts to handle fences.
@klukaszek
Copy link
Author

Note for commit: 30bbcee

The abstracted WebGPUCommandBuffer struct is pooled in the hash table, but the underlying GPUCommandEncoder struct must be created per frame due to WebGPU limitations.

GPUCommandEncoder is consumed and cannot be reused once finish() is called on the encoder. finish() is what returns the GPUCommandBuffer that we need.

@TheSpydog
Copy link
Collaborator

Took a quick look at the latest. In addition to cosmonaut's unresolved review comments, I have a few additional things to mention.

First, the high level, non-WebGPU stuff:

  • Please undo all your whitespace changes for SDL_gpu.h and SDL_gpu.c. They make the actual meat of the diff hard to parse. We can make any formatting tweaks in a separate PR.
  • In fact, all of the changes in SDL_gpu.c should be reverted except for adding the WebGPUDriver to the driver list, and the WGSL hint logic. If any of the remaining changes are needed as hacks to temporarily work around something broken, you can denote them with // HACK so we know what we're looking at during a review.
  • I can't tell if SDL_emscriptenevents.c was changed intentionally or if a rebase borked it somehow? If some changes were intentional, they should be split off into a separate PR since they're not directly GPU related.
  • It looks like SDL_video.c was only changed to add a debug log. It's fine to add SDL_Log("whatever") for your local testing, but please delete these kinds of logs before committing.

For the WebGPU-specific logic:

  • If webgpu.h is needed for compilation (or LSP support), we should probably include it in the repo directly like we do with the Khronos headers.
  • GenerateMipmaps can be implemented via blits. See the D3D12 implementation.
  • Most of the hash+caching that Vulkan does (for render passes, etc.) is purely to work around Vulkan-specific API quirks. WebGPU shouldn't need any of this.
  • The per-thread command pools are not necessary. They're needed for Vulkan since it has threading restrictions around command pool creation and command buffer creation, but WebGPU creates encoders directly from the device, so that shouldn't be needed. You can probably copy off the the Metal implementation, since it and the WebGPU APIs are pretty similar.
  • As cosmonaut said, the binding model here (using shader reflection) is completely wrong. It would probably be best to delete all the existing binding-related logic and rewrite it from scratch. I would recommend doing that sooner rather than later. It's less exciting than ticking off a box for a new function, but this is a massively fundamental part of the driver, and we cannot merge this without this completely reworked.

As a general piece of advice, I highly recommend looking at all 3 backends (Vulkan, D3D12, Metal) when implementing a new system / filling out a new function to see how they each work. Metal is probably better to copy off of than Vulkan since it's a closer match for the higher-level WebGPU API, but please try to understand why we did things a certain way before copying them wholesale into the WebGPU driver.

On that note, I strongly encourage you to ask questions if you're not totally sure why something is the way it is. We'll gladly fill you in on the rationale. You do not have to (and should not!) reinvent the wheel for this driver.

@klukaszek
Copy link
Author

Thanks for the feedback!

As for the non-webgpu stuff I was planning on grabbing fresh copies of the upstream files and then force pushing them to fix that the diff issue.

As for the WebGPU related things, I'll gladly revert the caching changes since they bloat the tab memory footprint significantly.

I still haven't touched the shader binding logic but I took notes based on the shader wiki and have it as the biggest TODO on my list.

I'll take a closer look at the Metal backend over the course of my break this week and hopefully get lots done!

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