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 the smooth transition since developers control which version of emscripten they use.
A couple weeks after an update like this one, the "old" version will be removed. This means that the "old" version of the items below will start being removed from Chromium/Dawn starting on 2021-04-21.
Previous PSAs:
The WGSL specification has made a lot of progress with most of the builtin functions specified and nice ergonomics improvements (with more likely coming). The implementation in Chromium is still evolving rapidly to catch up with the specification so we aren't offering a gradual deprecation like for the WebGPU API yet.
Around the last PSA, WGSL code looked like this:
It now looks like this:
There are a lot of breaking changes this time, and a couple of them might require large changes (GPUBindGroupLayoutEntry
and GPURenderPipelineDescriptor
). The changes come both from feedback of WebGPU early adopters as well as some more focus on ergonomics from the WebGPU group.
The group looked at most of the API to see if changes are needed and no breaking changes are in the pipeline. That's why we are hopeful that there will be fewer breaking changed in the future.
OUTPUT_ATTACHMENT
-> RENDER_ATTACHMENT
WebGPU PR
Contrary to Vulkan, WebGPU doesn't have "input attachment" so there is not symmetry needed with OUTPUT_ATTACHMENT
. For clarity it is renamed to RENDER_ATTACHMENT
. In JavaScript the following changes are needed:
Likewise when using Dawn’s API, changes are needed:
GPUBindGroupLayoutEntry
changesMain WebGPU PR
In order to keep the complexity of BindGroupLayoutEntry
from exploding as more capabilities are added, it was refactored to isolate the properties needed for each type of binding.
An example of the change needed in JavaScript:
An example of the changes needed when using Dawn's API (note that at least one of buffer.type
, sampler.type
, texture.sampleType
or storageTexture.access+format
must be set):
Main WebGPU PR
The GPURenderPipelineDescriptor format has been re-organized for clarity. See the table below for converting deprecated GPURenderPipelineDescriptors to the new format.
Old key | New Key |
---|---|
vertexStage.module |
vertex.module |
vertexStage.entryPoint |
vertex.entryPoint |
vertexState.vertexBuffers |
vertex.buffers |
vertexState.indexFormat |
primitive.stripIndexFormat |
primitiveTopology |
primitive.topology |
rasterizationState.frontFace |
primitive.frontFace |
rasterizationState.cullMode |
primitive.cullMode |
rasterizationState.depthBias |
depthStencil.depthBias |
rasterizationState.depthBiasSlopeScale |
depthStencil.depthBiasSlopeScale |
rasterizationState.depthBiasClamp |
depthStencil.depthBiasClamp |
depthStencilState.format |
depthStencil.format |
depthStencilState.depthWriteEnabled |
depthStencil.depthWriteEnabled |
depthStencilState.depthCompare |
depthStencil.depthCompare |
depthStencilState.stencilFront |
depthStencil.stencilFront |
depthStencilState.stencilBack |
depthStencil.stencilBack |
depthStencilState.stencilReadMask |
depthStencil.stencilReadMask |
depthStencilState.stencilWriteMask |
depthStencil.stencilWriteMask |
sampleCount |
multisample.count |
sampleMask |
multisample.mask |
alphaToCoverageEnabled |
multisample.alphaToCoverageEnabled |
fragmentStage.module |
fragment.module |
fragmentStage.entryPoint |
fragment.entryPoint |
colorStates[i].format |
fragment.targets[i].format |
colorStates[i].writeMask |
fragment.targets[i].writeMask |
colorStates[i].colorBlend |
fragment.targets[i].blend.color |
colorStates[i].alphaBlend |
fragment.targets[i].blend.alpha |
Note that Chromium/Dawn will now validate that fragment.targets[i].blend
is undefined
/ nullptr
if fragment.targets[i].format
is a non-filterable format (i.e. integer or float32).
Some types part of the GPUPipelineDescriptor
have been renamed. No changes are needed in JavaScript, but the following updates are required when using Dawn's API:
Previous type | New type |
---|---|
wgpu::BlendDescriptor |
wgpu::BlendComponent |
wgpu::StencilFaceStateDescriptor |
wgpu::StencilFaceState |
wgpu::VertexAttributeDescriptor |
wgpu::VertexAttributes |
wgpu::VertexBufferLayoutDescriptor |
wgpu::VertexBufferLayout |
Also when using Dawn's API the new render pipeline descriptor is called wgpu::RenderPipelineDescriptor2
and passed to wgpu::Device::CreateRenderPipeline2
. Of course this is only temporary and after the deprecation period, the "2" variant will be deprecated in favor of the old names (but with the new structure).
GPUDevice.defaultQueue
-> GPUDevice.queue
WebGPU PR
Since WebGPU does not yet support multiple queues, and many applications will only ever need one queue, the GPUDevice
's default queue has been renamed to simply queue
to reduce the amount of typing needed.
An example of the change needed in JavaScript:
Likewise when using Dawn’s API, changes are needed:
GPUFence
-> GPUQueue.onSubmittedWorkDone
WebGPU PR
Since WebGPU does not yet support multiple queues, the GPUFence
could only be used to watch the completion of work submitted to queues. Because of this extremely limited use, they are replaced with GPUQueue.onSubmittedWorkDone
which is a Promise
that resolves when the queue is done processing work submitted until this point.
In JavaScript the following changes are needed:
Likewise when using Dawn’s API, changes are needed:
WebGPU PR
WebGPU's vertex formats were using "char"
, "short"
and similar terminology which wasn't used anywhere else (and isn't very descriptive if you don't know C/C++). To make it more clear it has been changed to use names like "uint8"
, "sint16"
and "float32"
. See the table below for correspondances in the JavaScript API and Dawn's API.
Old Javascript | New Javascript | Old Dawn | New Dawn |
---|---|---|---|
"uchar2" |
"uint8x2" |
UChar2 |
Uint8x2 |
"uchar4" |
"uint8x4" |
UChar4 |
Uint8x4 |
"char2" |
"sint8x2" |
Char2 |
Sint8x2 |
"char4" |
"sint8x4" |
Char4 |
Sint8x4 |
"uchar2norm" |
"unorm8x2" |
UChar2Norm |
Unorm8x2 |
"uchar4norm" |
"unorm8x4" |
UChar4Norm |
Unorm8x4 |
"char2norm" |
"snorm8x2" |
Char2Norm |
Snorm8x2 |
"char4norm" |
"snorm8x4" |
Char4Norm |
Snorm8x4 |
"ushort2" |
"uint16x2" |
UShort2 |
Uint16x2 |
"ushort4" |
"uint16x4" |
UShort4 |
Uint16x4 |
"short2" |
"sint16x2" |
Short2 |
Sint16x2 |
"short4" |
"sint16x4" |
Short4 |
Sint16x4 |
"ushort2norm" |
"unorm16x2" |
UShort2Norm |
Unorm16x2 |
"ushort4norm" |
"unorm16x4" |
UShort4Norm |
Unorm16x4 |
"short2norm" |
"snorm16x2" |
Short2Norm |
Snorm16x2 |
"short4norm" |
"snorm16x4" |
Short4Norm |
Snorm16x4 |
"half2" |
"float16x2" |
Half2 |
Float16x2 |
"half4" |
"float16x4" |
Half4 |
Float16x4 |
"float" |
"float32" |
Float |
Float32 |
"float2" |
"float32x2" |
Float2 |
Float32x2 |
"float3" |
"float32x3" |
Float3 |
Float32x3 |
"float4" |
"float32x4" |
Float4 |
Float32x4 |
"uint" |
"uint32" |
UInt |
Uint32 |
"uint2" |
"uint32x2" |
UInt2 |
Uint32x2 |
"uint3" |
"uint32x3" |
UInt3 |
Uint32x3 |
"uint4" |
"uint32x4" |
UInt4 |
Uint32x4 |
"int" |
"sint32" |
SInt |
Uint32 |
"int2" |
"sint32x2" |
Int2 |
Sint32x2 |
"int3" |
"sint32x3" |
Int3 |
Sint32x3 |
"int4" |
"sint32x4" |
Int4 |
Sint32x4 |
WebGPU PR
To avoid confusion with the concept of TextureView
s, the types BufferCopyView
, TextureCopyView
, and ImageBitmapCopyView
have been renamed to ImageCopyBuffer
, ImageCopyTexture
, and ImageCopyImageBitmap
respectively.
Since these types are dictionaries this rename has no effect on JavaScript code, other than to make the documentation a bit clearer.
When using Dawn's API, changes are needed:
bytesPerRow
and rowsPerImage
changesPreviously when doing 1D copies, validation ignored the value bytesPerRow
so it was valid to set it to 0, or another value too small. This will now cause a validation error. Instead set bytesPerRow
to wgpu::kCopyStrideUndefined
(the default struct member value) or a value larger than the copy's size in bytes.
Likewise previously when doing 2D copies, validation ignored the value rowsPerImage
. To avoid a validation error it must now be set to wgpu::kCopyStrideUndefined
(the default struct member value) or a value larger than the copy's height.
Finally when doing 3D copies, a 0 value for rowsPerImage
caused it to default to the copy height. This will now produce a validation error and you should use wgpu::kCopyStrideUndefined
instead (the default struct member value
These changes are made to be closer to the Javascript API where undefined
and 0 have different behaviors, and makes it easier to implement the Javascript API using Dawn's API.
WebGPU PR
Depending on context, the 3rd element of a GPUExtent3DDict
may represent either a depth, as in the case of a 3D texture, or the number of layers, as in the case of a 2D array texture. To clarify this dual purpose, the depth
attribute has been renamed to depthOrArrayLayers
.
An example of the change needed in JavaScript:
Likewise when using Dawn’s API, changes are needed:
Note that both Javascript and Dawn's API support shorthand declarations with unspecified values defaulting to one.
In Javascript:
Using Dawn's API:
It was determined that getSwapChainPreferredFormat
didn't need to be asynchronous, and that it was more appropriate for it to accept a GPUAdapter
than a GPUDevice
.
An example of the change needed in JavaScript:
The Dawn API does not currently have an equivalent call, so no changes are needed.
SetIndexBufferWithFormat
-> SetIndexBuffer
In Dawn's API SetIndexBuffer
was temporarily renamed to SetIndexBufferWithFormat
while its signature was changed. Now that the signature is changed, SetIndexBufferWithFormat
is deprecated in favor of SetIndexBuffer
(with the updated signature).
WebGPU PR
The WebGPU specification has started referring to what was previously called "Extensions" as "Features". This is because they include functionality that is present in the base spec but is not universally supported, and as such needs to be enabled explicitly (such as compressed texture formats).
In service of this change the extensions
attribute on both GPUAdapter
and GPUDevice
has been renamed to features
. Also, the extensions
key of the GPUDeviceDescriptor
dictionary has beed renamed to nonGuaranteedFeatures
. The name nonGuaranteedFeatures
was chosen to emphasize that the features requested in this array are not guaranteed to be supported on all platforms and devices, and as such should be verified against the GPUAdapter.features
list prior to requesting. As was the case prior to the rename, specifying a feature in the nonGuaranteedFeatures
that is not supported by the GPUAdapter
will cause requestDevice()
to fail.
An example of the change needed in JavaScript:
This change has not yet been made in Dawn, so no changes are required yet.
Most of the improvements to Dawn / Chromium for WebGPU are happening under the hood. Recently our focus has been on massively improving the security of our implementation in preparation for a future WebGPU Origin Trial. This includes reworking large parts of our cross-process remoting architecture, vastly expanded test coverage (and fixing all the tiny issues found in the spec etc), automatic fuzzing, etc.
Another huge effort is expanding usage of Tint, our WGSL compiler. Previously it directly converted WGSL to SPIR-V and we were using SPIRV-Cross for translation to MSL / HLSL / GLSL and reflection. SPIRV-Cross is a mature library but is not meant to handle untrusted/malicious inputs like you can find on the Web. Tint has been expanded to the point where Dawn can use it for shader reflection and WGSL -> HLSL translation.
OffscreenCanvas now supports the "gpupresent"
context type! It can currently be used to detach the content of the canvas into and ImageBitmap with transferToImageBitmap()
. Additional features like using WebGPU on an OffscreenCanvas
received from transferToOffscreenCanvas()
and OffscreenCanvas.convertToBlob()
will follow shortly.
When a GPURenderPipeline
or GPUComputePipeline
is created with GPUDevice.createRenderPipeline()
or GPUDevice.createComputePipeline()
respectively, they return immediately. On the GPU process side it means that the pipelines have to be compiled immediately and stall the execution of further commands (because they could be used for drawing immediately). This can easily cause frame hitching and at worst compiling a lot of pipelines can trigger the "GPU watchdog" that will lose the WebGPU devices.
New methods for creating pipelines asynchronously, GPUDevice.createRenderPipelineAsync()
and GPUDevice.createComputePipelineAsync()
, have been added to address this. These methods accept the same descriptors as their synchronous counterparts but asynchronously return the pipeline once it is fully ready to be used. Using pipelines returned from the create*PipelineAsync()
methods can help reduce hitches on first use compared to those returned from create*Pipeline()
. This is a similar idea to WebGL's KHR_parallel_shader_compile
but more Javacript-idiomatic.
Note that currently these methods aren't completely asynchronous internally but work is ongoing to improve their performance.
Using the feature in Javascript is done like below:
Using the feature in Dawn's API is done like below:
"depth-clamping"
is a new optional WebGPU feature that allows controlling the behavior of fragments with a depth that fall outside of the [0, 1]
range. By default without this extension they are discarded, instead when setting GPUPrimitiveState.clampDepth = true
the fragments will be kept and their depth clamped to [0, 1]. This is particularly useful when rendering shadow maps so that geometry outside of the light's frustum still occludes geometry correctly (otherwise it would just "disappear" from the shadow).
Using the feature in Javascript is done like below:
Using the feature in Dawn's API, when a wgpu::Device
with the feature has been created is done like below. Extensibility of descriptors in C++ is done with "chained structures" a bit like Vulkan:
It is now possible to specify a GPUSampler
's maximum anisotropy for anisotropic filtering. For historical reasons this features is not guaranteed to be everywhere so the WebGPU specification doesn't require that values higher than 1 do something, but that should be the case everywhere in practice.
Using the feature is done by adding maxAnisotropy
to the descriptor passed to GPUDevice.createSampler
:
When using Dawn's API maxAnisotropy
can be set in a similar way: