Skip to content

Debugging wgpu Applications

Nicolas Silva edited this page Jul 15, 2024 · 23 revisions

This page describes all the tricks and tools aimed at investigating issues related to wgpu.

Native API Validation

wgpu's goal is to guarantee that only valid workloads reach the low-level APIs under the hood. If anything is not correct, we are going to complain about it. You should never need to listen to the low-level API complains.

This is a big work-in-progress, however. So in the meantime, it's useful to hear what the low-level validation has to say.

Vulkan and OpenGL

On Khronos APIs we redirect all the validation output to the console. You will see it as long as:

  1. you run a debug build
  2. you have the validation layers installed (i.e. LunarG SDK)
  3. you capture logging (with something like env_logger)

D3D

COM

The refcount of a COM object is 9 32 bit words behind the pointer's destination and is 4 bytes. As such, subtract 36 from the pointer's address to get a pointer to that object's refcount. This allows you to use data breakpoints to see all changes to the object's refcount, useful for detecting leaks and other such things.

Metal

You can either set METAL_DEVICE_WRAPPER_TYPE=1 in the environment, or launch from XCode, where validation options are configurable in project menus (and enabled by default). To launch from XCode, you can create a project of "External Build System" type, picking cargo or any other command as the build system. Then you'd need to edit the target configuration and select the executable.

A full guide that explains how to launch from Xcode is available in Debugging with Xcode.

Mac Leaks

To look for leaks using "instruments", you need to add a special thing to your executable signature. Write the following to entitle.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd"\>
<plist version="1.0">
    <dict>
        <key>com.apple.security.get-task-allow</key>
        <true/>
    </dict>
</plist>

Then run codesign -s - -v -f --entitlements=entitle.xml /path/to/my/app

Source

GPU Debugging

If the validation is clean, and you are getting incorrect rendering or computing, it's time to dive into GPU debugging. Many tools exist today:

  • RenderDoc can be used on Windows and Linux to capture Vulkan, D3D, and OpenGL.
    • If RenderDoc is used with a compute shader without any normal rendering components, device.start_output() must be called before enqueuing work for RenderDoc to pick up the pipeline.
    • Vulkan initialization may fail in RenderDoc under wayland (see issue 3889). To work around it, force winit to use x11 by setting the WAYLAND_DISPLAY environment variable to an empty string for winit versions 0.28.2 and onward, or the WINIT_UNIX_BACKEND environment variable to "x11" for winit versions up to 0.29.1. In doubt, do both.
  • PIX can be used on Windows to capture D3D12
  • XCode has powerful Metal GPU capture support with full shader debugging.

In order to run your application from XCode, you can create an empty project, select your executable binary, and provide the command line parameters in the project settings.

Capturing WebGPU in Chrome on PIX

https://gist.github.com/Popov72/41f71cbf8d55f2cb8cae93f439eee347

CPU Debugging

As wgpu is a Rust project, all the regular methods of debugging would work. It's convenient to launch an application from XCode (e.g. as an "External build system" type project), or Visual Studio (just go to "Open Project" and select the executable, adjust the path and parameters).

When looking at a crash/panic, first step is typically enabling RUST_BACKTRACE=1 in the environment. This will spew out the stack trace, which helps you to locate the problem.

Rubber Duck Debugging

Hop on to "#wgpu:matrix.org" and try to explain what happens and why. Maybe we listen. Maybe you'll figure out the answer by then ;)

Tracing Infrastructure

API tracing is built into wgpu-core under the "trace" feature. To record a trace through wgpu-rs, first make sure this feature is enabled (in wgpu-rs).

There is a trace_path optional parameter in wgpu::Adapter::request_device (code). When it's set to an existing folder path, the device will be recording a trace of everything that's going on, to this folder.

Once the trace is recorded, you can zip the folder and attach it to an issue. This should allow anyone, including the developers, to reproduce the issue on their machines, using the player:

cd player
cargo run --features winit -- <trace_folder_path>

If the program crashed or was interrupted in the midst of recording, you may need to edit the trace folder's trace.ron file and add a closing ] bracket to the end. When that bracket is missing, the player exits with a message like:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value:
Error { code: Eof, position: Position { line: 96233, col: 1 } }', player/src/bin/play.rs:28:70

Read more at initial PR.

Running in Gecko

If you encounter a problem with something that runs fine outside of Gecko but fails to run in Gecko, there is a way to trace the API use by the browser. Simply run Firefox Nightly (with prefs enabled for WebGPU, obviously) under WGPU_TRACE environment pointing to a writable folder, and it will record the API trace. Setting MOZ_DISABLE_GPU_SANDBOX=1 might also be required if the GPU process is sandboxed by default. This trace can be replayed as usual, or even compared to a trace you can get by running the same application natively, to find out what the browser doesn't do right.

To replay a capture from Firefox and inspect it in renderdoc or similar tools, it is best not to build the player with the "winit" cargo feature.

When Firefox renders a WebGPU canvas, the result is done in a texture rather than a window's swap chain, and that texture is then shared and put into the screen via other means that aren't part of the wgpu trace. Since there is no window swap chain involved, renderdoc can't guess when a frame starts and ends, so it has to be helped by manually inserting begin/end capture markers. When built without the winit feature, the player replays the commands without rendering them into window, the player adds these markers at the begining and end of the trace whereas it doesn't when the winit feature is enabled since it assumes the trace will eventually render into the winit window's swapchain.

This applies to Firefox as well as any other software using wgpu to render content into a texture but not to present that texture into a window.