What's New in WebGPU (Chrome 129)

François Beaufort
François Beaufort

HDR support with canvas tone mapping mode

Web developers have limited options for delivering HDR content, relying primarily on <img> and <video> elements. The <canvas> element, however, remains restricted to SDR. Generating dynamic HDR content within a canvas requires encoding its contents as an HDR image before displaying it (for an example see this demo).

The new GPUCanvasToneMappingMode parameter in the WebGPU canvas configuration now allows WebGPU to draw colors brighter than white (#FFFFFF). It does so through the following modes:

  • "standard": The default behavior restricts content to the SDR range of the screen. This mode is accomplished by clamping all color values in the color space of the screen to the [0, 1] interval.

  • "extended": Unlocks the full HDR range of the screen. This mode matches "standard" in the [0, 1] range of the screen. Clamping or projection is done to the extended dynamic range of the screen but not [0, 1].

The following code snippet shows you to configure a canvas for high dynamic range.

const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();

const canvas = document.querySelector("canvas");
const context = canvas.getContext("webgpu");

context.configure({
  device,
  format: "rgba16float",
  toneMapping: { mode: "extended" },
});

Explore HDR with WebGPU by checking out the Particles (HDR) sample and WebGPU HDR example, and see the chromestatus entry.

A laptop with an HDR screen displaying a vibrant image.
The Particles (HDR) sample displayed on a HDR screen.

Expanded subgroups support

Following the announcement of subgroups experimentation, the subgroup built-ins functions are now available for use in both compute shaders and fragment shaders. They are no longer restricted to just compute shaders. See issue 354738715.

Note that the subgroup_size built-in value is currently buggy in fragment shaders. Avoid it for now.

Furthermore, the following subgroup built-ins functions have been added:

  • subgroupAdd(value): Returns the summation of all active invocations values across the subgroup.
  • subgroupExclusiveAdd(value): Returns the exclusive scan summation of all active invocations values across the subgroup.
  • subgroupMul(value): Returns the multiplication of all active invocations values across the subgroup.
  • subgroupExclusiveMul(value): Returns the exclusive scan multiplication of all active invocations values across the subgroup.

  • subgroupAnd(value): Returns the binary AND of all active invocations values across the subgroup.
  • subgroupOr(value): Returns the binary OR of all active invocations values across the subgroup.
  • subgroupXor(value): Returns the binary XOR of all active invocations values across the subgroup.

  • subgroupMin(value): Returns the minimal value of all active invocations values across the subgroup.
  • subgroupMax(value): Returns the maximal value of all active invocations values across the subgroup.

  • subgroupAll(value): Returns true if value is true for all active invocations in the subgroup.
  • subgroupAny(value): Returns true if value is true for any active invocation in the subgroup.

  • subgroupElect(): Returns true if this invocation has the lowest subgroup_invocation_id among active invocations in the subgroup.
  • subgroupBroadcastFirst(value): Broadcasts value from the active invocation with the lowest subgroup_invocation_id in the subgroup to all other active invocations.

  • subgroupShuffle(value, id): Returns value from the active invocation whose subgroup_invocation_id matches id.
  • subgroupShuffleXor(value, mask): Returns value from the active invocation whose subgroup_invocation_id matches subgroup_invocation_id ^ mask. mask must be dynamically uniform.
  • subgroupShuffleUp(value, delta): Returns value from the active invocation whose subgroup_invocation_id matches subgroup_invocation_id - delta.
  • subgroupShuffleDown(value, delta): Returns value from the active invocation whose subgroup_invocation_id matches subgroup_invocation_id + delta.

  • quadBroadcast(value, id): Broadcasts value from the quad invocation with id equal to id. id must be a constant-expression.
  • quadSwapX(value): Swaps value between invocations in the quad in the X direction.
  • quadSwapY(value): Swaps value between invocations in the quad in the Y direction.
  • quadSwapDiagonal(value): Swaps value between invocations in the quad diagonally.

Dawn updates

The wgpu::PrimitiveState struct now directly includes depth clip control setting, eliminating the need for a separate wgpu::PrimitiveDepthClipControl struct. To learn more, see the following code snippet and the webgpu-headers PR.

// Before
wgpu::PrimitiveState primitive = {};
wgpu::PrimitiveDepthClipControl depthClipControl;
depthClipControl.unclippedDepth = true;
primitive.nextInChain = &depthClipControl;
// Now
wgpu::PrimitiveState primitive = {};
primitive.unclippedDepth = true;

This covers only some of the key highlights. Check out the exhaustive list of commits.

What's New in WebGPU

A list of everything that has been covered in the What's New in WebGPU series.

Chrome 130

Chrome 129

Chrome 128

Chrome 127

Chrome 126

Chrome 125

Chrome 124

Chrome 123

Chrome 122

Chrome 121

Chrome 120

Chrome 119

Chrome 118

Chrome 117

Chrome 116

Chrome 115

Chrome 114

Chrome 113