Vega renderer

vega-webgpu-renderer

Draws a Vega scenegraph on the GPU. Register it, pass renderer: 'webgpu', and the rest of the spec is unchanged.

Live demo Mark playground GitHub npm, not published yet

What it is

Vega ships a canvas renderer and an svg renderer. This is a third one, built on WebGPU. It registers itself with Vega's renderer module, so nothing about a spec changes: the same scenegraph is drawn with GPU pipelines instead of 2D canvas calls.

The canvas renderer is the reference. Every spec in the corpus is rendered both ways and compared pixel by pixel on every commit, so a difference is a tracked defect rather than a surprise. What is still off is listed under features and roadmap.

Needs a browser with WebGPU. If navigator.gpu is undefined the renderer will not register, so keep canvas as the fallback.

Use it

Script tag

The renderer registers itself when loaded after Vega.

<script src="https://cdn.jsdelivr.net/npm/vega@6/build/vega.min.js"></script>
<script src="https://kanadaat.github.io/vega-webgpu/releases/1_2_0/vega-webgpu-renderer.js"></script>
<div id="vis"></div>
<script>
  fetch('https://vega.github.io/vega/examples/bar-chart.vg.json')
    .then(res => res.json())
    .then(spec => {
      new vega.View(vega.parse(spec), {
        renderer: 'webgpu',
        container: '#vis',
        hover: true,
      }).runAsync();
    });
</script>

Every hosted build is listed under versions, each with its own page.

Match the Vega major version. 2.0.0 and later target Vega 6. Everything before it targets Vega 5 and throws Class constructor cannot be invoked without 'new' when loaded next to Vega 6, because a renderer registers itself against whichever Vega is already on the page.

Renderer Load this Vega
2.0.0 and later https://cdn.jsdelivr.net/npm/vega@6/build/vega.min.js
1.2.0 and earlier https://cdn.jsdelivr.net/npm/vega@5/build/vega.min.js

npm

Not published yet. Until it is, take a hosted build with the script tag above.

npm install vega-webgpu-renderer
import 'vega-webgpu-renderer'; // registers the 'webgpu' renderer

const view = new vega.View(vega.parse(spec), {
  renderer: 'webgpu',
  container: '#vis',
});

Falling back

Pick the renderer once, at view construction.

const renderer = navigator.gpu ? 'webgpu' : 'canvas';
new vega.View(vega.parse(spec), { renderer, container: '#vis' }).runAsync();

Config

Options live on view._renderer.wgOptions and can be changed between frames.

const view = new vega.View(vega.parse(spec), { renderer: 'webgpu', container: '#vis' });
view._renderer.wgOptions.debugLog = true;
Option What it does Default Since
sampleCount MSAA samples per pixel: 4 for antialiased, 1 for plain. Anything else falls back to 4. Pipelines rebuild on the next render. 4 2.0.0
redrawOnZoom Follow browser zoom. Zoom changes devicePixelRatio, and the default resizes the canvas and redraws so the view stays sharp. When false the canvas keeps the ratio it was first sized at and the browser scales it, which is softer but skips the redraw and cannot grow past the texture cap. true 2.0.0
renderLock Skip re-entrant render calls while a frame is in flight. The most recent request always runs, so an interactive chart stays responsive under a drag. true 1.1.1
cacheShapes Keep triangulated shape geometry between frames instead of rebuilding it. Costs memory on a scene whose geometry changes every frame. true 1.1.0
debugLog Log per-frame render timings to the console. false 1.0.0
offscreen Render into a texture the renderer owns and never touch the canvas swapchain. For a headless runner with no compositor, where acquiring the swapchain destroys the device. The canvas stays blank, so the frame is only reachable through captureFrame(). false 2.0.0

renderBatch and simpleLine were removed in 2.0.0. The line mark batches its segments unconditionally now, so neither had anything left to switch.

Feature completeness

All twelve of Vega's mark types are drawn: arc, area, group, image, line, path, rect, rule, shape, symbol, text and trail. Fill, stroke, opacity, clipping, corner radii, symbol shapes and rotation, and text layout all work on every mark that has them. What follows is the properties that do not yet reach every mark.

Mark Gradient fill Gradient stroke strokeDash strokeCap blend

Every mark name is a link into the playground, where you can tick these properties on and off and watch canvas and WebGPU draw the same spec side by side.

Where a gradient is not supported the mark draws a flat colour from the gradient's first stop. Where strokeDash is not read the stroke draws solid, which is most visible on a dashed axis grid, since Vega renders those as rule marks.

strokeJoin and strokeMiterLimit are read by nobody. Triangulated marks miter at Vega's default limit of 10 and the rest join round, so only a spec that asks for something other than the default differs.

Known differences from canvas

Roadmap

Broad strokes. The full list, with the measurement behind each item, is in ToDo.md.

2.0.0

In progress. Parity, not improvement.

  • The properties still ignored above: dashes outside line and group, gradient strokes on the rest, blend on group, image and text, joins and miter limit, round caps on path, area and shape
  • Analytic edge coverage for triangulated marks, the largest single difference from canvas and the cause of most of the rest
  • A stroked path drawn as segments rather than a triangulated ribbon
  • Widen the vega-typings renderer union to include webgpu, then publish

After 2.0.0

Worth doing, not worth holding a release for.

  • Viewport clipping, so a very tall view draws only what is on screen
  • Tiled rendering for a canvas past the GPU's texture cap
  • Per-item compositing, which is what would reproduce canvas's polygon seams
  • The eleven blend modes WebGPU cannot express without reading the destination
  • Mipmaps, so a heavily downscaled image matches canvas
  • Performance: feed d3-geo a path sink, reuse buffers, cache bind groups

Set up locally

git clone https://github.com/KaNaDaAT/vega-webgpu.git
cd vega-webgpu
npm install
npm run build     # UMD, minified and ESM bundles plus type declarations, into build/
npm run serve     # serve the repo at http://localhost:5500

Then open http://localhost:5500/test/?spec=bar&renderer=webgpu&version=dev to run the local build against any spec in test/specs-valid. The page can show canvas and WebGPU side by side and a pixel diff between them.

Command What it does
npm run dev Rollup in watch mode
npm test Render every spec both ways and compare
npm run test:report Open the comparison gallery from the last run
npm run typecheck Strict TypeScript, no emit
npm run lint eslint

Versions

Each build is hosted here and can be loaded straight from a script tag.

Version Changes Files
2.0.0-rc2 Built for Vega 6. Every mark type is drawn, most edges are antialiased analytically rather than by MSAA, and text goes through a glyph atlas. (prerelease) js min
1.2.0 Performance work on lines, clipping support and a path offset fix. (needs Vega 5) js min
1.1.1 Performance work on paths, plus the renderLock option. (needs Vega 5) js min
1.1.0 Overall improvements to performance, types and structure. (needs Vega 5) js min
1.0.0 First WebGPU implementation. (needs Vega 5) js min