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:
+17
-10
@@ -1,14 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build the WebAssembly target (openra3.html/.js/.wasm) with the Emscripten image.
|
||||
# Build the WebAssembly target (openra3.js/.wasm + worker + page) with the
|
||||
# Emscripten image.
|
||||
#
|
||||
# Browsers forbid synchronous on-demand file reads on the main thread, so assets
|
||||
# are preloaded into the module's filesystem. Prefer a compact, per-map set: one
|
||||
# map plus the tiles it uses:
|
||||
# The engine runs inside a plain Web Worker and renders into an OffscreenCanvas
|
||||
# with no SDL, so assets load on demand: point OPENRA3_WEB_ASSETS at the
|
||||
# extracted asset tree and the build generates an assets manifest, embeds it, and
|
||||
# registers every file as an Emscripten lazy file at startup. The browser then
|
||||
# fetches each asset only when the engine opens it.
|
||||
#
|
||||
# OPENRA3_WEB_ASSETS="$(mktemp -d)" \
|
||||
# openra3 textures --map map_mp_2_feasel1 # copy these under the set's terrain/
|
||||
# OPENRA3_WEB_ASSETS=/path/to/assets scripts/build-wasm.sh
|
||||
#
|
||||
# then point OPENRA3_WEB_ASSETS at that directory.
|
||||
# open http://localhost:8199/index.html
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
@@ -19,13 +21,18 @@ docker build --target dev -f "$ROOT/Dockerfile.wasm" -t "$IMAGE" "$ROOT"
|
||||
|
||||
mounts=(-v "$ROOT:/work")
|
||||
configure=(-DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/emscripten-wasm.cmake -DCMAKE_BUILD_TYPE=Release)
|
||||
serve_assets=()
|
||||
if [ -n "${OPENRA3_WEB_ASSETS:-}" ]; then
|
||||
mounts+=(-v "$OPENRA3_WEB_ASSETS:/assets:ro")
|
||||
configure+=(-DOPENRA3_WEB_ASSETS=/assets)
|
||||
manifest="$ROOT/$BUILD_DIR/assets.manifest.json"
|
||||
mkdir -p "$(dirname "$manifest")"
|
||||
python3 "$ROOT/scripts/make_web_manifest.py" --root "$OPENRA3_WEB_ASSETS" --out "$manifest"
|
||||
configure+=(-DOPENRA3_WEB_MANIFEST="/work/$BUILD_DIR/assets.manifest.json")
|
||||
serve_assets=(--assets "$OPENRA3_WEB_ASSETS")
|
||||
fi
|
||||
|
||||
docker run --rm "${mounts[@]}" -w /work "$IMAGE" cmake -S . -B "$BUILD_DIR" -G Ninja "${configure[@]}"
|
||||
docker run --rm "${mounts[@]}" -w /work "$IMAGE" cmake --build "$BUILD_DIR" -j
|
||||
|
||||
echo "WebAssembly: $ROOT/$BUILD_DIR/bin/openra3.html (+ .js/.wasm/.data)"
|
||||
echo "serve: python3 \"$ROOT/apps/web/serve.py\" --root \"$ROOT/$BUILD_DIR/bin\" --port 8199"
|
||||
echo "WebAssembly: $ROOT/$BUILD_DIR/bin/index.html (+ openra3.js/.wasm, openra3.worker.js)"
|
||||
echo "serve: python3 \"$ROOT/apps/web/serve.py\" --root \"$ROOT/$BUILD_DIR/bin\" ${serve_assets[*]:-}"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate the wasm lazy-asset manifest.
|
||||
|
||||
The wasm build runs the engine on a worker, where synchronous XHR is legal, so
|
||||
assets are registered with ``FS.createLazyFile`` instead of being preloaded: the
|
||||
browser fetches each file only when the engine first opens it. This script lists
|
||||
every file under the assets root (as POSIX-relative paths) as
|
||||
``{"files": [...]}``; CMake preloads the manifest to ``/assets.manifest.json``.
|
||||
|
||||
scripts/make_web_manifest.py --root /path/to/assets --out assets.manifest.json
|
||||
|
||||
Serve the tree at ``<wasm-root>/assets`` (or use ``serve.py --assets``).
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Generate the OpenRA3 wasm asset manifest")
|
||||
parser.add_argument("--root", required=True, help="assets root to enumerate")
|
||||
parser.add_argument("--out", required=True, help="manifest JSON to write")
|
||||
args = parser.parse_args()
|
||||
|
||||
if not os.path.isdir(args.root):
|
||||
print(f"root not found: {args.root}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
files = []
|
||||
for dirpath, dirnames, filenames in os.walk(args.root):
|
||||
dirnames.sort()
|
||||
for name in sorted(filenames):
|
||||
rel = os.path.relpath(os.path.join(dirpath, name), args.root).replace(os.sep, "/")
|
||||
files.append(rel)
|
||||
|
||||
with open(args.out, "w", encoding="utf-8") as handle:
|
||||
json.dump({"files": files}, handle, separators=(",", ":"))
|
||||
handle.write("\n")
|
||||
print(f"wrote {args.out} ({len(files)} files)")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user