Skip to content

sokol: a WebGPU build of the browser player - #3096

Merged
nesbox merged 3 commits into
mainfrom
sokol-webgpu
Oct 17, 2026
Merged

nesbox merged 3 commits into
mainfrom
sokol-webgpu

Conversation

@nesbox

@nesbox nesbox commented Oct 17, 2026

Copy link
Copy Markdown
Owner

Adds a WebGPU backend for the browser build, behind BUILD_SOKOL_WGPU (off by default), and a CI job that builds the set the site serves.

The engine needed nothing: the studio and the core never touch gl*, the renderer speaks sg_* only, and the shaders have carried WGSL beside GLSL and MSL since they were generated with --ifdef. What the build asks for is the layer — SOKOL_WGPU in place of SOKOL_GLES3 — and the emdawnwebgpu port on the compile and link lines.

The two backends cannot share a build: the port replaces the WebGL2 linker settings, and sokol takes exactly one backend at compile time.

What's in it

  • cmake/sokol.cmake: the backend switch and the link flags. Two costs of the port are there: it carries the header every C file includes, so it goes on the compile flags too; and it brings C++ of its own into a C link, so -sDEFAULT_TO_CXX and the stack exports are named — unnamed, the universal build dies in callMain with __emscripten_stack_alloc is not a function. The exports live in the WebGPU branch only; naming them keeps the three in the module, which a GLES3 build drops unused.
  • CMakeLists.txt: the option, and a FATAL_ERROR for asking for it where it cannot take (not Emscripten, or without sokol) rather than a build that quietly keeps the other backend.
  • .github/workflows/build.yml: a job of its own, html-webgpu — 15 per-language players and the universal one, staged under build/players-webgpu/ and uploaded as tic80-html-webgpu. It is separate from html so that a WebGPU-only break stops its own artifact and nothing else, where inside html it would keep every GLES3 player off the dev instance.

The wasm carries the name its loader asks for: the per-language pairs are tic80.js + tic80<lang>.wasm, the universal one is tic80.js + tic80webgpu.wasm, and -DTIC80_TARGET=tic80webgpu is what makes the loader ask for that name.

Verified

  • Both shapes run in a browser: the universal build boots the IDE and the lua build plays a cartridge at 59–60 FPS, through WebGPU.
  • A build without the flag is byte-identical to the same commit without this change (inert.js, inert.wasm).
  • The job's checks were run against real builds, positive and negative: a loader renamed away from its wasm fails the run, as does a duplicated player.
  • The workflow parses as YAML; the server half is dev-tested against this artifact layout.

The site serves these through its own deploy (nesbox/tic80-server); this PR only builds and uploads the set.

🤖 Generated with Claude Code

nesbox and others added 3 commits October 16, 2026 21:39
The browser's other backend, behind BUILD_SOKOL_WGPU, off by default: the
flag selects SOKOL_WGPU in place of SOKOL_GLES3 and links the emdawnwebgpu
port, because the two backends cannot share a build — the port replaces the
WebGL2 linker settings, and sokol takes exactly one backend at compile time.

The engine needs nothing: the studio and the core never touch gl*, and the
renderer speaks sg_* only, so what a build asks for is the layer and the
shaders, which have carried WGSL beside GLSL and MSL since they were
generated with --ifdef.

Two things the port costs, both in cmake/sokol.cmake: it carries the header
every C file includes, so it goes on the compile flags as well as the link
line, and it brings C++ of its own into a C link, which moves the stack
exports out of the default set — unnamed, the universal build dies in
callMain with "__emscripten_stack_alloc is not a function".

The CI gets a job of its own, html-webgpu, rather than a second set of
passes inside html: a WebGPU-only break stops its own artifact and nothing
else, where inside html it would keep every GLES3 player off the dev
instance. It builds the same fifteen per-language players and the universal
one with the flag on, checks each wasm for the editors and each loader for
the WebGPU shim, and uploads tic80-html-webgpu in the layout the site's
deploy reads.

Co-Authored-By: Claude Code <noreply@anthropic.com>
…lag cannot lie

A fresh review of the diff found the CI job producing a player that cannot
start, and three smaller things:

- The universal WebGPU build did not set -DTIC80_TARGET, so its loader asked
  for tic80.wasm while the job renamed the wasm to tic80webgpu.wasm. The name
  is not decoration: the target decides what the loader calls locateFile()
  with, and the site's deploy checks for the pair the name produces. The step
  now builds tic80webgpu and copies both files under their own names.

- The check step reads the loader's own wasm name back and compares it with
  the file beside it, which is what catches a rename on the way out — the
  markers and the file list cannot see it, because the wasm next to tic80.js
  is simply not the one it asks for.

- BUILD_SOKOL_WGPU asked for on a platform where it cannot take is now a
  FATAL_ERROR rather than a build that quietly keeps the other backend. The
  guard sits beside the SDL/SOKOL one.

- The export list stays where it was: naming the stack exports keeps them in
  the module, so moving them to the shared line grew every GLES3 build. They
  are the WebGPU branch's, and a GLES3 build of the same commit is
  byte-identical with and without this change.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
@nesbox nesbox self-assigned this Oct 17, 2026
@nesbox
nesbox merged commit dd97983 into main Oct 17, 2026
17 checks passed
@nesbox
nesbox deleted the sokol-webgpu branch October 17, 2026 07:16
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.

1 participant