Try   HackMD

Changelog for WebGPU in Chromium / Dawn 96

Chromium's WebGPU implementation and Dawn's API try to closely follow changes to the WebGPU specification. When the WebGPU IDL changes, Chromium and Dawn will try to support both the deprecated and the new version of the IDL at the same time so prototypes can be updated. In JavaScript, uses of the deprecated path will result in a console warning, while when using Dawn directly, the deprecated path will print a warning to stderr.

Note that all changes to Dawn's API make it closer to webgpu.h that we hope will allow applications to target both Dawn, and wgpu in native before being compiled in WASM. Emscripten will also be updated from the "old" to the "new" API but won't have a smooth transition since developers control which version of emscripten they use.

Items deprecated in this changelog will be removed in the next branched version of Chromium. They will also be removed in Chromium Canary and top of tree Dawn two weeks after this changelog is published in Canary and top of tree Dawn (so starting 2021-11-20).

Previous PSAs / changelogs:

New deprecations

Implicit GPUBindGroupLayout compatibility changes

WebGPU PR

This is a change with no deprecation period because no content that we could see was relying on the behavior. To help developers use implicit bind group layouts (BGL) correctly, they are now only allowed to be used with the pipeline they were created for. This prevents a common source of errors where the implicit BGL changed when binding in the shader became statically unused. This was confusing when the BGL was used with a different pipeline that still statically used some bindings.

const pipeline = device.createRenderPipeline({ // no layout, so the implicit ones are used. }); const implicitBGL = pipeline.getBindGroupLayout(0); const otherPipeline = device.createRenderPipeline({}); // This code will always produce an error because `implicitBGL` is only // compatible with `pipeline` const pass = createSomeRenderPassEncoder(); pass.setBindGroup(0, implicitBGL); pass.setPipeline(otherPipeline); pass.draw(3);

0 as undefined in the C API

Previsouly in Dawn's API 0 could be used in various places to mean the same as JavaScript undefined (where the implementation does the obvious correct thing as much as possible). This is now using special values like WGPU_ARRAY_LAYER_COUNT_UNDEFINED instead because the WebGPU defines that a value of 0 is invalid in many of these cases.

Places where these updates are necessary:

  • wgpu::TextureViewDescriptor::arrayLayerCount: replace 0 with wgpu::kArrayLayerCountUndefined.
  • wgpu::TextureViewDescriptor::mipLevelCount: replace 0 with wgpu::kMipLevelCountUndefined.
  • SetIndexBuffer and SetVertexBuffer: replace 0 with wgpu::kWholeSize for the size argument.
  • wgpu::Buffer::MapAsync: replace 0 with wgpu::kWholeMapSize for the size argument.

New features and improvements

Massively improved error messages

Error messages produced for validation errors in Dawn / Chromium used to be inscrutable, for example:

Buffer binding doesn't fit in the buffer

Most of the error messages have now been updated to provide much more information about the object / values being validated and why they fail, context of where this validation was happening, the label of objects involved if set, debug groups and more! See this Twitter thread for an example and more information!

Note that M96 contains roughly the first half of these changes and that error messages will keep improving in the future.

Vertex-only GPURenderPipeline

It is now possible to create render pipelines with only a vertex stage. This is useful for example when doing a depth-prepass that only writes to the depth buffer: only rasterization is needed and the fragment shader execution would be pure overhead.

When using the JavaScript API this can be done with:

const pipeline = device.createRenderPipeline({ vertex: { //... }, // no `fragment` entry! });

Similarly when using Dawn's API:

wgpu::RenderPipelineDescriptor desc; desc.fragment = nullptr; // fill other members wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&desc);

GPUDevice.createRenderPipelineAsync is now actually async

Previously GPUDevice.createRenderPipelineAsync was just a shim over createRenderPipeline, which meant that sending a lot of compilation for async compilation would stall further operations (and could cause the GPU process timeout to lose the device in Chromium). Async render pipeline creation is now correctly implemented asynchronously and spread over multiple threads when possible.

WGSL: using global let variables to size arrays

It is now possible to size arrays with global constants. Previously arrays could only be sized with literals. Concretely it is now possible to write:

let UMBRELLA_COUNT = 16u; fn foo() { // Previously you had to hardcode a 16 below. var umbrellas : array<Umbralla, UMBRELLA_COUNT>; }

WGSL: the num_workgroups builtin is now supported

This builtin mirrors the arguments that are passed to the GPUComputePassEncoder.dispatch call and the content of the indirect buffer in GPUComputePassEncoder.dispatchIndirect. It is useful to locally perform in an invocation computations that depend on the complete size of a dispatch call.

[[stage(compute)]] fn compute_stuff([[builtin(num_workgroups)]] NumWorkgroups : vec3<u32>) { // Use NumWorkgroups! }