Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

GPU acceleration

Helia does its heavy maths on the GPU when there is one, through wgpu, a Rust library that speaks Vulkan, DX12, Metal and OpenGL. There is nothing to install beyond your normal graphics driver, and no vendor-specific toolkit: the same code runs on AMD, NVIDIA, Intel, Apple and Raspberry Pi graphics.

Backends

PlatformAPIs wgpu usesNotes
LinuxVulkan, then GLESNeeds a Vulkan driver for your GPU (Mesa for AMD, Intel and Raspberry Pi; NVIDIA's own driver).
WindowsDX12, VulkanProvided by the graphics driver.
macOSMetalBuilt in.

Choosing between GPU and CPU

The --backend option of helia stack (and the Backend setting in the desktop app, and backend in an API job) takes three values:

ValueBehaviour
auto (default)Ask wgpu for the best hardware adapter. If it finds none, or only a software adapter, use the CPU path.
cpuAlways use the CPU.
gpuUse a GPU. If there is no real GPU, fall back to wgpu's software adapter; if there is no adapter at all, fail with an error.

Helia prints which one it chose. On the command line it is the backend: line of the summary, for example backend: GPU (<adapter> (Vulkan, DiscreteGpu)) or backend: CPU (16 threads). The desktop app shows it in a chip in the top bar, and the API reports it under backends in GET /api/v1/version.

The CPU path

The CPU path is not a poor relation. It is the specification: the GPU kernels are tested to give the same results as the CPU code (see The GPU parity rule). It is multi-threaded and vectorised, so a machine without a usable GPU still stacks, only more slowly, and the output is equivalent either way.

What runs on the GPU today

  • Colouring the finished stack through a colour preset: a table lookup with linear interpolation, one thread per pixel. It matches the CPU reference to within 1e-5 per sample.
  • Sharpening and the everyday edits (levels, curves, unsharp mask, a-trous wavelets and denoise, Richardson-Lucy deconvolution): separable convolution passes, element-wise kernels and, for an Airy blur, a direct 2-D convolution. The image stays on the device across a run of these operations. The noise level of a wavelet layer is estimated from a few thousand sampled pixels, which are the only values that come back to the host in between. They match the CPU reference to within 1e-4 (about 1e-6 observed); the live preview in the app uses them. Crop, rotate, flip and resize run on the CPU.
  • Scoring every frame, straight from the stored bytes: decode, debayer, blur and the sharpness reduction all run on the device.
  • Global registration by phase correlation, with Helia's own mixed-radix FFT (lengths up to 2048) written in WGSL.
  • Alignment point correlation.
  • Stacking.

Frames are sent in batches with two in flight, so uploading the next batch overlaps work on the current one.

The robust stacking modes (--robust sigma|huber|winsor) run on the GPU for the whole-frame stack, in two passes; with alignment points their per-point tiles still combine on the CPU. Still on the CPU: registration of captures whose transform length would exceed 2048, and, by design, fitting the correlation peaks and interpolating the shifts. The GPU returns the correlation surfaces, and the same CPU code makes the decision on either path, so the CPU and GPU paths give the same answer (registration agrees to about 1e-5 px).

Measured speed

Wall-clock seconds for helia stack with --keep 25%, end to end. Measured 2026-10-18 on a laptop: 16-thread CPU and an NVIDIA RTX 3050 Ti Laptop GPU (4 GB, Vulkan), commit ae49ddc. The laptop GPU throttles when hot, so expect run-to-run variation of 10% or so.

CaptureFramesCPU, beforeCPU, nowGPU, beforeGPU, now
1920x1080 16-bit Bayer SER1008.32.913.21.6
1920x1080 8-bit mono SER63131.115.040.85.8
720x540 8-bit Bayer AVI100015.55.420.52.9
1920x1080 8-bit Bayer AVI63070.519.273.110.7

"Before" is the first release of the stacker, where only scoring and accumulation ran on the GPU. Use helia stack --timings to see where the time goes on your own machine.

On a data-centre GPU (NVIDIA A100) these small captures were slower than on the same machine's CPU, which points at start-up and batch size rather than compute; that is being looked at (#10).

Software adapters

A software adapter is a GPU implemented on the CPU: WARP on Windows, lavapipe (Mesa) on Linux. They exist so that programs run on machines with no GPU, and so that Helia's automated tests can exercise the GPU code on ordinary build servers. They are slower than Helia's own multi-threaded CPU path.

How wgpu ranks adapters matters here. Helia asks for the high-performance choice, and wgpu orders what it finds as discrete GPU, integrated GPU, other, virtual GPU and, last, a CPU-hosted adapter. auto skips a CPU-hosted adapter altogether, so a Linux machine with no GPU but with Mesa's lavapipe installed uses Helia's faster CPU path (backend: CPU (...) in the summary). --backend gpu still accepts a software adapter when there is no real GPU, which is mainly useful for testing. Helia's own tests ask for the software adapter explicitly (see Building and testing).

Troubleshooting

The summary says backend: CPU and you expected a GPU.

  • Linux: install the Vulkan loader and your driver (sudo apt-get install libvulkan1 mesa-vulkan-drivers, or NVIDIA's driver), then check that vulkaninfo --summary (from the vulkan-tools package) lists your GPU. If it does not, Helia cannot see it either.
  • Windows: update your graphics driver from the manufacturer. Remote desktop sessions and some virtual machines hide the GPU; run Helia on the local console.
  • macOS: nothing to install; Metal is built in.
  • A laptop with two GPUs: Helia asks for the high-performance one. If the system gives it the integrated GPU, use your system's graphics settings to run Helia on the discrete GPU.
  • Run helia stack ... --backend gpu. Unlike auto, it reports an error when no adapter can be opened, and the message says why.

"no suitable GPU adapter found" or "could not open GPU device". These come from --backend gpu. The first means no adapter of the requested kind exists; the second means an adapter exists but refused to open (a driver problem). Updating the driver is the usual fix.

The GPU is used but the result differs very slightly from the CPU. That is expected. Floating-point sums are not bit-for-bit identical across devices; the tests bound the difference (see the parity page).

A bug that only happens on the GPU. Re-run with --backend cpu. If the CPU result is right, report it with your operating system, GPU, driver version and helia --version (see Contributing).