v0.7.0: SDL-free wasm worker backend (WebGPU + WebGL2) with on-demand assets
The engine now runs on a plain Web Worker (no SDL, no PROXY_TO_PTHREAD: in a pthread Emscripten proxies every filesystem syscall to the main browser thread, where synchronous XHR - and thus FS.createLazyFile - is forbidden). A page/worker pair transfers an OffscreenCanvas and forwards DOM input; assets load lazily from an embedded manifest, so entering a map fetches only that map and its tiles. Presentation: ra3.webgpu (WebGPU via Emscripten's emdawnwebgpu port, WGSL shaders, the default on wasm) and ra3.wasmgl (WebGL2, GLSL ES). The legacy SDL ra3.webgl backend is removed.
This commit is contained in:
@@ -25,7 +25,8 @@ extracting ore and fighting until one side is wiped out. The balance is the
|
||||
**retail Red Alert 3 balance**, pinned in `ra3.data` from EA's open RA3 XML:
|
||||
damage types, `ArmorTemplate` percentages, weapon target masks, build costs and
|
||||
the ore economy. Presentation offers **Vulkan** (`ra3.vulkan`), **Direct3D 11 / 12**
|
||||
(`ra3.dx`) and **WebGL 2** (`ra3.webgl`, the wasm build) GPU backends with an SDL
|
||||
(`ra3.dx`), **WebGPU** (`ra3.webgpu`, the wasm worker backend) and **WebGL 2**
|
||||
(`ra3.wasmgl`, the SDL-free wasm worker fallback) GPU backends with an SDL
|
||||
software blit and null fallbacks; the backend is selectable from the in-game menu
|
||||
and the software renderer still produces headless images. Terrain tiles
|
||||
cross-fade the way the retail `Terrain.fx` does (a per-cell blend ramp plus a
|
||||
@@ -96,7 +97,8 @@ built-in test map so the project still builds and runs in CI.
|
||||
| `src/ui/ra3.ui.*.cppm` | SDL3 window viewer and menu (null backend when SDL3 is absent) |
|
||||
| `src/vulkan/ra3.vulkan.*.cppm` | Vulkan presentation backend and menu (null fallback without a loader) |
|
||||
| `src/dx/ra3.dx.*.cppm` | Direct3D 11/12 presentation backend (runtime HLSL; null fallback off Windows) |
|
||||
| `src/webgl/ra3.webgl.*.cppm` | WebGL2 presentation backend for the wasm build (GLSL ES; null fallback off Emscripten) |
|
||||
| `src/wasmgl/ra3.wasmgl.*.cppm` | SDL-free wasm backend: engine on a Web Worker, WebGL2 on an OffscreenCanvas, lazy assets (null fallback off Emscripten) |
|
||||
| `src/webgpu/ra3.webgpu.*.cppm` | WebGPU wasm backend (Dawn `emdawnwebgpu`, WGSL; the default on wasm, falls back to `ra3.wasmgl`) |
|
||||
| `third_party/libenderlog/` | vendored C++26 module logger (`import ender.log;`, MIT) with a native stack fallback for libc++ |
|
||||
| `src/ra3.cppm` | umbrella module re-exporting the SDK |
|
||||
| `apps/openra3/main.cpp` | `menu` / `maps` / `skirmish` / `render` CLI |
|
||||
@@ -141,7 +143,7 @@ scripts/build-windows.sh
|
||||
# Windows (arm64) -> build/windows-arm64/bin/openra3.exe + SDL3.dll, .zip (cross)
|
||||
scripts/build-windows-arm64.sh
|
||||
|
||||
# WebAssembly -> build/wasm/bin/openra3.html (+ .js/.wasm)
|
||||
# WebAssembly -> build/wasm/bin/index.html (+ openra3.js/.wasm, openra3.worker.js)
|
||||
scripts/build-wasm.sh
|
||||
```
|
||||
|
||||
@@ -183,31 +185,36 @@ runtime, so neither image needs a Vulkan SDK.
|
||||
## WebAssembly
|
||||
|
||||
OpenRA3 also builds to **WebAssembly** with Emscripten (`scripts/build-wasm.sh`),
|
||||
producing `openra3.html` + `.js` + `.wasm`. The browser gets the same GPU renderer
|
||||
as the desktop builds: a **`ra3.webgl`** backend peers with Vulkan/Direct3D — SDL3
|
||||
provides the canvas and input, and GLES 3.0 / WebGL2 runs the 2D blit and the
|
||||
terrain raymarcher (GLSL ES ports of the desktop shaders).
|
||||
producing `openra3.js` + `.wasm` plus the `index.html` page and
|
||||
`openra3.worker.js` bootstrap. The browser gets the same GPU renderer as the
|
||||
desktop builds: the engine runs on a **Web Worker** (no SDL, no blocking of the
|
||||
page) and draws into an **OffscreenCanvas** handed over by the page. The default
|
||||
wasm backend is **`ra3.webgpu`** (WebGPU, WGSL shaders via Emscripten's
|
||||
`emdawnwebgpu` port); it falls back to **`ra3.wasmgl`** (WebGL2, the GLSL ES
|
||||
ports of the desktop shaders) when WebGPU is unavailable. The page keeps the DOM
|
||||
(input, resize) and forwards events to the worker, so the main thread stays
|
||||
responsive.
|
||||
|
||||
Browsers forbid synchronous on-demand file reads on the main thread, so the
|
||||
assets are **preloaded into the module's filesystem** at build time. Bundle a
|
||||
compact per-map set (one map plus the tiles it uses) rather than the whole dump:
|
||||
Because the runtime's main thread lives in the worker, its filesystem is local
|
||||
to it and assets load **on demand**: point `OPENRA3_WEB_ASSETS` at the extracted
|
||||
asset tree and the build embeds an `assets.manifest.json`; at startup the worker
|
||||
registers every listed file as a lazy file, so the browser fetches a file only
|
||||
when the engine first opens it (entering a map pulls just that map and the tiles
|
||||
it uses, not the whole tree).
|
||||
|
||||
```bash
|
||||
# 1. which loose TGAs does the map resolve to?
|
||||
openra3 textures --map map_mp_2_feasel1 # prints paths under assets/terrain
|
||||
# build (generates the manifest from the extracted assets, embeds it)
|
||||
OPENRA3_WEB_ASSETS=/path/to/assets scripts/build-wasm.sh
|
||||
|
||||
# 2. stage one map + those tiles (+ maps/map_names.tsv) into a directory, then
|
||||
OPENRA3_WEB_ASSETS=/path/to/that/set scripts/build-wasm.sh
|
||||
|
||||
# 3. serve the build output (a Range-capable server is included)
|
||||
python3 apps/web/serve.py --root build/wasm/bin --port 8199
|
||||
# open http://localhost:8199/openra3.html
|
||||
# serve the build output (Range-capable, COOP/COEP; --assets is the lazy tree)
|
||||
python3 apps/web/serve.py --root build/wasm/bin --assets /path/to/assets
|
||||
# open http://localhost:8199/index.html
|
||||
```
|
||||
|
||||
The engine's frame loops are blocking; the wasm build yields to the browser via
|
||||
Asyncify (`display::sleep_frame` calls `emscripten_sleep`), so the page repaints
|
||||
and handles input. Log records go to `console.log` at their real level (the
|
||||
console sink uses stdout on the web instead of stderr). Without
|
||||
The engine's frame loops are blocking; the wasm build yields to the worker's
|
||||
event loop via Asyncify (`display::sleep_frame` calls `emscripten_sleep`), so
|
||||
the page repaints and handles input. Log records go to `console.log` at their
|
||||
real level (the console sink uses stdout on the web instead of stderr). Without
|
||||
`OPENRA3_WEB_ASSETS` the module still loads and starts, but exits at the menu
|
||||
because no maps are found.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user