55<p align =" center " >
66 <a href =" https://github.com/Devsh-Graphics-Programming/llvmpipe2wasm/actions/workflows/smoke.yml " >
77 <img src="https://github.com/Devsh-Graphics-Programming/llvmpipe2wasm/actions/workflows/smoke.yml/badge.svg" alt="Build Status" /></a>
8- <a href =" https://opensource. org/licenses/Apache-2.0 " >
9- <img src="https://img.shields.io/badge/license-Apache%202.0 -blue" alt="License: Apache 2.0 " /></a>
8+ <a href =" https://www.gnu. org/licenses/agpl-3.0.en.html " >
9+ <img src="https://img.shields.io/badge/license-AGPL%20v3 -blue" alt="License AGPL v3 " /></a>
1010 <a href =" https://discord.gg/krsBcABm7u " >
1111 <img src="https://img.shields.io/discord/308323056592486420?label=discord&logo=discord&logoColor=white&color=7289DA" alt="Join our Discord" /></a>
1212</p >
1515
1616This repository provides a CMake-consumable WebAssembly build of Mesa llvmpipe and lavapipe.
1717
18- Public link targets:
18+ ## What you get
19+
20+ - CMake targets for wasm llvmpipe and lavapipe
21+ - In-tree consumption with ` add_subdirectory `
22+ - Relocatable package consumption with ` find_package `
23+ - Runtime shader tooling with DXC in Wasm and Clang in Wasm
24+ - Fast Wasm shader execution path backend (currently validated on compute dispatch)
25+
26+ Public link targets
1927
2028- ` webvulkan::llvmpipe_wasm `
2129- ` webvulkan::lavapipe_wasm `
2230- ` webvulkan::shader_tools `
2331
24- ## Scope
25-
26- What this repository does:
27- - Builds and exposes Mesa llvmpipe/lavapipe wasm archives as CMake targets
28- - Supports in-tree consumption via ` add_subdirectory `
29- - Supports relocatable package consumption via ` find_package `
30- - Provides runtime smoke checks for CI validation
32+ ## What is in Wasm
3133
32- What this repository does not do for consumers:
33- - It is not a full Vulkan browser runtime
34- - It does not provide a browser app framework or rendering engine
35- - It does not replace the consumer's own Vulkan/WebGPU integration layer
34+ - Clang in Wasm is available for LLVM IR to Wasm module generation
35+ - DXC in Wasm is available for HLSL to SPIR-V generation
3636
37- Current build mode:
38- - Driver output is currently static-only (` .a ` )
39- - Shared driver output mode is planned
37+ ## Why a Mesa fork
4038
41- ## Mesa fork
39+ Mesa source is fetched from
4240
43- This project fetches Mesa from our fork:
4441- ` https://github.com/Devsh-Graphics-Programming/mesa `
4542
46- Why we use a fork:
47- - We carry a small set of wasm-focused patches needed for this project.
48- - Current patch scope is focused on lavapipe build behavior for Emscripten so the driver can be consumed in our wasm flow.
49- - Non-Emscripten paths remain unchanged.
43+ The fork carries wasm-specific patches required for this flow
44+
45+ - Emscripten build and linking adjustments for lavapipe
46+ - runtime shader key capture and runtime module lookup hooks
47+ - runtime Wasm dispatch integration for shader execution experiments
48+
49+ Non-Emscripten paths are kept intact.
50+
51+ ## Why a DXC fork
52+
53+ DXC source is fetched from
54+
55+ - ` https://github.com/Devsh-Graphics-Programming/DirectXShaderCompiler/tree/wasm `
56+
57+ The fork carries build-system patches required to compile DXC to Wasm.
58+
59+ ## Why no LLVM JIT in Wasm
60+
61+ LLVM maintainers explicitly state LLJIT does not support WebAssembly in this form
62+ ` https://discourse.llvm.org/t/does-lljit-support-webassembly/74864/3 `
63+
64+ That blocks the classic in-process ORC LLJIT path inside the Wasm sandbox.
65+
66+ ## Why no full NIR to Wasm backend
67+
68+ Mesa does not ship a production NIR to Wasm backend for this use case.
69+ Writing and maintaining one from scratch is a large compiler project.
70+
71+ ## Our solution
72+
73+ - Use DXC in Wasm to compile HLSL source to SPIR-V at runtime
74+ - Use Clang in Wasm to compile LLVM IR to Wasm runtime modules
75+ - Build and ship lavapipe and llvmpipe as CMake-consumable wasm targets
76+ - Keep the integration path centered on standard Vulkan API usage through lavapipe
77+
78+ ## Wait, but you have a big shader tooling payload
79+
80+ - Yes, that is the point and we are fine with it.
81+ - Tooling artifacts are already distributed in compressed ` .zip ` bundles.
82+ - Runtime delivery compression is deployment-specific and should be handled by your host or CDN (gzip or brotli).
83+ - If this payload size blocks your use case, sponsor NIR-to-Wasm backend work or LLVM JIT in Wasm R&D and contact Devsh Graphics Programming.
84+
85+ ## Backend status disclaimer
86+
87+ This backend is under active development.
88+ Interfaces and behavior can still change while the runtime path is being expanded.
5089
51- ## Shader toolchain direction
90+ What is implemented now
5291
53- Why not LLVM JIT-in-Wasm right now:
54- - LLVM maintainer discussion: ` https://discourse.llvm.org/t/rfc-building-llvm-for-webassembly/79073 `
55- - In this project setup, a production-grade in-process LLVM JIT path inside the Wasm sandbox is not currently a practical path.
92+ - Runtime HLSL to SPIR-V compilation in Wasm through DXC
93+ - Runtime LLVM IR to Wasm module compilation in Wasm through Clang
94+ - Driver-side shader key registration and runtime Wasm module lookup
95+ - Fast Wasm execution path validated for Vulkan compute dispatch in CI
96+
97+ What is not implemented yet
98+
99+ - Full Vulkan stage and feature coverage across all real-world shader patterns
100+ - Full conformance and performance tuning for broad hardware and workload sets
101+ - Stable long-term public API for the runtime shader tooling layer
102+ - Shared-driver output mode (current driver artifact is static only)
103+
104+ ## How we validate it
105+
106+ We run ` runtime_smoke ` in CI as the runtime validation test.
107+
108+ The test validates
109+
110+ 1 . HLSL to SPIR-V compilation with DXC in Wasm.
111+ 2 . LLVM IR to Wasm module compilation with Clang in Wasm.
112+ 3 . Runtime registration of SPIR-V and Wasm module for the driver shader key.
113+ 4 . Vulkan loader flow through Volk using ` vk_icdGetInstanceProcAddr ` from the wasm ICD path.
114+ 5 . Vulkan compute pipeline creation and real dispatch execution.
115+ 6 . Output correctness checks for the dispatched shader.
116+ 7 . Driver identity and provider checks to confirm the lavapipe wasm path.
117+ 8 . Timing comparison between both runtime modes on micro and realistic dispatch profiles.
118+
119+ Runtime modes validated in CI
120+
121+ - ` lavapipe_runtime_smoke_fast_wasm `
122+ - ` lavapipe_runtime_smoke_raw_llvm_ir `
123+
124+ Dispatch profiles validated in CI
125+
126+ - ` micro ` profile for dispatch-overhead sensitivity
127+ - ` realistic ` profile for larger dispatch-grid usage
128+
129+ ## Gains
130+
131+ On the current local setup we measured
132+
133+ - ` micro ` profile ` fast_wasm ` vs ` raw_llvm_ir ` about ` 10.55x ` lower wall time
134+ - ` realistic ` profile ` fast_wasm ` vs ` raw_llvm_ir ` about ` 8.04x ` lower wall time
135+
136+ Shader behavior in this benchmark is intentionally simple and deterministic.
137+ It runs integer mixing ops and writes ` 0x12345678 ` into a storage buffer.
138+
139+ Dispatch details in this run
140+
141+ - Workgroup size in this shader is ` numthreads(1,1,1) `
142+ - ` micro ` profile records ` 1024 ` times ` vkCmdDispatch(1,1,1) ` per submit and runs ` 16 ` submits so total dispatch calls are ` 16384 `
143+ - ` realistic ` profile records ` 64 ` times ` vkCmdDispatch(4,1,1) ` per submit and runs ` 8 ` submits so total dispatch calls are ` 512 `
144+ - The shader writes and validates one 32-bit value in the bound storage buffer
145+
146+ Log excerpt
147+
148+ ``` text
149+ dispatch timing summary
150+ mode=fast_wasm
151+ profile=micro
152+ samples=5
153+ min_ms=0.000723
154+ avg_ms=0.001163
155+ max_ms=0.001605
156+ proof.execute_path=fast_wasm
157+ proof.interpreter=disabled_for_dispatch
158+ proof.llvm_ir_wasm_provider=clang/clang llvm-ir+shared-memory-shim
159+
160+ dispatch timing summary
161+ mode=raw_llvm_ir
162+ profile=micro
163+ samples=5
164+ min_ms=0.010564
165+ avg_ms=0.012268
166+ max_ms=0.013631
167+ proof.execute_path=inline_wasm_module
168+ proof.interpreter=not_used
169+ proof.llvm_ir_wasm_provider=inline-wasm-module
170+
171+ dispatch timing summary
172+ mode=fast_wasm
173+ profile=realistic
174+ samples=5
175+ min_ms=0.001315
176+ avg_ms=0.001992
177+ max_ms=0.002637
178+ proof.execute_path=fast_wasm
179+ proof.interpreter=disabled_for_dispatch
180+ proof.llvm_ir_wasm_provider=clang/clang llvm-ir+shared-memory-shim
181+
182+ dispatch timing summary
183+ mode=raw_llvm_ir
184+ profile=realistic
185+ samples=5
186+ min_ms=0.012064
187+ avg_ms=0.016007
188+ max_ms=0.023143
189+ proof.execute_path=inline_wasm_module
190+ proof.interpreter=not_used
191+ proof.llvm_ir_wasm_provider=inline-wasm-module
192+ ```
56193
57- Why not full NIR->Wasm backend today:
58- - Mesa does not ship a complete NIR->Wasm backend for this use case.
59- - A full backend from scratch is a large compiler project and not realistic for a single maintainer effort.
194+ Fairness note for this measurement
60195
61- Current strategy:
62- - Keep ` clang_wasm_runtime_smoke ` as a clang-in-wasm toolchain proof path.
63- - Keep Vulkan runtime validation on the current Mesa wasm execution path, while preparing a future direct integration path.
196+ - Local dev PC ` AMD Ryzen 5 5600G ` ` 6C/12T ` , ` Windows 11 Pro 64-bit (10.0.26200) `
197+ - The same benchmark output can be verified in this repository GitHub Actions CI logs
198+
199+ ## Licensing
200+
201+ This project is licensed under GNU AGPL v3.
202+
203+ For dual licensing including proprietary or commercial terms contact Devsh Graphics Programming.
64204
65205## Default dependency mode
66206
67- Default configuration uses the latest prebuilt LLVM bundle from this repository:
207+ Default configuration uses the latest prebuilt LLVM bundle from this repository.
68208
69- - ` llvm-wasm-prebuilt-latest ` release channel
209+ - ` llvm-wasm-prebuilt-latest `
70210
71- To force source LLVM build:
211+ To force source LLVM build
72212
73213``` powershell
74214cmake -S . -B build -G Ninja -DLLVM_PROVIDER=source
75215```
76216
77- ## Use from source ( ` add_subdirectory ` )
217+ ## Use from source with add_subdirectory
78218
79219``` cmake
80220add_subdirectory(path/to/llvmpipe2wasm)
@@ -88,50 +228,49 @@ cmake -S . -B build -G Ninja
88228cmake --build build
89229```
90230
91- ## Build-time shader toolchain helper
231+ ## Use as relocatable package with find_package
92232
93- This repository exposes a reusable CMake helper:
94- - ` webvulkan_compile_opencl_to_spirv(...) `
233+ Build and install
95234
96- It compiles OpenCL C source to SPIR-V through Wasmer package runtime (` clspv ` entrypoint by default), and can be used directly in consumer builds.
235+ ``` powershell
236+ cmake -S . -B build -G Ninja
237+ cmake --build build
238+ cmake --install build --prefix <install-prefix>
239+ ```
97240
98- Example:
241+ Consume
99242
100243``` cmake
101- add_subdirectory(path/to/llvmpipe2wasm)
102-
103- set(MY_SHADER_SPV "${CMAKE_BINARY_DIR}/shaders/write_const.spv")
104- webvulkan_compile_opencl_to_spirv(
105- SOURCE "${CMAKE_SOURCE_DIR}/shaders/write_const.cl"
106- OUTPUT "${MY_SHADER_SPV}"
107- )
244+ find_package(WebVulkanLlvmpipeWasm REQUIRED CONFIG)
108245
109- add_custom_target(my_shaders DEPENDS "${MY_SHADER_SPV}")
110246add_executable(my_app src/main.c)
111- add_dependencies(my_app my_shaders)
112247target_link_libraries(my_app PRIVATE webvulkan::llvmpipe_wasm)
113248```
114249
115- ## Use as relocatable package ( ` find_package ` )
250+ ## Build-time shader helper
116251
117- Build and install:
252+ Reusable helper
118253
119- ``` powershell
120- cmake -S . -B build -G Ninja
121- cmake --build build
122- cmake --install build --prefix <install-prefix>
123- ```
254+ - ` webvulkan_compile_opencl_to_spirv(...) `
124255
125- Consume:
256+ Example
126257
127258``` cmake
128- find_package(WebVulkanLlvmpipeWasm REQUIRED CONFIG)
259+ add_subdirectory(path/to/llvmpipe2wasm)
260+
261+ set(MY_SHADER_SPV "${CMAKE_BINARY_DIR}/shaders/write_const.spv")
262+ webvulkan_compile_opencl_to_spirv(
263+ SOURCE "${CMAKE_SOURCE_DIR}/shaders/write_const.cl"
264+ OUTPUT "${MY_SHADER_SPV}"
265+ )
129266
267+ add_custom_target(my_shaders DEPENDS "${MY_SHADER_SPV}")
130268add_executable(my_app src/main.c)
269+ add_dependencies(my_app my_shaders)
131270target_link_libraries(my_app PRIVATE webvulkan::llvmpipe_wasm)
132271```
133272
134- The same shader helper is available in package mode:
273+ The same helper is available in package mode.
135274
136275``` cmake
137276find_package(WebVulkanLlvmpipeWasm REQUIRED CONFIG)
@@ -143,34 +282,25 @@ webvulkan_compile_opencl_to_spirv(
143282)
144283```
145284
146- ## Volk smoke path
285+ ## Current limitations
147286
148- ` runtime_smoke ` includes a dedicated Volk-based runtime check.
149-
150- It validates:
151- - ` volkInitializeCustom ` is used with ` vk_icdGetInstanceProcAddr ` from the linked wasm driver archive
152- - Vulkan instance creation and physical device enumeration succeed
153- - Required ICD entrypoints are resolved through that same dispatch path
154-
155- This gives a realistic loader flow for consumers that use Volk, while keeping dispatch pinned to the wasm ICD path.
156-
157- Current shader-path split:
158- - ` lavapipe_runtime_smoke ` injects runtime-generated SPIR-V (from Wasmer ` clspv ` by default) into the smoke module and validates Vulkan compute dispatch in the Mesa wasm driver.
159- - ` clang_wasm_runtime_smoke ` validates the clang-in-wasm toolchain path and runs an SPIR-V probe through a Wasmer runtime command (` clspv ` package entrypoint by default).
160- - If that command is unavailable, smoke falls back to a direct ` --target=spirv32 ` probe and reports the provider and failure reason.
287+ - Driver output is static only ` .a `
288+ - Shared driver output mode is planned
289+ - This repository is not a full browser rendering framework
161290
162291## Release channels
163292
164- - ` llvm-wasm-prebuilt-latest ` contains only the LLVM prebuilt bundle
165- - ` webvulkan-package-latest ` contains only the relocatable CMake package
293+ - ` llvm-wasm-prebuilt-latest ` includes LLVM prebuilt bundle only
294+ - ` webvulkan-package-latest ` includes relocatable CMake package only
295+
296+ Each channel ships
166297
167- Each channel ships:
168298- bundle ` .zip `
169299- checksum ` .sha256 `
170300- Sigstore signature ` .sig `
171301- Sigstore certificate ` .pem `
172302
173- Verify a downloaded bundle:
303+ Verify a downloaded bundle
174304
175305``` bash
176306sha256sum -c < bundle> .zip.sha256
0 commit comments