Skip to content

Commit 98d96fe

Browse files
committed
Checkpoint Runtime smoke modes and Wasm tooling integration
1 parent c58a7dd commit 98d96fe

8 files changed

Lines changed: 1650 additions & 479 deletions

LICENSE

Lines changed: 661 additions & 201 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 209 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
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>
@@ -15,66 +15,206 @@
1515

1616
This 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
74214
cmake -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
80220
add_subdirectory(path/to/llvmpipe2wasm)
@@ -88,50 +228,49 @@ cmake -S . -B build -G Ninja
88228
cmake --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}")
110246
add_executable(my_app src/main.c)
111-
add_dependencies(my_app my_shaders)
112247
target_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}")
130268
add_executable(my_app src/main.c)
269+
add_dependencies(my_app my_shaders)
131270
target_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
137276
find_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
176306
sha256sum -c <bundle>.zip.sha256

0 commit comments

Comments
 (0)