# VTK’s WebGPU backend now selects its implementation at runtime

**URL:** https://discourse.vtk.org/t/vtk-s-webgpu-backend-now-selects-its-implementation-at-runtime/16568
**Category:** Development
**Tags:** webgpu
**Created:** [October 25, 2026, 10:31pm UTC](https://discourse.vtk.org/t/vtk-s-webgpu-backend-now-selects-its-implementation-at-runtime/16568 "2026-10-25T22:31:09Z")
**Posts on this page:** 1
**Page:** 1

<div class="post-metadata">

### Author: ![sankhesh](https://discourse.vtk.org/user_avatar/discourse.vtk.org/sankhesh/32/73_2.png) [@sankhesh](https://discourse.vtk.org/u/sankhesh)
#### Post date: [October 25, 2026, 10:31pm UTC](https://discourse.vtk.org/t/vtk-s-webgpu-backend-now-selects-its-implementation-at-runtime/16568/1 "2026-10-25T22:31:09Z")

</div>

Until recently, VTK’s WebGPU backend could only talk to the implementation it was  
compiled against. WebGPU is a specification with several implementations — [Dawn](https://github.com/google/dawn)  
backs Chrome and is what most desktop applications build against, [wgpu-native](https://github.com/gfx-rs/wgpu-native)  
backs Firefox and ships as a Rust library, and in a browser the implementation  
_is_ the browser. They expose the same C API, but the choice was made by whoever  
ran CMake rather than whoever ran the program.

That had a long tail. A VTK binary could not be handed to a user with a different  
implementation installed, testing against a second implementation meant a second  
build tree, and a distribution that wanted to update Dawn had to rebuild  
everything downstream of it.

On master, the module resolves its 202 entry points at startup instead, links  
none of them, and runs the same binary on Dawn, wgpu-native, or emdawnwebgpu in  
the browser.

 ![Before: VTK links one implementation at build time. After: VTK calls a dispatch table that any implementation can fill at runtime.](https://discourse.vtk.org/uploads/default/original/2X/d/dcd4ea330184586accde0ce9d68b82fabfab2693.png)

_The build no longer decides. The module calls through a table of function  
pointers that the loader fills once, from whichever library it finds at startup._

## How it works

Every `wgpu*` function VTK calls has a slot in a generated dispatch table, and  
the header that declares it also redirects the name:

```cpp
struct vtkWebGPUProcDispatch
{
  WGPUProcQueueSubmit QueueSubmit = nullptr;
  WGPUProcBufferAddRef BufferAddRef = nullptr;
  /* ... 200 more ... */
};

extern VTK_WEBGPU_PROC_EXPORT vtkWebGPUProcDispatch vtkWebGPUProcs;

#define wgpuQueueSubmit vtkWebGPUProcs.QueueSubmit
#define wgpuBufferAddRef vtkWebGPUProcs.BufferAddRef

```

 ![A wgpu call in VTK source is redirected by a forced include to a dispatch table entry, then to the loaded implementation. Installed headers and consumer code are not redirected.](https://discourse.vtk.org/uploads/default/original/2X/4/471b18d92514d8c571699981c39b104bdc4e1537.svg)

_The redirect is a forced include, so it covers the module’s own sources and its  
tests and stops there. An application that includes a VTK header still sees the  
plain names._

The table is generated from the [vendored upstream](https://github.com/webgpu-native/webgpu-headers) `webgpu.h` by a script [generate\_proc\_dispatch.py](https://gitlab.kitware.com/vtk/vtk/-/blob/d2249a1d5b69782203bee8b01357bc459454c81a/Rendering/WebGPU/generate_proc_dispatch.py), and the output is checked in rather than generated at configure time. VTK has to build without a Python interpreter, including its Emscripten cross-builds,  
and committing the generated file also keeps every entry point visible in review.

## Dawn is no longer needed to configure

Configuring with `VTK_ENABLE_WEBGPU=ON` used to require Dawn to be installed. As  
of [!13787](https://gitlab.kitware.com/vtk/vtk/-/merge_requests/13787) it no  
longer does. The backend speaks the standard C API and resolves its entry points  
at runtime, so Dawn is wanted at configure time only to declare Dawn’s own  
extensions, which go beyond the standard API.

Those extensions now sit behind their own option:

```sh
cmake -DVTK_ENABLE_WEBGPU=ON \
      -DVTK_USE_DAWN_WEBGPU=ON \
      -DDawn_DIR=/opt/dawn/lib/cmake/Dawn ...

```

`VTK_USE_DAWN_WEBGPU` defaults to `OFF`, requires `VTK_ENABLE_WEBGPU`, and is  
unavailable on Emscripten. Turning it on makes Dawn a hard requirement:  
configuration fails with an error when `Dawn_DIR` does not point at one, rather  
than quietly building without the extensions. Leaving it `OFF` does not stop a  
Dawn library from being loaded at runtime — it only means the extensions are not  
declared.

## Choosing an implementation

Three places are searched, in order, and the first that loads wins:

1. `VTK_WEBGPU_LIBRARY`, an explicit path — the override.
2. The names implementations are normally installed under: `libwebgpu_dawn.so`,  
`libwgpu_native.so`, and so on.
3. The library the build was configured against, tried only when  
`VTK_WEBGPU_LIBRARY` was unset — an explicit override that fails to load is  
an error, not a reason to fall back. This candidate is compiled in only when  
`VTK_USE_DAWN_WEBGPU=ON` and the install is not relocatable, so a default  
WebGPU build has nothing here at all.

```sh
export VTK_WEBGPU_LIBRARY=/opt/dawn/lib/libwebgpu_dawn.so

```

One thing worth noting because it is easy to hit: **Dawn has to be built as a  
shared library**, `DAWN_BUILD_MONOLITHIC_LIBRARY=SHARED`. The default is static,  
and a `.a` has no symbols to resolve at runtime.

If nothing resolves, initialization fails with a diagnostic naming the first  
missing entry point, so an application can fall back to another rendering backend  
rather than crashing on the first draw.

## What it looks like

 ![Iron protein potential rendered as four nested translucent isosurfaces by VTK's WebGPU backend.](https://discourse.vtk.org/uploads/default/original/2X/8/8698dfc991f38d9415cd7e93b2274436942aa328.jpeg)

_Iron protein potential, four nested isosurfaces. Rendered by VTK’s WebGPU  
backend through a dispatch table filled at startup — Dawn on macOS, in this case._

Nothing about the output changes, which is the point. The same executable, with  
`VTK_WEBGPU_LIBRARY` pointed at a different library, produces this through a  
different implementation; wgpu-native on Linux drives the same scene.
