python, viz, gpu  -  2023-02-04

On WebGPU, wgpu-py, and pygfx

I'm part of a team building a novel render engine based on WebGPU (the successor to OpenGL). In this post I explain what WebGPU is, how it came about, and how we're using it to power our graphics.

Introductions

Before diving deeper, let's briefly introduce the three names in the title!

WebGPU is:

wgpu-py is:

pygfx is:

It's all about abstractions

A GPU (Graphics processing unit) is a piece of hardware dedicated to creating images. Driven by the game industry, they have become impessive machines that can perform massivel parallel computations.

To let programmers control this beast you need an API: a specification for a set of instructions that programmers can call to create graphics. And you need a driver that implements this API: a piece of software (often developed by the hardware manufacturer) that makes the GPU do the work corresponding to the programmer's instructions.

Examples of such API's that you might have heard of are OpenGL and DirectX. More modern variants are Vulkan, Metal and DX12.

These graphics API's are low level: there are a lot of knobs to turn, which is why programmers build higher level API's on top. More abstract sets of functions that are easier to use.

WebGPU is one such abstraction. And pygfx is an even higher-level abstraction built on WebGPU.

Line counts

These levels of abstraction can be illustrated by looking at the code to draw a triangle, which is probably the simplest thing one can visualize on a GPU.

A bit of history

OpenGL

OpenGL witnessed the evolution of GPU hardware, and evolved along with it. As GPU's gained new features, new API surface was added to OpenGL. As GPU's became more powerful, new abstractions were needed to harnass that power. In some cases these new abstractions replaced older counterparts, but in the name of backwards compatibility, the old API stayed.

You can imagine that implementing drivers for OpenGL became increasingly complex. On top of that, OpenGL has a lot of global state, and all instructions can happen at any time. It's up to the driver to do the right thing - even when it might not be clear what this is. Such ambiguities lead to different behaviours for different drivers/hardware, and are often the cause of driver bugs.

Vulkan, Metal and DX12

The response to this was Vulkan: an API that is much lower-level than OpenGL, with better structure, resulting in clear rules about when certain instructions are possible. Vulkan is so low level you can almost touch the hardware, and this was the idea: this makes it much easier to create reliable drivers!

MacOS decided to develop its own API, called Metal, following a similar strategy as Vulkan. On Windows DX12 followed suit.

Soon after Vulkan became a thing, people started wondering whether there'd be a WebVulkan. This - fortunately - never happened.

WebGPU

Instead, a group of people from Mozilla, Google and Apple started WebGPU, a specification for a graphics API in the browser. They also started building an implementation, called wgpu.

This wgpu is implemented in the Rust programming language (just like Firefox). It is itself composed of several layers, but the important thing is that it uses either Vulkan, Metal or DX12 to talk to the GPU.

Just like WebAssembly, WebGPU is not just for the browser; via wgpu-native the API is also available to other languages on the desktop!

About wgpu-py

We created wgpu-py to bring WebGPU to the Python ecosystem. Since the WebGPU API is specified for JavaScript, we translate it to a Pythonic equivalent API. The implementation is based on wgpu-native (via cffi).

Since both the WebGPU spec and the wgpu implementation are both under development, we regularly need to update to their latest versions. This process is partly automated, but can nevertheless be a tedious process.

The WGSL shading language

Another interesting thing to note is that for WebGPU they decided on a brand new shading language called WGSL. If you already know e.g. GLSL, the concepts are very familiar, but the syntax is different (Rust-like, to be specific).

About pygfx

The pygfx library is much higher-level than WebGPU. It's API is inspired by ThreeJS (a popular render engine based on WebGL), but with a special focus on scientific visualisation.

A benefit of basing pygfx on wgpu is a more reliable basis with fewer driver bugs and more consistency across different hardware. Further, in wgpu objects are "prepared" in pipeline objects, which can be drawn with just a few calls, avoiding expensive (Python) overhead. In other words: we can efficiently render large numbers of objects!

Object model

Pygfx uses an object model similar to ThreeJS:

This separation of concerns, using WorldObject, Geometry and Material provides high flexibility. For exampple, the below image shows the same object, with the same geometry, but with different materials applied.

In a similar fashion pygfx has a Volume object, which can be visualized with volume rendering, or using volume slices, by selecting the corresponding material.

Interesting features

Some notable features of pygfx include:

Summary

I explained how OpenGL is outdated, and is being replaced with Vulkan and friends. WebGPU is a higher level API built on top of these. And that is what we use to create our new render engine, pygfx.

In more detail: