# gles-on-webgpu **Repository Path**: xarray/gles-on-webgpu ## Basic Information - **Project Name**: gles-on-webgpu - **Description**: A self-contained OpenGL ES / EGL implementation whose backend is WebGPU (Dawn). Created by Trae and DeepSeek4. - **Primary Language**: C++ - **License**: BSD-3-Clause - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2026-09-11 - **Last Updated**: 2026-09-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # gles-on-webgpu A self-contained **OpenGL ES / EGL implementation whose only backend is WebGPU**. Your application talks OpenGL ES (vertex attributes, textures, GLSL ES shaders, `glDrawArrays`, ...) and every command is executed through [Dawn](https://dawn.googlesource.com/dawn), Google's WebGPU implementation. ``` +-------------------+ +---------------+ +-----------+ +--------+ | GLES / EGL app | --> | GLES front- | --> | wgpu | --> | Dawn | | (glDrawArrays) | | end (libANGLE)| | backend | | WebGPU | +-------------------+ +---------------+ +-----------+ +--------+ | | | +-- WGSL shaders +-- GLSL ES translator (ESSL -> WGSL) ``` The GLES/EGL front-end, the WebGPU backend and the GLSL ES → WGSL translator are derived from Google's [ANGLE](https://chromium.googlesource.com/angle/angle) and **vendored into this repository** (`include/` + `src/`), so the project builds and is versioned without an external ANGLE checkout. Only the WebGPU backend is kept; the D3D/GL/Vulkan/Metal/CL backends are not included. ## Repository layout ``` CAPABILITIES.md What GLES/EGL is implemented, how, and what is missing include/ Public headers (EGL, GLES, GLSLANG/ShaderLang.h, ...) src/ common/ ANGLE utility layer compiler/ GLSL ES preprocessor + translator (including the WGSL writer) libANGLE/ GLES/EGL front-end renderer/wgpu/ The WebGPU back-end libGLESv2/ EGL + GLES entry points libEGL/ image_util/ gpu_info_util/ thirdparty/ Third-party dependencies (git submodules) cmake/ CMake modules (Dawn + ANGLE build descriptions) wasm/ Emscripten-only glue: Dawn shim, canvas window surface, HTML shell tests/ Unit / smoke tests examples/ Windowed GLFW example (native) / textured quad page (wasm) ``` ## Third-party dependencies Managed as git submodules under `thirdparty/`: | Submodule | Used by | | --- | --- | | `dawn` | The WebGPU implementation | | `abseil-cpp`, `spirv-tools`, `spirv-headers`, `vulkan-headers`, `vulkan-loader`, `vulkan-utility-libraries`, `glslang`, `jinja2`, `googletest`, `EGL-Registry`, `OpenGL-Registry` | Dawn | | `zlib` | ANGLE's shader-program blob cache compression | Fetch them with: ```sh git submodule update --init --recursive ``` (Only the native build needs these. The wasm build takes its WebGPU implementation from the browser through Emscripten's `emdawnwebgpu` port, so it does not build Dawn at all - see [WebAssembly (Emscripten)](#webassembly-emscripten).) GLFW is **not** vendored: the example uses the system GLFW (`libglfw3-dev`) via pkg-config. If you prefer to vendor it, put a GLFW checkout in `thirdparty/glfw` and configure with `-DGLFW_ROOT=`. ## Building Prerequisites: CMake ≥ 3.22, a C++20 compiler (GCC 13+ or Clang 16+), Python 3 with `jinja2` + `markupsafe` (Dawn's generators), and — for the window example — a Vulkan driver, X11 development files including `libx11-xcb-dev`, and GLFW 3. ```sh git submodule update --init --recursive cmake -S . -B out cmake --build out -j"$(nproc)" ctest --test-dir out --output-on-failure ``` Options: | Option | Default | Meaning | | --- | --- | --- | | `GLES_ON_WEBGPU_PLATFORM` | `auto` | `native` (local compiler, Dawn), `wasm` (Emscripten, the browser's WebGPU) or `auto` to pick from the toolchain | | `GLES_ON_WEBGPU_BUILD_DAWN` | `ON` | Build Dawn from `thirdparty/dawn` | | `GLES_ON_WEBGPU_BUILD_TESTS` | `ON` | Build the tests | | `GLES_ON_WEBGPU_BUILD_GLFW_EXAMPLE` | `ON` | Build the GLFW window example | | `GLES_ON_WEBGPU_ENABLE_X11` | `ON` | X11 window surfaces (needed by the example) | | `GLES_ON_WEBGPU_ENABLE_NULL_BACKEND` | `ON` | Keep Dawn's Null backend (headless CI) | | `GLES_ON_WEBGPU_VULKAN_ICD` | *(empty)* | Vulkan ICD json exported as `VK_ICD_FILENAMES` for the tests | | `DAWN_DIR` | `thirdparty/dawn` | Dawn checkout location | | `GLES_ON_WEBGPU_DAWN_INCLUDE_DIRS`, `GLES_ON_WEBGPU_DAWN_LIBRARIES` | *(empty)* | Prebuilt Dawn (only with `GLES_ON_WEBGPU_BUILD_DAWN=OFF`) | On machines whose default Vulkan driver cannot create a device (e.g. an installed-but-unusable proprietary ICD), point the tests at a working one — Mesa's software rasteriser works headless: ```sh cmake -S . -B out -DGLES_ON_WEBGPU_VULKAN_ICD=/usr/share/vulkan/icd.d/lvp_icd.json ``` ## WebAssembly (Emscripten) The same sources also build for the browser, where the WebGPU implementation is the browser's instead of Dawn: Emscripten's [`emdawnwebgpu`](https://dawn.googlesource.com/dawn/+/refs/heads/main/src/emdawnwebgpu/README.md) port provides `webgpu.h` and the JavaScript half of the bindings. ```sh source /path/to/emsdk/emsdk_env.sh emcmake cmake -S . -B out-wasm -DGLES_ON_WEBGPU_PLATFORM=wasm cmake --build out-wasm -j"$(nproc)" python3 -m http.server 8099 --directory out-wasm/examples # then open http://localhost:8099/gles-on-webgpu-textured-quad.html ``` `GLES_ON_WEBGPU_PLATFORM` defaults to `auto`, which resolves to `wasm` when the Emscripten toolchain drives the configure and to `native` otherwise; asking for `wasm` without that toolchain fails at configure time rather than building something surprising. In the wasm configuration Dawn is not built (the port is downloaded and cached by Emscripten on first use), the tests are skipped (they drive a native offscreen display) and the example builds to `out-wasm/examples/gles-on-webgpu-textured-quad.html`. Worth knowing: * The backend is synchronous: it blocks on WebGPU futures through `wgpuInstanceWaitAny`. Those only advance from the browser's event loop, so the wasm build links with `-sASYNCIFY` - which is also what makes the `WGPUInstanceFeatureName_TimedWaitAny` the backend requests available at all. * The canvas is an ordinary EGL window surface. The example passes the CSS selector `"#canvas"` as the native window, and [wasm/WindowSurfaceWgpuEmscripten.cpp](wasm/WindowSurfaceWgpuEmscripten.cpp) turns it into a `WGPUEmscriptenSurfaceSourceCanvasHTMLSelector`. * There is no Dawn to hand the backend a `DawnProcTable`, so [wasm/dawn_shim](wasm/dawn_shim) declares one built from the browser's `webgpu.h` entry points. * Pthreads are deliberately left out: `-pthread` would make the module require a cross-origin isolated page. The few pthread entry points ANGLE calls anyway are stubbed in [wasm/emscripten_stubs.cpp](wasm/emscripten_stubs.cpp). * The browser needs a usable WebGPU adapter. If `navigator.gpu.requestAdapter()` returns nothing the page says so instead of leaving a blank canvas. ## Tests The tests are native-only. * `wgsl_translation` — compiles a GLES3 vertex/fragment shader pair through the translator with `SH_WGSL_OUTPUT` and checks that valid WGSL is produced. Needs no GPU. * `offscreen_clear` — creates a WebGPU-backed EGL display (`EGL_PLATFORM_ANGLE_TYPE_WEBGPU_ANGLE`), renders into a pbuffer with plain GLES2 and reads the pixel back. * `textured_quad` — renders into a pbuffer with GLES3: first a solid-colour quad (vertex buffer, vertex attributes, `glDrawArrays`, a `vec4` uniform), then a quad sampling a 2x2 `sampler2D` texture. The four quadrant centres are read back and compared against the expected texel colours, which also pins down the orientation of the rendered geometry. ```sh ctest --test-dir out -R offscreen_clear -V ctest --test-dir out -R textured_quad -V ``` ## GLFW example `examples/glfw_textured_quad.cpp` opens a window, sets up a vertex buffer with vertex attributes, uploads a procedurally generated 2D texture, compiles GLSL ES vertex/fragment shaders and draws a textured rotating quad with `glDrawArrays`, presented via `eglSwapBuffers` — all executed by WebGPU. ```sh cmake --build out --target gles-on-webgpu-glfw ./out/examples/gles-on-webgpu-glfw ``` GLFW is used **only** to create the window and to obtain its native X11 handle (`glfwGetX11Window`); no OpenGL context is created (`GLFW_NO_API`). The window is handed to EGL together with `EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE = EGL_PLATFORM_X11_EXT`, so the backend creates a `WGPUSurfaceSourceXlibWindow` surface. The same source builds for the browser, where the canvas takes the window's place; see [WebAssembly (Emscripten)](#webassembly-emscripten). ## Portability: ANGLE patches and toolchains The vendored sources are kept **byte-identical to the ANGLE checkout**. Where a fix is needed, a patched copy is generated into `/patched_sources/` and compiled instead of the original (see `angle_portable_source()` in [cmake/angle.cmake](cmake/angle.cmake)). The patched tree mirrors the ANGLE tree and is put in front of the sources on the include path, so a patched **header** shadows the original just like a patched `.cpp` does. Every patch matches on exact text, and configure fails with a clear message if that text is gone, so refreshing the vendored sources cannot silently drop a fix. ### Backend patches — applied with every compiler These fix the wgpu backend in the "WebGPU-only" configuration, which ANGLE's own build never exercises. They are not toolchain workarounds: | File | Problem | | --- | --- | | `src/libANGLE/Display.cpp` | `#error No default ANGLE platform type` — no branch for a WebGPU-only build | | `src/libANGLE/renderer/wgpu/DisplayWgpu.cpp` | advertises `maxPBufferWidth/Height = 0`, which rejects every `eglCreatePbufferSurface()` | | `src/libANGLE/renderer/wgpu/wgpu_utils.cpp` | advertises `MAX_COMBINED_SHADER_OUTPUT_RESOURCES = 0`, which makes any fragment shader with an output fail to link | ### Toolchain patches — GCC / libstdc++ and Emscripten ANGLE is developed against Clang/libc++, and these are workarounds for the toolchains this project is actually built with. The textual GCC fixes are gated on `CMAKE_CXX_COMPILER_ID STREQUAL "GNU"` (Clang against libstdc++ needs them too, while Clang against libc++ and MSVC do not — see the `TODO(portability)` in `angle_portable_source()`); the Emscripten ones on `GLES_ON_WEBGPU_PLATFORM STREQUAL "wasm"`. * [cmake/angle_gcc_compat.h](cmake/angle_gcc_compat.h) is force-included (`-include`) for C++ sources under GCC **and** under Emscripten. It pulls in standard headers that ANGLE relies on being provided transitively (`PullExpressionsIntoFunctions.cpp` uses `std::variant`, `RewritePipelineVariables.cpp` uses `std::cout`, ...); neither libstdc++ nor Emscripten's libc++ does that for it. * Files that need a textual fix: | File | Problem | | --- | --- | | `src/compiler/translator/IntermNode.cpp` | GCC cannot deduce `base::StrictNumeric`'s conversion operator in `new T[checkedSize]` | | `src/libANGLE/renderer/wgpu/FramebufferWgpu.cpp` | unconditionally includes `<__config>`, a libc++ internal header | | `src/common/SimpleMutex.h` | Emscripten has no `` yet counts as Linux, so the futex mutex has to fall back to `std::mutex` | | `src/libANGLE/renderer/wgpu/DisplayWgpu.cpp` | Emscripten counts as Linux too, which would classify the canvas as "no window surface" | | `src/libANGLE/renderer/wgpu/SurfaceWgpu.cpp` | surface usages have to be narrowed to what a canvas actually supports | ### Platforms Linux (native) and the browser (wasm) at the moment. The native build uses `ANGLE_IS_LINUX`, X11 surfaces and `thirdparty/zlib`; the wasm build goes through Emscripten, canvas surfaces and the browser's WebGPU, and leaves pthreads out. The backend already contains the Wayland, Win32 and Metal-layer window surfaces, but they are not wired into the build; see the `TODO(portability)` in the top-level [CMakeLists.txt](CMakeLists.txt). ## Configuration notes The build mirrors ANGLE's GN configuration for the WebGPU-only case: * `ANGLE_ENABLE_WGPU` is defined for the translator, the GLES front-end and the backend. * `ANGLE_ENABLE_EXPLICIT_CONTEXT` is on, as ANGLE enables it whenever the WebGPU backend is on. * Frame capture is disabled (`ANGLE_CAPTURE_ENABLED=0`), so flatbuffers/rapidjson are not needed. * The optional Rust IR translator (`angle_ir`) is not used; the pure C++ translator emits WGSL, so no Rust toolchain is required. * The default EGL/GLES entry points are compiled in, so a statically linked binary can call `egl*` / `gl*` directly without an EGL loader. ## Roadmap * Wayland window surfaces (`WindowSurfaceWgpuWayland.cpp` is present but not built). * Running the tests under node/browser so the wasm build is covered by CI too. ## License BSD 3-Clause; see [LICENSE](LICENSE). The sources under `include/` and `src/` are ported from [ANGLE](https://chromium.googlesource.com/angle/angle) (Copyright 2018 The ANGLE Project Authors), which is also BSD 3-Clause. The original copyright headers are kept in every ported file. Because clauses 1 and 3 of that license must be carried over, the ported code cannot be re-licensed under a laxer license such as MIT; the project therefore uses BSD 3-Clause throughout. Third-party components (Dawn, Abseil, SPIRV-Tools, glslang, ...) are tracked as git submodules under `thirdparty/` and keep their own licenses. See [NOTICE](NOTICE) for the full list and for the non-endorsement statement.