# Technical Documentation: google/dawn

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/google/dawn)

### Overview (README.md)

<div align="center">
  <img
      title="Dawn's logo"
      alt="Dawn's logo: a sun rising behind a stylized mountain inspired by the WebGPU logo."
      src="docs/imgs/dawn_logo_notext.png"
      width="50%">

  
  [](https://matrix.to/#/#webgpu-dawn:matrix.org)
</div>

# Dawn, a WebGPU implementation

Dawn is an open-source and cross-platform implementation of the [WebGPU](https://webgpu.dev) standard.
More precisely it implements [`webgpu.h`](https://github.com/webgpu-native/webgpu-headers/blob/main/webgpu.h) that is a one-to-one mapping with the WebGPU IDL.
Dawn is meant to be integrated as part of a larger system and is the underlying implementation of WebGPU in Chromium.

Dawn provides several WebGPU building blocks:
 - **WebGPU C/C++ headers** that applications and other building blocks use.
   - The `webgpu.h` version that Dawn implements.
   - A C++ wrapper for the `webgpu.h`.
 - **A "native" implementation of WebGPU** using platforms' GPU APIs: D3D12, Metal, Vulkan and OpenGL. See [per API support](docs/support.md) for more details.
 - **A client-server implementation of WebGPU** for applications that are in a sandbox without access to native drivers
 - **Tint** is a compiler for the WebGPU Shader Language (WGSL) that can be used in standalone to convert shaders from and to WGSL.

Helpful links:

 - [Dawn bug tracker](https://issues.chromium.org/savedsearches/6783309) if you find issues with Dawn. Create a new issue [here](https://issues.chromium.org/issues/new?noWizard=true&component=1570784).
 - [Tint bug tracker](https://issues.chromium.org/savedsearches/6783217) if you find issues with Tint. Create a new issue [here](https://issues.chromium.org/issues/new?noWizard=true&component=1571063).
 - [Dawn's mailing list](https://groups.google.com/g/dawn-graphics) for other discussions related to Dawn.
 - [Dawn's source code](https://dawn.googlesource.com/dawn)
 - [Dawn's Matrix chatroom](https://matrix.to/#/#webgpu-dawn:matrix.org) for live discussion around contributing or using Dawn.
 - [WebGPU's Matrix chatroom](https://matrix.to/#/#WebGPU:matrix.org)

## Documentation table of content

Developer documentation:

 - [Dawn overview](docs/dawn/overview.md)
 - [Building](docs/building.md)
 - [Contributing](CONTRIBUTING.md)
 - [Code of Conduct](CODE_OF_CONDUCT.md)
 - [Testing Dawn](docs/dawn/testing.md)
 - [Testing Tint](docs/tint/testing.md)
 - [Running the WebGPU CTS with the standalone runner](docs/standalone_cts_runner.md)
 - [Debugging Dawn](docs/dawn/debugging.md)
 - [Dawn's infrastructure](docs/dawn/infra.md)
 - [Dawn errors](docs/dawn/errors.md)
 - [Tint experimental extensions](docs/tint/experimental_extensions.md)
 - [Quickstart with CMake](docs/quickstart-cmake.md)
 - [Becoming a committer](docs/becoming-committer.md)


User documentation: (TODO, figure out what overlaps with the webgpu.h docs)

## License

BSD 3-Clause License, please see [LICENSE](/LICENSE).

## Disclaimer

This is not an officially supported Google product.

---

### Dawn Overview (`docs/dawn/overview.md`)

# Dawn repository overview

This repository contains the implementation of Dawn, which is itself composed of two main
libraries (dawn_native and dawn_wire), along with support libraries, tests, and samples. Dawn makes
heavy use of code-generation based on the `dawn.json` file that describes the native WebGPU API. It
is used to generate the API headers, C++ wrapper, parts of the client-server implementation, and
more!

## Directory structure

- [`dawn.json`](../../src/dawn/dawn.json): contains a description of the native WebGPU in JSON form.
  It is the data model that's used by the code generators.
- [`dawn_wire.json`](../../src/dawn/dawn_wire.json): contains additional information used to
  generate `dawn_wire` files, such as commands in addition to regular WebGPU commands.
- [`generator`](../../generator): directory containg the code generators and their templates.
  Generators are based on Jinja2 and parse data-models from JSON files.
    - [`dawn_json_generator.py`](../../generator/dawn_json_generator.py): the main code generator
      that outputs the WebGPU headers, C++ wrapper, client-server implementation, etc.
    - [`templates`](../../generator/templates): Jinja2 templates for the generator, with
      subdirectories for groups of templates that are all used in the same library.
- [`include`](../../include):
    - [`dawn`](../../include/dawn): public headers with subdirectories for each library. Note that
      some headers are auto-generated and not present directly in the directory.
- [`infra`](../../infra): configuration file for the commit-queue infrastructure.
- [`scripts`](../../scripts): contains a grab-bag of files that are used for building Dawn, in
  testing, etc.
- [`src`](../../src):
  - [`dawn`](../../src/dawn): root directory for Dawn code
      - [`common`](../../src/dawn/common): helper code that is allowed to be used by Dawn's core
        libraries, `dawn_native` and `dawn_wire`. Also allowed for use in all other Dawn targets.
      - [`fuzzers`](../../src/dawn/fuzzers): various fuzzers for Dawn that are running
        in [Clusterfuzz](https://google.github.io/clusterfuzz/).
      - [`native`](../../src/dawn/native): code for the implementation of WebGPU on top of graphics
        APIs. Files in this folder are the "frontend" while subdirectories are "backends".
         - `<backend>`: code for the implementation of the backend on a specific graphics API, for
           example `d3d12`, `metal` or `vulkan`.
      - [`samples`](../../src/dawn/samples): a small collection of samples using the native WebGPU
        API. They were mostly used when bringing up Dawn for the first time, and to test the
        `WGPUSurface` object.
      - [`tests`](../../src/dawn/tests):
        - [`end2end`](../../src/dawn/tests/end2end): tests for the execution of the WebGPU API and
          require a GPU to run.
        - [`perf_tests`](../../src/dawn/tests/perf_tests): benchmarks for various aspects of Dawn.
        - [`unittests`](../../src/dawn/tests/unittests): code unittests of internal classes, but
          also by extension WebGPU API tests that don't require a GPU to run.
          - [`validation`](../../src/dawn/tests/unittests/validation): WebGPU validation tests not
            using the GPU (frontend tests)
        - [`white_box`](../../src/dawn/tests/white_box): tests using the GPU that need to access the
          internals of `dawn_native` or `dawn_wire`.
      - [`wire`](../../src/dawn/wire): code for an implementation of WebGPU as a client-server
        architecture.
      - [`utils`](../../src/dawn/utils): helper code to use Dawn used by tests and samples but
        disallowed for `dawn_native` and `dawn_wire`.
      - [`platform`](../../src/dawn/platform): definition of interfaces for dependency injection in
        `dawn_native` or `dawn_wire`.
- [`third_party`](../../third_party): directory where dependencies live as well as their
  buildfiles.

## Dawn Native (`dawn_native`)

The largest library in Dawn is `dawn_native` which implements the WebGPU API by translating to
native graphics APIs such as D3D12, Metal or Vulkan. It is composed of a frontend that does all the
state-tracking and validation, and backends that do the actual translation to the native graphics
APIs.

`dawn_native` hosts the [spirv-val](https://github.com/KhronosGroup/SPIRV-Tools) for validation of
SPIR-V shaders and uses [Tint](../../src/tint) shader translator to convert WGSL shaders to an
equivalent shader for use in the native graphics API (HLSL for D3D12, MSL for Metal or Vulkan
SPIR-V for Vulkan).

## Dawn Wire (`dawn_wire`)

A second library that implements both a client that takes WebGPU commands and serializes them into a
buffer, and a server that deserializes commands from a buffer, validates they are well-formed and
calls the relevant WebGPU commands. Some server to client communication also happens so the API's
callbacks work properly.

Note that `dawn_wire` is meant to do as little state-tracking as possible so that the client can be
lean and defer most of the heavy processing to the server side where the server calls into
`dawn_native`.

## Dawn Proc (`dawn_proc`)

Normally libraries implementing `webgpu.h` should implement function like `wgpuDeviceCreateBuffer`
but instead `dawn_native` and `dawn_wire` implement the `dawnProcTable` which is a structure
containing all the WebGPU functions Dawn implements. Then a `dawn_proc` library contains a static
version of this `dawnProcTable` and for example forwards `wgpuDeviceCreateBuffer` to the
`procTable.deviceCreateBuffer` function pointer. This is useful in two ways:

 - It allows deciding at runtime whether to use `dawn_native` and `dawn_wire`, which is useful to
   test both paths with the same binary in our infrastructure.
 - It avoids applications that know they will only use Dawn to query all entrypoints at once instead
   of using `wgpuGetProcAddress` repeatedly.

## Code generation

When the WebGPU API evolves, a lot of places in Dawn have to be updated, so to reduce efforts, Dawn
relies heavily on code generation for things like headers, proc tables and de/serialization. For
more information, see [codegen.md](codegen.md).

---

### Imgs README (`docs/imgs/README.md`)

Dawn's logo and derivatives found in this folder are under the [Creative Commons Attribution 4.0 International (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/) license.

---

### Tint Spirv Reader Overview (`docs/tint/spirv-reader-overview.md`)

# Converting SPIR-V to WGSL

This document describes the challenges in converting SPIR-V into WGSL.

Note: Unless otherwise specified, the namespace for C++ code is
`tint::spirv::reader::`.

## Overall flow

1. Validate the SPIR-V input.

   The SPIR-V module (binary blob) is validated against rules for
   Vulkan 1.1, using the SPIRV-Tools validator.

   This allows the rest of the flow to ignore invalid inputs.
   However, the SPIR-V might still be rejected in a later step because:

   - it uses features unavailable in WGSL, or
   - the SPIR-V Reader is insufficiently smart, or
   - the translated program tries to do something rejected by WGSL's rules
     (which are checked by Tint's Resolver).

2. Load the SPIR-V binary into an in-memory representation.

   The SPIR-V reader uses the in-memory representation of the SPIR-V
   module defined by the SPIRV-Tools optimizer.  That provides
   convenient representation of basic structures such as:

    - instructions
    - types
    - constants
    - functions
    - basic blocks

   and provides analyses for:

    - relating definitions to uses (spvtools::opt::analysis::DefUseMgr)
    - types (spvtools::opt::analysis:TypeManager)
    - constants (spvtools::opt::analysis:ConstantManager)

   Note: The SPIR-V is not modified by the SPIR-V Reader.

3. Translate the SPIR-V module into Tint's AST.

   The AST is valid for WGSL except for some small exceptions which are
   cleaned up by transformations.

4. Post-process the AST to make it valid for WGSL.

   Example:
   - Rewrite strided arrays and matrices (remove `@stride` attribute)
   - Rewrite atomic functions
   - Remove unreachable statements, to satisfy WGSL's behaviour analysis.


## Overcoming mismatches between SPIR-V and WGSL

### Remapping builtin inputs and outputs

SPIR-V for Vulkan models builtin inputs and outputs as variables
in Input and Output storage classes.

WGSL builtin inputs are parameters to the entry point, and
builtin outputs are result values of the entry point.

See [spirv-input-output-variables.md](spirv-input-output-variables.md)

### We only care about `gl_Position` from `gl_PerVertex`

Glslang SPIR-V output for a vertex shader has a `gl_PerVertex`
output variable with four members:

- `gl_Position`
- `gl_PointSize`
- `gl_ClipDistance`
- `gl_CullDistance`

WGSL only supports the `position` builtin variable.

The SPIR-V Reader has a bunch of carveouts so it only generates the
position variable. In partcular, it tracks which expressions are actually
accesses into the per-vertex variable, and ignores accesses to other
parts of the structure, and remaps accesses of the position member.

### `gl_PointSize` must be 1.0

It's a WGSL rule.  SPIR-V is more flexible, and the SPIR-V Reader
checks that any assignment to (the equivalent of) `gl_PointSize`
must the constant value 1.0.

### Remapping sample mask inputs and outputs

There's some shenanigans here I don't recall.
See the SkipReason enum.

### Integer signedness

In SPIR-V, the instruction determines the signedness of an operation,
not the types of its operands.

For example:

    %uint = OpTypeInt 32 0 ; u32 type
    %int = OpTypeInt 32 1 ; i32 type

    %int_1 = OpConstant %int 1  ;  WGSL 1i
    %uint_2 = OpConstant %uint 2 ; WGSL 2u

    ; You can mix signs of an operand, and the instruction
    ; tells you the result type.
    %sum_uint = OpIAdd %uint %int %int_1 %uint_2
    %sum_int = OpIAdd %int %int %int_1 %uint_2

However, WGSL arithmetic tends to require the operands and
result type for an operation to all have the same signedness.

So the above might translate to WGSL as:

    let sum_uint: u32 = bitcast<u32>(1i) + 2u;
    let sum_int: i32 = 1i + bitcast<i32>(2u);

See:
* ParserImpl::RectifyOperandSignedness
* ParserImpl::RectifySecondOperandSignedness
* ParserImpl::RectifyForcedResultType

### Translating textures and samplers

SPIR-V textures and samplers are module-scope variables
in UniformConstant storage class.
These map directly to WGSL variables.

For a sampled-image operation, SPIR-V will:
- load the image value from a texture variable
- load the sampler value from a sampler variable
- form a "sampled image" value using `SpvOpSampledImage`
- then use that sampled image value in a image operation
  such as `SpvOpImageSampleImplicitLod`

For an image operation that is not a sampled-image operation
(e.g. OpImageLoad or OpImageWrite), then the steps are similar
except without a sampler (clearly), and without invoking
`OpSampledImage`.

In contrast to the SPIR-V code pattern, the WGSL builtin requires
the texture and sampler value to be passed in as separate parameters.
Secondly, they are passed in by value, by naming the variables
themselves and relying on WGSL's "Load Rule" to pass the handle
value into the callee.

When the SPIR-V Reader translates a texture builtin, it traces
backward through the `OpSampledImage` operation (if any),
back through the load, and all the way back to the `OpVariable`
declaration.  It does this for both the image/texture variable and
the sampler variable (if applicable).  It then uses the names
of those variables as the corresponding arguments to the WGSL
texture builtin.

### Passing textures and samplers into helper functions

Glslang generates SPIR-V where texture and sampler formal parameters
are as pointer-to-UniformConstant.

WGSL models them as passing texture and sampler values themselves,
conceptually as opaque handles.  This is similar to GLSL, but unlike
SPIR-V.

To support textures and samplers as arguments to user-defined functions,
we extend the tracing logic so it knows to bottom out at OpFunctionParameter.

Also, code that generates function declarations now understands formal
parameters declared as a pointer to uniform-constant as
well as direct image and sampler values.

Example GLSL compute shader:

    #version 450

    layout(set=0,binding=0) uniform texture2D im;
    layout(set=0,binding=1) uniform sampler s;

    vec4 helper(texture2D imparam, sampler sparam) {
      return texture(sampler2D(imparam,sparam),vec2(0));
    }

    void main() {
      vec4 v = helper(im,s);
    }

SPIR-V generated by Glslang (Shaderc's glslc):

    ; SPIR-V
    ; Version: 1.0
    ; Generator: Google Shaderc over Glslang; 10
    ; Bound: 32
    ; Schema: 0
                   OpCapability Shader
              %1 = OpExtInstImport "GLSL.std.450"
                   OpMemoryModel Logical GLSL450
                   OpEntryPoint GLCompute %main "main"
                   OpExecutionMode %main LocalSize 1 1 1
                   OpSource GLSL 450
                   OpSourceExtension "GL_GOOGLE_cpp_style_line_directive"
                   OpSourceExtension "GL_GOOGLE_include_directive"
                   OpName %main "main"
                   OpName %helper_t21_p1_ "helper(t21;p1;"
                   OpName %imparam "imparam"
                   OpName %sparam "sparam"
                   OpName %v "v"
                   OpName %im "im"
                   OpName %s "s"
                   OpDecorate %im DescriptorSet 0
                   OpDecorate %im Binding 0
                   OpDecorate %s DescriptorSet 0
                   OpDecorate %s Binding 1
           %void = OpTypeVoid
              %3 = OpTypeFunction %void
          %float = OpTypeFloat 32
              %7 = OpTypeImage %float 2D 0 0 0 1 Unknown
    %_ptr_UniformConstant_7 = OpTypePointer UniformConstant %7
              %9 = OpTypeSampler
    %_ptr_UniformConstant_9 = OpTypePointer UniformConstant %9
        %v4float = OpTypeVector %float 4
             %12 = OpTypeFunction %v4float %_ptr_UniformConstant_7 %_ptr_UniformConstant_9
             %19 = OpTypeSampledImage %7
        %v2float = OpTypeVector %float 2
        %float_0 = OpConstant %float 0
             %23 = OpConstantComposite %v2float %float_0 %float_0
    %_ptr_Function_v4float = OpTypePointer Function %v4float
             %im = OpVariable %_ptr_UniformConstant_7 UniformConstant
              %s = OpVariable %_ptr_UniformConstant_9 UniformConstant
           %main = OpFunction %void None %3
              %5 = OpLabel
              %v = OpVariable %_ptr_Function_v4float Function
             %31 = OpFunctionCall %v4float %helper_t21_p1_ %im %s
                   OpStore %v %31
                   OpReturn
                   OpFunctionEnd
    %helper_t21_p1_ = OpFunction %v4float None %12
        %imparam = OpFunctionParameter %_ptr_UniformConstant_7
         %sparam = OpFunctionParameter %_ptr_UniformConstant_9
             %16 = OpLabel
             %17 = OpLoad %7 %imparam
             %18 = OpLoad %9 %sparam
             %20 = OpSampledImage %19 %17 %18
             %24 = OpImageSampleExplicitLod %v4float %20 %23 Lod %float_0
                   OpReturnValue %24
                   OpFunctionEnd

What the SPIR-V Reader currently generates:

    @group(0) @binding(0) var im : texture_2d<f32>;

    @group(0) @binding(1) var s : sampler;

    fn helper_t21_p1_(imparam : texture_2d<f32>, sparam : sampler) -> vec4<f32> {
      let x_24 : vec4<f32> = textureSampleLevel(imparam, sparam, vec2<f32>(0.0f, 0.0f), 0.0f);
      return x_24;
    }

    fn main_1() {
      var v : vec4<f32>;
      let x_31 : vec4<f32> = helper_t21_p1_(im, s);
      v = x_31;
      return;
    }

    @compute @workgroup_size(1i, 1i, 1i)
    fn main() {
      main_1();
    }

### Dimensionality mismatch in texture builtins

Vulkan SPIR-V is fairly forgiving in the dimensionality
of input coordinates and result values of texturing operations.
There is some localized rewriting of values to satisfy the overloads
of WGSL's texture builtin functions.

### Reconstructing structured control flow

This is subtle.

- Use structural dominance (but we didn't have the name at the time).
  See SPIR-V 1.6 Rev 2 for updated definitions.
- See the big comment at the start of reader/spirv/function.cc
- See internal presentations.

Basically:
* Compute a "structured order" for structurally reachable basic blocks.
* Traversing in structured order, use a stack-based algorithn to
  identify intervals of blocks corresponding to structured constructs.
  For example, loop construct, continue construct, if-selection,
  switch-selection, and case-construct. Constructs can be nested,
  hence the need for a stack.  This is akin to "drawing braces"
  around statements, to form block-statements that will appear in
  the output. This step performs some validation, which may now be
  redundant with the SPIRV-Tools validator. This is defensive
  programming, and some tests skip use of the SPIRV-Tools validator.
* Traversing in structured order, identify structured exits from the
  constructs identified in the previous step. This determines what
  control flow edges correspond to `break`, `continue`, and `return`,
  as needed.
* Traversing in structured order, generate statements for instructions.
  This uses a stack corresponding to nested constructs. The kind of
  each construct being entered or exited determines emission of control
  flow constructs (WGSL's `if`, `loop`, `continuing`, `switch`, `case`).

### Preserving execution order

An instruction inside a SPIR-V instruction is one of:

- control flow: see the previous section
- combinatorial: think of this as an ALU operation, i.e. the effect
  is purely to evaluate a result value from the values of its operands.
  It has no side effects, and is not affected by external state such
  as memory or the actions of other invocations in its subgroup.
  Examples:  arithmetic, OpCopyObject
- interacts with memory or other invocations in some way.
  Examples: load, store, atomics, barriers, (subgroup operations when we
  get them)
- function calls: functions are not analyzed to see if they are pure,
  so we assume function calls are non-combinatorial.

To preserve execution order, all non-combinatorial instructions must
be translated as their own separate statement.  For example, an OpStore
maps to an assignment statement.

However, combinatorial instructions can be emitted at any point
in evaluation, provided data flow constraints are satisfied: input
values are available, and such that the resulting value is generated
in time for consumption by downstream uses.

The SPIR-V Reader uses a heuristic to choose when to emit combinatorial
values:
- if a combinatorial expression only has one use, *and*
- its use is in the same structured construct as its definition, *then*
- emit the expression at the place where it is consumed.

Otherwise, make a `let` declaration for the value.

Why:
- If a value has many uses, then computing it once can save effort.
  Preserve that choice if it was made by an upstream optimizing compiler.
- If a value is consumed in a different structured construct, then the
  site of its consumption may be inside a loop, and we don't want to
  sink the computation into the loop, thereby causing spurious extra
  evaluation.

This heuristic generates halfway-readable code, greatly reducing the
varbosity of code in the common case.

### Hoisting and phis

SPIR-V uses SSA (static single assignment).  The key requirement is
that the definition of a value must dominate its uses.

WGSL uses lexical scoping.

It is easy enough for a human or an optimizing compiler to generate
SSA cases which do not map cleanly to a lexically scoped value.

Example pseudo-GLSL:

    void main() {
      if (cond) {
        const uint x = 1;
      } else {
        return;
      }
      const uint y = x;  // x's definition dominates this use.
    }

This isn't valid GLSL and its analog would not be a valid WGSL
program because x is used outside the scope of its declaration.

Additionally, SSA uses `phi` nodes to transmit values from predecessor
basic blocks that would otherwise not be visible (because the
parent does not dominate the consuming basic block).  An example
is sending the updated value of a loop induction variable back to
the top of the loop.

The SPIR-V reader handles these cases by tracking:
- where a value definition occurs
- the span of basic blocks, in structured order, where there
  are uses of the value.

If the uses of a value span structured contructs which are not
contained by the construct containing the definition (or
if the value is a `phi` node), then we "hoist" the value
into a variable:

- create a function-scope variable at the top of the structured
  construct that spans all the uses, so that all the uses
  are in scope of that variable declaration.

- for a non-phi: generate an assignment to that variable corresponding
  to the value definition in the original SPIR-V.

- for a phi: generate an assigment to that variable at the end of
  each predecessor block for that phi, assigning the value to be
  transmitted from that phi.

This scheme works for values which can be the stored in a variable.

It does not work for pointers. However, we don't think we need
to solve this case any time soon as it is uncommon or hard/impossible
to generate via standard tooling.
See https://crbug.com/tint/98 and https://crbug.com/tint/837

## Mapping types

SPIR-V has a recursive type system. Types are defined, given result IDs,
before any functions are defined, and before any constant values using
the corresponding types.

WGSL also has a recursive type system. However, except for structure types,
types are spelled inline at their uses.

## Texture and sampler types

SPIR-V image types map to WGSL types, but the WGSL type is determined
more by usage (what operations are performed on it) than by declaration.

For example, Vulkan ignores the "Depth" operand of the image type
declaration (OpTypeImage).
See [16.1 Image Operations Overview](https://registry.khronos.org/vulkan/specs/1.3/html/vkspec.html#_image_operations_overview).
Instead, we must infer that a texture is a depth texture because
it is used by image instructions using a depth-reference, e.g.
OpImageSampleDrefImplicitLod vs. OpImageSampleImplicitLod.

Similarly, SPIR-V only has one sampler type.  The use of the
sampler determines whether it maps to a WGSL `sampler` or
`sampler_comparison` (for depth sampling).

The SPIR-V Reader scans uses of each texture and sampler
in the module to infer the appropriate target WGSL type.
See ParserImpl::RegisterHandleUsage

In Vulkan SPIR-V it is possible to use the same sampler for regular
sampling and depth-reference sampling.  In this case the SPIR-V Reader
will infer a depth texture, but then the generated program will fail WGSL
validation.

For example, this GLSL fragment shader:

    #version 450

    layout(set=1,binding=0) uniform texture2D tInput;
    layout(set=1,binding=1) uniform sampler s;

    void main() {
      vec4 v = texture(sampler2D(tInput,s),vec2(0));
      float f = texture(sampler2DShadow(tInput,s),vec3(0));
    }

Converts to this WGSL shader:

    @group(1) @binding(0) var tInput : texture_depth_2d;

    @group(1) @binding(1) var s : sampler_comparison;

    fn main_1() {
      var v : vec4<f32>;
      var f : f32;
      let x_23 : vec4<f32> = vec4<f32>(textureSample(tInput, s, vec2<f32>(0.0f, 0.0f)), 0.0f, 0.0f, 0.0f);
      v = x_23;
      let x_34 : f32 = textureSampleCompare(tInput, s, vec3<f32>(0.0f, 0.0f, 0.0f).xy, vec3<f32>(0.0f, 0.0f, 0.0f).z);
      f = x_34;
      return;
    }

    @fragment
    fn main() {
      main_1();
    }

But then this fails validation:

    error: no matching call to textureSample(texture_depth_2d, sampler_comparison, vec2<f32>)
    15 candidate functions: ...

## References and pointers

SPIR-V has a pointer type.

A SPIR-V pointer type corresponds to a WGSL memory view. WGSL has two
memory view types: a reference type, and a pointer type.

See [spirv-ptr-ref.md](spirv-ptr-ref.md) for details on the translation.

## Mapping buffer types

Vulkan SPIR-V expresses a Uniform Buffer Object (UBO), or
a WGSL 'uniform buffer' as:

- an OpVariable in Uniform storage class
- its pointee type (store type) is a Block-decorated structure type

Vulkan SPIR-V has two ways to express a Shader Storage Buffer Object (SSBO),
or a WGSL 'storage buffer' as either deprecated-style:

- an OpVariable in Uniform storage class
- its pointee type (store type) is a BufferBlock-decorated structure type

or as new-style:

- an OpVariable in StorageBuffer storage class
- its pointee type (store type) is a Block-decorated structure type

Deprecated-style storage buffer was the only option in un-extended
Vulkan 1.0. It is generated by tools that want to generate code for
the broadest reach.  This includes DXC.

New-style storage buffer requires the use of the `OpExtension
"SPV_KHR_storage_buffer_storage_class"` or SPIR-V 1.3 or later
(Vulkan 1.1 or later).

Additionally, a storage buffer in SPIR-V may be marked as NonWritable.
Perhaps surprisingly, this is typically done by marking *all* the
members of the top-level (Buffer)Block-decorated structure as NonWritable.
(This is the common translation because that's what Glslang does.)

Translation of uniform buffers is straightforward.

However, the SPIR-V Reader must support both the deprecated and the new
styles of storage buffers.

Additionally:
- a storage buffer with all NonWritable members is translated with `read`
  access mode. This becomes a part of its WGSL reference type (and hence
  corresponding pointer type).
- a storage buffer without all NonWritable members is translated with
  an explicit `read_write` access mode. This becomes a part of its
  WGSL reference type (and hence corresponding pointer type).

Note that read-only vs. read-write is a property of the pointee-type in SPIR-V,
but in WGSL it's part of the reference type (not the store type).

To handle this mismatch, the SPIR-V Reader has bookkeeping to map
each pointer value (inside a function) back to through to the originating
variable. This originating variable may be a buffer variable which then
tells us which address space and access mode to use for a locally-defined
pointer value.

Since baseline SPIR-V does not allow passing pointers to buffers into
user-defined helper functions, we don't need to handle this buffer type
remapping into function formal parameters.

## Mapping OpArrayLength

The OpArrayLength instruction takes a pointer to the enclosing
structure (the pointee type of the storage buffer variable).

But the WGSL arrayLength builtin variable takes a pointer to the
member inside that structure.

A small local adjustment is sufficient here.

---

### Becoming Committer (`docs/becoming-committer.md`)

# Becoming a committer

Committers are people that are considered to have write access to the Dawn repository. All CLs
submitted to the Dawn repository need to be reviewed by one or (for non-trivial ones) two
committers. In addition to this requirement, all CLs must also be reviewed or authored by an OWNER
of the changed files.

This privilege is granted with some expectation of responsibility: committers are people who care
about the Dawn project and want to help it meet its goals. A committer is not just someone who can
make changes, but someone who has demonstrated their ability to collaborate with the team, get the
most knowledgeable people to review code, contribute high-quality code, and follow through to fix
issues (in code or tests).

A committer is a contributor to the Dawn project’s success and a citizen helping the projects
succeed.

## How to become a committer?

_Note to Googlers: the process is a bit different for WebGPU team members, reach out to
 an @google.com top-level OWNER directly._

In a nutshell, contribute 20 non-trivial patches and get at least three different people to review
them (you'll need three people to support you). Then ask someone to nominate you. You're
demonstrating your:

*   commitment to the project (20 good patches requires a lot of your valuable time),
*   ability to collaborate with the team,
*   understanding of how the team works (policies, processes for testing and code review, etc),
*   understanding of the projects' code base and coding style, and
*   ability to write good code (last but certainly not least)

A current committer nominates you by sending email to one of the top-level OWNERs containing:

*   your first and last name
*   your email address in Gerrit
*   an explanation of why you should be a committer,
*   embedded list of links to revisions (about top 10) containing your patches

Top-level OWNERs will deliberate and come to a consensus, targeting to reach it in 5 working days.
Once you get approval, you are granted additional CR+2 review permissions. In some cases the
process could drag out slightly. Ping OWNERs and keep writing patches! Even in the rare cases where
a nomination fails, the objection is usually something easy to address like “more patches” or “not
enough people are familiar with this person’s work.”

Two-factor authentication with a security key is a requirement for adding your account as a
committer. If you haven‘t done so already, you’ll need to set up a security key on your account.
For more information about this requirement see [Gerrit
ReAuth](https://chromium.googlesource.com/chromium/src/+/main/docs/gerrit_reauth.md).

## Maintaining committer status

You don't really need to do much to maintain committer status: just keep being awesome and helping
the Dawn project!

In the unhappy event that a committer continues to disregard good citizenship (or actively disrupts
the project), we may need to revoke that person's status. The process is the same as for nominating
a new committer: someone suggests the revocation with a good reason and OWNERs deliberate. We hope
that's simple enough, and that we never have to test it in practice.

In addition, as a security measure, if you are inactive on Gerrit (no upload, no comment and no
review) for more than a year, we may revoke your committer privileges. An email notification is
sent about 7 days prior to the removal. This is not meant as a punishment, so if you wish to resume
contributing after that, contact top level OWNERs to ask that it be restored, and we will normally
do so.

(This document was inspired
by [https://v8.dev/docs/become-committer](https://v8.dev/docs/become-committer) itself inspired by
its Chromium equivalent)

---

### Building (`docs/building.md`)

# Building Dawn

Generally, Dawn's tooling and system dependencies are set by Chromium.
Therefore Chromium's build environment, provided by `GN` and `depot_tools`,
is Dawn's primary build environment. However, Dawn can be built as a
standalone C++ project, often with alternative build generators and C++
compilers, as described below.

If you are developing Dawn itself, see also
[development-tips.md](development-tips.md).

## System requirements

 * Git
 * `depot_tools` in your path
 * A build generator, one of:
    * `GN`, provided by `depot_tools`
    * `CMake` 3.16 or later
 * Ninja 1.12 (or another build tool supported by CMake)
 * Python 3, for fetching dependencies
 * Go 1.23 or later: https://go.dev/
 * A C++20 compiler
    * When using GN, the default compiler will be provided automatically.
    * Otherwise, check the platform details below.

- Linux
  - CPU:
    - x86-64 (also known as amd64)
    - arm64
    - x86: untested
    - mips, mips64: untested
  - The `pkg-config` command:
    ```sh
    # Install pkg-config on Ubuntu
    sudo apt-get install pkg-config
    ```
  - C++20 compiler:
    - Clang 19 or later. This the primary supported compiler.
    - GCC 12 or later

- Mac
  - CPU:
    - Apple Silicon
    - Intel x86-64
  - [Xcode](https://developer.apple.com/xcode/) 12.2+.
  - The macOS 13.0 SDK. Run `xcode-select` to check whether you have it.
    ```sh
    ls `xcode-select -p`/SDKs
    ```

- Windows
  - CPU:
    - x86-64 (also known as amd64)
    - arm64 support is experimental
  - Windows SDK 10.0.26100.x or a later version.
  - C++20 compiler:
    - Clang, as provided by GN. This is the primary supported compiler.
    - MSVC 19.41, or Visual Studio 2022 v17.11 or later.
      Note: Dawn is tested daily with MSVC 19.43 or VS 2022 v17.13


## Get the code and its dependencies

### Using `depot_tools`

Dawn uses the Chromium build system and dependency management so you need to [install depot_tools]
and add it to the PATH.

[install depot_tools]:
http://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html#_setting_up

```sh
# Clone the repo as "dawn"
git clone https://dawn.googlesource.com/dawn dawn && cd dawn

# Bootstrap the gclient configuration
cp scripts/standalone.gclient .gclient

# Fetch external dependencies and toolchains with gclient
gclient sync
```

The standalone build dependencies includes Microsoft Direct 3D WARP with the following terms:

```
The Microsoft Direct 3D WARP software is available as part of this download and is
licensed to you by Microsoft Corporation (or one of its affiliates). The Microsoft
Direct 3D WARP software is governed by an end user license agreement (“EULA”)
which can be found at this link or successor links as applicable:
https://www.nuget.org/packages/Microsoft.Direct3D.WARP/1.0.20-preview/License?.
By downloading the Microsoft Direct 3D WARP software you agree to the EULA terms.
```

### Without `depot_tools`

If you cannot or do not want to depend on `depot_tools`, you may use the
`tools/fetch_dawn_dependencies.py` to clone the dependencies' repositories:

```sh
# Clone the repo as "dawn"
git clone https://dawn.googlesource.com/dawn dawn && cd dawn

# Fetch dependencies (loosely equivalent to gclient sync)
python tools/fetch_dawn_dependencies.py
```

Use `python tools/fetch_dawn_dependencies.py -h` to know more about the available options. Contrary
to `depot_tools`, this scripts does not figure out option-dependent requirements automatically.

On Windows, configuring CMake with both `DAWN_FETCH_DEPENDENCIES=ON` and
`DAWN_USE_AGILITY_SDK=ON` downloads the Agility SDK version pinned in `DEPS`.

### Linux dependencies

The following packages are needed to build Dawn. (Package names are the Ubuntu names).

* `libfuse2`
* `libxrandr-dev`
* `libxinerama-dev`
* `libxcursor-dev`
( `libxkbcommon-dev`
* `libxi-dev`
* `mesa-common-dev`
* `libx11-xcb-dev`
* `pkg-config`
* `nodejs`
* `npm`

```sh
sudo apt-get install libfuse2 libxrandr-dev libxinerama-dev libxcursor-dev libxkbcommon-dev libxi-dev mesa-common-dev libx11-xcb-dev pkg-config nodejs npm
```

Note, `nodejs` and `npm` are only needed if building `dawn.node`.


## Build Dawn

### Compiling using CMake + Ninja

```sh
mkdir -p out/Debug
cd out/Debug
cmake -GNinja ../..
ninja # or autoninja
```

### Compiling using CMake + make

```sh
mkdir -p out/Debug
cd out/Debug
cmake ../..
make # -j N for N-way parallel build
```

### Compiling using gn + ninja

```sh
mkdir -p out/Debug
gn gen out/Debug
autoninja -C out/Debug
```

The most common GN build option is `is_debug=true/false`; otherwise
`gn args out/Debug --list` shows all the possible options.

On macOS you'll want to add the `use_system_xcode=true` in most cases.
(and if you're a googler please get XCode from go/xcode).

To generate a Microsoft Visual Studio solution, add `ide=vs2022` and
`ninja-executable=<dawn parent directory>\dawn\third_party\ninja\ninja.exe`.
The .sln file will be created in the output directory specified.

For CMake builds, you can enable installation with the `DAWN_ENABLE_INSTALL` option
and use `find_package(Dawn)` in your CMake project to discover Dawn and link with
the `dawn::webgpu_dawn` target. Please see [Quickstart with CMake](./quickstart-cmake.md)
for step-by-step instructions.

### Cross-Compilation

Cross-compilation is provided on a best-effort basis, primarily for the purpose
of compilation checks during local Dawn development. It only supports Clang.
It may not always work, and the resulting binaries are not guaranteed to work.

1.  Add your target OS to the `target_os` array in `.gclient`. Or, use
    `standalone-maximal.gclient` and follow the instructions in it.
    This will enable downloading toolchains and any target-specific Dawn DEPS.
    - Windows: ~16GB.
    - Mac: ~4GB.
    - Android: ~2.5GB.
1.  `gclient sync`.
1.  Create a new `out/*` directory for each target build with the relevant GN
    args. The following configurations have been tested:

    - `target_os = "mac"` `target_cpu = "arm64"`/`"x64"` on Linux hosts
    - `target_os = "win"` `target_cpu = "x64"` on Linux and Mac hosts
        - On Mac hosts, many Windows files can't build using Siso remote builds.
          Use `autoninja --offline` (or `autoninja -k=0 ; autoninja --offline`
          to first compile everything possible with Siso, then compile the rest
          locally). See <https://crbug.com/446124900>.
    - `target_os = "android` `target_cpu = "arm64"` on Linux hosts (note below)

For background, see also
[this guide](https://chromium.googlesource.com/chromium/src/+/main/docs/win_cross.md).

#### For Android targets (Linux hosts)

**Cross-compilation for Android as described above works to compile targets, but
the resulting binaries have not been tested.** If you want to run them you *may*
need to set up an Android Chromium build (which contains Dawn) and build Dawn
targets (like `dawn_unittests`, `dawn_end2end_tests`) in the Chromium build.

### Using ccache for CMake builds

There is a substantial number of source files that are needed to be
built for Dawn and its dependencies (~thousands), which can lead to
long compile times on resource bound machines (i.e. laptops),
especially when performing clean builds. The general advice for this
situation is use the gn build with a remote compile system like siso,
if available.

An alternative if remote compilation isn't available, or if using
CMake is essential for your workflow, is to use
[ccache](https://ccache.dev/) to avoid rebuilding targets. For full
details on using ccache see the project's documentation, but below is
an example of how to use it on a Debian based Linux dev env.

```sh
# One time Install and setup of ccache
apt-get install ccache
ccache -F 0 -M 0  # Sets the number of files and size of the cache to unlimited, the default is 5 GiB. Choose values that make sense for your situation.

# Setup CMake + Ninja building using cccache
mkdir -p out/cache
cd out/cache
cmake -GNinja -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache <Any other CMake flags> ../.
ninja # or autoninja

# Optional: In another terminal, monitor the status of the cache. (Will have a lot of misses on the first run)
watch ccache -s
```

Using ccache will not help with the initial build of Dawn, since the
targets still need to be built at least once locally, but subsequent
builds, even after a clean operation should be much faster. Some
operations like linking are not cache-able, so will need to be run on
every build, but the bulk of file compilations should be
cache-able. Updating the repo, editing source files, and changing build
flags will all cause misses, since entries in the cache are based on
flags + contents of the source file.

### Weird CMake build breaks on Linux

If you see errors like this:

```
error: satisfaction of constraint 'is_constructible_v<_Tp, _Up>' depends on itself
  && is_constructible_v<_Tp, _Up>
     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
```
or similar complaints about constraints depending on themselves, this
is a known [issue](https://github.com/llvm/llvm-project/issues/62096)
with older versions of Clang (17ish through 19ish) and newer versions
of GCC (15+).

Specifically Clang by default uses the standard library from GCC, and
GCC 15 introduces a construct that older Clang thinks is out of spec,
thought it is not.

Currently there is no provided hermetic Linux build for CMake to be
used to avoid this problem, so you either need to use a newer version
of Clang or use an older version of GCC for your entire system.

For some environments currently, Clang 19.x is the newest version
available, and GCC 15 is the default compiler, so it cannot be
down-rev'd.

In this case you will need to install a parallel GCC toolchain and
tell Clang to use that.

For Debian based systems this can be done as follows:

```sh
sudo apt-get install gcc-14 libgcc-14-dev
...
cmake -DCMAKE_CXX_FLAGS="--gcc-install-dir=/usr/lib/gcc/x86_64-linux-gnu/14" <other cmake flags> <path to repo root>
```

This assumes gcc-14 is installed to
`/usr/lib/gcc/x86_64-linux-gnu/14`, which may not be true depending on
your specific distro and architecture.

It is likely CMake will not correctly pick up this type of dev env
change if you run this in an existing build directory, so it is
recommended that you setup a new build directory to use this.

### Fuzzers on MacOS

As of Late Oct 2025, fuzzing on a dev Mac is not in a good state.

The old workaround for fuzzing with XCode 16.X should still work, but that is
not the Chromium supported version of XCode so there may be other issues. That
workaround does not work for XCode 26.X, which is supported by Chromium.

If you attempt to build and run the fuzzers without a workaround, you will
either get build failures like this:

```sh
ld: file not found:/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/11.0.0/lib/darwin/libclang_rt.fuzzer_osx.a
```

or if using a component build, you will get a similar runtime issue for a
missing `.dylib`.

The root cause for this is that the build of llvm shipped as part of the XCode
SDK does not include the standard support libraries for fuzzing.

The workaround for this depends on which version of XCode you are trying to use.

#### Using XCode 16.X

The workaround for 16.X has been to install a fully featured version llvm onto
your system, for example via Homebrew, `brew install llvm`, (you might have to
use `llvm@XX` where `XX` is the major version of llvm in the XCode SDK). And
then using environment variables, i.e. `CC=<path to Homebrew clang> cmake
-GNinja ../..`, to set up the build including the full llvm toolchain to get the
missing libraries onto the search path. This will actually create a hybrid
toolchain using some elements of the XCode SDK and the llvm SDK, because there
is some Apple specific framework stuff that isn't in mainstream llvm.

#### Using XCode 26.X

As mentioned above, the 16.X workaround appears to no longer work due to drift
between Apple's llvm and the mainline version. Trying to create a hybrid
toolchain will lead to compiling issues from the XCode standard headers using
deprecated functions according to mainstream llvm, even when using the same
major version.

For 26.X there is workaround done by installing `llvm@17` (since AppleClang is
version 17) from Homebrew, and then copying over the missing library files
(i.e. `libclang_rt.fuzzer_osx.a`) from the Homebrew directory to the
CommandLineTools toolchain directory. (Cannot be the XCode toolchain directory,
because that is write protected).

You will then need to be using the CLT toolchain as your active toolchain

```sh
sudo xcode-select -s /Library/Developer/CommandLineTools
```

This allows for building and debugging the fuzzers, but the impact of this on
other builds and/or system stability is unknown. It shouldn't have a significant
impact, since the standard SDK doesn't ship with these libraries, so nothing
else should be using them, but the Homebrew version of llvm will not have all of
Apple's patches, so may be incompatible in subtle ways if these libraries do get
used somehow.

#### Using hermetic builds

It should be possible to use the same hermetic toolchain that the bots
use for dev builds of the fuzzers, since the bots build and run the fuzzer fine.

Unfortunately the standard setup on a dev machine is 'mostly' hermetic. It does
use the hermetic version clang and other tools, but it also does use some of the
system framework stuff that comes from XCode, and that is where the build issues
come in from, when trying to build/run the fuzzers.

There is some support for getting a truly hermetic build on a dev machine,
<https://source.chromium.org/chromium/chromium/src/+/main:third_party/dawn/src/cmake/HermeticXcode/>,
but that has not been tested with building the fuzzers, and probably needs work
to be a drop-in solution here.

### Reproducing bot specific environments on Windows + CMake

When investigating build issues being seen by CI/CQ it is sometimes necessary
to replicate the exact environment from a builder/bot for local debugging.

As part of a Dawn checkout the toolchains used by the builders should already be
checked out in your repo. For Windows, using CMake, to use them instead of the
system installed toolchain, there is a script supplied to set up the
environment.

Instead of using the system batch file to set up the environment, i.e.

```sh
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat
```
use the appropriate script from `third_party\depot_tools\win_toolchain`, i.e.
```sh
 C:\src\dawn\third_party\depot_tools\win_toolchain\vs_files\68a20d6dee\Windows Kits\10\bin\SetEnv.cmd
```

---

### Clang Tidy (`docs/clang-tidy.md`)

# Running Clang-Tidy

* Add `"checkout_clang_tidy": True` to `.gclient` file in the `custom_vars`
  (or use `standalone-maximal.gclient` which includes this).

  ```py
  "custom_vars": {
    "checkout_clang_tidy": True,
  }
  ```
* `gclient sync`
* Set up a build directory for the configuration you want to run. It doesn't
  need to be built yet. This can be a native build or a cross-compiled build.
* Run `tools/run-tricium-clang-tidy.py --help` for instructions. Running the
  tool with `--default` takes about 12 minutes on a 10-core M1 Pro laptop and
  under 2.5 minutes on a 64-core (128-thread) cloud VM.
* This will output `out/*/clang-tidy-*-findings.json` with the full findings,
  and `out/*/clang-tidy-*-summary.txt` with a nicely-formatted list of results.
* Once you have findings, you can automatically apply fixes across any set of
  files for a specific check (if it
  [supports autofix](https://clang.llvm.org/extra/clang-tidy/checks/list.html)):
  Run `tools/apply-clang-tidy-fixes.py --help` for instructions.

Tips:

* It's recommended to [set up clangd](./development-tips.md) and open a file
  in your IDE to see the errors in context (and to get an autofix button).
  Note the `FastCheckFilter` option mentioned there.

  In VSCode you can `less` the summary file to your terminal and then
  cmd-click/ctrl-click the lines to take you to the exact source line.

For additional info on running Clang-Tidy locally, see
[these instructions](https://chromium.googlesource.com/chromium/src/+/main/docs/clang_tidy.md).

---

### Dangling Pointer Detector (`docs/dangling-pointer-detector.md`)

# Dangling Pointer Detector

A pointer is dangling when it references freed memory. Typical examples can be found
[here](https://docs.google.com/document/d/11YYsyPF9rQv_QFf982Khie3YuNPXV0NdhzJPojpZfco/edit?resourcekey=0-h1dr1uDzZGU7YWHth5TRAQ#heading=h.wxt96wl0k0sq).

Dangling pointers are not a problem unless they are subsequently dereferenced and/or used for other
purposes. Proving that dangling pointers are unused has turned out to be difficult in general,
especially in face of future modifications to the code. Hence, they are a source of UaF bugs and
highly discouraged unless you are able to ensure that they can never be used after the pointed-to
objects are freed.

Dawn tests are configured to detect dangling pointers.

## Level of support

The dangling pointer detector is an optional dependency of Dawn. It is enabled only when running
tests in the dawn_standalone configuration. It is also enforced as part of Chrome's tests.

It is currently enforced under this configuration:

| System      | Compiler | Build system | Directory   | Final embedder       | Misc                         |
| ----------- | -------- | ------------ | ----------- | -------------------- | ---------------------------- |
| ✅ Android  | ✅ Clang | ✅ GN        | ✅ src/dawn | ✅ Dawn (standalone) | ❌ windows + debug           |
| ✅ Windows  | ✅ GCC   | ❌ CMake     | ❌ src/tint | ❌ Skia              | ❌ windows + component_build |
| ✅ Mac      | ❌ MSVC  | ❌ Bazel     |             | ✅ Chrome            | ❌ sanitizers                |
| ✅ Linux    | ❌ _     | ❌ _         |             | ❌ _                 | ✅ _                         |
| ✅ ChromeOS |          |              |             |                      |                              |
| ✅ Fuchsia  |          |              |             |                      |                              |
| ❌ iOS      |          |              |             |                      |                              |
| ❌ _        |          |              |             |                      |                              |

## raw_ptr<>

A `raw_ptr<T>` is a non-owning pointer. When using this kind of pointer, the severity of UAFs can be
reduced, because they are protected by
[MiraclePtr/BackupRefPtr](https://security.googleblog.com/2022/09/use-after-freedom-miracleptr.html)

A `raw_ptr<T>` is transparently usable as a `T*`. It should be used in class and struct member
variable.

In general, it shouldn't be used elsewhere: local variable, function arguments, etc...

## raw_ptr flavors

`raw_ptr<T>` comes in 3 main flavors:
```cpp
raw_ptr<T> ptr_never_dangling;
raw_ptr<T, DisableDanglingPtrDetection> ptr_allowed_to_dangle;
raw_ptr<T, DanglingUntriaged> ptr_dangling_to_investigate;
```

The `DisableDanglingPtrDetection` option can be used to annotate “intentional-and-safe” dangling
pointers. It is meant to be used as a last resort, only if there is no better way to re-architecture
the code.

The `DanglingUntriaged` means the pointer was dangling at the time of the
initial rewrite. We should investigate why. No new occurrences should be added.

## Pointer arithmetic

The use of pointer arithmetic with `raw_ptr<T>` is discouraged and disabled by default. Usually a
container like `std::span<>` should be used instead of the `raw_ptr`.

The `AllowPtrArithmetic` option can be used to enable pointer arithmetic. For instance:
```
raw_ptr<T, AllowPtrArithmetic> artihmetic_ptr.
```

## Chrome's documentation

The dangling pointer detector is part of Chrome too. You can use the main
documentation:
- [Dangling pointers detector](https://chromium.googlesource.com/chromium/src/+/main/docs/dangling_ptr.md)
- [Fixing dangling pointers guide](https://chromium.googlesource.com/chromium/src/+/main/docs/dangling_ptr_guide.md)

## Dawn's specificities.

Contrary to chrome, Dawn is not yet configured to display useful StackTraces and TaskTraces. It displays the error:
```
DanglingPointerDetector: A pointer was dangling!
                         Documentation: https://source.chromium.org/chromium/chromium/src/+/main:third_party/dawn/docs/dangling-pointer-detector.md
```

To debug you can:
- **remotely**: Find a failing bot on the CQ running tests inside Chrome. You
  will find useful debugging informations.
- **locally**: Use a debugger to display the StackTrace. Dawn is configured to crash when the
  dangling raw_ptr is released. If needed, you can also understand where the memory was freed by
  replacing `SetDanglingRawPtrReleasedFn` by `SetDanglingRawPtrDetectedFn` in
  src/dawn/tests/PartitionAllocSupport.cpp`.

## Other resources
- [MiraclePtr in Dawn design doc](https://docs.google.com/document/d/1wz45t0alQthsIU9P7_rQcfQyqnrBMXzrOjSzdQo-V-A/edit#heading=h.vn4i6wy373x7)
- [MiraclePtr in chrome](https://chromium.googlesource.com/chromium/src/+/ddc017f9569973a731a574be4199d8400616f5a5/base/memory/raw_ptr.md)
- [Use-after-freedom: MiraclePtr](https://security.googleblog.com/2022/09/use-after-freedom-miracleptr.html)
- [MiraclePtr: protecting users from use-after-free vulnerabilities on more platforms](https://security.googleblog.com/2024/01/miracleptr-protecting-users-from-use.html)

---

### Development Tips (`docs/development-tips.md`)

# Tips for developing Dawn code

This doc is specifically for people working on Dawn itself (not just using it
as a dependency).

## Build/run as much stuff as possible locally, to find bugs before they hit CQ

Especially useful for any change or refactor that affects multiple backends.

- Use `scripts/standalone-maximal.gclient` instead of `standalone.gclient`.
- Set the `dawn_maximal = true` and `use_libfuzzer = true` GN args to default to
  building as many targets as possible given your host OS.
- Build all targets (target `all`, which is the default if none is specified).
- Develop on Linux or Mac, and set up build directories that cross-compile for
  other OSes (see [building.md](building.md)).
  - `dawn_maximal = true` is optional but still useful for cross-compile builds.
- Also note you can run `gn gen --check` to locally run the presubmit check
  that C++ includes match the build files.

## Set up your IDE to use clangd

Provides instant inline compilation errors in the editor. Significantly speeds
up the development/iteration process, compared with recompiling the project.

- Do the steps above, so that clangd will know how to build most files.
- Do a full build in each build directory (to create generated files, etc.).
- Set `"custom_vars": { "checkout_clangd": True }` in `.gclient`
  (or just use `standalone-maximal.gclient`).
- Point your IDE to the binary at
  `third_party/llvm-build/Release+Asserts/bin/clangd`.
  - In VSCode: `clangd.path`
- Configure clangd: Paste the relevant text below into `.clangd` and then update
  the `out/` paths in that file according to your needs.
  - Note the `PathMatch` paths aren't intended to be perfect, but please update
    this doc if you find platform-specific files that they miss.
  - If you have just one build directory, you can alternatively pass this as
    a clangd argument: `--compile-commands-dir=out/Debug`
    - In VSCode: `clangd.arguments`
  - For Chromium builds, take a look at
    [`chrome-remote-index`](https://github.com/clangd/chrome-remote-index/blob/main/docs/index.md).


Other documentation that may be useful:

- [Clangd docs on how to integrate clangd in various editors](https://clangd.llvm.org/installation.html#editor-plugins)
- [Chromium docs on using clangd](https://chromium.googlesource.com/chromium/src/+/main/docs/clangd.md)

### `.clangd`

For Mac/Linux hosts:

```yml
# This first section applies by default. Additional "fragments" below *also*
# apply, and may overwrite configs from less-specific fragments.
# Docs: https://clangd.llvm.org/config
Diagnostics:
  ClangTidy:
    # Enable all Clang-Tidy checks in clangd. If this is too slow, use "Loose".
    FastCheckFilter: None
CompileFlags:
  CompilationDatabase: out/Debug
  Add:
    - "-ferror-limit=0"
    - "-fsafe-buffer-usage-suggestions"
    # Additional personal configs can go here OR in your user-wide
    # `clangd/config.yml` (https://clangd.llvm.org/config#files). Suggestions:
    # - `--header-insertion=never` if you find yourself accidentally adding
    #   random headers when autocomplete goes sideways
    # - `--background-index=false` if you only want support in the current file
    #   (and includes), and don't want to index the whole project in the
    #   background (for symbol search, find references, etc.). Saves battery
    #   but otherwise isn't super necessary for Dawn since it's pretty small.
---
If:
  PathMatch:
    - "src/dawn/native/d3d.*"
    - "src/.*_[Ww]in.*"
    - "src/.*_[Dd]3[Dd].*"
CompileFlags:
  CompilationDatabase: out/Debug-win
```

For Linux hosts, also add this:

```yml
---
If:
  PathMatch:
    - "src/dawn/native/metal/.*"
    - "src/.*_[Mm]ac.*"
    - "src/.*_[Aa]pple.*"
    - "src/.*_[Mm]etal.*"
CompileFlags:
  CompilationDatabase: out/Debug-mac
---
If:
  PathMatch:
    - "src/.*[Aa]ndroid.*"
    - "src/.*AHardwareBuffer.*"
    - "src/.*AHB.*"
CompileFlags:
  CompilationDatabase: out/Debug-android
```

---

### Quickstart Cmake (`docs/quickstart-cmake.md`)

# Quickstart With CMake

This document is designed to allow you to build Dawn with CMake and link to it from other CMake projects. If you're planning to work on Dawn specifically, please use `gclient` and `depot_tools`. It ensures
that you always get the right dependencies and compilers.

## Prerequisites

- A compatible platform (e.g. Windows, macOS, Linux, etc.). Most platforms are fully supported.
- A compatible C++ compiler supporting at least C++20. Most major compilers are supported.
- Git for interacting with the Dawn source code repository.
- CMake for building your project and Dawn. Dawn supports CMake 3.16+.

## Getting the Dawn Code

```shell
# Change to the directory where you want to create the code repository
$ git clone https://dawn.googlesource.com/dawn
Cloning into 'dawn'...
```

Git will create the repository within a directory named `dawn`. Navigate into this directory and configure the project.

## Build and install Dawn with CMake
```shell
$ cd dawn
$ cmake -S . -B out/Release -DDAWN_FETCH_DEPENDENCIES=ON -DDAWN_ENABLE_INSTALL=ON -DCMAKE_BUILD_TYPE=Release
...
-- Configuring done (34.9s)
-- Generating done (0.8s)
-- Build files have been written to: ${PWD}/out/Release
```

- `DAWN_FETCH_DEPENDENCIES=ON` uses a Python script `fetch_dawn_dependencies.py` as a less tedious alternative to setting up depot_tools and fetching dependencies via `gclient`.
- `DAWN_ENABLE_INSTALL=ON` configures the project to install binaries, headers and cmake configuration files in a directory of your choice. This allows other projects to discover Dawn in CMake and link with it.

Now you can build Dawn:

```shell
$ cmake --build out/Release
...
[100%] Linking CXX static library libdawn_utils.a
[100%] Built target dawn_utils
```

Once you have built the Dawn library, install it with `cmake`

```shell
$ cmake --install out/Release --prefix install/Release
```

This installs Dawn from the `out/Release` build tree into `install/Release`.

## Linking your code to the Dawn installation

### Create test code

```shell
$ mkdir TestDawn
$ cd TestDawn
```

Now, create a `hello_webgpu.cpp` C++ file within the `TestDawn` directory.

```
/* Detailed source-code truncated for AI context efficiency. */
```

### Create CMakeLists.txt file

Now, create a `CMakeLists.txt` file within the `TestDawn` directory like the following:

```cmake
cmake_minimum_required(VERSION 3.13)

project(hello_webgpu)

set(CMAKE_CXX_STANDARD 20)

find_package(Dawn REQUIRED)
add_executable(hello_webgpu hello_webgpu.cpp)

# Declare dependency on the dawn::webgpu_dawn library
target_link_libraries(hello_webgpu dawn::webgpu_dawn)
```

For more information on how to create CMakeLists.txt files, consult the [CMake Tutorial](https://cmake.org/getting-started/).

### Build test project

Configure the CMake build from a fresh binary directory. This configuration is called an “out of source” build and is the preferred method for CMake projects.

```shell
$ export CMAKE_PREFIX_PATH=/path/to/dawn/install/Release
$ cmake -S . -B out/Release -DCMAKE_BUILD_TYPE=Release
...
-- Configuring done (0.2s)
-- Generating done (0.0s)
-- Build files have been written to: ${PWD}/out/Release
```

Now build our test application:

```shell
$ cmake --build out/Release
[ 50%] Building CXX object CMakeFiles/hello_webgpu.dir/hello_webgpu.cpp.o
[100%] Linking CXX executable hello_webgpu
[100%] Built target hello_webgpu
```

Now run your binary:

```shell
$ ./out/Release/hello_webgpu
VendorID: 8086
Vendor: intel
Architecture: gen-12lp
DeviceID: 9a60
Name: Intel(R) UHD Graphics (TGL GT1)
Driver description: Intel open-source Mesa driver: Mesa 23.2.1-1ubuntu3.1~22.04.2
```

Note: You may move the contents of `install/Release` to any other directory and `cmake` will still be able to discover dawn as long as `CMAKE_PREFIX_PATH` points to the new install tree.

---

### Rolling Cts (`docs/rolling_cts.md`)

# Rolling CTS

Notes and instructions for rolling CTS manually.

* [Roll history](https://dawn-review.googlesource.com/q/file:third_party/webgpu-cts+-%22%5BDO+NOT+SUBMIT%5D%22)

## Setup
There is a one-time setup needed to authenticate `gcloud` before the cts
roller can be used:

* `sudo apt install -y google-cloud-cli`
* `gcloud auth login`
* `gcloud auth application-default login`

### Verification
You can verify that you're authenticated by executing:

* `gcloud auth list`

which should list your authorizations


## Rolling CTS
The CTS roll can take a long time (sometimes >15 hrs) so it's
recommended to run it in `tmux`, `screen` or some other tool to allow
you to leave the session running in the background.

* `./tools/run cts roll`

---

### Standalone Cts Runner (`docs/standalone_cts_runner.md`)

# Running the WebGPU CTS with the standalone runner

`dawn_standalone_cts_runner` is a single executable that statically links V8 and
Dawn and runs the WebGPU CTS directly, without a Node.js installation.

The usual way to run the CTS is [`dawn.node`](../src/dawn/node/README.md), a
native addon loaded by Node.js. The standalone runner instead embeds its own V8
and supplies the subset of the Node API the CTS uses. That removes the external
Node.js dependency. This is important for platforms like Android where Node.js
is not available.

Both runners execute the same CTS JavaScript and the same Dawn bindings.

## Fetching dependencies

Follow [`docs/building.md`](building.md), using
`scripts/standalone-with-v8.gclient` in the `Get the code` step so `gclient`
fetches both the Node bindings (`dawn_node`) and V8 (`checkout_v8`):

```sh
# Clone the repo as "dawn"
git clone https://dawn.googlesource.com/dawn dawn && cd dawn

# Bootstrap the gclient configuration for the standalone CTS runner
cp scripts/standalone-with-v8.gclient .gclient

# Fetch external dependencies and toolchains with gclient
gclient sync
```

If you are configuring an existing checkout, ensure `.gclient` sets both
`"checkout_v8": True` and `"dawn_node": True` in `custom_vars` before running
`gclient sync`. Without `checkout_v8`, the `dawn_standalone_cts_runner` target
is not declared.

## Building the runner

Generate a release build directory and compile `dawn_standalone_cts_runner`
(building this target compiles only the standalone runner executable and its
dependencies, not the `dawn.node` shared library):

```sh
gn gen out/Release --args="is_debug=false dawn_use_swiftshader=false"
autoninja -C out/Release dawn_standalone_cts_runner
```

- `is_debug=false` is strongly recommended: the default debug build compiles a
  debug V8, which runs the CTS roughly 17x slower than a release build.
- `dawn_use_swiftshader=false` skips building Dawn's bundled SwiftShader
  renderer and ensures the runner uses the system's drivers. Remove this option
  if you want to test against SwiftShader.

## Building the CTS JavaScript

The runner executes the CTS's compiled `out-node/` tree, which is not checked
in. Any Node.js installation (`>= 16.0.0`) with `npm` and `npx` can build it. If
you do not have `node`, `npm`, and `npx` installed on your system, you can add
the prebuilt Node.js fetched by `gclient sync` under `third_party/node/` to your
`PATH` first:

```sh
# Optional: use the Node.js fetched by DEPS if npm/npx are not already installed.
# Replace linux-amd64 with linux-arm64, mac-amd64, or mac-arm64 as needed
# (or $PWD/third_party/node/windows-amd64 on Windows).
export PATH="$PWD/third_party/node/linux-amd64/bin:$PATH"
```

Install the CTS dependencies and build `out-node/`:

```sh
cd third_party/webgpu-cts
npm ci
npx grunt node
cd ../..
```

`npm ci` alone is not sufficient: `npx grunt node` runs the Grunt tasks that
generate `out-node/`.

## Running the CTS

Run from the Dawn root (or pass `--cts-dir <dir>` if the CTS lives outside
`third_party/webgpu-cts`):

```sh
out/Release/dawn_standalone_cts_runner -q 'webgpu:api,operation,queue,*'
```

Quote the query: CTS queries contain `;` and `"`, which most shells will
otherwise interpret. Pass `--help` to list all supported flags.

### Selecting a GPU, and setting Dawn toggles

`--dawn-flag` is passed through to the Dawn provider. It takes a single
`key=value` and may be repeated:

```sh
out/Release/dawn_standalone_cts_runner \
    --dawn-flag backend=vulkan \
    --dawn-flag enable-dawn-features=dump_shaders \
    -q 'webgpu:api,operation,queue,*'
```

The recognised keys are:

- `backend=<null|webgpu|d3d11|d3d12|d3d|metal|vulkan|vk|opengl|gl|opengles|gles>`
  (defaults to `d3d12` on Windows, `vulkan` on Linux/Android, and `metal` on
  macOS; can also be set via the `DAWNNODE_BACKEND` environment variable)
- `adapter=<name>` — substring match against the adapter device name. Pass an
  invalid name (for example `adapter=help`) to list all available adapters in
  the error output
- `validate=<1|true>` — enable full backend validation layers
- `verbose=1` — print the selected GPU adapter name
- `enable-dawn-features=<toggles>` — comma-separated list of
  [Dawn toggles](https://dawn.googlesource.com/dawn/+/refs/heads/main/src/dawn/native/Toggles.cpp)
- `disable-dawn-features=<toggles>` — comma-separated list of Dawn toggles

## Running on Android

There is no Node.js build for Android in the checkout, so the standalone runner
is the only way to run the command-line CTS directly on an Android device.
Cross-compiling for Android currently requires a Linux host (the GN build and
Android NDK toolchain in Dawn's checkout do not support building for Android
from Windows or macOS).

### Checkout

On a Linux host, add `target_os = ["android"]` to `.gclient` and sync the
Android NDK and toolchain dependencies:

```sh
printf '\ntarget_os = ["android"]\n' >> .gclient
gclient sync
```

### Build

Generate an `arm64` Android release build on Linux and compile the runner:

```sh
gn gen out/Android --args='target_os="android" target_cpu="arm64" is_debug=false'
autoninja -C out/Android dawn_standalone_cts_runner
```

### Staging onto the device

Only the executable and the compiled CTS JavaScript are needed on the device.

> [!NOTE]
> `dawn.node` is deliberately **not** pushed. The runner's module loader returns
> its statically-linked binding for `require('dawn.node')`, so the shared
> library is never loaded.

```sh
DEV=/data/local/tmp/cts
adb shell "mkdir -p $DEV/bin $DEV/webgpu-cts"

adb push out/Android/dawn_standalone_cts_runner $DEV/bin/
adb shell "chmod 755 $DEV/bin/dawn_standalone_cts_runner"

# out-node is ~2200 files; a single archive transfers far faster than letting
# adb sync each file individually. Include cmdline.ts as well, which both the
# runner and cmdline.js check as a repository-root marker.
tar -C third_party/webgpu-cts -czf /tmp/cts.tgz \
    out-node src/common/runtime/cmdline.ts
adb push /tmp/cts.tgz $DEV/webgpu-cts/
adb shell "cd $DEV/webgpu-cts && tar xzf cts.tgz && rm cts.tgz"
rm /tmp/cts.tgz
```

### Running

```sh
adb shell '/data/local/tmp/cts/bin/dawn_standalone_cts_runner \
    --cts-dir /data/local/tmp/cts/webgpu-cts \
    -q "webgpu:api,operation,queue,*"'
```

## Future plans

The setup above is more manual than it should be. The intent is to teach
[`run-cts`](../tools/src/cmd/run-cts) about the standalone runner, so that
selecting it becomes a flag on the tool already used to run the CTS, with the
CTS build, query sharding, expectations handling and result reporting all
handled as they are today for `dawn.node`.

Once that lands, most of this document reduces to a single `./tools/run run-cts`
invocation, and the manual staging steps for Android are expected to be driven
by the tool as well.

---

### Support (`docs/support.md`)

# Platform and API support for Dawn/Tint

## Khronos's Vulkan

Vulkan is supported with minimal features, limits and extensions being required (what's required has
been measured to be widely available). Vulkan is the preferred target API on platforms that don't
have other "modern" GPU APIs. Vulkan is supported as best effort on other platforms (e.g. Windows
and macOS).

**Required version**: Vulkan 1.1.

**Required features**: `depthBiasClamp`, `fragmentStoresAndAtomics`, `fullDrawIndexUint32`,
  `imageCubeArray`, `independentBlend`, `sampleRateShading`, and either `textureCompressionBC` or
  both of `textureCompressionETC` and `textureCompressionASTC_LDR`.

**Required limits**: they are too detailed to describe here, but in general should be widely
  supported. See the [WebGPU limits](https://gpuweb.github.io/gpuweb/#limits) that mostly
  correspond to Vulkan limits.

**Operating system support**:

 - Linux: Supported.
 - ChromeOS: Supported.
 - Android: Work in progress.
 - Fuchsia: Work in progress.

## Microsoft's D3D12

D3D12 is supported with feature level 11.1, or feature level 11.0 with Resource Binding Tier 2. This
is the vast majority of D3D12 devices. Supported shader models are 5.1 and above. It is the
preferred target API when available.

**Windows flavor support**:

 - Win32: Supported.
 - UWP: Supported, best effort.
 - Xbox: Not supported, contributions welcome.

## Apple's Metal

Metal 2.3 is supported and is the preferred target API when available.

**Apple OS support:**

 - macOS: supported on version 13.0+.
 - iOS: supported on version 14.0+, best effort.
 - tvOS/ipadOS/...: Not supported, contributions welcome.

## Khronos's OpenGL family

Support for OpenGL is in progress with the aim to make OpenGL ES 3.1 (with extensions and limits)
supported through EGL. Other flavors of OpenGL (desktop OpenGL) or binding APIs (GLX, WGL, EAGL,
CGL) are supported as best effort with contributions welcome.

## Microsoft's D3D11

A work-in-progress D3D11 backend is available but not considered ready for production uses yet.

---

### Dawn Asserts Vs Check (`docs/dawn/asserts-vs-check.md`)

# Dawn: Checking asserts [Bug 442860471](https://crbug.com/442860471)

petermcneeley@

Apr 15, 2026

*There has been an influx of security bugs for Dawn due to a capability phase shift in AI-assisted
 vulnerability discovery.*

In Dawn, often these are triggered by going **outside known assumptions** or they become real
security issues when further **assumptions are invalidated**.

## Fix: `DAWN_UNREACHABLE`

A good example of this is the assumption that all switches on enums are valid enums. Some minor
vulnerability can lead to full execution control via the undefined behavior of a switch table that
jumps to an invalid enum. Example <https://crbug.com/499159695>

To resolve this, all `DAWN_UNREACHABLE`s now check at runtime that unexpected fallthroughs (switch
and function) will do a full `CHECK` (as in immediately crash/ICE in release).

This induces ~ 2 instructions per switch and is deemed to be acceptable.

See CL: <https://dawn-review.git.corp.google.com/c/dawn/+/301675>

## Fix: `DAWN_ASSERT` to `DAWN_CHECK`

There are other cases where an assumption would have caught a security issue but does not because
the assumption in code is `DAWN_ASSERT`, not a check.

Example <https://crbug.com/500609033>

The conclusion is certain `DAWN_ASSERT`s will be promoted to release `DAWN_CHECK`s.

This is **more controversial** than the unreachable case as some of these checks can be expensive
and some can be in hot code paths (paths that are called many times per frame/submit).

For the initial phase, `DAWN_ASSERT`s that will be promoted are:

* Cheap 1-2 instruction checks (null/bounds/error)
* Avoid checks that would be found via null page
    * There is no point in checking a null pointer if the very next line it will trigger a null page
      segfault (which is not considered a Chrome vulnerability)
* Hot code paths are allowed for checking memory bounds

CL: <https://dawn-review.git.corp.google.com/c/dawn/+/302235>


## Fix: `DAWN_ASSERT` to `DAWN_RELEASE_ASSUME`

Previously, in release, `DAWN_ASSERT` was converted into `__builtin_assume`. This is likely a
surprise to most developers and can lead to dangerous outcomes.

This `assume` tells the compiler that a specific expression holds. The compiler then can do code
generation based on this assumption. If the assumption turns out to have been false, the result can
be undefined behavior (UB).

Here is an example of `DAWN_ASSERT` being converted to an `assume` and then removing mandatory
bounds checks. (This was very BAD) CL: <https://dawn-review.git.corp.google.com/c/dawn/+/260462>


In conclusion, we should move away from an assert implying an assume, as this can lead to unintended
consequences. The macro `DAWN_ASSERT` will simply assert in debug and be a no-op in release. For
the cases where the intention really was to inform the compiler about assumptions we should use
`DAWN_RELEASE_ASSUME`.

## Drawback(s)

The main drawback here is when do we get to remove these checks? **The answer is likely never!**

However, it is likely that within the next year we will be able to ask advanced AI security models
what would be the impact of removing specific checks. It might even be that every future change in
Dawn will get evaluated for security by an AI model.

---

### Dawn Buffer Mapping (`docs/dawn/buffer_mapping.md`)

- Buffer mapping dawn wire memory transfer interface design
    - https://docs.google.com/document/d/1JOhCpmJ_JyNZJtX6MVbSgxjtG1TdKORdeOGYtSfVYjk/edit?usp=sharing&resourcekey=0-1bFi47mR1jkBLdRFxcTVig
- TODO: make a md doc targeted at code walkthrough for contributors

---

### Dawn Codegen (`docs/dawn/codegen.md`)

# Dawn's code generators.

Dawn relies on a lot of code generation to produce boilerplate code, especially webgpu.h-related
code. They start by reading some JSON files (and sometimes XML too), process the data into an
in-memory representation that's then used by some [Jinja2](https://jinja.palletsprojects.com/)
templates to generate the code. This is similar to the model/view separation in Web development.

Generators are based on [generator_lib.py](../../generator/generator_lib.py) which provides
facilities for integrating in build systems and using Jinja2. Templates can be found
in [`generator/templates`](../../generator/templates) and the generated files are in
`out/<Debug/Release/foo>/gen/src` when building Dawn in standalone. Generated files can also be
found in [Chromium's code
search](https://source.chromium.org/chromium/chromium/src/+/main:out/Debug/gen/third_party/dawn/src/).

## Dawn "JSON API" generators

Most of the code generation is done from [`dawn.json`](../../src/dawn/dawn.json) which is a JSON
description of the WebGPU API with extra annotation used by some of the generators. The code for
all the "Dawn JSON" generators is
in [`dawn_json_generator.py`](../../generator/dawn_json_generator.py) (with templates in the
regular template dir).

At this time it is used to generate:

 - the Dawn, Emscripten, and upstream webgpu-native `webgpu.h` C header
 - the Dawn and Emscripten `webgpu_cpp.h` C++ wrapper over the C header
 - libraries that implements `webgpu.h` by calling in a static or `thread_local` proc table
 - the [Emscripten](https://emscripten.org/) WebGPU binding implementation
 - a GMock version of the API with its proc table for testing
 - validation helper functions for dawn_native
 - the definition of dawn_native's proc table
 - dawn_native's internal version of the webgpu.h types
 - utilities for working with dawn_native's chained structs
 - a lot of dawn_wire parts, see below

Internally `dawn.json` is a dictionary from the "canonical name" of things to their definition.
The "canonical name" is a space-separated (mostly) lower-case version of the name that's parsed
into a `Name` Python object. Then that name can be turned into various casings with `.CamelCase()`
`.SNAKE_CASE()`, etc. When `dawn.json` things reference each other, it is always via
these "canonical names".

The `"_metadata"` key in the JSON file is used by flexible templates for generating various Web
Standard API that contains following metadata:

 - `"api"` a string, the name of the Web API
 - `"namespace"` a string, the namespace of C++ wrapper
 - `"c_prefix"` (optional) a string, the prefix of C function and data type, it will default to
   upper-case of `"namespace"` if it's not provided.
 - `"proc_table_prefix"` a string, the prefix of proc table.
 - `"impl_dir"` a string, the directory of API implementation
 - `"native_namespace"` a string, the namespace of native implementation
 - `"copyright_year"` (optional) a string, templates will use the year of copyright.

The basic schema is that every entry is a thing with a `"category"` key what determines the
sub-schema to apply to that thing. Categories and their sub-shema are defined below. Several parts
of the schema use the concept of "record" which is a list of "record members" which are a
combination of a type, a name and other metadata. For example the list of arguments of a function
is a record. The list of structure members is a record. This combined concept is useful for the
dawn_wire generator to generate code for structure and function calls in a very similar way.

Most items and sub-items can include a list of `"tags"`, which, if specified, conditionally includes
the item if any of its tags appears in the `enabled_tags` configuration passed to `parse_json`.
This is used to include and exclude various items for Dawn, Emscripten, or upstream header
variants. Tags are applied in the "parse_json" step ([rather than
later](https://docs.google.com/document/d/1fBniVOxx3-hQbxHMugEPcQsaXaKBZYVO8yG9iXJp-fU/edit?usp=sharing)):
this has the benefit of automatically catching when, for a particular tag configuration, an
included item references an excluded item.

When used on enum values, `"tags"` may add an additional prefix to the enum value. This is to
implement [implementation-specific ranges of
enums](https://github.com/webgpu-native/webgpu-headers/issues/214). All compat enums are to start
at `0x0002_0000`, Emscripten enums are to start at `0x0004_0000`, and all Dawn enums are to start
at `0x0005_0000`. Use of `"compat"`, `"emscripten"`, or `"dawn"` tags will add these values,
respectively. So, an enum with value `"3"` but tag `"emscripten"` will actually use value
`0x0002_0003`. Multi-implementation native enums start at `0x0001_0000`, so this value is added if
the `"native"` tag is present and no other value-impacting tag is applied.

Items (such as objects, structures, functions, callbacks, and callback infos) and object methods can also include a `"stable_version"` field, specified via the `addins` mechanism in [`dawn_version.json`](../../src/dawn/dawn_version.json). This is a monotonically increasing integer version number corresponding to a date with the format `YYYYMMDDPP`, where `PP` is an additional two-digit identifier in case multiple patches land on the same day. Given a particular version, if the version is `>=` an API piece's `"stable_version"`, Dawn guarantees that the API exists and works to the best of our ability. For objects, the top-level `"stable_version"` indicates when the object itself was stably introduced, i.e. when its `AddRef` and `Release` methods (and usually `SetLabel`) were considered stable. Individual object methods (`"object::method"`) also have their own `"stable_version"` entry since they can be introduced at a later time.

A **record** is a list of **record members**, each of which is a dictionary with the following
schema:

 - `"name"` a string
 - `"type"` a string, the name of the base type for this member
 - `"annotation"` a string, default to "value". Define the C annotation to apply to the base type.
   Allowed annotations are `"value"` (the default), `"*"`, `"const*"`
 - `"length"` (default to 1 if not set), a string. Defines length of the array pointed to for
   pointer arguments. If not set the length is implicitly 1 (so not an array), but otherwise it can
   be set to the name of another member in the same record that will contain the length of the
   array (this is heavily used in the `fooCount` `foos` pattern in the API).
 - `"optional"` (default to false) a boolean that says whether this member is optional. Member
   records can be optional if they are pointers (otherwise dawn_wire will always try to dereference
   them), objects (otherwise dawn_wire will always try to encode their ID and crash), or if they
   have a `"default"` key. Optional pointers and objects will always default to `nullptr` (unless
   `"no_default"` is set to `true`).
 - `"default"` (optional) a number or string. If set the record member will use that value as
   default value. Depending on the member's category it can be a number, a string containing a
   number, or the name of an enum/bitmask value.
   - Dawn implements "trivial defaulting" for enums, similarly to the upstream WebGPU spec's WebIDL:
     if a zero-valued enum (usually called `Undefined`) is passed in, Dawn applies the default
     value specified here. See `WithTrivialFrontendDefaults()` in `api_structs_defaults.h` for how
     this works.
 - `"wire_is_data_only"` (default to false) a boolean that says whether it is safe to directly
   return a pointer of this member that is pointing to a piece of memory in the transfer buffer
   into dawn_wire. To prevent TOCTOU attacks, by default in dawn_wire we must ensure every single
   value returned to dawn_native a copy of what's in the wire, so `"wire_is_data_only"` is set to
   true only when the member is data-only and don't impact control flow.

**`"native"`** native types that can be referenced by name in other things.
 - `"wire transparent"` (defaults to true) a boolean that indicates whether the native type should
   be transparent (serialized as is) on the wire.

**`"typedef"`** (usually only used for gradual deprecations):
 - `"type"`: the name of the things this is a typedef for.

**`"enum"`** an `uint32_t`-based enum value.
 - `"values"` an array of enum values. Each value is a dictionary containing:
   - `"name"` a string
   - `"value"` a number that can be decimal or hexadecimal
   - `"jsrepr"` (optional) a string to allow overriding how this value map to Javascript for the
     Emscripten bits
   - `"valid"` (defaults to true) a boolean that controls whether the dawn_native validation
     utilities will consider this enum value valid.
 - `"emscripten_no_enum_table"` (optional) if true, skips generating an enum table in
   `library_webgpu_enum_tables.js`

**`"bitmask"`** an `uint32_t`-based bitmask. It is similar to **`"enum"`** but can be output
  differently.

**`"function pointer"`** defines a function pointer type that can be used by other things.
 - `"returns"` a string that's the name of the return type
 - `"args"` a **record**, so an array of **record members**

**`"structure"`**
 - `"members"` a **record**, so an array of **record members**
 - `"extensible"` (defaults to false) a boolean defining if this is an "extensible" WebGPU
   structure (i.e. has `nextInChain`). "descriptor" structures should usually have this set to
   true.
 - `"chained"` (defaults to None) a string defining if this is a structure that can be "chained" in
   a WebGPU structure (i.e. has `nextInChain` and `sType`) and in which direction ('in' for inputs
   to WebGPU, 'out' for outputs)
 - `"chain roots"` (defaults to []) a list of strings that are the canonical names of structures
   that can be extended by this structure.

**`"object"`**
 - `**methods**` an array of methods for this object. Note that "release" and "reference" don't need
   to be specified. Each method is a dictionary containing:
   - `"name"` a string
   - `"return_type"` (default to no return type) a string that's the name of the return type.
   - `"arguments"` a **record**, so an array of **record members**
   - `"no autolock"`: a boolean flag (default is false) indicates that the method's generated code
     won't automatically do thread synchronization. This flag can only be true on device or device
     child objects currently.
 - `"no autolock"`: a boolean flag (default is false) to indicate that the object's generated code
   won't automatically do thread synchronization. This will override individual method's `"no
   autolock"` flag. This flag can only be true on device or device child objects currently.

**`"constant"`**
 - `"type"`: a string, the name of the base data type
 - `"value"`: a string, the value is defined with preprocessor macro

**`"function"`** declares a function that not belongs to any class.
 - `"returns"` a string that's the name of the return type
 - `"args"` a **record**, so an array of **record members**

## Dawn "wire" generators

The generator for the pieces of dawn_wire need additional data which is found
in [`dawn_wire_json`](../../src/dawn/dawn_wire.json). Examples of pieces that are generated are:

 - `WireCmd.cpp/.h` the most important piece: the meat of the serialization / deserialization code
   for WebGPU structures and commands
 - `ServerHandlers/Doers.cpp` that does the complete handling of all regular WebGPU methods in the
   server
 - `ApiProcs.cpp` that implements the complete handling of all regular WebGPU methods in the client

Most of the WebGPU methods can be handled automatically by the wire client/server but some of them
need custom handling (for example because they handle callbacks or need client-side state
tracking). `dawn_wire.json` defines which methods need special handling, and extra wire commands
that can be used by that special handling (and will get `WireCmd` support).

The schema of `dawn_wire.json` is a dictionary with the following keys:
 - `"commands"` an array of **records** defining extra client->server commands that can be used in
   special-cased code path.
   - Each **record member** can have an extra `"skip_serialize"` key that's a boolean that default
     to false and makes `WireCmd` skip it on its on-wire format.
 - `"return commands"` like `"commands"` but in revers, an array of **records** defining extra
   server->client commands
 - `"special items"` a dictionary containing various lists of methods or object that require special
   handling in places in the dawn_wire autogenerated files
   - `"client_side_structures"`: a list of structure that we shouldn't generate
     serialization/deserialization code for because they are client-side only
   - `"client_handwritten_commands"`: a list of methods that are written manually and won't be
     automatically generated in the client
   - `"client_side_commands"`: a list of methods that won't be automatically generated in the
     server. Gets added to `"client_handwritten_commands"`
   - `"client_special_objects"`: a list of objects that need special manual state-tracking in the
     client and won't be autogenerated
   - `"server_custom_pre_handler_commands"`: a list of methods that will run custom "pre-handlers"
     before calling the autogenerated handlers in the server
   - `"server_handwrittten_commands"`: a list of methods that are written manually and won't be
     automatically generated in the server.
   - `server_reverse_object_lookup_objects`: a list of objects for which the server will maintain an
     object -> ID mapping.

## Dawn "native" generators

The generator for the pieces of dawn_native need additional data which is found
in [`dawn_native_json`](../../src/dawn/dawn_native.json). Examples of pieces that are generated
are:

 - `ProcTable.cpp` that implements all of the WebGPU function by converting arguments and forwarding
   to the correct method/function in `dawn::native`.
 - `dawn_platform_autogen.h` and `wgpu_structs_autogen.cpp` that define the `dawn::native`
   equivalents of types in `webgpu.h` (for example with objects being actual pointers to objects
   and not just opaque handles) as well as conversion functions.
 - `api_absl_format.cpp` to make it easy to print any WebGPU type in error messages.

 The schema of `dawn_native.json` is a dictionary with the following keys:
  - `"metadata"` a dictionary containing various other containers that can be used in templates. Its
    keys are:
    - `addins`: A dictionary mapping fully qualified instance names (structs, struct members,
      functions, function arguments, objects, object methods, or method arguments) to a dictionary
      of attribute addins (e.g. `{"spanify": false}` or `{"index_type": "BindingIndex"}`).

## Dawn "Kotlin" generators

The generator for the Android Kotlin/JNI bindings needs additional data which is found
in [`dawn_kotlin.json`](../../src/dawn/dawn_kotlin.json). It generates the Kotlin classes (one per
object, structure, enum and callback) and the JNI glue that converts between Kotlin objects and
`webgpu.h` structures.

The schema of `dawn_kotlin.json` is a dictionary with the following keys:
 - `"kotlin_package"` a string, the package for the generated Kotlin classes.
 - `"jni_primitives"` / `"jni_signatures"` dictionaries mapping native type names to their JNI C
   type and JNI signature letter.
 - `"kdocs_blocklist"` / `"kdocs_replacements"` filters applied to the upstream documentation when
   generating KDoc.
 - `"metadata"` a dictionary with the same shape as in `dawn_native.json`:
   - `addins`: maps fully qualified instance names to attribute addins. Supported addins are:
     - `"omitted": true` on a structure, enum, function pointer, function, object method,
       or **structure member** to exclude it from the Kotlin API. It is not supported on
       function/method arguments (the generator asserts) since the native call would silently
       receive a zero-initialized value.
     - `"kotlin_name": "<name>"` on a function or object method to override its Kotlin name (e.g.
       `"create instance": {"kotlin_name": "createGPUInstance"}`).
     - `"additional_members": [...]` on a structure to add Kotlin-only members that have no native
       counterpart. Each entry has a `"name"`, a `"type"` (either a `dawn.json` type name or a
       Kotlin type when `"category": "kotlin type"`), and optional `"category"`, `"annotation"`,
       `"optional"` and `"default_value"` keys.
     - `"graduated_batches": [[...], ...]` on a structure, a list of member-name batches that were
       added after the structure first shipped, used to emit hidden legacy constructors for binary
       compatibility.

## OpenGL loader generator

The code to load OpenGL entrypoints from a `GetProcAddress` function is generated
from [`gl.xml`](../../third_party/OpenGL-Registry/src/xml/gl.xml) and the [list of
extensions](../../src/dawn/native/opengl/supported_extensions.json) it supports.

---

### Dawn Debug Markers (`docs/dawn/debug_markers.md`)

# Debug Markers

Dawn provides debug tooling integration for each backend.

Debugging markers are exposed through this API: ``` partial GPUProgrammablePassEncoder { void
pushDebugGroup(const char * markerLabel); void popDebugGroup(); void insertDebugMarker(const char *
markerLabel); }; ```

These APIs will result in silent no-ops if they are used without setting up the execution
environment properly. Each backend has a specific process for setting up this environment.

## D3D12

Debug markers on D3D12 are implemented with the [PIX Event
Runtime](https://blogs.msdn.microsoft.com/pix/winpixeventruntime/).

To enable marker functionality, you must: 1. Click the download link on
https://www.nuget.org/packages/WinPixEventRuntime 2. Rename the .nupkg file to a .zip extension,
then extract its contents. 3. Copy `bin\WinPixEventRuntime.dll` into the same directory as
`libdawn_native.dll`. 4. Launch your application.

You may now call the debug marker APIs mentioned above and see them from your GPU debugging tool.
When using your tool, it is supported to both launch your application with the debugger attached,
or attach the debugger while your application is running.

D3D12 debug markers have been tested with [Microsoft PIX](https://devblogs.microsoft.com/pix/)
and [Intel Graphics Frame Analyzer](https://software.intel.com/en-us/gpa/graphics-frame-analyzer).

Unfortunately, PIX's UI does does not lend itself to capturing single frame applications like tests.
You must enable capture from within your application. To do this in Dawn tests, pass
the --begin-capture-on-startup flag to dawn_end2end_tests.exe.

## Vulkan

Debug markers on Vulkan are implemented
with [VK_EXT_debug_utils](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_EXT_debug_utils.html).

To enable marker functionality, you must launch your application from your debugging tool. Attaching
to an already running application is not supported.

Vulkan markers have been tested with [RenderDoc](https://renderdoc.org/).

## Metal

Debug markers on Metal are used with the XCode debugger.

To enable marker functionality, you must launch your application from XCode and use [GPU Frame
Capture](https://developer.apple.com/documentation/metal/tools_profiling_and_debugging/metal_gpu_capture).

## OpenGL

Debug markers on OpenGL are not implemented and will result in a silent no-op. This is due to low
adoption of the GL_EXT_debug_marker extension in Linux device drivers.

---

### Dawn Debugging (`docs/dawn/debugging.md`)

# Debugging Dawn

## Toggles There are various debug-related Toggles that can help diagnose issues. Useful debug
   toggles:

 - `dump_shaders`: Log input WGSL shaders and translated backend shaders (MSL/ HLSL/DXBC/DXIL /
   SPIR-V).
 - `disable_symbol_renaming`: As much as possible, disable renaming of symbols (variables, function
   names, etc.). This can make dumped shaders more readable.
 - `emit_hlsl_debug_symbols`: Sets the D3DCOMPILE_SKIP_OPTIMIZATION and D3DCOMPILE_DEBUG compilation
   flags when compiling HLSL code.
 - `use_user_defined_labels_in_backend`: Forward object labels to the backend so that they can be
   seen in native debugging tools like RenderDoc, PIX, or Mac Instruments.

Toggles may be enabled/disabled in different ways.

- **In code:**

  Use extension struct `DawnTogglesDescriptor` chained on `DeviceDescriptor`.

  For example:
  ```c++
  const char* const enabledToggles[] = {"dump_shaders", "disable_symbol_renaming"};

  wgpu::DawnTogglesDescriptor deviceTogglesDesc;
  deviceTogglesDesc.enabledToggles = enabledToggles;
  deviceTogglesDesc.enabledTogglesCount = 2;

  wgpu::DeviceDescriptor deviceDescriptor;
  deviceDescriptor.nextInChain = &deviceTogglesDesc;
  ```

- **Command-line for Chrome**

  Run Chrome with command line flags`--enable-dawn-features` and/or `--disable-dawn-features` to
  force enable/disable toggles. Toggles should be comma-delimited.

  For example:
  `--enable-dawn-features=dump_shaders,disable_symbol_renaming`

- **Command-line for dawn_end2end_tests/dawn_unittests**

  Run Dawn test binaries with command line flags`--enable-toggles` and/or `--disable-toggles` to
  force enable/disable toggles. Toggles should be comma-delimited.

  For example:
  `dawn_end2end_tests --enable-toggles=dump_shaders,disable_symbol_renaming`

## Environment Variables

 - `DAWN_DEBUG_BREAK_ON_ERROR`

    Errors in WebGPU are reported asynchronously which may make debugging difficult because at the
    time an error is reported, you can't easily create a breakpoint to inspect the callstack in
    your application.

    Setting `DAWN_DEBUG_BREAK_ON_ERROR` to a non-empty, non-zero value will immediately causes the
    program to terminate as fast as possible, at an address that's inside the function that used
    the macro, and with a signal that's different from a segfault.
    See ([`DAWN_DETAIL_TRAP`](https://source.chromium.org/chromium/chromium/src/+/main:third_party/dawn/src/utils/force_crash.h?q=DAWN_DETAIL_TRAP))

## Tracing Native GPU API usage

Setting the environment variable `DAWN_TRACE_FILE_BASE` to some filename tells dawn to save a GPU
trace. Setting `DAWN_TRACE_DEVICE_FILTER` to some string lets you filter which devices are traced
based on the device's label. If `DAWN_TRACE_DEVICE_FILTER` is set then, if the label contains that
substring the device will be traced. Otherwise all devices are traced.

Tracing happens from device creation until device destruction.

Note that tracing is not currently implemented on all backends.

* DirectX11 - not yet implemented
* DirectX12 - not yet implemented
* OpenGL - not yet implemented
* Metal - implemented.

  Saves a .gputrace file that can be loaded into XCode's Metal Debugger.

  `MTL_CAPTURE_ENABLED=1` must also be set. Note that (anecdotally) this appears
  to disable (some?) validation layer outputs.

* Vulkan - not yet implemented

## Capturing with RenderDoc in Chrome

Currently only supported on Windows using the D3D12 backend.

Launch Chrome with the following flags:

```bash
set RENDERDOC_HOOK_EGL=0 && path\to\chrome.exe --no-sandbox --disable-gpu-sandbox --disable-direct-composition --gpu-startup-dialog --enable-dawn-features=enable_renderdoc_process_injection <path\to\chrome.exe>
```

This will start Chrome with a dialog that says "Gpu starting with pid: <pid>".

In RenderDoc, in Tools -> Settings, check "Enable process injection" and restart RenderDoc.

Go to File -> Inject into Process, search for "gpu" in the list of processes and double-click the
chrome.exe one with the title "Google Chrome GPU". This is the GPU process.

Go back to the Chrome GPU dialog and click OK to close it. The GPU process will resume.

Now open a WebGPU application in a tab, and as soon as it starts rendering, each frame will be
automatically captured and sent to RenderDoc. To stop the captures, switch tabs, close the tab, or
exit Chrome.

Note that you can use the "Refresh" button in RenderDoc's "Inject into Process" tab the next time
Chrome is launched to select the GPU process again.

---

### Dawn Device Facilities (`docs/dawn/device_facilities.md`)

# Devices

In Dawn the `Device` is a "god object" that contains a lot of facilities useful for the whole object
graph that descends from it. There a number of facilities common to all backends that live in the
frontend and backend-specific facilities. Example of frontend facilities are the management of
content-less object caches, or the toggle management. Example of backend facilities are GPU memory
allocators or the backing API function pointer table.

## Frontend facilities

### Error Handling

Dawn (dawn_native) uses the [Error.h](../../src/dawn/native/Error.h) error handling to robustly
handle errors. With `DAWN_TRY` errors bubble up all the way to, and are "consumed" by the
entry-point that was called by the application. Error consumption uses `Device::ConsumeError` that
expose them via the WebGPU "error scopes" and can also influence the device lifecycle by notifying
of a device loss, or triggering a device loss..

See [Error.h](../../src/dawn/native/Error.h) for more information about using errors.

### Device Lifecycle

The device lifecycle is a bit more complicated than other objects in Dawn for multiple reasons:

 - The device initialization creates facilities in both the backend and the frontend, which can
   fail. When a device fails to initialize, it should still be possible to destroy it without
   crashing.
 - Execution of commands on the GPU must be finished before the device can be destroyed (because
   there's no-one to "DeleteWhenUnused" the device).
 - On creation a device might want to run some GPU commands (like initializing zero-buffers), which
   must be completed before it is destroyed.
 - A device can become "disconnected" when a TDR or hot-unplug happens. In this case, destruction of
   the device doesn't need to wait on GPU commands to finish because they just disappeared.

There is a state machine `State` defined in [Device.h](../../src/dawn/native/Device.h) that controls
all of the above. The most common state is `Alive` when there are potentially GPU commands
executing.

Initialization of a device looks like the following:

 - `DeviceBase::DeviceBase` is called and does mostly nothing except setting `State` to
   `BeingCreated` (and initial toggles).
 - `backend::Device::Initialize` creates things like the underlying device and other stuff that
   doesn't run GPU commands.
 - It then calls `DeviceBase::Initialize` that enables the `DeviceBase` facilities and sets the
   `State` to `Alive`.
 - Optionally, `backend::Device::Initialize` can now enqueue GPU commands for its initialization.
 - The device is ready to be used by the application!

While it is `Alive` the device can notify it has been disconnected by the backend, in which case it
jumps directly to the `Disconnected` state. Internal errors, or a call to `LoseForTesting` can also
disconnect the device, but in the underlying API commands are still running, so the frontend will
finish all commands (with `WaitForIdleForDesctruction`) and prevent any new commands to be
enqueued (by setting state to `BeingDisconnected`). After this the device is set in the
`Disconnected` state. If an `Alive` device is destroyed, then a similar flow to `LoseForTesting
happens`.

All this ensures that during destruction or forceful disconnect of the device, it properly gets to
the `Disconnected` state with no commands executing on the GPU. After disconnecting, frontend will
call `backend::Device::DestroyImpl` so that it can properly free driver objects.

### Toggles

Toggles are booleans that control code paths inside of Dawn, like lazy-clearing resources or using
D3D12 render passes. They aren't just booleans close to the code path they control, because
embedders of Dawn like Chromium want to be able to surface what toggles are used by a device (like
in about:gpu).

Toggles are to be used for any optional code path in Dawn, including:

 - Workarounds for driver bugs.
 - Disabling select parts of the validation or robustness.
 - Enabling limitations that help with testing.
 - Using more advanced or optional backend API features.

Toggles can be queried using `DeviceBase::IsToggleEnabled`:

```
bool useRenderPass = device->IsToggleEnabled(Toggle::UseD3D12RenderPass);
```

Toggles are defined in a table in [Toggles.cpp](../../src/dawn/native/Toggles.cpp) that also
includes their name and description. The name can be used to require enabling of a toggle or, at
the contrary, require the disabling of a toggle. This is particularly useful in tests so that the
two sides of a code path can be tested (for example using D3D12 render passes and not).

Here's an example of a test that is run in the D3D12 backend both with the D3D12 render passes
required to be disabled, and in the default configuration.

```
DAWN_INSTANTIATE_TEST(RenderPassTest, D3D12Backend(),
D3D12Backend({}, {"use_d3d12_render_pass"}));
// The {} is the list of required enabled toggles, {"..."} the required disabled ones.
```

The toggles state of a device is decided by the adapter when creating it. The steps of device
toggles state decision looks as follows:

 - The device toggles state initialized to required device toggles from the DawnTogglesDescriptor
   chained in device descriptor.
 - The frontend (i.e. not backend-specific) default toggles are set (unless already required) using
   `TogglesState::Default`.
 - Any backend device toggle that not supported is forced set to a proper state in
   `Adapter::SetupBackendDeviceToggles` using `TogglesState::ForceSet`.
 - The backend device default toggles are applied (unless already set) in
   `Adapter::SetupBackendDeviceToggles` using `TogglesState::Default`.

Forcing toggles should only be done when there is no "safe" option for the toggle. This is to avoid
crashes during testing when the tests try to use both sides of a toggle. For toggles that are safe
to enable, like workarounds, the tests can run against the base configuration and with the toggle
enabled. For toggles that are safe to disable, like using more advanced backing API features, the
tests can run against the base configuration and with the toggle disabled.

### Immutable object caches

A number of WebGPU objects are immutable once created, and can be expensive to create, like
pipelines. `DeviceBase` contains caches for these objects so that they are free to create the
second time. This is also useful to be able to compare objects by pointers like `BindGroupLayouts`
since two BGLs would be equal iff they are the same object.

### Format Tables

The frontend has a `Format` structure that represent all the information that are known about a
particular WebGPU format for this Device based on the enabled features. Formats are precomputed at
device initialization and can be queried from a WebGPU format either assuming the format is a valid
enum, or in a safe manner that doesn't do this assumption. A reference to these formats can be
stored persistently as they have the same lifetime as the `Device`.

Formats also have an "index" so that backends can create parallel tables for internal information
about formats, like what they translate to in the backing API.

### Object factory

Like WebGPU's device object, `DeviceBase` is an factory with methods to create all kinds of other
WebGPU objects. WebGPU has some objects that aren't created from the device, like the texture view,
but in Dawn these creations also go through `DeviceBase` so that there is a single factory for each
backend.

---

### Dawn Errors (`docs/dawn/errors.md`)

# Dawn Errors

Dawn produces errors for several reasons. The most common is validation errors, indicating that a
given descriptor, configuration, state, or action is not valid according to the WebGPU spec. Errors
can also be produced during exceptional circumstances such as the system running out of GPU memory
or the device being lost.

The messages attached to these errors will frequently be one of the primary tools developers use to
debug problems their applications, so it is important that the messages Dawn returns are useful.

Following the guidelines in document will help ensure that Dawn's errors are clear, informative, and
consistent.

## Returning Errors

Since errors are expected to be an exceptional case, it's important that code that produces an error
doesn't adversely impact the performance of the error-free path. The best way to ensure that is to
make sure that all errors are returned from within an `if` statement that uses the
`DAWN_UNLIKELY()` macro to indicate that the expression is not expected to evaluate to true. For
example:

```C++
if (DAWN_UNLIKELY(offset > buffer.size)) {
  return DAWN_VALIDATION_ERROR("Offset (%u) is larger than the size (%u) of %s."
    offset, buffer.size, buffer);
}
```

To simplify producing validation errors, it's strongly suggested that the `DAWN_INVALID_IF()` macro
is used, which will wrap the expression in the `DAWN_UNLIKELY()` macro for you:

```C++
// This is equivalent to the previous example.
DAWN_INVALID_IF(offset > buffer.size, "Offset (%u) is larger than the size (%u) of %s."
    offset, buffer.size, buffer);
```

// TODO: Cover `MaybeError`, `ResultOrError<T>`, `DAWN_TRY(_ASSIGN)`, `DAWN_TRY_CONTEXT`, etc...

## Error message formatting

Errors returned from `DAWN_INVALID_IF()` or `DAWN_VALIDATION_ERROR()` should follow these guidelines:

**Write error messages as complete sentences. (First word capitalized, ends with a period, etc.)**
 * Example: `Command encoding has already finished.`
 * Instead of: `encoder finished`

**Error messages should be in the present tense.**
 * Example: `Buffer is not large enough...`
 * Instead of: `Buffer was not large enough...`

**When possible any values mentioned should be immediately followed in parentheses by the given value.**
 * Example: `("Array stride (%u) is not...", stride)`
 * Output: `Array stride (16) is not...`

**When possible any object or descriptors should be represented by the object formatted as a string.**
 * Example: `("The %s size (%s) is...", buffer, buffer.size)`
 * Output: `The [Buffer] size (512) is...` or `The [Buffer "Label"] size (512) is...`

**Enum and bitmask values should be formatted as strings rather than integers or hex values.**
 * Example: `("The %s format (%s) is...", texture, texture.format)`
 * Output: `The [Texture "Label"] format (TextureFormat::RGBA8Unorm) is...`

**When possible state both the given value and the expected value or limit.**
 * Example: `("Offset (%u) is larger than the size (%u) of %s.", offset, buffer.size, buffer)`
 * Output: `Offset (256) is larger than the size (144) of [Buffer "Label"].`

**State errors in terms of what failed, rather than how to satisfy the rule.**
 * Example: `Binding size (3) is less than the minimum binding size (32).`
 * Instead of: `Binding size (3) must not be less than the minimum binding size (32).`

**Don't repeat information given in context.**
 * See next section for details

## Error Context

When calling functions that perform validation consider if calling `DAWN_TRY_CONTEXT()` rather than
`DAWN_TRY()` is appropriate. Context messages, when provided, will be appended to any validation
errors as a type of human readable "callstack". An error with context messages appears will be
formatted as:

```
<Primary error message.>
 - While <context message lvl 2>
 - While <context message lvl 1>
 - While <context message lvl 0>
```

For example, if a validation error occurs while validating the creation of a BindGroup, the message
may be:

```
Binding size (256) is larger than the size (80) of [Buffer "View Matrix"].
 - While validating entries[1] as a Buffer
 - While validating [BindGroupDescriptor "Frame Bind Group"] against [BindGroupLayout]
 - While calling CreateBindGroup
```

// TODO: Guidelines about when to include context

## Context message formatting

Context messages should follow these guidelines:

**Begin with the action being taken, starting with a lower case. `- While ` will be appended by Dawn.**
 * Example: `("validating primitive state")`
 * Output: `- While validating primitive state`

**When looping through arrays, indicate the array name and index.**
 * Example: `("validating buffers[%u]", i)`
 * Output: `- While validating buffers[2]`

**Indicate which descriptors or objects are being examined in as high-level a context as possible.**
 * Example: `("validating % against %", descriptor, descriptor->layout)`
 * Output: `- While validating [BindGroupDescriptor "Label"] against [BindGroupLayout]`

**When possible, indicate the function call being made as the top-level context, as well as the parameters passed.**
 * Example: `("calling %s.CreatePipelineLayout(%s).", this, descriptor)`
 * Output: `- While calling [Device].CreatePipelineLayout([PipelineLayoutDescriptor]).`

# Internal Error Classes
There are multiple levels of validation and error handling which happen in Dawn. There is WebGPU API
validation which directly validates the user input. There is backend validation to make sure the
provided user input can execute on a given backend, there is backend error handling (e.g. when a
request to the Vulkan API fails due to Out of Memory).

Within all of this, Dawn may need to execute backend functionality in service of a user API call.
For example, when uploading a texture, Dawn internally may need to allocate a transfer buffer, copy
the user data into that buffer and then upload. (This is what is referred to as reentrant in Dawn).
Any validation error in this reentrant behaviour *must* be upgraded to a DeviceLost because the
system internally is in an invalid state.

## Error Types

Internally Dawn currently supports 6 types of error, they are all listed in `src/dawn/native/Error.h`
inside the `InternalErrorType` enumeration.

1. **None** -- no error generated
2. **Validation** -- An API validation error has occurred
3. **BackendDeviceLost** -- A call in the backend has caused the device to be lost. The device is
                            now gone, there is no cleanup to do and the device must not be
                            used anymore.
4. **Unrecoverable** -- There was an error that will cause a device lost. Internal resources which
                        use the device will be cleaned up before we intentionally lose the device.
5. **PipelineUncategorized** -- An error occurred in the backend (this could be the shader compiler
                                unable to complete due to lack of registers) that is not device
                                fatal, but the users request could not be handled. This does not
                                need to cause a device loss.
6. **OutOfMemory** -- There was an out-of-memory caused by the request. This should cause a device
                      lost as we are in an inconsistent state, modulo a few specific spec permitted
                      places (e.g. Createbuffer/Texture/QueueSet/ResourceTable).

## Classes

### ErrorData, UnrecoverableError, ValidationError, UnknownError

The content of an error is stored in a primary `ErrorData` class. That class is then wrapped by the
error classes, `UnknownError`, `UnrecoverableError`, `ValidationError`.

The `ErrorData` class stores all the actual error information, the type, the message, any other
content. Each of the `Error` classes stores a `unique_ptr` to an `ErrorData`.

In general, code preferably produces a `Validation` or `Unrecoverable` error. In the case where we
have both validation and unrecoverable errors being produced, the `Unknown` error is used.
`UnknownError` stores both **Validation** and other types of error types. In general using
`UnknownError` should be used as a last resort. The code *should* be structured in such a way that
validation is followed by non-validation errors. There are cases, like in the backends where we
have a "validation sandwich" where we must validate, perform backend operation, validate, perform
operation, and these require `UnknownError` usage.

The `UnknownError` provides a way to retrieve the error data as a `Validation` or `Unrecoverable`
error but the `InternalErrorType` must match. It *must* be **Validation** to retrieve a `Validation`
error and it must be something other than **Validation** for an `Unrecoverable` error.

An `UnknownError` can be initialized with either a `Validation` or `Unrecoverable` error.

An `UnrecoverableError` can be initialized with an `Unknown` error, but if that error was
**Validation** it will become **Unrecoverable**.

### MaybeValError, MaybeError, MaybeUnknownError && ResultOrValError, ResultOrError, ResultOrUnknownError

The `Maybe` classes are `Result<void, E>` types, the `ResultOr` classes are `Result<T, E>` types
which wrap the matching error type related to the name. They allow returning errors from methods and
work with the various macros to pass errors up the system.

There are certain conversion restrictions between the result types.

* A `Validation` error and a `UnrecoverableError` can be placed in a `MaybeUnknown` or
  `ResultOrUnknown` error.
* A `Validation` error can be placed into a `MaybeError` or `ResultOrError` but it will convert to
  **Unrecoverable** and no longer be a validation error.
* A `Unrecoverable` error *cannot* be placed into a `Validation` error. This will trigger a
  compile error.

### Error Adapter

The `ErrorAdapter` is used to convert between various error types. The adapter will be created
templated on one of the `Error` classes. This template is then used to determine which of the
conversion methods are available. (The adapter has an `operator <type>` overload for each of the
`Result`, `Maybe` entries and the `unique_ptr` for each `Error` type.

## Macros

Most of the work with the error classes is done through macros in the `src/dawn/native/Error.h` file.
Some of the more interesting macros are:

* `DAWN_VALIDATION_ERROR` -- Creates a **Validation** `Validation` error.
* `DAWN_PIPELINE_UNCATEGORIZED_ERROR` -- Makes a **PipelineUncategorized** `Unrecoverable` error.
* `DAWN_BACKEND_DEVICE_LOST_ERROR` -- Makes a **BackendDeviceLost** `Unrecoverable` error.
* `DAWN_UNRECOVERABLE_ERROR` -- Makes an **Unrecoverable** `Unrecoverable` error.
* `DAWN_OUT_OF_MEMORY_ERROR` -- Makes an **OutOfMemory** `Unrecoverable` error.

* `DAWN_INVALID_IF`  -- Creates a validation error if the given condition is false
* `DAWN_PIPELINE_UNCATEGORIZED_IF` -- Creates the pipeline error if the condition is false
* `DAWN_UNRECOVERABLE_ERROR_IF` -- Creates an unrecoverable error if the condition is false

* `DAWN_TRY` -- Will execute the given command. If the command returns a `Result` set to an error
                it will use the `ErrorAdapter` to return the correct type for the parent method.
* `DAWN_TRY_ASSIGN` -- Similar to the above, but will move the success value out of the `Result`
                       and into the given variable.

## Error Sink

Once an error has propagated, the `ErrorSink` methods are used to consume the error. (The `Device`,
`Instance` and `EncoderContext` are all `ErrorSink` subclasses). Each of the subclass implements a
`ConsumeErrorImpl` method which takes an `UnknownError` and emits it in some fashion.

---

### Dawn External Resources (`docs/dawn/external_resources.md`)

# Dawn's external resources links

## Design Docs

- Buffer mapping dawn wire memory transfer interface design
    - https://docs.google.com/document/d/1JOhCpmJ_JyNZJtX6MVbSgxjtG1TdKORdeOGYtSfVYjk/edit?usp=sharing&resourcekey=0-1bFi47mR1jkBLdRFxcTVig

---

### Dawn Fuzzing (`docs/dawn/fuzzing.md`)

# Fuzzing Dawn

## Building

To enable building of the fuzzers, add `use_libfuzzer = true` to `args.gn`.

The fuzzers can be built using the `fuzzers` target (e.g. `ninja fuzzers`)

## `dawn_wire_server_and_frontend_fuzzer`

The `dawn_wire_server_and_frontend_fuzzer` sets up Dawn using the Null backend, and passes inputs to
the wire server. This fuzzes the `dawn_wire` deserialization, as well as Dawn's frontend
validation.

## `dawn_wire_server_and_vulkan_backend_fuzzer`

The `dawn_wire_server_and_vulkan_backend_fuzzer` is like `dawn_wire_server_and_frontend_fuzzer` but
it runs using a Vulkan CPU backend such as Swiftshader. This fuzzer supports error injection by
using the first bytes of the fuzzing input as a Vulkan call index for which to mock a failure.

## Automatic Seed Corpus Generation

Using a seed corpus significantly improves the efficiency of fuzzing. Dawn's fuzzers use interesting
testcases discovered in previous fuzzing runs to seed future runs. Fuzzing can be further improved
by using Dawn tests as a example of API usage which allows the fuzzer to quickly discover and use
new API entrypoints and usage patterns.

Dawn has a CI
builder [cron-linux-clang-rel-x64](https://ci.chromium.org/p/dawn/builders/ci/cron-linux-clang-rel-x64)
which runs on a periodic schedule. This bot runs the `dawn_end2end_tests` and `dawn_unittests`
using the wire and writes out traces of the commands. This can manually be done by running:
`<test_binary> --use-wire --wire-trace-dir=tmp_dir`. The output directory will contain one trace
for each test, where the traces are prepended with `0xFFFFFFFFFFFFFFFF`. The header is the callsite
index at which the error injector should inject an error. If the fuzzer doesn't support error
injection it will skip the header. [cron-linux-clang-rel-x64] then hashes the output files to
produce unique names and uploads them to the fuzzer corpus directories. Please see the
`dawn.py`[https://source.chromium.org/chromium/chromium/tools/build/+/main:recipes/recipes/dawn.py]
recipe for specific details.

Regenerating the seed corpus keeps it up to date when Dawn's API or wire protocol changes.

---

### Dawn Infra (`docs/dawn/infra.md`)

# Dawn's Continuous Testing Infrastructure

Dawn uses Chromium's continuous integration (CI) infrastructure to continually run tests on changes
to Dawn and provide a way for developers to run tests against their changes before submitting. CI
bots continually build and run tests for batches of new changes (although in practice, it is often
once per submitted CL), and Try bots build and run developers' pending changes before submission.
Dawn uses two different builder setups:

1. Standalone Dawn checkouts, which directly check out Dawn and its dependencies. This is used to
   provide test coverage of Dawn itself.
2. Chromium checkouts, which are capable of building Dawn targets. This is used to provide coverage
   of Dawn tests using Chromium's versions of dependencies while also allowing Dawn-related Chromium
   tests to run. For example, most WebGPU CTS testing is done on Chromium builders in order to make
   use of a full Chromium browser.

- [Dawn CI Builders](https://ci.chromium.org/p/dawn/g/ci/builders)
- [Dawn Try Builders](https://ci.chromium.org/p/dawn/g/try/builders)
- [chromium.dawn Waterfall](https://ci.chromium.org/p/chromium/g/chromium.dawn/console)

For additional information on GPU testing in Chromium, please
see [[chromium/src]//docs/gpu/gpu_testing_bot_details.md](https://chromium.googlesource.com/chromium/src.git/+/main/docs/gpu/gpu_testing_bot_details.md).

## Dawn CI/Try Builders

Standalone Dawn builders are defined in the following Starlark files, which are used to generate
files consumed by the builders when
running [//infra/config/global/main.star](https://source.chromium.org/chromium/chromium/src/+/main:third_party/dawn/infra/config/global/main.star):

- [//infra/config/global/gn_standalone_ci.star](https://source.chromium.org/chromium/chromium/src/+/main:third_party/dawn/infra/config/global/gn_standalone_ci.star), which defines all CI builders which use GN as their buildsystem. This encompasses the majority of Dawn's CI builders.
- [//infra/config/global/gn_standalone_try.star](https://source.chromium.org/chromium/chromium/src/+/main:third_party/dawn/infra/config/global/gn_standalone_try.star), which defines al try builders which use GN as their buildsystem. This includes both manual-only trybots and the ones automatically added to all CLs. This encompasses the majority of Dawn's try builders.
- [//infra/config/global/legacy_builders.star](https://source.chromium.org/chromium/chromium/src/+/main:third_party/dawn/infra/config/global/legacy_builders.star), which defines all other CI and try builders such as those that use CMake or mirror Chromium builders. Long-term, all builders in this file will either be removed or moved to appropriately named files.

There are additional `chromium/try` builders, but those are described later in this document.

These bots are defined in both buckets luci.dawn.ci and luci.dawn.try, though their ACL permissions
differ. luci.dawn.ci bots will be scheduled regularly based
on [[dawn]//infra/config/global/luci-scheduler.cfg](../../infra/config/global/generated/luci-scheduler.cfg).
luci.dawn.try bots will be triggered on the CQ based
on [[dawn]//infra/config/global/commit-queue.cfg](../../infra/config/global/generated/commit-queue.cfg).

Build status for both CI and Try builders can be seen at
this [console](https://ci.chromium.org/p/dawn) which is generated
from [[dawn]//infra/config/global/luci-milo.cfg](../../infra/config/global/generated/luci-milo.cfg).

## Dawn Build Recipe

There are two recipes for building Dawn/Tint, one for [GN
based](https://source.chromium.org/chromium/infra/infra_superproject/+/main:build/recipes/recipes/dawn/gn_v2.py)
builds and one for [CMake
based](https://source.chromium.org/chromium/infra/infra_superproject/+/main:build/recipes/recipes/dawn/cmake.py)
builds.

The specific instructions on how to build the project are contained in the project repos build
files. These recipe files are primary concerned with coordinating how to build and test the project
for CQ/CI. The high level steps of this process:

1. Check out the repo
2. Compile the project (both Dawn & Tint)
   - Depending on the specific environment this may include multiple configurations, i.e. with
     `dawn.node` or fuzzing enabled in addition to the default build options
3. Run various tests
   - Including unit tests, end-to-end tests, CTS, and others as appropriate for the specific build
     configuration
4. _Optionally_ Generate and upload fuzzer corpora
   - This is controlled by running a special set of tests and only runs on one CI builder that is
     run once every 24 hours

**Note:** For Googlers there is an internal doc ([go/dawn-luci-guide](go/dawn-luci-guide)) with
  details on how to update the build recipes and test them.

## Dawn Chromium-Based CI Waterfall Bots

The [`chromium.dawn`](https://ci.chromium.org/p/chromium/g/chromium.dawn/console) waterfall consists
of the bots specified in the `chromium.dawn` section
of [[chromium/src]//testing/buildbot/waterfalls.pyl](https://source.chromium.org/search/?q=file:waterfalls.pyl%20chromium.dawn).
Bots named "Builder" are responsible for building top-of-tree Dawn, whereas bots named "DEPS
Builder" are responsible for building Chromium's DEPS version of Dawn.

The other bots, such as "Dawn Linux x64 DEPS Release (Intel HD 630)" receive the build products from
the Builders and are responsible for running tests. The Tester configuration may specify `mixins`
from [[chromium/src]//testing/buildbot/mixins.pyl](https://source.chromium.org/search/?q=file:buildbot/mixins.pyl)
which help specify bot test dimensions like OS version and GPU vendor. The Tester configuration
also specifies `test_suites`
from [[chromium/src]//testing/buildbot/test_suites.pyl](https://source.chromium.org/search/?q=file:buildbot/test_suites.pyl%20dawn_end2end_tests)
which declare the tests are arguments passed to tests that should be run on the bot.

The parent builders and child testers are configured in Chromium's `//infra/config/` files. CI
builders are
defined [here](https://source.chromium.org/chromium/chromium/src/+/main:infra/config/subprojects/chromium/ci/chromium.dawn.star)
while try builders are
defined [here](https://source.chromium.org/chromium/chromium/src/+/main:infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star).

## Dawn Chromium-Based Tryjobs

[This Starlark
file](https://source.chromium.org/chromium/chromium/src/+/main:third_party/dawn/infra/config/global/legacy_builders.star)
defines mirrors of Chromium trybots for use as Dawn trybots. This allows Dawn changes to be tested
in tests which require Chromium before actually attempting to roll Dawn into Chromium, such as
running the WebGPU CTS in a full Chromium browser. The `chromium_dawn_tryjob` entries define
mirrors which are automatically added to all Dawn CLs. These correspond to builder names such as
`linux-dawn-rel` or `mac-dawn-rel`. The `luci.cq_tryjob_verifier` entries define manual-only trybot
mirrors. These are never automatically added, but can be manually added through Gerrit or CL
footers.

## Bot Allocation

Dawn shares all of its builder capacity and test hardware with the Chrome GPU team. Pool assignments
for both are defined in [this
file](https://chrome-internal.googlesource.com/infradata/config/+/refs/heads/main/configs/chromium-swarm/starlark/bots/chromium/gpu.star)
(Google only). A capacity dashboard for these physical machines and GCE instances can be found at
go/chrome-gpu-capacity-dashboard (Google only).

**Note:** At the time of writing, it is recommended to select "Old UI" at the top right of the
  capacity dashboard, as the new UI has some performance issues.

While all builders for a given OS share the same pools (e.g. all Linux trybots share the same pool
of Linux GCE instances), builders typically have `max_concurrent_builds` set to limit how many
builds can be running at once. This is to smooth out usage spikes and help prevent the physical
test hardware from being overwhelmed.

---

### Dawn Litert Lm Benchmark (`docs/dawn/litert_lm_benchmark.md`)

# LiteRT-LM Benchmarks

This document provides instructions for Dawn developers on how to configure, build, and run the
LiteRT-LM benchmarks against Dawn, as well as how to inspect and interpret the benchmark output.

## Overview

LiteRT-LM is an on-device Large Language Model (LLM) inference runtime. In Dawn, it benchmarks
real-world WebGPU compute shader execution and pipeline dispatch performance using the
`libwebgpu_dawn` shared library.

### Benchmark Metrics

The benchmark reports several phases of execution. Not all reported numbers are affected by changes
to Dawn:

#### Prefill Speed (`tokens/sec`)

Measures prompt evaluation throughput while processing input tokens into the KV-cache.

*WebGPU relevance:* Highly relevant. This phase is GPU compute-bound, exercising matrix
 multiplication and attention shaders as well as buffer memory bandwidth.

#### Decode Speed (`tokens/sec`)

Measures autoregressive generation throughput while predicting output tokens one by one.

*WebGPU relevance:* Highly relevant. This phase is memory-bandwidth and dispatch-latency bound,
 making it a direct measure of Dawn's command submission and dispatch overhead.

#### Time to First Token (TTFT)

Measures the wall-clock time from submitting the prompt to emitting the first generated token.

*WebGPU relevance:* Relevant. Dominated by the initial WebGPU prefill execution plus the first
 decode step.

#### Init Executor

Measures the time required to initialize the engine before inference begins.

*WebGPU relevance:* Relevant. Directly captures WebGPU device acquisition, pipeline layout creation,
 and shader compilation times.

#### Tokenizer and Metadata Operations

Includes `Init Tokenizer`, `Init LLM metadata`, and `TextToTokenIds`.

*WebGPU relevance:* Not relevant. These are host-side CPU operations (SentencePiece tokenization,
 protobuf decoding) that do not execute WebGPU code.

## Prerequisites (Windows)

Linux and macOS need no setup beyond `gclient sync`. Windows builds LiteRT-LM with Bazel and MSVC,
which requires the [upstream
prerequisites](https://github.com/google-ai-edge/LiteRT-LM/blob/main/docs/getting-started/build-and-run.md#deploy_to_windows):
Visual Studio 2022, Git for Windows, Python 3, and a JDK with `JAVA_HOME` set.

Two machine settings are also required:

- **Developer Mode** (Settings → System → For developers), so Bazel and `cargo-bazel` can create
  symlinks without elevation. Without it the build fails with `os error 1314`.
- **`LongPathsEnabled`** set to `1` under `HKLM\SYSTEM\CurrentControlSet\Control\FileSystem`.

## 1. Fetch dependencies

LiteRT-LM sources, prebuilt accelerators, and benchmark model weights are managed conditionally
in [`DEPS`](../../DEPS) to keep checkout sizes small for developers who do not need them.

To enable LiteRT-LM, add `"checkout_litert_lm": True` to `custom_vars` in your `.gclient` file:

```python
solutions = [
  {
    "name": ".",
    "url": "https://dawn.googlesource.com/dawn",
    "deps_file": "DEPS",
    "managed": False,
    "custom_vars": {
      "checkout_litert_lm": True,
    },
  },
]
```

After editing `.gclient`, sync dependencies:

```sh
gclient sync
```

This fetches Bazel (`tools/bazelisk/`), the LiteRT-LM source tree (`third_party/litert-lm/src/`),
and the CIPD package containing `model.litertlm` and precompiled platform
accelerators (`third_party/litert-lm/data/`).

## 2. Build

### Generate Build Directory

Use the [`tools/setup-build`](../../tools/setup-build) script to configure a release build:

```sh
./tools/setup-build gn release
```

This generates a GN build configuration in `out/gn-release` (and updates the `out/active` symlink to
point to it) with optimizations enabled and debugging/sanitizer overhead disabled.

### Build the Benchmark Target

Build the benchmark executable using `autoninja`:

```sh
autoninja -C out/active litert_lm
```

### How the Build Works

1. GN first builds Dawn's monolithic shared library:
   - Linux: `out/active/libwebgpu_dawn.so`
   - macOS: `out/active/libwebgpu_dawn.dylib`
   - Windows: `out/active/webgpu_dawn.dll`
2. GN executes [`third_party/litert-lm/build_litert_lm.py`](../../third_party/litert-lm/build_litert_lm.py), which:
   - Configures Bazelisk with the hermetic Clang/LLVM toolchain from Dawn's
     `third_party/llvm-build` (on Linux/macOS) or Visual Studio toolchain (on Windows).
   - Links against the newly compiled local `libwebgpu_dawn` and platform prebuilts.
   - Builds the Bazel target `//runtime/engine:litert_lm_advanced_main`.
   - Copies the resulting `litert_lm_advanced_main` binary and required shared libraries into
     `out/active/`.

## 3. Run

You can run the benchmark binary directly from the root of your Dawn checkout.

### Linux

```sh
LD_LIBRARY_PATH=out/active ./out/active/litert_lm_advanced_main \
  --benchmark \
  --backend=gpu \
  --model_path=third_party/litert-lm/data/model.litertlm
```

### macOS

```sh
DYLD_LIBRARY_PATH=out/active ./out/active/litert_lm_advanced_main \
  --benchmark \
  --backend=gpu \
  --model_path=third_party/litert-lm/data/model.litertlm
```

### Windows

```powershell
.\out\active\litert_lm_advanced_main.exe `
  --benchmark `
  --backend=gpu `
  --model_path=third_party\litert-lm\data\model.litertlm
```

## Troubleshooting

### LiteRT-LM dependencies not found

Ensure `"checkout_litert_lm": True` is present under `custom_vars` in your `.gclient` file, then run
`gclient sync`.

### GPU accelerator could not be loaded and registered

The prebuilt `libLiteRtWebGpuAccelerator` cannot find `libwebgpu_dawn`. Ensure that
`LD_LIBRARY_PATH=out/active` (Linux) or `DYLD_LIBRARY_PATH=out/active` (macOS) is set when running
the command.

### Symbol lookup errors or sanitizer crashes

Prebuilt LiteRT binaries are not built with sanitizers. Avoid ASan/UBSan configurations when
benchmarking LiteRT-LM and use a clean `is_debug = false` release build.

---

### Dawn Testing (`docs/dawn/testing.md`)

# Testing Dawn

(TODO)

## Dawn Perf Tests

For benchmarking with `dawn_perf_tests`, it's best to build inside a Chromium checkout using the
following GN args:

```
is_official_build = true  # Enables highest optimization level, using LTO on some platforms
use_dawn = true           # Required to build Dawn
use_cfi_icall=false       # Required because Dawn dynamically loads function pointers, and we don't
                          # sanitize them yet.
```

A Chromium checkout is required for the highest optimization flags. It is possible to build and run
`dawn_perf_tests` from a standalone Dawn checkout as well, only using GN arg `is_debug=false`. For
more information on building, please see [building.md](../building.md).

### Terminology

 - Iteration: The unit of work being measured. It could be a frame, a draw call, a data upload, a
   computation, etc. `dawn_perf_tests` metrics are reported as time per iteration.
 - Step: A group of Iterations run together. The number of `iterationsPerStep` is provided to the
   constructor of `DawnPerfTestBase`.
 - Trial: A group of Steps run consecutively. `kNumTrials` are run for each test. A Step in a Trial
   is run repetitively for approximately `kCalibrationRunTimeSeconds`. Metrics are accumulated
   per-trial and reported as the total time divided by `numSteps * iterationsPerStep`.
   `maxStepsInFlight` is passed to the `DawnPerfTestsBase` constructor to limit the number of Steps
   pipelined.

(See [`//src/dawn/tests/perf_tests/DawnPerfTest.h`](https://cs.chromium.org/chromium/src/third_party/dawn/src/dawn/tests/perf_tests/DawnPerfTest.h)
for the values of the constants).

### Metrics

`dawn_perf_tests` measures the following metrics:
 - `wall_time`: The time per iteration, including time waiting for the GPU between Steps in a
   Trial.
 - `cpu_time`: The time per iteration, not including time waiting for the GPU between Steps in a
   Trial.
 - `validation_time`: The time for CommandBuffer / RenderBundle validation.
 - `recording_time`: The time to convert Dawn commands to native commands.

Metrics are reported according to the format specified at
[[chromium]//build/recipes/performance_log_processor.py](https://cs.chromium.org/chromium/build/recipes/performance_log_processor.py)

### Dumping Trace Files

The test harness supports a `--trace-file=path/to/trace.json` argument where Dawn trace events can
be dumped. The traces can be viewed in Chrome's `about://tracing` viewer.

### Test Runner

[`//scripts/perf_test_runner.py`](https://cs.chromium.org/chromium/src/third_party/dawn/scripts/perf_test_runner.py) may be run to continuously run a test and report mean times and variances.

Currently the script looks in the `out/Release` build directory and measures the `wall_time`
metric (hardcoded into the script). These should eventually become arguments.

Example usage:

```
scripts/perf_test_runner.py DrawCallPerf.Run/Vulkan__e_skip_validation
```

### Tests

**BufferUploadPerf**

Tests repetitively uploading data to the GPU using either `WriteBuffer` or `CreateBuffer` with
`mappedAtCreation = true`.

**DrawCallPerf**

DrawCallPerf tests drawing a simple triangle with many ways of encoding commands,
binding, and uploading data to the GPU. The rationale for this is the following:
  - Static/Multiple/Dynamic vertex buffers: Tests switching buffer bindings. This has
    a state tracking cost as well as a GPU driver cost.
  - Static/Multiple/Dynamic bind groups: Same rationale as vertex buffers
  - Static/Dynamic pipelines: In addition to a change to GPU state, changing the pipeline
    layout incurs additional state tracking costs in Dawn.
  - With/Without render bundles: All of the above can have lower validation costs if
    precomputed in a render bundle.
  - Static/Dynamic data: Updating data for each draw is a common use case. It also tests
    the efficiency of resource transitions.

## LiteRT-LM Benchmarks

See [litert_lm_benchmark.md](litert_lm_benchmark.md) for instructions on configuring, building, and
running the LiteRT-LM benchmarks with Dawn.

## Testing with the CTS

There are various ways to test dawn against the [WebGPU CTS](https://github.com/gpuweb/cts).

### With dawn.node
See [src/dawn/node/README.md](../../src/dawn/node/README.md).

### With a Chromium build manually
Go to [https://gpuweb.github.io/cts/standalone/](https://gpuweb.github.io/cts/standalone)
or clone [the CTS repo](https://github.com/gpuweb/cts) and serve it locally

```sh
git clone https://github.com/gpuweb/cts.git
cd cts
npm ci
npm start
```

Then open `http://localhost:8080/standalone`.

### On the CQ
You can run a specific version of the CTS with top of tree Dawn in Chromium.
First, upload the version you want to test to your own github fork of the CTS.
Note the commit-hash. Then run:

```sh
./tools/run cts roll --max-attempts 0 --repo <repo-url> --revision <commit-hash>
```

Example:

```sh
./tools/run cts roll --max-attempts 0 --repo https://github.com/greggman/cts.git --revision 1b3173b36a1084917e0ff29e5f40c6290b6f8adc
```

The roll script take a couple of minutes as it downloads that version of the CTS and
creates a roll patch. Once the roll script says it's waiting on the bots to finish
you can kill (Ctrl-C) the script. The CQ will continue to run the tests.

#### Filtering - running less than the full CTS
You can also specify a CTS query with `--test-query <query>`, for example, test all the
WGSL builtins `... --test-query "webgpu:shader,execution,expression,call,builtin,*"`.
You can also filter tests with a glob `--test-filter <glob>`, for example, only run tests
with `BindGroup` in the test name `... --test-filter "*BindGroup*"`.

See  `tools/src/cmd/cts/roll/roll.go` for other options.

#### Testing with a different version of Dawn

The roll is just a normal CL on gerrit so, after creating a roll CL, follow the instructions
on gerrit to download that CL. Apply any dawn changes. Then re-upload with
`git cl upload --force --bypass-hooks` and start a dry run on gerrit.

---

### Tint Arch (`docs/tint/arch.md`)

# Tint Architecture

```
/* Detailed source-code truncated for AI context efficiency. */
```

## Reader

Readers are responsible for parsing a shader program and populating a
`ProgramBuilder` with the parsed AST and symbol information.

The WGSL reader is a recursive descent parser. It closely follows the WGSL
grammar in the naming of the parse methods.

## ProgramBuilder

A `ProgramBuilder` is the interface to construct an immutable `Program`.
There are a large number of helper methods for simplifying the creation of the
AST nodes. A `ProgramBuilder` can only be used once, and must be discarded after
the `Program` is constructed.

A `Program` is built from the `ProgramBuilder` via a call to
`resolver::Resolve()`, which will perform validation and semantic analysis.
The returned program will contain the semantic information which can be obtained
by calling `Program::Sem()`.

At any time before building the `Program`, `ProgramBuilder::IsValid()` may be
called to ensure that no error diagnostics have been raised during the
construction of the AST. This includes parser syntax errors, but not semantic
validation which happens during the `Resolve` phase.

If further changes to the `Program` are needed (say via a `Transform`) then a
new `ProgramBuilder` can be produced by cloning the `Program` into a new
`ProgramBuilder`.

Unlike `Program`s, `ProgramBuilder`s are not part of the public Tint API.

## AST

The Abstract Syntax Tree is a directed acyclic graph of `ast::Node`s which
encode the syntactic structure of the WGSL program.

The root of the AST is the `ast::Module` class which holds each of the declared
functions, variables and user declared types (type aliases and structures).

Each `ast::Node` represents a **single** part of the program's source, and so
`ast::Node`s are not shared.

The AST does not perform any verification of its content. For example, the
`ast::Array` node has numeric size parameter, which is not validated to be
within the WGSL specification limits until validation of the `Resolver`.

## Semantic information

Semantic information is held by `sem::Node`s which describe the program at
a higher / more abstract level than the AST. This includes information such as
the resolved type of each expression, the resolved overload of a builtin
function call, and the module scoped variables used by each function.

Semantic information is generated by the `Resolver` when the `Program`
is built from a `ProgramBuilder`.

The `sem::Info` class holds a map of `ast::Node`s to `sem::Node`s.
This map is **many-to-one** - i.e. while a AST node might have a single
corresponding semantic node, the reverse may not be true. For example:
many `ast::IdentifierExpression` nodes may map to a single `sem::Variable`,
and so the `sem::Variable` does not have a single corresponding
`ast::Node`.

Unlike `ast::Node`s, semantic nodes may not necessarily form a directed acyclic
graph, and the semantic graph may contain diamonds.

## Types

AST types are regular AST nodes, in that they uniquely represent a single part
of the parsed source code. Unlike semantic types, identical AST types are not
de-duplicated as they refer to the source usage of the type.

Semantic types are constructed during the `Resolver` phase, and are held by
the `Program` or `ProgramBuilder`.

Each `sem::Type` node **uniquely** represents a particular WGSL type within the
program, so you can compare `type::Type*` pointers to check for type
equivalence. For example, a `Program` will only hold one instance of the
`sem::I32` semantic type, no matter how many times an `i32` is mentioned in the
source program.

WGSL type aliases resolve to their target semantic type. For example, given:

```wgsl
type MyI32 = i32;
const a : i32 = 1;
const b : MyI32 = 2;
```

The **semantic** types for the variables `a` and `b` will both be the same
`sem::I32` node pointer.

## Symbols

Symbols represent a unique string identifier in the source program. These string
identifiers are transformed into symbols within the `Reader`s.

During the Writer phase, symbols may be emitted as strings using a `Namer`.
A `Namer` may output the symbol in any form that preserves the uniqueness of
that symbol.

## Resolver

The `Resolver` will automatically run when a `Program` is built.
A `Resolver` creates the `Program`s semantic information by analyzing the
`Program`s AST and type information.

The `Resolver` will validate to make sure the generated `Program` is
semantically valid.

## Program

A `Program` holds an immutable version of the information from the
`ProgramBuilder` along with semantic information generated by the
`Resolver`.

`Program::IsValid()` may be called to ensure the program is structurally correct
**and** semantically valid, and that the `Resolver` did not report any errors
during validation.

Unlike the `ProgramBuilder`, a `Program` is fully immutable, and is part of the
public Tint API. The immutable nature of `Program`s make these entirely safe
to share between multiple threads without the use of synchronization primitives.

## Inspector

The inspectors job is to go through the `Program` and pull out various pieces of
information. The information may be used to pass information into the downstream
compilers (things like specialization constants) or may be used to pass into
transforms to update the AST before generating the resulting code.

The input `Program` to the inspector must be valid (pass validation).

## Transforms

There maybe various transforms we want to run over the `Program`.
This is for things like Vertex Pulling or Robust Buffer Access.

A transform operates by cloning the input `Program` into a new `ProgramBuilder`,
applying the required changes, and then finally building and returning a new
output `Program`. As the resolver is always run when a `Program` is built,
Transforms will always emit a `Program` with semantic information.

The input `Program` to a transform must be valid (pass validation).
If the input `Program` of a transform is valid then the transform must guarantee
that the output program is also valid.

## Writers

A writer is responsible for writing the `Program` in the target shader language.

The input `Program` to a writer must be valid (pass validation).

---

### Tint Benchmark (`docs/tint/benchmark.md`)

# Tint Benchmark

The `tint_benchmark` executable measures the cost of translating shaders from WGSL to each backend language.
The benchmark uses [Google Benchmark](https://github.com/google/benchmark), and is integrated into [Chromium's performance waterfall](https://chromium.googlesource.com/chromium/src.git/+/HEAD/docs/speed/perf_waterfall.md).

## Benchmark inputs

Shaders used for benchmarking can be provided as either WGSL or SPIR-V.
The shaders are embedded in the binary at build time in order to avoid runtime dependencies.
A [Python script](https://source.chromium.org/chromium/chromium/src/+/main:third_party/dawn/src/tint/cmd/bench/generate_benchmark_inputs.py) is used to generate a header file that contains all of the benchmark shaders, and the macros that register benchmarks with the Google Benchmark harness.

SPIR-V shaders are converted to WGSL as an offline step using Tint, as the SPIR-V reader is not available on the waterfall bots.
The generated WGSL files are checked in to the repo and the same script is used by CQ to check that the generated files are up to date.

The script lists the paths to all of the benchmark shaders, which are either in the end-to-end test directory (`test/tint/benchmark/`) or provided as external shaders in `third_party/benchmark_shaders/`.

## Adding benchmarks

Files that end with `_bench.cc` are automatically included in the benchmark binary.
To benchmark a component of Tint against the benchmark shaders, define a function that performs the benchmarking and then register it with the `TINT_BENCHMARK_PROGRAMS` macro.
For example, the SPIR-V backend benchmark code is in [`src/tint/lang/spirv/writer/writer_bench.cc`](https://source.chromium.org/chromium/chromium/src/+/main:third_party/dawn/src/tint/lang/spirv/writer/writer_bench.cc).

Other parts of Tint can be benchmarked independently from the benchmark shader corpus by registering the benchmark function with the `BENCHMARK` macro.

## Chromium performance waterfall

The `tint_benchmark` binary is a dependency of the `performance_test_suite_template_base` template in [chrome/test/BUILD.gn](https://source.chromium.org/chromium/chromium/src/+/refs/tags/131.0.6735.1:chrome/test/BUILD.gn;l=5909).

The [`bot_platforms.py`](https://source.chromium.org/chromium/chromium/src/+/main:tools/perf/core/bot_platforms.py) script controls which platforms run the benchmark.
The benchmark was added to the waterfall in [this CL](https://chromium-review.googlesource.com/c/chromium/src/+/5815585).

The [chrome.perf console](https://ci.chromium.org/p/chrome/g/chrome.perf/console) shows the status of the builder and tester bots, and can be used to check that changes to the benchmarking setup are working correctly.

## Viewing benchmark data

View benchmark data with the [perf.luci.app](https://perf.luci.app/) dashboard.
The units for Tint benchmarks are nanoseconds.

The query parameters are:

- `benchmark` - select `tint_benchmark`
- `bot` - select the waterfall bot from which to view data
- `test` - select the component of Tint to focus on (e.g. a specific backend)
- `subtest_1` - select the benchmark shader to focus on

---

### Tint Compound Statements (`docs/tint/compound_statements.md`)

# Compound Statements

Compound statements are statements that can hold other statements.

This document maps the WGSL compound statements to their semantic tree representations.

## if statement

WGSL:
```
if (condition_a) {
    statement_a;
} else if (condition_b) {
    statement_b;
} else {
    statement_c;
}
```

Semantic tree:
```
sem::IfStatement {
    condition_a
    sem::BlockStatement {
        statement_a
    }
    sem::IfStatement {
        condition_b
        sem::BlockStatement {
            statement_b
        }
        sem::BlockStatement {
            statement_c
        }
    }
}
```

## for loop

WGSL:
```
for (initializer; condition; continuing) {
    statement;
}
```

Semantic tree:
```
sem::ForLoopStatement {
    sem::Statement  initializer
    sem::Expression condition
    sem::Statement  continuing

    sem::LoopBlockStatement {
        sem::Statement statement
    }
}
```

## while

WGSL:
```
while (condition) {
    statement;
}
```

Semantic tree:
```
sem::WhileStatement {
    sem::Expression condition

    sem::LoopBlockStatement {
        sem::Statement statement
    }
}
```

## loop

WGSL:
```
loop (condition) {
    statement_a;
    continuing {
        statement_b;
    }
}
```

Semantic tree:
```
sem::LoopStatement {
    sem::Expression condition

    sem::LoopBlockStatement {
        sem::Statement statement_a
        sem::LoopContinuingBlockStatement {
            sem::Statement statement_b
        }
    }
}
```


## switch statement

WGSL:
```
switch (condition) {
    case literal_a, literal_b: {
        statement_a;
    }
    default {
        statement_b;
    }
}
```

Semantic tree:
```
sem::SwitchStatement {
    sem::Expression condition
    sem::CaseStatement {
        sem::BlockStatement {
            sem::Statement statement_a
        }
    }
    sem::CaseStatement {
        sem::BlockStatement {
            sem::Statement statement_b
        }
    }
}
```

---

### Tint Coverage Info (`docs/tint/coverage-info.md`)

# Generating and viewing Tint code-coverage

Requirements:

* Host running Linux or macOS
* [vpython3](https://chromium.googlesource.com/chromium/tools/depot_tools/+/HEAD/vpython3)
  (usually provided by `depot_tools`)
* Clang toolchain

## Building Tint with coverage generation enabled

To generate coverage, you must build with Clang source-based coverage
instrumentation enabled.

### Using GN (Recommended)

Add the following flags to your `args.gn`:

```gn
use_clang_coverage = true
is_component_build = false
is_debug = false
dcheck_always_on = true
```

If you are building a fuzzer target, also include:

```gn
optimize_for_fuzzing = false
use_clang_modules = false
use_libfuzzer = true
```

### Using CMake

Add the additional `-DDAWN_EMIT_COVERAGE=1` CMake flag.

To use the `coverage.py` script's automated building features, you
must use the Ninja generator:

```bash
cmake -G Ninja -DDAWN_EMIT_COVERAGE=1 ..
```

If you use a different generator (like Make), you must build your
targets manually and use the `--no-compile` flag with the script.

## Generating Coverage Reports

Use the `tools/code_coverage/coverage.py` script to run your targets
and generate reports. (This is the Chromium maintained script that
replaces the previous Tint only script,
`tools/tint-generate-coverage`).

### 1. Generate LCOV for VSCode

To use the [VSCode Coverage
Gutters](https://marketplace.visualstudio.com/items?itemName=ryanluker.vscode-coverage-gutters)
extension, generate an LCOV tracefile:

```bash
vpython3 tools/code_coverage/coverage.py tint_unittests \
    -b out/gn-coverage \
    -o coverage_report \
    --format lcov \
    -c 'out/gn-coverage/tint_unittests'
```


The resulting file will be located at
`coverage_report/linux/coverage.lcov`.

### 2. Generate an HTML Report

To generate a browsable HTML report with line-by-line coverage:

```bash
vpython3 tools/code_coverage/coverage.py tint_unittests \
    --no-component-view \
    -b out/gn-coverage \
    -o coverage_report \
    --format html \
    -c 'out/gn-coverage/tint_unittests'
```

Open `coverage_report/linux/index.html` in your browser.

### 3. Advanced: Fuzzers with Dynamic Objects (Mesa/Vulkan)

If you are running a fuzzer that dynamically loads a Vulkan driver
(like Mesa's `lvp`), you need to explicitly include the driver object
and map its source paths:

```bash
vpython3 tools/code_coverage/coverage.py tint_ir_fuzzer \
    --no-component-view \
    -b out/gn-coverage \
    -o coverage_report \
    --format html \
    -a out/gn-coverage/libvulkan_lvp.so \
    --path-equivalence=/third_party/mesa,$(pwd)/third_party/mesa \
    --path-equivalence=$(pwd)/out/gn-coverage/src,$(pwd)/out/gn-coverage/gen/third_party/mesa/mesa_build_setup/src \
    -i '\/usr\/.*' \
    -c 'out/gn-coverage/tint_ir_fuzzer -max_total_time=30'
```

*   `-a`: Adds the dynamically loaded driver as object source to the
    coverage calculation.
*   `--path-equivalence`: Maps paths recorded in the binary (which may
    be absolute or relative to build artifacts) to your local
    checkout.
*   Multiple `--path-equivalence` flags can be provided to handle
    different source roots (e.g., standard sources vs. generated
    files).
*   `-i '\/usr\/.*` is used to not include system files that are
    outside the source tree.

## Troubleshooting

### 403 error when generating HTML reports
If you see something like this:
```
/* Detailed source-code truncated for AI context efficiency. */
```

then the likely cause is missing `--no-component-view`  from the flags.

The component view functionality depends on the mapping of directories
in a Chromium checkout to components in the issue tracker, so
misbehaves when run in a Dawn checkout. Fixing this would add
complexity upstream for a view that doesn't provide any useful
information in the report, since it would just tell you all of the
code in the Dawn repo belongs to either the Dawn or Tint component.

---

### Tint Diagnostics Guide (`docs/tint/diagnostics_guide.md`)

# Tint diagnostic style guide

This guide provides a set of best practices when writing code that emits
diagnostic messages in Tint. These diagnostics are messages presented to the
user in case of error or warning.

The goal of this document is to have our diagnostic messages be clear and
understandable to our users, so that problems are easy to fix, and to try and
keep a consistent style.

## Message style

* Start diagnostic messages with a lower-case letter
* Try to keep the message to a single sentence, if possible
* Do not end the message with punctuation (full stop, exclamation mark, etc)

**Don't:**

```
shader.wgsl:7:1 error: Cannot take the address of expression.
```

**Do:**

```
shader.wgsl:7:1 error: cannot take the address of expression
```

**Justification:**

Succinct messages are more important than grammatical correctness. \
This style matches the style found in most other compilers.

## Prefer to use a `Source` location instead of quoting the code in the message

**Don't:**

```
shader.wgsl:5:7 error: cannot multiply 'expr_a * expr_b' with types i32 and f32

var res : f32 = expr_a * expr_b
                ^^^^^^^^^^^^^^^
```

**Do:**

```
shader.wgsl:5:7 error: cannot multiply types i32 and f32

var res : f32 = expr_a * expr_b
                ^^^^^^^^^^^^^^^
```

**Justification:**

The highlighted line provides even more contextual information than the quoted
source, and duplicating this information doesn't provide any more help to the
developer. \
Quoting single word identifiers or keywords from the source is not discouraged.

## Use `note` diagnostics for providing additional links to relevant code

**Don't:**

```
shader.wgsl:5:11 error: type cannot be used in address space 'storage' as it is non-host-shareable

    cond : bool;
           ^^^^
```

**Do:**

```
shader.wgsl:5:11 error: type cannot be used in address space 'storage' as it is non-host-shareable

    cond : bool;
           ^^^^

shader.wgsl:8:4 note: while instantiating variable 'StorageBuffer'

var<storage> sb : StorageBuffer;
             ^^
```

**Justification:**

To properly understand some diagnostics requires looking at more than a single
line. \
Multi-source links can greatly reduce the time it takes to properly
understand a diagnostic message. \
This is especially important for diagnostics raised from complex whole-program
analysis, but can also greatly aid simple diagnostics like symbol collision errors.

## Use simple terminology

**Don't:**

```
shader.wgsl:7:1 error: the originating variable of the left-hand side of an assignment expression must not be declared with read access control.
```

**Do:**

```
shader.wgsl:7:1 error: cannot assign to variable with read access control

x.y = 1;
^^^^^^^

shader.wgsl:2:8 note: read access control declared here

var<storage, read> x : i32;
             ^^^^
```

**Justification:**

Diagnostics will be read by Web developers who may not be native English
speakers and are unlikely to be familiar with WGSL specification terminology.
Too much technical jargon can be intimidating and confusing. \
Diagnostics should give enough information to explain what's wrong, and most
importantly, give enough information so that a fix actionable.

**Caution:** Be careful to not over simplify. Use the specification terminology
if there's potential ambiguity by not including it.

---

### Tint End To End Tests (`docs/tint/end-to-end-tests.md`)

# Tint end-to-end tests

This repo contains a large number of end-to-end tests at `<dawn>/test/tint`.

## Test files

Test input files have either the `.wgsl`, `.spv` or `.spvasm` file extension.

Each test input file is tested against each of the Tint backends. There are `<number-of-input-files>` &times; `<number-of-tint-backends>` tests that are performed on an unfiltered end-to-end test run.

Each backend test can have an **expectation file**. This expectation file sits next to the input file, with a `<input-file>.expected.<format>` extension. For example the test `test/foo.wgsl` would have the HLSL expectation file `test/foo.wgsl.expected.hlsl`.

An expectation file contains the expected output of Tint, when passed the input file for the given backend.

If the first line of the expectation file starts `SKIP`, then the test will be skipped instead of failing the end-to-end test run. It is good practice to include after the `SKIP` a reason for why the test is being skipped, along with any additional details, such as compiler error messages.

## Running

To run the end-to-end tests use the `./tools/run tests` script, passing the path to the tint executable with the `--tint` (defaulting to `./out/active/tint`):

    ./tools/run tests

You can pass a list of globs, directories or file paths as extra arguments to specify what tests you
want to run.

You can pass `--help` to see the full list of command line flags.\
The most commonly used flags are:

| flag                 | description |
|----------------------|-------------|
|`--format`            | Filters the tests to the particular backend.<br>Example: `--format hlsl` will just test the HLSL backend.
|`--generate-expected` | Generate expectation files for the tests that previously had no expectation file, or were marked as `SKIP` but now pass.
|`--generate-skip`     | Generate `SKIP` expectation files for tests that are not currently passing.

## Authoring guidelines

Each test should be as small as possible, and focused on the particular feature being tested.

Use sub-directories whenever possible to group similar tests, and try to keep the pattern of directories as consistent as possible between different tests. This helps filter tests with glob patterns.

---

### Tint Experimental Extensions (`docs/tint/experimental_extensions.md`)

# Experimental extensions

Sometimes a language feature proposed for WGSL requires experiementation
to prove its worth.  Tint needs to support these, in general to enable
that experimentation.

The steps for doing so are:

1. Choose a name for the feature, to be used in an `enable` directive.
   An experimental extension should use prefix of `google_experimental_`
   Example:

      enable google_experimental_f16;

2. Write down what the feature is supposed to mean.
   This informs the Tint implementation, and tells shader authors what
   has changed.
   Ideally, this will take the form of one of the following:

   - A PR against the WGSL spec.

   - A description of what the contents of that PR would be, committed
     as a document in this Tint repository.

3. File a tracking bug for adding the feature.
   Note: Should the Tint repo have a label for experimental features?

4. File a tracking bug for removing the feature or converting it to
   non-experimental.

5. Write a plan for removal of the experiment.
   - Ideally, this plan is committed to this repository, especially the
     description of public activities and commitments. However, we recognize
     that some internal goals or metrics may be sensitive, and can be hidden.
   - The plan is about process, not technical details.  It should include:
       - Who is the point of contact for this feature? The point of contact
         is responsible when the feature causes an issue or gets in the way.
       - What is your target date for declaring the experiment a success or
         failure. In Chrome an experiment must be shipped or removed, in
         finite time.
       - What experience are you hoping to gain?  Do you have target metrics?
       - What approvals, if any, do you need from W3C? What is your plan to
         present your case to W3C?
       - The bug tracking removal of the experiment.

---

### Tint Fuzzers (`docs/tint/fuzzers.md`)

# Tint Fuzzers

Tint currently has two fuzzer target executables:
[`tint_wgsl_fuzzer`](#tint_wgsl_fuzzer) which takes WGSL source code
as input and [`tint_ir_fuzzer`](#tint_ir_fuzzer) which takes a
protobuf binary file as input.

Both fuzzers are implemented using
[libFuzzer](https://llvm.org/docs/LibFuzzer.html), and are
continuously run by Chromium's ClusterFuzz infrastructure.

[Fuzzer targets are automatically found via `gn
refs`](https://chromium.googlesource.com/chromium/src/+/HEAD/testing/libfuzzer/libFuzzer_integration.md). See
[`tint.gni`](../../src/tint/tint.gni) for the core fuzzer target
rules.

Tint's fuzzers are implemented as functions registered with the
macros:

- [`TINT_WGSL_PROGRAM_FUZZER()`](#registering-a-new-tintprogram-fuzzer)
  registers a fuzzer function that is handed a `tint::Program`.
- [`TINT_IR_MODULE_FUZZER()`](#registering-a-new-tintcoreirmodule-fuzzer)
  registers a fuzzer function that is handed a
  `tint::core::ir::Module`.

## Building

The `tint_wgsl_fuzzer` target can be built with either CMake or GN:

- CMake: Define `TINT_BUILD_FUZZERS=1` (pass `-DTINT_BUILD_FUZZERS=1`
  to `cmake`)
- GN: Define `use_libfuzzer = true` in `args.gn`.

Building `tint_ir_fuzzer` requires additional flags to be set:

- CMake: Define `TINT_BUILD_IR_BINARY=1` (pass
  `-DTINT_BUILD_IR_BINARY=1` to `cmake`)
- GN: Define `tint_build_ir_binary = true` in `args.gn`.

## Running fuzzers

### Local fuzzing

The [`tint_wgsl_fuzzer`](#tint_wgsl_fuzzer) and
[`tint_ir_fuzzer`](#tint_ir_fuzzer) executables accept the [standard
`libFuzzer` command line
arguments](https://llvm.org/docs/LibFuzzer.html#options) with
[extended command line arguments](#extended-command-line-arguments)
described below.

There's also a helper tool to run the fuzzers locally:

- To run the local fuzzers across the full number of CPU threads
  available on the system, seeded with a corpus based on
  [`test/tint`](../../test/tint), and using the dictionary in
  `src/tint/cmd/fuzz/wgsl/dictionary.txt` run:

  `tools/run fuzz run`

- To check that all the test files, [`test/tint`](../../test/tint) by
  default, pass the fuzzers without crashing and then exit, run:

  `tools/run fuzz check`

  Note: This is run by Dawn's CQ to check that fuzzers aren't
  accidentally broken.

- To run the local fuzzers using a generated corpus of .wgsl files:

  `tools/run fuzz run -inputs out/Fuzzer/wgsl_corpus`

Note: The above commands will run in the default WGSL mode only.  If
you want to run using the IR fuzzer, you need to pass in `--ir` to run
in IR mode.

Examples of IR mode:

```bash
tools/run fuzz run -ir
tools/run fuzz run -ir -inputs out/Fuzzer/ir_corpus
tools/run fuzz check -ir
```

`-ir` mode without any provided inputs will convert the default WGSL
inputs from [`test/tint`](../../test/tint) on-the-fly to .tirb
files. A manually provided input of .tirb files can be provided. There
is currently no facility to detect that an explicit input directory
contains .wgsl files that need converting to .tirb files.

See below for how to generate the corpus locally

### Generating fuzzer corpora

Running fuzzers without any inputs is relatively inefficient, since
the fuzzing algorithm has to discover the structure of the input
format via generating random inputs. This can be alleviated by
providing a corpus of examples to start from. The fuzzer will not be
restricted by this initial corpus, since it will mutate and recombine
the examples to find new and interesting inputs.

The helper tool is capable of producing appropriate corpora from test
files via the `generate` subcommand. `generate` requires you to also pass
in `-out`, since otherwise the output would go to an ephemeral tmp
directory and not be useable.

#### tint_wgsl_fuzzer

To generate a corpus based on the default test files from
[`test/tint`](../../test/tint) no additional flags are needed

```bash
tools/run fuzz generate -out out/Fuzzer/wgsl_corpus
```

`-inputs` can be used to provide custom inputs to the tool. It will
use any .wgsl files found in the inputs that don't also include the
string `.expected.` in their name.

```bash
tools/run fuzz generate -inputs my_super_cool_tests/ -out out/Fuzzer/wgsl_corpus
```

`-out` will overwrite existing files that collide with the output file
names, but does not create the output directory or remove its
contents, so that you can merge multiple input sources together via
multiple calls to the tool.

The corpus can be used with the above helper tool via the `-inputs`
flag or may be directly passed into a manual invocation of
`tint_wgsl_fuzzer`.

An example of generating and then using a custom WGSL corpus:

```bash
autoninja -C out/Fuzzer fuzzer_corpus_tools  # Guarantees the fuzzers and another tooling is present
tools/run fuzz generate -inputs my_super_cool_tests/ -out out/Fuzzer/wgsl_corpus

# And then either
out/Fuzzer/tint_wgsl_fuzzer -dict=src/tint/cmd/fuzz/wgsl/dictionary.txt out/Fuzzer/wgsl_corpus

# or

tools/run fuzz run -inputs out/Fuzzer/wgsl_corpus
```

#### tint_ir_fuzzer

IR corpus generation requires the `ir_fuzz_as` binary to be present in
the build directory. This directory is `out/active` by default, but
can be manually specified via `-build`. The GN target
`fuzzer_corpus_tools` can be used to build this binary and any of the
other binaries that the helper tool may need.

The corpus generation process for a IR corpus is the same as above,
except using the `-ir` flag.

The inputs are expected to be .wgsl files like the WGSL generation, but
the output will .tirb files. Similarly to the WGSL mode generation
inputs with `.expected.` will be filtered out.

Generating a corpus based on the default WGSL tests:

```bash
tools/run fuzz generate -ir -out out/Fuzzer/wgsl_corpus
```

Generating a corpus based on custom WGSL files:

```bash
tools/run fuzz generate -ir -input my_super_cool_tests/ -out out/Fuzzer/wgsl_corpus
```

An example of generating and using a custom IR corpus:

```bash
autoninja -C out/Fuzzer fuzzer_corpus_tools  # Guarantees the fuzzers and another tooling is present
tools/run fuzz generate -ir -inputs my_super_cool_tests/ -out out/Fuzzer/ir_corpus

# And then either
out/Fuzzer/tint_ir_fuzzer out/Fuzzer/ir_corpus

# or

tools/run fuzz run -ir -inputs out/Fuzzer/ir_corpus
```

#### Minimizing the corpus

Minimizing a fuzzer corpus is no longer supported as part of the
tooling. It was rarely used, since ClusterFuzz does it automatically,
and added significant complexity to the tooling. If you need to
minimize a corpus manually please consult the libFuzzer documentation
for [details](https://llvm.org/docs/LibFuzzer.html#corpus)

## Writing fuzzers

### Registering a new `tint::Program` fuzzer

1. Create a new source file with a `_fuzz.cc` suffix.
2. `#include "src/tint/cmd/fuzz/wgsl/fuzz.h"`
3. Define a function in a (possibly nested) anonymous namespace with
   one of the signatures:

   - `void MyFuzzer(const tint::Program& program /*, ...additional fuzzed parameters... */) {`
   - `void MyFuzzer(const tint::Program& program, const tint::fuzz::wgsl::Context& context /*, ...additional fuzzed parameters... */) {`

   The optional `context` parameter holds information about the
    `Program` and the environment used to run the fuzzers.

   [Note: Any number of additional fuzzer-populated parameters can be
   appended to the function signature.](#additional-fuzzer-data)

4. Implement your fuzzer function, using `TINT_ICE()` to catch invalid
   state. Return early if the fuzzer cannot handle the input.
5. At the bottom of the file, in the global namespace, register the
   fuzzer with: `TINT_WGSL_PROGRAM_FUZZER(MyFuzzer);`
6. Use `tools/run gen build` to generate the build files for this new
   fuzzer.

Example:

```c++
#include "src/tint/cmd/fuzz/wgsl/fuzz.h"

namespace tint::my_namespace {
namespace {

bool CanRun(const tint::Program& program) {
    if (program.AST().HasOverrides()) {
        return false;  // Overrides are not supported.
    }
    return true;
}

void MyWGSLFuzzer(const tint::Program& program, bool a_fuzzer_provided_value) {
    if (!CanRun(program)) {
        return;
    }

    // Check something with program.
}

}  // namespace
}  // namespace tint::my_namespace

TINT_WGSL_PROGRAM_FUZZER(tint::my_namespace::MyWGSLFuzzer);
```

### Registering a new `tint::core::ir::Module` fuzzer

1. Create a new source file with a `_fuzz.cc` suffix.
2. `#include "src/tint/cmd/fuzz/ir/fuzz.h"`
3. Define a function in a (possibly nested) anonymous namespace with
   the signature:

   - `void MyFuzzer(core::ir::Module& module /*, ...additional fuzzed parameters... */) {`

   [Note: Any number of additional fuzzer-populated parameters can be
   appended to the function signature.](#additional-fuzzer-data)

4. Implement your fuzzer function, using `TINT_ICE()` to catch invalid
   state. Return early if the fuzzer cannot handle the input.
5. At the bottom of the file, in the global namespace, register the
   fuzzer with: `TINT_IR_MODULE_FUZZER(MyFuzzer);`

  [Note: Often different capabilities need to be enabled before/after
  a fuzzer function runs to ensure validation passes, these can be
  declared using `pre_capabilities` and `post_capabilities` when
  registering the fuzzer ]

6. Use `tools/run gen build` to generate the build files for this new
   fuzzer.

Example:

```c++
#include "src/tint/cmd/fuzz/ir/fuzz.h"

namespace tint::my_namespace {
namespace {

void MyIRFuzzer(core::ir::Module& module) {
    // Do something interesting with module.
}

}  // namespace
}  // namespace tint::my_namespace

TINT_IR_MODULE_FUZZER(tint::my_namespace::MyIRFuzzer);
```

### Additional fuzzer data

WGSL and IR fuzzer functions can also declare any number of additional
parameters, which will be populated with fuzzer provided data. These
additional parameters must come at the end of the signatures described
above, and can be of the following types:

- Any integer, float or bool type.

- Any structure reflected with `TINT_REFLECT`.

  Note: It's recommended to use a `const` reference, for these to
  avoid pass-by-value overheads.

- Any enum reflected with `TINT_REFLECT_ENUM_RANGE`.

## Executable targets

Tint has two fuzzer executable targets:

### `tint_wgsl_fuzzer`

`tint_wgsl_fuzzer` [accepts WGSL textual
input](https://llvm.org/docs/LibFuzzer.html#options) and parses line
comments (`//`) as a base-64 binary encoded data stream for the
[additional fuzzer parameters](additional-fuzzer-data).

The entry point for the fuzzer lives at
[`src/tint/cmd/fuzz/wgsl/main_fuzz.cc`](../../src/tint/cmd/fuzz/wgsl/main_fuzz.cc).

#### Extended command line arguments

On top of the [standard `libFuzzer` command line
arguments](https://llvm.org/docs/LibFuzzer.html#options), the fuzzer
supports the following extended command line arguments:

- `--help`: lists the command line arguments.
- `--filter=<name>`: only runs the fuzzer functions that contain the
  given string in its name.
- `--concurrent`: each of the fuzzer functions will be run on a
  separate, concurrent thread. This potentially offers performance
  improvements, and also tests for concurrent execution.
- `--verbose` : prints verbose information about what the fuzzer is
  doing.
- `--dump` : prints shader source, including input WGSL, and generated
  HLSL, MSL, and GLSL.

#### Behavior

The `tint_wgsl_fuzzer` will do the following:

- Base-64 decode the line comments data from the WGSL source, used to
  populate the [additional fuzzer parameters](additional-fuzzer-data).
- Parse and resolve the WGSL input, and will early-return if there are
  any parser errors.
- Invoke each of the fuzzer functions registered with a call to
  `TINT_WGSL_PROGRAM_FUZZER()`
- Automatically convert the `Program` to an IR module and run the
  function for each function registered with
  `TINT_IR_MODULE_FUZZER()`. Note: The `Program` is converted to an IR
  module for each registered IR fuzzer as the module is mutable.

### `tint_ir_fuzzer`

`tint_ir_fuzzer` accepts binary [protocol
buffer](https://protobuf.dev/) inputs and uses
[`libprotobuf-mutator`](https://github.com/google/libprotobuf-mutator)
for mutating this binary format directly.

The suffix `.tirb` is sometimes used for IR fuzz test case files,
which helps our tooling infer the format. The fuzzer itself does not
generate/depend on this suffix though, instead using the `libFuzzer`
standard prefixes, i.e. `crash-...` and `slow-...`, when needed.

The input protobuf is defined in
[src/tint/utils/protos/ir_fuzz/ir_fuzz.proto](../../src/tint/utils/protos/ir_fuzz/ir_fuzz.proto)
is a composite of two elements, a protobuf
[src/tint/utils/protos/ir/ir.proto](../../src/tint/utils/protos/ir/ir.proto)
that defines the actual IR for the test case, and an opaque binary
blob that includes the [additional fuzzer
parameters](additional-fuzzer-data) akin to `tint_wgsl_fuzzer`.

The entry point for the fuzzer lives at
[`src/tint/cmd/fuzz/ir/main_fuzz.cc`](../../src/tint/cmd/fuzz/ir/main_fuzz.cc).

#### Extended command line arguments

This fuzzer accepts the same set of flags as `tint_wgsl_fuzzer` (both
`libFuzzer` and extended), expect `--dump` which is currently not
supported.

#### Behavior
The `tint_ir_fuzzer` will do the following:

- Decode the binary encoded protobuf into a `Program` for the
  IR module and a binary blob that will be passed into each function
  as options. If the input cannot be decoded due to being invalid or
  containing select constructs that can never occur in real world
  inputs, then the fuzzer will early return.
- Validate that decoded IR module, and will early-return if there are
  any errors.
- For each fuzzer function registered via `TINT_IR_PROGRAM_FUZZER()`,
  make a copy of the IR module, since it is mutable, and then invoke
  the fuzzer function.
- Validate that outputted IR module state, and raise an error if it
  fails, since this indicates a functional issue with a transform or
  the initial validation missed an illegal state.

#### Working with test cases

Since the input test cases for `tint_ir_fuzzer` are in a non-human
readable format, there is additional tooling needed for working with
them.

##### ir_fuzz_dis

For displaying the IR contents of a test case file (i.e. `crash-...`,
or `foo.tirb`), there is a disassembler `ir_fuzz_dis`, which is the
easiest way to dump out the contents of a test case. This disassembler
supports a subset of the Tint CLI for outputting in various formats
and to files, etc.

The disassembler has two significant known limitations though.

First, it does not dump out the binary options blob. This is because
this data is interpreted on a per-fuzzer function basis, so there
isn't a general human readable interpretation of its contents. If you
need to understand how the binary blob is being handled by a fuzzer
you will either need to add logging or use a debugger.

The other known limitation is that this disassembler depends on Tint's
IR printing code, which assumes well formed inputs. Sometimes it will
fail to output anything, or more subtly not output values it did't
expect. For example if an instruction only expects 2 params and there
are actually 3 in the IR, then the disassembler may only print the
first two. The validator should catch these issues and give you useful
warnings, but since it is a WIP itself there may be omissions in its
implementation.

##### protoc

Due to the second limitation on the disassembler, there are times
where you will need to dump the raw contents of the binary protocol
buffer in a textual format. This can be done using the tool `protoc`
which is part of `libprotobuf` and either installed via a system
package or built as part building the fuzzers. A full tutorial on
using protoc is beyond the scope of this document, see [protobuf
docs](https://protobuf.dev/getting-started/cpptutorial/) for more
details, but the basic invocation looks like this:

```bash
out/Fuzzer/protoc --decode tint.cmd.fuzz.ir.pb.Root ./src/tint/utils/protos/ir_fuzz/ir_fuzz.proto < ./input.tirb
```

The important thing to note is the use of `--decode
tint.cmd.fuzz.ir.pb.Root` to decode just the IR portion of the input,
but still needing to supply top-level `ir_fuzz.proto` as the format.

##### ir_fuzz_as

For generating binary test cases a rudimentary assembler,
`ir_fuzz_as`, has been implemented.

It is primarily used for converting the Tint test shaders into a seed
corpus for `tint_ir_fuzzer`, but can be used to convert user supplied
shaders into IR tests cases.

Similar to the disassembler it only operates on the shader/IR portion
of the test case format, and does not support embedding/manipulating
the binary blob portion. (There is currently no roundtrip workflow for
taking a test case binary, unpacking it, modifying it, and repacking
it with original blob, other then manually unpacking and packing
protobufs, which is left as an exercise to the reader).

It also suffers from the same limitation of the disassembler with
regards to not handling malformed inputs particularly well, since it
depends on the general Tint CLI parsing/printing infrastructure.

It is capable of dumping raw text of the IR protobuf that it
generates, which can be useful for understanding how the IR is being
encoded/decoded, since you can take a snippet of valid WGSL and dump
out what the protobuf looks like for it.

## Debugging

To debug a specific registered fuzzer function, one strategy is to add
a `TINT_ICE` call at the top of the function, and then run the fuzzer
with `-filter <name>` to have it only run that specific fuzzer. When
the function is called, the libfuzzer harness will emit a crash file
that can be used as input on subsequent runs. Remove the `TINT_ICE`
and run the fuzzer again using this crash file.

For example, if we wish to debug `tint::msl::writer::IRFuzzer`, we
would first insert a `TINT_ICE` at the top:

```c++
Result<SuccessType> IRFuzzer(core::ir::Module& module,
                             const fuzz::ir::Context& context,
                             Options options) {
    TINT_ICE() << "Crash";
    // Comment out the rest of the body to avoid unreachable code warnings
}
```

Build and run the fuzzer, filtering in this function:

```bash
autoninja -C out/libfuzz tint_wgsl_fuzzer
out/libfuzz/tint_wgsl_fuzzer -filter=tint::msl::writer::IRFuzzer
```

It can take a little while before libfuzzer generates a valid input
WGSL, but eventually it will call into the function and crash on the
ICE:

```
...
#71607  NEW    cov: 3633 ft: 8316 corp: 1045/8248b lim: 25 exec/s: 2469 rss: 158Mb L: 8/25 MS: 2 ShuffleBytes-PersAutoDict- DE: "true"-
#71669  NEW    cov: 3633 ft: 8317 corp: 1046/8266b lim: 25 exec/s: 2471 rss: 158Mb L: 18/25 MS: 2 CMP-ChangeByte- DE: "if"-
#71697  REDUCE cov: 3633 ft: 8317 corp: 1046/8264b lim: 25 exec/s: 2472 rss: 158Mb L: 6/25 MS: 3 PersAutoDict-ChangeBit-EraseBytes- DE: "\001\002"-
ICE while running fuzzer: 'tint::msl::writer::IRFuzzer'
..\..\src\tint\lang\msl\writer\writer_fuzz.cc:63 internal compiler error: Crash
==25204== ERROR: libFuzzer: deadly signal
NOTE: libFuzzer has rudimentary signal handlers.
      Combine libFuzzer with AddressSanitizer or similar for better crash reports.
SUMMARY: libFuzzer: deadly signal
MS: 1 PersAutoDict- DE: "or"-; base unit: b34d87c378ebbbfbfd475303dcc75d1d1b2a7c7a
0x2f,0x2f,0x33,0x33,0x33,0x33,0x6f,0x72,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x2a,0x2a,0x30,0x32,
//3333or3333333333**02
artifact_prefix='./'; Test unit written to ./crash-21563a85afd5322d9e17c1c43fd3d4029778d6e7
Base64: Ly8zMzMzb3IzMzMzMzMzMzMzKiowMg==
```

Note that the second to last line specifies that the input test was
written to a file. Now we can remove the `TINT_ICE` and run the fuzzer
with just this file as input:

```bash
out/libfuzz/tint_wgsl_fuzzer ./crash-21563a85afd5322d9e17c1c43fd3d4029778d6e7
```

## Profiling

It is possible to profile the fuzzers using callgrind from
[valgrind](https://valgrind.org/). Profiling reports from callgrind
can be processed using `callgrind_annotate` to get human readable text
or visualized using
[KCachegrind](https://kcachegrind.github.io/html/Home.html).

**Note:** `callgrind` is not a sampling profiler (e.g. `perf`), which
means it traces all the calls during the run which produces more
complete data about the execution, but at the cost of causing a
significant (i.e. orders of magnitude) performance hit. If you are
looking for a fast, approximate profiling tool I would recommend
looking at using `perf`.

### Prerequisites
#### System

You will need to have valgrind (and optionally KCachegrind) installed
on your machine. For APT based machines this looks like:

```bash
sudo apt get valgrind

# Optionally for visualization
sudo apt get kcachegrind
```

**Note:** If you are not running KDE or another QT based DE,
installing KCachegrind will likely pull in a bunch of framework
dependencies. Alternatives include: QCachegrind which is a lighter
weight version of KCachegrind, and
[gprof2dot](https://github.com/jrfonseca/gprof2dot) which supports
outputting a variety of profiling formats to Graphviz dot files

#### Build Configuration

Because profiling requires symbol information and is going to be doing
a bunch of call interception, there are some build flags that must be
enabled and some that must be disabled in your `args.gn`

```gn
# If not enabled, profiling will still run, the trace will just be
# empty
is_debug = true

# Technically will run with sanitizers on, but execution will likely
# halt early due to the sanitizers triggering on the profiling
# hooks. These flags are off by default in args.gn, but if you are
# working with fuzzers regularly, you may have them enabled for your
# specific build
is_asan = false
is_msan = false
is_<any other san> = false

```

### Example Workflow

The steps for profiling a hypothetical WGSL fuzzer test case that is
causing timeouts.

After doing whatever other triage I normally would do and determining
that this is a performance bottleneck problem that requires
profiling. The first step will be to actually generate the profiling
trace.

```bash
valgrind \
    --tool=callgrind \
    --trace-children=yes \
    --dump-instr=yes \
    --collect-jumps=yes \
    out/gn-callgrind/tint_wgsl_fuzzer triage/really_slow.wgsl
```

* `--trace-children=yes` is required for valgrind to follow flow
across forks. Without this you will just get a trace of the libfuzzer
framework
* `--dump-instr=yes` and `--collect-jumps=yes` are needed to generate
proper data attributing time up to parents. Without these flags each
function call will be only assessed the time actually spent in the
function's code and not include the time of anything it calls into.

**Note:** This profiling can be very slow, this is a start executing
and go get a snack type of task.

After the profiling is done, I manually process the output, since the
paths to files in report will be relative to the binary location, i.e
`out/gn-callgrind`, so will include a leading `../../`. The original
output file will be something like `callgrind.out.<pid>` where <pid>
is the process id for the specific invocation.

```bash
sed -i 's#\./\.\./\.\./#./#g' callgrind.out.<pid> > callgrind.out.fixed.<pid>
```

And then to visualize and start inspecting the trace

```bash
kcachegrind callgrind.out.fixed.<pid>
```

As mentioned you can use `callgrind_annotate`, which is installed as
part of valgrind, to pull out information like the N most expensive
functions. The man page for `callgrind_annotate` is relatively brief,
so I would suggest using your favourite search engine to find examples
of specific usages.

---

### Tint Fuzzing Experiments (`docs/tint/fuzzing-experiments.md`)

# Tint Fuzzing Experiments

The Tint fuzzing experiment/benchmarking framework is designed to
automate the process of comparing changes to the Tint fuzzers. It
measures hardware performance, fuzzer coverage percentage rates, and
test case execution rates to come up with abstract performance scores
to compare versions of the fuzzers.

This framework is part of the general fuzz utility for Tint found in
`./tools/src/cmd/fuzz`.

This document explains the high-level architecture of the experiments
framework, the environment prerequisites, and a step-by-step
walkthrough of how to define, execute, and process an
experiment. Along with a reference section at the end for the
experiment configuration JSON format,

---

## Prerequisites

Running fuzzing experiments requires a local Dawn repository and a GN
build directory configured for fuzzing and coverage.

To support correct instrumentation, your GN build (`args.gn`) must
include at least the following flags:

```gn
use_libfuzzer = true
tint_build_wgsl_reader = true
use_clang_coverage = true
optimize_for_fuzzing = false  # Cannot be used with coverage enabled
```

Additional flags must be enabled depending on the fuzzers selected:
- **IR Fuzzers (`tint_ir_fuzzer`, `tint_ir_mesa_fuzzer` )**: Require
  `tint_build_ir_binary = true` and `tint_has_protobuf = true`.
- **Mesa Fuzzers (`tint_wgsl_mesa_fuzzer`, `tint_ir_mesa_fuzzer`)**:
  Require `tint_build_mesa = true` and
  `tint_build_fuzzer_vulkan_support = true`.

The tool will produce an error if the flags are not correctly
configured. The tool does not support using a pre-built version of the
Tint fuzzers, nor the CMake builds.

**Note:** For the most applicable results it is recommended that all
backends are turned on (`tint_build_hlsl_writer = true`,
`tint_build_msl_writer = true`, `tint_build_spv_writer = true`)

**Note:** Though the fuzzers can be built and run with sanitizers
turned on (`is_asan = true`, etc), this is not recommended, because
the sanitizers tend to dominate the runtime of the execution, so
produce substantially lower quality data

---

## Defining an Experiment

An experiment is represented by a dedicated directory containing:
1. An `experiment.json` configuration file.
2. A `corpora/` directory holding the seed corpora for the various
   fuzzing modes.

### Directory Structure

```directory
my_experiment/
├── experiment.json
└── corpora/
    ├── wgsl_seed/
    │   ├── shader1.wgsl
    │   └── shader2.wgsl
    └── empty/
```

### Writing `experiment.json`

The `experiment.json` defines variables like the commit hash to check
out, specific fuzzers to test, and duration of the experiments.

Here is an example setup for comparing fuzzer performance using both a
standard corpus and an empty directory as starting points:

```json
{
    "name": "example",
    "hash": "4c2395a860ab76b44f7256c25281dfd3a3680192",
    "fuzzers": ["tint_wgsl_fuzzer", "tint_ir_fuzzer"],
    "burnin_enabled": false,
    "normalization_duration": 10,
    "normalization_iterations": 2,
    "wgsl_normalization_corpus": "wgsl_seed",
    "ir_normalization_corpus": "empty",
    "wgsl_corpora": [
        { "name": "wgsl_full", "path": "wgsl_seed" },
        { "name": "wgsl_empty", "path": "empty" }
    ],
    "ir_corpora": [
        { "name": "ir_empty", "path": "empty" }
    ],
    "default_iterations": 5,
    "durations": [
        {"runs": 10000},
        {"runs": 50000},
        {"runs": 100000}
    ]
}
```

- `name` is just a user visible title for this experiment which will
  appear in reports/logging, but has no semantic meaning
- `hash` is the specific version of the Dawn repo that will be checked
  out to build the fuzzers. (The tool will be built and run from what
  ever hash the repo is at when you call it, not this version)
- `fuzzers` are the specific fuzzer binaries to test
- `burnin_enabled`, `normalization_duration`, and
  `normalization_iterations` are turned down or off in this example so
  that the setup phases run fast, but these should be removed or
  increased when generating statistically valid data
- `*_normalization_corpus` are the corpora to use when establishing
  baselines for normalizing performance numbers between
  machines/environments
- `*_corpora` are the various starting corpora to experiment using
- `default_iterations` specifies how many times to run each experiment
  if not explicitly overridden
- `durations` specifies the sets experiments to run, broadly you will
  want to specify a spread of values here to get a reasonable
  graph performance over time, since the framework doesn't
  sample these values during the experiment run

Full details on the various options are detailed below.

**Note:** The number of actual runs performed will be `# of fuzzers` X
`# of corpora` X `# of durations` X `# of iterations`

---

## Running an Experiment

To start the experiment on your system, execute the `fuzz` helper with
the `experiment` subcommand:

```bash
tools/run fuzz experiment -build out/fuzzers my_experiment/
```

where `out/fuzzers` is a GN configured build with the correct values
set in `args.gn`

**Note:** `experiment` does support the `-j` flag for running
concurrent operations, but defaults it to **1**. This is because
saturating the RAM or CPU in the execution environment (e.g. -j `max
num of cores`) can significantly impact the results. It is recommended
to experiment with a mini-version of an experiment.json to determine
an appropriate value to use, (try num of cores / 2 or / 4 as a
starting point)

### Execution Pipeline

The framework guides execution through the following phases:

1. **Validation & Sync**: The tool checks the active GN arguments in
   your build directory to ensure they match the requirements. It then
   checks out the requested commit hash (`hash` field) and runs
   `gclient sync` automatically. (The state of repo should be returned
   to the original state)
2. **Binary Preparation**: It builds the selected fuzzer binaries and
   copies them, alongside their dynamic library dependencies and
   required LLVM tools (`llvm-profdata`, `llvm-cov`), into
   `my_experiment/bin/`.
3. **Burn-in (Optional)**: If enabled, it executes multiple parallel
   workloads for 5 minutes (`burnin_duration` & `burnin_enabled`) to
   bring the physical host's CPU to a thermal steady state. This
   prevents throttling and normalization profiling skews during the
   experiment.
4. **Normalization**: It executes multiple normalization profiling
   runs for each fuzzer against its designated normalization corpus
   (controlled by `normalization_duration` and
   `normalization_iterations`). The resulting average execution rate
   (runs/sec) and standard error (SEM) are saved to
   `normalization_scores.json` and are used to compute "Normalized CPU
   Seconds" for each fuzzer. All individual profiling runs are
   archived in `normalization_iterations.csv`.
5. **Task Execution**: It calculates the Cartesian product of
   experiments (Fuzzer × Corpus × Duration × Iteration). These are run
   concurrently across available CPU cores (configurable via
   `-j`). Results are saved directly under
   `my_experiment/results/`. Each task directory stores a `state.json`
   file, captured logs, mutated corpus files, and `.profraw` coverage
   profiles.

---

## Processing and Analyzing Results

Once execution has completed, you can aggregate and process results
using the `analyze` subcommand:

```bash
tools/run fuzz analyze my_experiment/
```

### Analysis Pipeline

1. **Coverage Generation**: The analyzer locates the `.profraw` file
   for each completed iteration, merges them using `llvm-profdata`,
   and executes `coverage.py` to produce standard `.lcov` coverage
   logs.
2. **Component Mapping**: It parses the `.lcov` profiles and
   aggregates line coverage metrics separately for **Tint**
   (`src/tint/` and `src/utils/`), **Mesa** (`third_party/mesa/`), and
   **DXC** (`third_party/directx-headers/`).
3. **Statistics Computation**: It computes arithmetic means and
   standard errors for:
   - Coverage percentage
   - Normalized CPU seconds
   - Coverage rates (% of lines covered per CPU second, using
     quadrature error propagation)
4. **Report Export**:
   - `raw_iteration_data.csv`: A flat table of raw execution counts,
     actual runtimes, and coverage hits for every single iteration.
   - `calculated_statistics.csv`: A flat table of calculated
     statistics for every experiment category (Fuzzer × Corpus).
   - `experiment_report.md`: A human-readable Markdown summary report.

---

## Appendix: `experiment.json` Reference

The following sections define the full configuration schema for
`experiment.json`.

### Root Attributes

| Field                       | Type                   | Description                                                                                                                                  |
|:----------------------------|:-----------------------|:---------------------------------------------------------------------------------------------------------------------------------------------|
| `name`                      | `string`               | A human-readable identifier for the experiment, used in the logging/reports.                                                                 |
| `hash`                      | `string`               | The Git commit hash checkout from which the binaries should be compiled.                                                                     |
| `fuzzers`                   | `array of strings`     | Fuzzer target names to test(Supported values: `"tint_wgsl_fuzzer"`, `"tint_ir_fuzzer"`, `"tint_wgsl_mesa_fuzzer"`, `"tint_ir_mesa_fuzzer"`). |
| `timeout`                   | `integer` *optional*   | Timeout limit in seconds for a single fuzzer execution on a test case in libFuzzer (defaults to 5).                                          |
| `burnin_enabled`            | `boolean` *optional*   | If true, launches parallel workloads to warm up the machine before benching (defaults to true).                                              |
| `burnin_duration`           | `integer` *optional*   | Target duration in seconds for the initial thermal burn-in (defaults to 300).                                                                |
| `normalization_duration`    | `integer` *optional*   | Execution duration in seconds for each iteration of the normalization microbenchmarking phase (defaults to 60).                              |
| `normalization_iterations`  | `integer` *optional*   | Number of iterations to perform for normalization microbenchmarking (defaults to 5).                                                         |
| `wgsl_normalization_corpus` | `string`               | Directory path relative to the root `corpora/` to use when performing normalization on WGSL fuzzers.                                         |
| `ir_normalization_corpus`   | `string`               | Directory path relative to the root `corpora/` to use when performing normalization on IR fuzzers.                                           |
| `wgsl_corpora`              | `array of CorpusDef`   | Corpora definitions available to run with WGSL fuzzers.                                                                                      |
| `ir_corpora`                | `array of CorpusDef`   | Corpora definitions available to run with IR fuzzers.                                                                                        |
| `default_iterations`        | `integer`              | The default number of times to repeat every Fuzzer/Corpus/Duration combination if not otherwise specified.                                   |
| `durations`                 | `array of DurationDef` | The target run lengths defining the experiment stopping criteria.                                                                            |

### `CorpusDef` Format

| Field  | Type     | Description                                                              |
|:-------|:---------|:-------------------------------------------------------------------------|
| `name` | `string` | A human-readable identifier for the corpus, used in the logging/reports. |
| `path` | `string` | Directory path relative to the root `corpora/` directory.                |

### `DurationDef` Format

**Note**: Must specify exactly one and only one of `seconds` or
`runs`.

**Note**: Because of how libFuzzer operate `seconds` and `runs` are
approximate values. Fuzzing should run for at least this limit, but
will normally be a little over.

| Field        | Type                 | Description                                                                                                                                               |
|:-------------|:---------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------|
| `seconds`    | `integer` (optional) | Number of seconds to run for (maps to `-max_total_time`).                                                                                                 |
| `runs`       | `integer` (optional) | Number of inputs/test cases to run for(maps to `-runs`).                                                                                                  |
| `iterations` | `integer` (optional) | Override the experiment `default_iterations` specifically for this duration target. Useful for manually load balancing very long and very short durations |

---

### Tint Gen (`docs/tint/gen.md`)

# Code generation

Tint makes use of code generation tooling to emit source code and build files
used to build Tint.

All code is generated with `./tools/run gen`.

The generator uses heavy use of Go's [text/template](https://pkg.go.dev/text/template)
package. This is augmented with the functions declared in [`tools/src/template/template.go`](../../tools/src/template/template.go), and the data types provided by the tooling.

## Build file generation

The generator will scan all the Tint source directories to produce `BUILD.gn`
and `BUILD.cmake` files. These files contain the per-directory targets,
conditionals and inter-target dependency information which is labour-intensive
to maintain by hand.

The bulk of the build generator logic is in the file: [`tools/src/cmd/gen/build/build.go`](../../tools/src/cmd/gen/build/build.go)

### Targets

There are 8 kinds of build target:

* `cmd` targets are executables.
* `lib` targets are libraries used in production code, and can also be used as
  a dependency by any other target kind .
* `test` targets are libraries used by Tint unittests. Must not be used by
  production code.
* `test_cmd` are test executables.
* `bench` targets are libraries used by Tint benchmarks. Must not be used by
  production code.
* `bench_cmd` are benchmark executables.
* `fuzz` targets are libraries used by Tint fuzzers. Must not be used by
  production code.
* `fuzz_cmd` are fuzzer executables.

The build generator uses a file naming convention based on the file name before the extension to classify each source file to a single target kind.

* Source files named `test` or with a `_test` suffix are classed as `test` library targets. \
  Example: `parser_test.cc`.
* Source files named `bench` or with a `_bench` suffix are classed as `bench` library targets. \
  Example: `writer_bench.cc`.
* Source files named `fuzz` or with a `_fuzz` suffix are classed as `fuzz` library targets. \
  Example: `writer_fuzz.cc`.
* Source files with the name `main` are classed as executable targets.
  These typically exist under `src/tint/cmd`. \
  Example: `cmd/tint/main.cc`.
* Source files with the name `main_test` are classed as test executable targets.
  These typically exist under `src/tint/cmd/test`. \
  Example: `cmd/test/main_test.cc`.
* Source files with the name `main_bench` are classed as benchmark executable targets.
  These typically exist under `src/tint/cmd/bench`. \
  Example: `cmd/benchmark/main_bench.cc`.
* Source files with the name `main_fuzz` are classed as fuzzer executable targets.
  These typically exist under `src/tint/cmd/fuzz`. \
  Example: `cmd/benchmark/main_bench.cc`.
* All other files are considered `lib` targets. \
  Example: `parser.cc`.

Each source directory can have at most one `lib`, `test`, `test_main`, `bench`, `bench_main` or `cmd`
target.

The graph of target dependencies must be acyclic (DAG).

Target dependencies are automatically inferred from `#include`s made by the source files.
Additional target dependencies can be added with the use of a [`BUILD.cfg` file](#buildcfg-files).

### External dependencies

All external dependencies must be declared in [`src/tint/externals.json`](../../src/tint/externals.json).

The syntax of this file is:

```json
{
    "external-target-name": {
        "IncludePatterns": [
          /*
            A list of #include path patterns that refer to this external target.
            You may use the '*' wildcard for a single directory, or '**' as a multi-directory wildcard.
          */
          "myexternal/**.h",
        ],
        /* An optional build condition expression to wrap all uses of this external dependency */
        "Condition": "expression",
    },
    /* Repeat the above for all external targets */
}
```

### `GEN_BUILD` directives

Source and build files can be annotated with special directives in comments to control the build file generation.

| Directive | Description |
|-----------|-------------|
| `GEN_BUILD:IGNORE_FILE` | Add to a source file to have the file ignored by the generator <br> Example: `// GEN_BUILD:IGNORE_FILE` |
| `GEN_BUILD:IGNORE_INCLUDE` | Apply to the end of a `#include` in a source file to ignore this include for dependency analysis <br> Example: `#include "foo/bar.h"  // GEN_BUILD:IGNORE_INCLUDE` |
| `GEN_BUILD:CONDITION(`_cond_`)` | Applies the build condition for this source file. <br> Example: `// GEN_BUILD:CONDITION(is_linux)` |
| `GEN_BUILD:DO_NOT_GENERATE` | Prevents the `BUILD.*` file from being generated. <br> Example: `# GEN_BUILD:DO_NOT_GENERATE` |

### `BUILD.cfg` files

A source directory may have an optional `BUILD.cfg` JSON file. The syntax of this file is:

```json
{
  /* Build condition to apply to all targets of this directory. */
  "Condition": "cond",
  /* Options for the 'lib' target */
  "lib": { /* see TargetConfig */ },
  /* Options for the 'test' target */
  "test": { /* see TargetConfig */ },
  /* Options for the 'bench' target */
  "bench": { /* see TargetConfig */ },
  /* Options for the 'cmd' target */
  "cmd": { /* see TargetConfig */ },
}
```

All fields are optional.

The syntax of `TargetConfig` is:

```json
{
  /* An override for the output file name for the target */
  "OutputName": "name",
  /* An additional condition for building this target */
  "Condition": "cond",
  "AdditionalDependencies": {
    "Internal": [
      /*
        A list of target name patterns that should in added as dependencies to this target.
        You may use the '*' wildcard for a single directory, or '**' as a multi-directory wildcard.
      */
    ],
    "External": [
      /*
        A list of external targets that should in added as dependencies to this target.
        Must match an external dependency declared in src/tint/externals.json
      */
    ]
  },
}
```

All fields are optional.

## Templates

The build generator will emit a `BUILD.`_ext_ file in each source directory, for each `BUILD.`_ext_`.tmpl` file found in [`tools/src/cmd/gen/build`](../../tools/src/cmd/gen/build).

The template will be passed the [`Directory`](../../tools/src/cmd/gen/build/directory.go) as `$`.

---

### Tint Intrinsic Definition Files (`docs/tint/intrinsic_definition_files.md`)

# Intrinsic definition files

Tint uses intrinsic definition to generate intrinsic tables, end-to-end tests and C++ enums.

These definition files have a `.def` extension and exist under the language subdirectories of `src/tint/lang/`. \
For example, the WGSL definition file can be found at `src/tint/lang/wgsl/wgsl.def`.

A definition file can contain the following declarations:

* [Imports](#imports)
* [Enumerators](#enumerators)
* [Types](#types)
* [Type matchers](#type-matchers)
* [Overload declarations](#overload-declarations)
  * [Builtin Functions](#builtin-functions)
  * [Value constructors](#value-constructors)
  * [Value conversions](#value-conversions)
  * [Operators](#operators)

## Imports

Syntax: `import "` _**project-relative-path**_ `"`

An import is similar to a C++ `#include` statement. It imports the content of the project-relative
file into this definition file so that declarations can be shared between languages.

Example: `import "src/tint/lang/core/access.def"`

## Enumerators

An named enumerator of symbols can be declared with `enum`.

Example:

```c
enum diagnostic_severity {
  error
  warning
  info
  off
}
```

These enum declarations can be used by templates to generate an equivalent C++ enum, along with a parser and printer helper method, using the helper template [`src/tint/utils/templates/enums.tmpl.inc`](../../src/tint/utils/templates/enums.tmpl.inc). \
See [`src/tint/lang/wgsl/enums.h.tmpl`](../../src/tint/lang/wgsl/enums.h.tmpl) as an example.

Enum declarations can also be used as constraints on type template parameters and overload template parameters.

## Types

Syntax: `type` _**name**_ _**[**_ `<` _**type-or-number-templates**_ `>` _**]**_

Type declarations form the basic primitives are used to match intrinsic overloads. A type can be referenced by type matchers and by builtin overloads.

The simplest type declaration has the form: `type name`

A type can be templated with any number of sub-types and numbers, using a `<`...`>` suffix.

Examples:

| Example               | Description                      |
|-----------------------|----------------------------------|
| `type my_type`        | Declares a type called `my_type` |
| `type vec3<T>`        | Declares a type called `vec3` which accepts a templated sub-type |
| `type vec<N: num, T>` | Declares a type called `vec` which accepts a templated number `N` and sub-type `T` |

### Annotations

Types may be annotated with `@precedence(N)` to prioritize which type will be picked when multiple types of a matcher match. This is typically used to ensure that abstract numerical types materialize to the concrete type with the lowest conversion rank. Types with higher precedence values will be matched first.

Example:

```ts
@precedence(5) type ia // This is preferred over all others
@precedence(4) type fa
@precedence(3) type i32
@precedence(2) type u32
@precedence(1) type f32
@precedence(0) type f16
```

Types may also be annotated with `@display("`_string_`")` to customize how the type is printed in diagnostic message. The string may contain curly-brace references to the templated types.

Example:

```ts
@display("vec{N}<{T}>") type vec<N: num, T>
```

Would display as `vec2<i32>` if `N=2` and `T=i32`.

## Type Matchers

Syntax: `matcher` _**name**_ `:` _**type_1 [**_ `|`  _**type_2 [ ...**_ `|` _**type_n ] ]**_

A type matcher can be used by overloads to match one or more types.

## Overload declarations

The overload declarations declare all the built-in functions, Value constructors, type
conversions and unary and binary operators supported by the target language.

An overload declaration can declare a simple static-type declarations, as well as overload
declarations that can match a number of different argument types via the use of template types,
template numbers and type matchers.

All overload declarations share the same declaration patterns, but the kind of declaration has a different prefix.

* [Builtin Functions](#builtin-functions) have a `fn` prefix
* [Value constructors](#value-constructors) have a `ctor` prefix
* [Value conversions](#value-conversions) have a `conv` prefix
* [Operators](#operators) have a `op` prefix

For example: `fn isInf(f32) -> bool` declares an overload of the function `isInf` that accepts a single parameter of type `f32` and returns a `bool`.

### Parameters

All overload declarations use the same syntax for a parameter.

A parameter can be named or unnamed.

Unnamed parameters just use the name of constraint (type, type-matcher or template parameter).
Named parameters have a name and constraint, separated with a colon. Example `fn F(count : i32)`

| Code               | Description                                                                 |
|--------------------|-----------------------------------------------------------------------------|
| `fn F(i32)`        | function `F` has a single unnamed parameter, of type or matcher `i32`       |
| `fn F(count: i32)` | function `F` has a single parameter named `count`, of type or matcher `i32` |

Note: Parameters may use a type-matcher directly, but for `wgsl.def` it is encouraged to use an implicit template parameter. This helps the readability of the diagnostics produced. For example, instead of `fn F(scalar)`, prefer: `fn F[S: scalar](S)`.

### Templates

Builtin function overload declarations can support **explicit** and **implicit** template parameters.

Each template parameter has the syntax: _**name**_ _**[**_ `:` _**constraint ]**_
Where the optional _**constrant**_ can be a [type](#types), [type matcher](#type-matchers), or `: num` for a number template.

**Explicit template parameters:**

* Are optional in the overload declaration.
* Have the syntax: `<` _**template_parameter_list**_ `>`
* Must be declared before implicit template parameters (if declared) and parameter list.
* Must be provided by the call of the function, with exactly one template argument provided for each template parameter.
* May reference implicit template parameters.
* Can only be declared on [Builtin Functions](#builtin-functions).
* Do not support number templates.

**Implicit template parameters:**

* Are optional in the overload declaration.
* Are inferred from the arguments passed to the intrinsic.
* Have the syntax: `[` _**template_parameter_list**_ `]`
* Must be declared after explicit template parameters (if declared), and before the parameter list.
* Cannot reference explicit template parameters.
* Support number templates.

Examples:

```ts
// A function overload that has no explicit or implicit template parameters
fn F(i32) -> i32
```

```ts
// A function overload that accepts an explicit template parameter 'T', which matches any type.
// The overload takes a single parameter of type 'T' and returns a value of type 'T'
fn F<T>(T) -> T
```

```ts
// A function overload that accepts an implicit (inferred) template parameter 'T', which must be of
// the type or type-matcher 'scalar'.
// The overload takes a single parameter of type 'T' and returns a value of type 'T'
fn F[T: scalar](T) -> T
```

```ts
// A function overload that accepts an explicit template parameter 'T', which must be of a 'vec'
// type of the inferred element type 'E' and number 'N'.
// 'E' is an implicit template type parameter, and must match the type or type-matcher 'scalar'
// 'N' is an implicit template number parameter.
// The overload takes a single parameter of type 'T' and returns a value of type 'T'
fn F< T: vec<N, E> >[E: scalar, N: num](T) -> T
```

### Matching algorithm for a single overload

The goal of overload matching is to compare a function call's arguments and any
explicitly provided template types in the program source against an overload
declaration, to determine if the call satisfies the form and type constraints
of the overload. If the call matches an overload, then the overload is added
to the list of 'overload candidates' used for overload resolution (described below).

Prior to matching an overload, all template types are undefined.

Implicit template types are first defined with the type of the leftmost argument
that matches against that template type name. Subsequent arguments that attempt
to match against the template type name will either reject the overload or
refine the template, in one of 3 ways:

* (a) Fail to match, causing the overload to be immediately rejected.
* (b) Match the existing implicit template type, either exactly or via implicit
      conversion, and overload resolution continues.
* (c) Match via implicit conversion of the currently defined template type
      to the argument type. In this situation, the template type is refined
      with the more constrained argument type, and overload resolution
      continues.

To better understand, let's consider the following hypothetical overload declaration:

```ts
matcher scalar: f32 | i32 | u32 | bool
fn foo[T: scalar](T, T)
```

Where:


| Symbol        | Description                                                                      |
|---------------|----------------------------------------------------------------------------------|
|  `T`          | is the template type name                                                        |
| `scalar`      | is a matcher for the types `f32`, `i32`, `u32` or `bool`                         |
| `[T: scalar]` | declares the implicit template parameter `T`, with the constraint that `T` must match one of `f32`, `i32`, `u32` or `bool`. |

The process for resolving this overload is as follows:

1. The overload resolver begins by attempting to match the parameter types from left to right.
   The first argument type is compared against the parameter type `T`.
   As the implicit template type `T` has not been defined yet, `T` is defined as the type of the first argument.
   There's no verification that the `T` type is a scalar at this stage.
2. The second argument is then compared against the second parameter.
   As the template type `T` is now defined as the first argument type, the second argument type is
   compared against the type of `T`. Depending on the comparison of the argument type to the
   template type, either the actions of (a), (b) or (c) from above will occur.
3. If all the parameters matched, constraints on the template types need to be checked next.
   If the defined type does not match the constraint, then the overload is no longer considered.

This algorithm for matching a single overload is less general than the algorithm described in the
WGSL spec, but it makes the same decisions because the overloads defined by WGSL are monotonic
in the sense that once a template parameter has been refined, there is never a need to backtrack
and un-refine it to match a later argument.

The algorithm for matching template numbers is similar to matching template types, except numbers
need to exactly match across all uses - there is no implicit conversion. Template numbers may
match integer numbers or enumerators.

### Overload resolution for candidate overloads

If multiple candidate overloads match a given set of arguments, then a final overload resolution
pass needs to be performed. The arguments and overload parameter types for each candidate overload
are compared, following the algorithm described at: https:www.w3.org/TR/WGSL/#overload-resolution-section

If the candidate list contains a single entry, then that single candidate is picked, and no
overload resolution needs to be performed.

If the candidate list is empty, then the call fails to resolve and an error diagnostic is raised.

### Common attributes

Intrinsics that can be used in constant evaluation expressions are annotated with `@const` or `@const(` _**name**_ `)`.

The templates use this attribute to associate the overload with a function in [`src/tint/lang/core/constant/eval.h`](../../src/tint/lang/core/constant/eval.h).

## Builtin functions

Syntax: `fn` _**name [**_ `<` _**explicit-template-parameters**_ `>` _**] [**_ `[` _**implicit-template-parameters**_ `]` _**]**_`(` _**parameters**_ `) ->` _**return_type**_

Builtin function overloads support both implicit and explicit template parameters.

Builtin functions may be annotated with `@must_use` to prevent the function being used as a call-statement in WGSL.

### Examples

| Code                         | Description                                                                        |
|------------------------------|------------------------------------------------------------------------------------|
| `fn F()`                     | Function called `F`. No template types or numbers, no parameters, no return value` |
| `fn F() -> R`                | Function with `R` as the return type                                               |
| `fn F(f32, i32)`             | Two fixed-type, anonymous parameters                                               |
| `fn F(U : f32)`              | Single parameter with name `U`                                                     |
| `fn F[T](T)`                 | Single parameter of unconstrained implicit template type `T` (any type)            |
| `fn F[T: scalar](T)`         | Single parameter of constrained implict template type `T` (must match `scalar`)    |
| `fn F[T: fiu32](T) -> T`     | Single parameter of constrained implicit template type T (must match `fiu32`)<br>Return type is of the parameter type |
| `fn F[T, N: num](vec<N, T>)` | Single parameter of `vec` type with template number `N` and element template type `T` |
| `fn F[A: access](texture_storage_1d<f32_texel_format, A>)` | Single parameter of type `texture_storage_1d` type with implicit template number  `A` constrained to the enum `access`, and a texel format that that must match `f32_texel_format` |

## Value constructors

Syntax: `ctor` _**type-name [**_ `[` _**implicit-template-parameters**_ `]` _**]**_`(` _**parameters**_ `) ->` _**return_type**_

Value constructors construct a value of the given type.

## Value conversions

Syntax: `conv` _**type-name [**_ `[` _**implicit-template-parameters**_ `]` _**]**_`(` _**parameters**_ `) ->` _**return_type**_

Value conversions convert from one type to another.

## Operators

Unary operator syntax: `op` _**operator [**_ `[` _**implicit-template-parameters**_ `]` _**]**_`(` _**parameter**_ `) ->` _**return_type**_

Binary operator syntax: `op` _**operator [**_ `[` _**implicit-template-parameters**_ `]` _**]**_`(` _**lhs-parameter**_ `,` _**rhs-parameter**_ `) ->` _**return_type**_

---

### Tint Ir Extensions (`docs/tint/ir-extensions.md`)

# IR Extensions

The IR can be extended by the various backends as needed. This can be done through
custom transforms, capabilities, types, intrinsics or through subclasses of other values.

## Transforms
In a few cases we need to make transforms on the IR which are specific to a given backend.
In these cases the transforms live in the `writer/raise` folder for the backend (e.g.
`lang/msl/writer/raise/`). These transforms are then added into the `raise.cc` file to run the
transform when necessary.

Each transform defines its own configuration if necessary. This is typically done by defining a
`struct` in the transform `.h` file with a naming matching the transform with a  `Config` suffix.
(e.g. `ModuleConstant` transform has a `ModuleConstantConfig` structure). When the transform is
called in `raise` the configuration will be created from the generator configuration.

Transforms have a set of `Capabilities` they support. Often the list of supported capabilities is
added to the `.h` file so they can be shared with a fuzzer for the transform. The set of
capabilities is typically named `k` + transform name + `Capabilities` (e.g. `ModuleConstant` has
`kModuleConstantCapabilities`). The set of capabilities should be kept as minimal as possible for
the transform. The capabilities are used to control what extra features the IR validator will allow.

Transforms all follow a similar pattern of a free function named after the transform and then a
`State` struct with a `Process` method. The free function will run the validator if necessary and
then call the `State::Process` method.

## Intrinsics
Each backend has a `.def` file listing the intrinsics for that backend (e.g. `lang/msl/msl.def`).
These custom intrinsics can be types or instructions. See
[intrinsic_definition_files.md](intrinsic_defintion_files.md).

### Instructions
The intrinsic instructions typically match the signature of the backend, so may take values which
don't make sense coming from core IR or WGSL. (For instance, the `GroupNonUniform` SPIR-V
instructions take a `scope` parameter which doesn't exist, and isn't used, but is there to match the
API on the SPIR-V side.

The naming of the intrinsic is set to match the casing of the backend instruction. So,
`group_non_uniform_s_min` turns into `spirv::BuiltinFn::kGroupNonUniformSMin` which matches to the
`OpGroupNonUniformSMin` instruction in SPIR-V.

As instructions are added they need to be added to the `printer.cc` file for the backend in order to
emit the intrinsic. The instruction also needs to be added to the `builtin_fn.cc.tmpl` file for the
backend to add to the `GetSideEffects` switch. This lists if there are side effects to the
instruction which need to be accounted for when doing instruction inlining.

When these instructions are added to the IR they are done using the `BuiltinCall` class for that
backend. For example, to call the `kGroupNonUniformBroadcast` intrinsic in the SPIR-V backend we
call:

```
b_.Call<spirv::ir::BuiltinCall>(ty.u32(), spirv::BuiltinFn::kGroupNonUniformBroadcast, Vector{id}))
```

### Types
There are a few places to modify to create a new type. First, create the type `.h` and `.cc` files
in the backend `type/` folder. The type will inherit from `CastableBase<NewType, Type>` where
`NewType` is the name of the new type. At a minimum the class needs to override:

```
bool Equals(const UniqueNode& other) const override;

/// @returns the friendly name for this type
std::string FriendlyName() const override;

/// @param ctx the clone context
/// @returns a clone of this type
NewType* Clone(core::type::CloneContext& ctx) const override;
```

The class can also override:

```
/// @returns the size in bytes of the type. This may include tail padding.
/// @note opaque types will return a size of 0.
virtual uint32_t Size() const;

/// @returns the alignment in bytes of the type. This may include tail
/// padding.
/// @note opaque types will return a size of 0.
virtual uint32_t Align() const;
```

The `Align` and `Size` overrides are only necessary if the default of size 0 and align 0 do not work
for the new type.

The type can then be added to the `.def` file for the backend as `type new_type`. In order to use
the type in the type matches the `type_matches.h` file for the backend needs to be updated to have a
`MatchNewType` method and a `BuildNewType` method. The arguments end up matching the values passed
to the type implicit template parameters.

The new types can be created in the type manager by calling `ty.Get<spirv::types::NewType>()`,
providing any needed arguments for the type constructor.

### Enums
When defining custom enums in a given backend an extra attribute is needed to set the correct
namespace for the enum, otherwise it ends up in the core IR namespace. In the SPIR-V backend we want
the namespace to be `tint::spirv::type` so we declare enums as:

```
@ns("spirv::type") enum arrayed {
  NonArrayed
  Arrayed
}
```

The `@ns("spirv::type")` will put the enum into the correct namespace when emitted.

---

### Tint Ir (`docs/tint/ir.md`)

# Intermediate Representation

As Tint has grown the number of transforms on the AST has grown. This
growth has lead to several issues:

1. Transforms rebuild the AST and SEM which causes slowness
1. Transforming in AST can be difficult as the AST is hard to work with

In order to address these goals, an IR was introduced into Tint. The IR
is mutable, it holds the needed state in order to be transformed. The IR
is also translatable back into AST. It will be possible to generate an
AST, convert to IR, transform, and then rebuild a new AST. This
round-trip ability provides a few features:

1. Easy to integrate into current system by replacing AST transforms
   piecemeal
1. Easier to test as the resulting AST can be emitted as WGSL and
   compared.

The IR helps with the complexity of the AST transforms by limiting the
representations in the IR form. For example, instead of `for`, `while`
and `loop` constructs there is a single `loop` construct. `alias` and
`const_assert` nodes are not emitted into IR. Dead code maybe eliminated
during IR construction.

As the IR can convert into AST, we could potentially simplify the
SPIRV-Reader by generating IR directly. The IR is closer to what SPIR-V
looks like, so maybe a simpler transform.


## Design

The IR is composed of three primary concepts, `blocks`, `values`, and
`instructions`.

The `blocks` provide lists of `instructions` which are executed
sequentially. An `instruction` may contain `blocks`, `value` operands
and `value` results. A `value` is a block argument, function parameter,
constant result, or a result computed by an `instruction`.

Each `block` ends in a `terminator` expression. There are a few blocks
which maybe empty, `if-false block`, `loop-initializer` and
`loop-continuing`. Those blocks are optional. The `terminators` may hold
`values` which are returned out of the block being terminated.

Control flow is handled through `ControlInstructions`. These are the
`if`, `switch` and `loop` instructions. Each control instruction
contains `blocks` for the various control flow paths.

Transforming from AST to IR and back to AST is a lossy operation.
The resulting AST when converting back may not be the same as the
AST being provided. This transformation is intentional as it greatly
simplifies the number of things to consider in the IR. For instance:

* No `alias` nodes
* No `const_assert` nodes


### Code Structure
The code is contained in the `src/tint/lang/core/ir` folder and is broken down
into several classes. Note, the IR is a Tint _internal_ representation
and these files should _never_ appear in the public API.


#### Builder
The `Builder` class provides useful helper routines for creating IR
content. The Builder references an `ir::Module`.


#### Module
The top level of the IR is the `Module`. The module stores a list of
`functions`, the constant manager, the type manager, allocators and various other
bits of information needed by the IR.

A free `wgsl::reader::ProgramToIR` method is provided to convert from a
`Program` to an `ir::Module`. A similar `wgsl::writer::IRToProgram` free method is
provided for the reverse conversion.


#### Disassembly
An IR module can be emitted as IR assembly instructions using the disassembler. This
disassembly can be done by calling `core::ir::Disassembler()` and providing the IR
module to disassemble.


### Transforms
A transform system is available for IR. Each IR transform is a standalone process. There
is no transform manager. Data is provided to the transforms through their configuration.
Transforms mutate the IR in place, and can produce auxiliary information through their result if needed.

The common IR transforms live in `src/tint/lang/core/ir/transform`. These transforms
are for use by the various IR generator backends.

Each backend has custom transforms as needed. They live in the `raise`/ folder of the
specific backend.

The IR has a set of `Capabilities` which can be added/removed as transforms are
executed. The transforms know which capabilities they support and will ICE if
an unexpected capability is seen.


### Validation
The IR contains a validator. The validator checks for common errors
encountered when building IR. The validator is _not_ run in production
as any error is an internal coding error. The validator is executed by
the generators before starting, and after each transform executes in
debug mode.


### Naming
The instructions are internal to the IR, and the values in the IR do not
contain names. The IR module has a symbol table which can be used to
retrieve names for the instructions and values but those names may not
be the names provided originally by the AST.


### Control Flow

#### Block
A block contains the instruction lists for a section of code. A block
always ends in a terminator instruction.

The instructions in a block can be walked in linear fashion. The
control instructions do not need to be entered to walk a blocks
instructions.


#### Control Instructions
The control instructions provide containers for other blocks. These
include the `if`, `loop` and `switch` instruction. A control instruction
is a self contained scope on top of the containing block scope.


##### Control Instruction -- If
The if instruction is an `if-else` structure. There are no `else-if`
entries, they get moved into the `else` of the `if`. The if instruction
has two internal blocks, the `True` and `False` blocks.

A if instruction will _always_ have a `True` target. The `False` target
maybe empty.

The sub-blocks under the `if` can be terminated with `ExitIf`,
`ExitSwitch`, `ExitLoop`, `Continue` or `Return`.


#### Control Instruction -- Loop
All of the loop structures in AST merge down to a single IR `loop`
instruction.  The loop contains the `Body`, which is required and
optional `Initializer` and `Continuing` blocks.

The `loop` body can be terminated with a `ExitLoop`, `Continue`,
`NextIteration` or `Return`.

The `loop` continuing can be terminated with a `NextIteration`, or a
`BreakIf` terminator.

The `loop` initializer is always terminated with a `NextIteration`.

A while loop is decomposed as listed in the WGSL spec:

```
while (a < b) {
  c += 1;
}
```

becomes:

```
loop {
  if (!(a < b)) {
    break;
  }
  c += 1;
}
```

A for loop is decomposed as listed in the WGSL spec:
```
for (var i = 0; i < 10; i++) {
  c += 1;
}
```

becomes:

```
var i = 0;
loop {
  if (!(i < 10)) {
    break;
  }

  c += 1;

  continuing {
    i++;
  }
}
```

#### Control Instruction -- Switch
The switch instruction has a block for each of the `case/default` labels.

The `switch` case blocks can be terminated with an `ExitSwitch`,
`Continue`, or `Return`.


#### Expressions
All expressions in IR are single operations. There are no complex
expressions. Any complex expression in the AST is broken apart into the
simpler single operation components.

```
var a = b + c - (4 * k);
```

becomes:

```
%0:i32 = add %b, %c
%1:i32 = mul 4u, %k
%2:i32 = sub %0, %1
%v:ptr<function, i32, read_write> = var %2
```

This also means that many of the short forms `i += 1`, `i++` get
expanded into the longer form of `i = i + 1`.


##### Short-Circuit Expressions
The short-circuit expressions (e.g. `a && b`) are convert into an `if`
structure control flow.

```
let c = a() && b()
```

becomes

```
let c = a();
if (c) {
  c = b();
}
```

#### Values
There are several types of values used in the SSA form.

1. `Constant` Value
1. `InstructionResult` Value
1. `Function` Value
1. `FunctionParam` Value
1. `BlockParam` Value


##### Constant Value
All values in IR are concrete, there are no abstract values as
materialization has already happened. Each constant holds a lower level
`constant::Value` from the `src/tint/lang/core/ir/constant` system.


##### InstructionResult Value
The `InstructionResult` is the result of an instruction. The
temporaries are created as complex expressions are broken down into
pieces. The result tracks the usage for the value so you can determine
which instructions use this value. The `InstructionResult` also points
back to its `Source` instruction.


### Function Value
All `Functions` are values in the IR. This allows the function to be
provided as a Value argument to things like the `Call` instructions.


##### FunctionParam Value
The function param values are used to store information on the values
being passed into a function call.


### BlockParam Values
The `BlockParam` values are used to pass information for a
`MultiInBlock` (e.g. Loop body, and loop continuing).

There are no `Phi` instructions in the IR. Instead, `BlockParams` are used.
Instead of an `OpPhi` instruction which says which values came from
which parent blocks. Instead, values are passed into the blocks themselves
from the terminator of the previous block.

For example, block params can provide values into a loop, through the body
and through the continuing block.

```
loop {
    $B1: {  # initializer
      next_iteration 2, 4
    }
    $B2:(%1:i32, %2:i32) {  # body
      %3:i32 = add %1, %2
      continue %3
    }
    $B3:(%4:i32) {  # continuing
      %5:bool = lt %4, 10
      break-if %5
      next_iteration 6, 7
    }
}
```


## Alternatives Considered
Instead of going to a custom IR there are several possible other roads
that could be travelled.

### Mutable AST
Tint originally contained a mutable AST. This was converted to immutable
in order to allow processing over multiple threads and for safety
properties. Those desires still hold, the AST is public API, and we want
it to be as safe as possible, so keeping it immutable provides that
guarantee.

### Multiple Transforms With One Program Builder
Instead of generating an immutable AST after each transform, running
multiple transforms on the single program builder would remove some of
the performance penalties of going to and from immutable AST. While this
is true, the transforms use a combination of AST and SEM information.
When they transform they _do not_ create new SEM information. That
means, after a given transform, the SEM is out of date. In order to
re-generate the SEM the resolver needs to be rerun. Supporting this
would require being very careful on what transforms run together and
how they modify the AST.

### Adopt An Existing IR
There are already several IRs in the while, Mesa has NIR, LLVM has
LLVM IR. There are others, adopting one of those would remove the
requirements of writing and maintaining our own IR. While that is true,
there are several downsides to this re-use. The IRs are internal to the
library, so the API isn't public, LLVM IR changes with each iteration of
LLVM. This would require us to adapt the AST -> IR -> AST transform for
each modification of the IR.

They also end up being lower level then is strictly useful for us. While
the IR in Tint is a simplified form, we still have to be able to go back
to the high level structured form in order to emit the resulting HLSL,
MSL, GLSL, etc. (Only SPIR-V is a good match for the lowered IR form).
This transformation back is not a direction other IRs maybe interested
in so may have lost information, or require re-determining (determining
variables from SSA and PHI nodes for example).

Other technical reasons are the maintenance of BUILD.gn and CMake files
in order to integrate into our build systems, along with resulting
binary size questions from pulling in external systems.

---

### Tint Layering (`docs/tint/layering.md`)

# Tint Source Layering

The `BUILD.gn` is setup with small source units for various components
in Tint. The hierarchy of sources almost matches to folder structure in
tint (except for `base` which is a mix of things in `src/tint` and
`src/tint/util`.


```
/* Detailed source-code truncated for AI context efficiency. */
```

---

### Tint Origin Trial Changes (`docs/tint/origin-trial-changes.md`)

# Tint changes during Origin Trial

## Changes for M112

### Breaking changes

* Most builtin functions that return a value can no longer be used as a call statement. [tint:1844](crbug.com/tint/1844)
* The `sig` member of the return type of `frexp()` has been renamed to `fract`. [tint:1766](crbug.com/tint/1766)
* Calling a function with multiple pointer arguments that alias each other is now a error. [tint:1675](crbug.com/tint/1675)
* `type` deprecation has been removed. `alias` must be used now. [tint:1812](crbug.com/tint/1812)
* `static_assert` deprecation has been removed. `const_assert` must now be used. [tint:1807](crbug.com/tint/1807)

## Changes for M111

### New features

* The `workgroupUniformLoad` builtin function is now supported. [tint:1780](crbug.com/tint/1780)
* The `diagnostic` directive and `@diagnostic` attribute are now supported. [tint:1809](crbug.com/tint/1809)
  * The attribute is currently only supported on function declarations.

### Breaking changes

* You may need to add parentheses to less-than or greater-than binary expressions that now parse as template lists. For example `a(b<c, d>e)` will need parentheses around `b<c` or `d>e`. [tint:1810](crbug.com/tint/1810).
* Uniformity analysis failures are now an error [tint:880](crbug.com/tint/880)
  * The `derivative_uniformity` diagnostic filter can be used to modify the severity if needed.

## Deprecated Features

* The keyword to alias a type has been renamed from `type` to `alias`. [tint:1812](crbug.com/tint/1812)
* `static_assert` has been renamed to `const_assert`. [tint:1807](crbug.com/tint/1807)

## Changes for M110

### Breaking changes

* The `textureSampleLevel()` overload for `texture_external` has been removed. Use `textureSampleBaseClampToEdge()`. [tint:1671](crbug.com/tint/1671)

### Deprecated Features

* The `sig` member of the return type of `frexp()` has been renamed to `fract`. [tint:1757](crbug.com/tint/1757)
* Calling a function with multiple pointer arguments that alias each other is now a warning, and
  will become an error in a future release. [tint:1675](crbug.com/tint/1675)

## Changes for M109

### Breaking changes

* `textureDimensions()`, `textureNumLayers()` and `textureNumLevels()` now return unsigned integers / vectors. [tint:1526](crbug.com/tint/1526)
* The `@stage` attribute has been removed. The short forms should be used
  instead (`@vertex`, `@fragment`, or `@compute`). [tint:1503](crbug.com/tint/1503)
* Module-scope `let` is now an error. Use module-scope `const` instead. [tint:1580](crbug.com/tint/1584)
* Reserved words are now an error instead of a deprecation. [tint:1463](crbug.com/tint/1463)
* You may no longer use pointer parameters in `workgroup` address space. [tint:1721](crbug.com/tint/1721)

### New features

* Uniformity analysis failures are warnings again [tint:1728](crbug.com/tint/1728)
* You can now call texture builtins with a mix of signed and unsigned integer arguments. [tint:1733](crbug.com/tint/1733)

## Changes for M108

### New features

* `textureSampleBaseClampToEdge()` has been implemented. [tint:1671](crbug.com/tint/1671)

### Deprecated Features

* The `external_texture` overload of `textureSampleLevel()` has been deprecated. Use `textureSampleBaseClampToEdge()` instead. [tint:1671](crbug.com/tint/1671)

### Fixes

* Constant evaluation of type conversions where the value exceeds the limits of the target type have been fixed. [tint:1707](crbug.com/tint/1707)

## Changes for M107

### New features

* `saturate()` has been implemented. [tint:1591](crbug.com/tint/1591)

### Breaking changes

* Uniformity analysis failures are now an error [tint:880](crbug.com/tint/880)
* Indexing an array, vector or matrix with a compile-time expression that's out-of-bounds is now an error [tint:1665](crbug.com/tint/1665)

## Changes for M106

### New features

* `array()` constructor can now infer type and count. [tint:1628](crbug.com/tint/1628)
* `static_assert` statement has been added. [tint:1625](crbug.com/tint/1625)

### Deprecated Features

* The list of reserved words has been sync'd to the WGSL specification. [tint:1463](crbug.com/tint/1463)

## Changes for M105

### New features

* Module-scope `var<private>` can now infer the storage type, like function-scope `var`. [tint:1584](crbug.com/tint/1584)
* The `acosh`, `asinh`, and `atanh` builtin functions are now supported [tint:1465](crbug.com/tint/1465)

### Breaking changes

* The `smoothStep()` builtin has been removed (use `smoothstep` instead). [tint:1483](crbug.com/tint/1483)
* Module-scope `let` has been replaced with module-scope `const`. [tint:1580](crbug.com/tint/1584)
  * Note: Module-scope `const` does not support structure types. Use `var<private>` if you need a module-scope structure type.
* Struct members can no longer be separated with semicolons (use commas instead). [tint:1475](crbug.com/tint/1475)
* Single scalar matrix constructors have been removed. These were never part of the WGSL spec. [tint:1597](crbug.com/tint/1597)

### Deprecated Features

* The `@stage` attribute has been deprecated. The short forms should be used
  instead (`@vertex`, `@fragment`, or `@compute`). [tint:1503](crbug.com/tint/1503)

## Changes for M104

### New features

* Tint now supports abstract-numerics, removing the need to always suffix unsigned integers with `u` [tint:1504](crbug.com/tint/1504)
* Parsing of `@compute`, `@fragment` and `@vertex` added.

## Changes for M103

### New features

* Produce warnings for when calling barriers, textureSample, and derivative
builtins in non-uniform control flow [tint:880](crbug.com/tint/880)
* Matrix identity constructors and constructors for a single scalar value are now supported [tint:1545](crbug.com/tint/1545)

### Breaking changes

* Builtin `atomicCompareExchangeWeak` returns a struct instead of a vec2. [tint:1185](crbug.com/tint/1185)

## Changes for M102

### New Features

* Parentheses are no longer required around expressions for if and switch statements [tint:1424](crbug.com/tint/1424)
* Compound assignment statements are now supported. [tint:1325](https://crbug.com/tint/1325)
* Postfix increment and decrement statements are now supported. [tint:1488](crbug.com/tint/1488)
* The colon in case statements is now optional. [tint:1485](crbug.com/tint/1485)

### Breaking changes

* Struct members are now separated by commas. [tint:1475](crbug.com/tint/1475)
* The `@block` attribute has been removed. [tint:1324](crbug.com/tint/1324)
* The `@stride` attribute has been removed. [tint:1381](crbug.com/tint/1381)
* Attributes using `[[attribute]]` syntax are no longer supported. [tint:1382](crbug.com/tint/1382)
* The `elseif` keyword is no longer supported. [tint:1289](crbug.com/tint/1289)

### Deprecated Features

* The `smoothStep()` builtin has been renamed to `smoothstep()`. [tint:1483](crbug.com/tint/1483)

## Changes for M101

### New Features

* Tint now supports unicode identifiers. [tint:1437](crbug.com/tint/1437)

### Breaking changes

* The `isNan()`, `isInf()`, `isFinite()`, and `isNormal()` builtins have been removed. [tint:1312](https://crbug.com/tint/1312)

## Changes for M100

### Breaking changes

* The `@interpolate(flat)` attribute must now be specified on integral user-defined IO. [tint:1224](crbug.com/tint/1224)
* The `ignore()` intrinsic has been removed. Use phoney-assignment instead: `ignore(expr);` -> `_ = expr;`.
* `break` statements in `continuing` blocks are now correctly validated.

### New Features

* Module-scope declarations can now be declared in any order. [tint:1266](crbug.com/tint/1266)
* The `override` keyword and `@id()` attribute for pipeline-overridable constants are now supported, replacing the `@override` attribute. [tint:1403](crbug.com/tint/1403)

## Changes for M99

### Breaking changes

Obviously infinite loops (no condition, no break) are now a validation error.

### Deprecated Features

The following features have been deprecated and will be removed in M102:

* The `[[block]]` attribute has been deprecated. [tint:1324](https://crbug.com/tint/1324)
* Attributes now use the `@decoration` syntax instead of the `[[decoration]]` syntax. [tint:1382](https://crbug.com/tint/1382)
* `elseif` has been replaced with `else if`. [tint:1289](https://crbug.com/tint/1289)
* The `[[stride]]` attribute has been deprecated. [tint:1381](https://crbug.com/tint/1381)

### New Features

* Vector and matrix element type can now be inferred from constructor argument types. [tint:1334](https://crbug.com/tint/1334)
* Added builtins `degrees()` and `radians()` for converting between degrees and radians. [tint:1329](https://crbug.com/tint/1329)
* `let` arrays and matrices can now be dynamically indexed. [tint:1352](https://crbug.com/tint/1352)
* Storage and Uniform buffer types no longer have to be structures. [tint:1372](crbug.com/tint/1372)
* A struct declaration does not have to be followed by a semicolon. [tint:1380](crbug.com/tint/1380)

### Fixes

* Fixed an issue where for-loops that contain array or structure constructors in the loop initializer statements, condition expressions or continuing statements could fail to compile. [tint:1364](https://crbug.com/tint/1364)

## Changes for M98

### Breaking Changes

* Taking the address of a vector component is no longer allowed.
* Module-scope declarations can no longer alias a builtin name. [tint:1318](https://crbug.com/tint/1318)
* It is now an error to call a function either directly or transitively, from a loop continuing block, that uses `discard`. [tint:1302](https://crbug.com/tint/1302)

### Deprecated Features

* The `isNan()`, `isInf()`, `isFinite()` and `isNormal()` builtins has been deprecated and will be removed in M101. [tint:1312](https://crbug.com/tint/1312)

### New Features

* New texture gather builtins: `textureGather()` and `textureGatherCompare()`. [tint:1330](https://crbug.com/tint/1330)
* Shadowing is now fully supported. [tint:819](https://crbug.com/tint/819)
* The `dot()` builtin now supports integer vector types.
* Identifiers can now start with a single leading underscore.  [tint:1292](https://crbug.com/tint/1292)
* Control flow analysis has been improved, and functions no longer need to `return` if the statement is unreachable. [tint:1302](https://crbug.com/tint/1302)
* Unreachable statements now produce a warning instead of an error, to allow WGSL code to be updated to the new analysis behavior. These warnings may become errors in the future [gpuweb#2378](https://github.com/gpuweb/gpuweb/issues/2378)

### Fixes

* Fixed an issue where using a module-scoped `let` in a `workgroup_size` may result in a compilation error. [tint:1320](https://crbug.com/tint/1320)

## Changes for M97

### Breaking Changes

* Deprecated `modf()` and `frexp()` builtin overloads that take a pointer second parameter have been removed.
* Deprecated texture builtin functions that accepted a `read` access controlled storage texture have been removed.
* Storage textures must now only use the `write` access control.

### Deprecated Features

* The `ignore()` builtin has been replaced with phony-assignment. [gpuweb#2127](https://github.com/gpuweb/gpuweb/pull/2127)

### New Features

* `any()` and `all()` now support a `bool` parameter. These simply return the passed argument. [tint:1253](https://crbug.com/tint/1253)
* Call statements may now include functions that return a value (`ignore()` is no longer needed).
* The `interpolate(flat)` attribute can now be specified on integral user-defined IO. It will eventually become an error to define integral user-defined IO without this attribute.
* Matrix construction from scalar element values is now supported.

### Fixes

* Swizzling of `vec3` types in `storage` and `uniform` buffers has been fixed for Metal 1.x. [tint:1249](https://crbug.com/tint/1249)
* Calling a function that returns an unused value no longer produces an FXC compilation error. [tint:1259](https://crbug.com/tint/1259)
* `abs()` fixed for unsigned integers on SPIR-V backend

## Changes for M95

### New Features

* The size of an array can now be defined using a non-overridable module-scope constant
* The `num_workgroups` builtin is now supported.

### Fixes

* Hex floats: now correctly errors when the magnitude is non-zero, and the exponent would cause overflow. [tint:1150](https://crbug.com/tint/1150), [tint:1166](https://crbug.com/tint/1166)
* Identifiers beginning with an underscore are now correctly rejected.  [tint:1179](https://crbug.com/tint/1179)

---

### Tint Profiling (`docs/tint/profiling.md`)

# Tint Profiling

Profiling is always something of an art form due to the interactions between the compiler and the machine code.
Here is some guidance
* Profile fully optimized builds. This includes LTO (use_thin_lto = true in args.gn).
* Fix clock frequencies where possible (usually easier on external devices like ChromeOS/Android)
* Determinism (this is probably a given for running the tint compiler directly)

See `benchmarking.md` for tint shader benchmarking setup and existing benchmarking shaders.

## Magic Trace

Magic trace is tracing profiler that uses x64 architecture instrumentation (Intel PT) to get full traces without manual or compiler instrumentation (https://github.com/janestreet/magic-trace). It can be built from source or one can use the prebuilt binary directly (assuming a trusted party).
(https://magic-trace.org/)


```
sudo ~/magic_trace/magic-trace run  -full-execution -multi-thread ./out/active/tint -- ./test/tint/benchmark/shadow-fragment.wgsl --format spirv
```

The resulting trace (default file name 'trace.fxt') can be viewed in perfetto (https://ui.perfetto.dev/)
Example trace:
(https://ui.perfetto.dev/#!/?s=9c2017442ae51f9e7dc6dcf041bce7003ca2095a)


Magic trace can sometimes produce misleading traces due to the tracing mechanism.
* Missing trace data due to trace size limits
* Unrealistic (and incorrect) timing in the initial phase of tracing
* Broken or spliced traces due to internal errors that do not reflect the actual call history

---

### Tint Spirv Input Output Variables (`docs/tint/spirv-input-output-variables.md`)

# SPIR-V translation of shader input and output variables

WGSL [MR 1315](https://github.com/gpuweb/gpuweb/issues/1315) changed WGSL so
that pipeline inputs and outputs are handled similar to HLSL:

- Shader pipeline inputs are the WGSL entry point function arguments.
- Shader pipeline outputs are the WGSL entry point return value.

Note: In both cases, a struct may be used to pack multiple values together.
In that case, I/O specific attributes appear on struct members at the struct declaration.

Resource variables, e.g. buffers, samplers, and textures, are still declared
as variables at module scope.

## Vulkan SPIR-V today

SPIR-V for Vulkan models inputs and outputs as module-scope variables in
the Input and Output address spaces, respectively.

The `OpEntryPoint` instruction has a list of module-scope variables that must
be a superset of all the input and output variables that are statically
accessed in the shader call tree.
From SPIR-V 1.4 onward, all interface variables that might be statically accessed
must appear on that list.
So that includes all resource variables that might be statically accessed
by the shader call tree.

## Translation scheme for SPIR-V to WGSL

A translation scheme from SPIR-V to WGSL is as follows:

Each SPIR-V entry point maps to a set of Private variables proxying the
inputs and outputs, and two functions:

- An inner function with no arguments or return values, and whose body
  is the same as the original SPIR-V entry point.
- Original input variables are mapped to pseudo-in Private variables
  with the same store types, but no other attributes or properties copied.
  In Vulkan, Input variables don't have initalizers.
- Original output variables are mapped to pseudo-out Private variables
  with the same store types and optional initializer, but no other attributes
  or properties are copied.
- A wrapper entry point function whose arguments correspond in type, location
  and builtin attributes the original input variables, and whose return type is
  a structure containing members correspond in type, location, and builtin
  attributes to the original output variables.
  The body of the wrapper function the following phases:
  - Copy formal parameter values into pseudo-in variables.
    - Insert a bitcast if the WGSL builtin variable has different signedness
      from the SPIR-V declared type.
  - Execute the inner function.
  - Copy pseudo-out variables into the return structure.
    - Insert a bitcast if the WGSL builtin variable has different signedness
      from the SPIR-V declared type.
  - Return the return structure.

- Replace uses of the the original input/output variables to the pseudo-in and
  pseudo-out variables, respectively.
- Remap pointer-to-Input with pointer-to-Private
- Remap pointer-to-Output with pointer-to-Private

We are not concerned with the cost of extra copying input/output values.
First, the pipeline inputs/outputs tend to be small.
Second, we expect the backend compiler in the driver will be able to see
through the copying and optimize the result.

### Example


```glsl
    #version 450

    layout(location = 0) out vec4 frag_colour;
    layout(location = 0) in vec4 the_colour;

    void bar() {
      frag_colour = the_colour;
    }

    void main() {
        bar();
    }
```

Current translation, through SPIR-V, SPIR-V reader, WGSL writer:

```groovy
    @location(0) var<out> frag_colour : vec4<f32>;
    @location(0) var<in> the_colour : vec4<f32>;

    fn bar_() -> void {
      const x_14 : vec4<f32> = the_colour;
      frag_colour = x_14;
      return;
    }

    @fragment
    fn main() -> void {
      bar_();
      return;
    }
```

Proposed translation, through SPIR-V, SPIR-V reader, WGSL writer:

```groovy
    // 'in' variables are now 'private'.
    var<private> frag_colour : vec4<f32>;
    var<private> the_colour : vec4<f32>;

    fn bar_() -> void {
      // Accesses to the module-scope variables do not change.
      // This is a big simplifying advantage.
      const x_14 : vec4<f32> = the_colour;
      frag_colour = x_14;
      return;
    }

    fn main_inner() -> void {
      bar_();
      return;
    }

    // Declare a structure type to collect the return values.
    struct main_result_type {
      @location(0) frag_color : vec4<f32>;
    };

    @fragment
    fn main(

      // 'in' variables are entry point parameters
      @location(0) the_color_arg : vec4<f32>

    ) -> main_result_type {

      // Save 'in' arguments to 'private' variables.
      the_color = the_color_arg;

      // Initialize 'out' variables.
      // Use the zero value, since no initializer was specified.
      frag_color = vec4<f32>();

      // Invoke the original entry point.
      main_inner();

      // Collect outputs into a structure and return it.
      var result : main_outer_result_type;
      result.frag_color = frag_color;
      return result;
    }
```

Alternately, we could emit the body of the original entry point at
the point of invocation.
However that is more complex because the original entry point function
may return from multiple locations, and we would like to have only
a single exit path to construct and return the result value.

### Handling fragment discard

In SPIR-V `OpKill` causes immediate termination of the shader.
Is the shader obligated to write its outputs when `OpKill` is executed?

The Vulkan fragment operations are as follows:
(see [6. Fragment operations](https://www.khronos.org/registry/vulkan/specs/1.2/html/vkspec.html#fragops)).

* Scissor test
* Sample mask test
* Fragment shading
* Multisample coverage
* Depth bounds test
* Stencil test
* Depth test
* Sample counting
* Coverage reduction

After that, the fragment results are used to update output attachments, including
colour, depth, and stencil attachments.

Vulkan says:

> If a fragment operation results in all bits of the coverage mask being 0,
> the fragment is discarded, and no further operations are performed.
> Fragments can also be programmatically discarded in a fragment shader by executing one of
>
>     OpKill.

I interpret this to mean that the outputs of a discarded fragment are ignored.

Therefore, `OpKill` does not require us to modify the basic scheme from the previous
section.

The `OpDemoteToHelperInvocationEXT`
instruction is an alternative way to throw away a fragment, but which
does not immediately terminate execution of the invocation.
It is introduced in the [`SPV_EXT_demote_to_helper_invocation](http://htmlpreview.github.io/?https://github.com/KhronosGroup/SPIRV-Registry/blob/master/extensions/EXT/SPV_EXT_demote_to_helper_invocation.html)
extension.  WGSL does not have this feature, but we expect it will be introduced by a
future WGSL extension.  The same analysis applies to demote-to-helper.  When introduced,
it will not affect translation of pipeline outputs.

### Handling depth-replacing mode

A Vulkan fragment shader must write to the fragment depth builtin if and only if it
has a `DepthReplacing` execution mode. Otherwise behaviour is undefined.

We will ignore the case where the SPIR-V shader writes to the `FragDepth` builtin
and then discards the fragment.
This is justified because "no further operations" are performed by the pipeline
after the fragment is discarded, and that includes writing to depth output attachments.

Assuming the shader is valid, no special translation is required.

### Handling output sample mask

By the same reasoning as for depth-replacing, it is ok to incidentally not write
to the sample-mask builtin variable when the fragment is discarded.

### Handling clip distance and cull distance

Most builtin variables are scalars or vectors.
However, the `ClipDistance` and `CullDistance` builtin variables are arrays of 32-bit float values.
Each entry defines a clip half-plane (respectively cull half-plane)
A Vulkan implementation must support array sizes of up to 8 elements.

How prevalent are shaders that use these features?
These variables are supported when Vulkan features `shaderClipDistance` and `shaderCullDistance`
are supported.
According to gpuinfo.org as of this writing, those
Vulkan features appear to be nearly universally supported on Windows devices (>99%),
but by only 70% on Android.
It appears that Qualcomm devices support them, but Mali devices do not (e.g. Mali-G77).

The proposed translation scheme forces a copy of each array from private
variables into the return value of a vertex shader, or into a private
variable of a fragment shader.
In addition to the register pressure, there may be a performance degradation
due to the bulk copying of data.

We think this is an acceptable tradeoff for the gain in usability and
consistency with other pipeline inputs and outputs.

## Translation scheme for WGSL AST to SPIR-V

To translate from the WGSL AST to SPIR-V, do the following:

- Each entry point formal parameter is mapped to a SPIR-V `Input` variable.
  - Struct and array inputs may have to be broken down into individual variables.
- The return of the entry point is broken down into fields, with one
  `Output` variable per field.
- In the above, builtins must be separated from user attributes.
  - Builtin attributes are moved to the corresponding variable.
  - Location and interpolation attributes are moved to the corresponding
    variables.
- This translation relies on the fact that pipeline inputs and pipeline
  outputs are IO-shareable types. IO-shareable types are always storable,
  and can be the store type of input/output variables.
- Input function parameters will be automatically initialized by the system
  as part of setting up the pipeline inputs to the entry point.
- Replace each return statement in the entry point with a code sequence
  which writes the return value components to the synthesized output variables,
  and then executes an `OpReturn` (without value).

This translation is sufficient even for fragment shaders with discard.
In that case, outputs will be ignored because downstream pipeline
operations will not be performed.
This is the same rationale as for translation from SPIR-V to WGSL AST.

---

### Tint Spirv Ptr Ref (`docs/tint/spirv-ptr-ref.md`)

# SPIR-V translation of WGSL pointers and references

WGSL was updated to have two kinds of memory views: pointers and references.
See https://github.com/gpuweb/gpuweb/pull/1569

In summary:

* Reference types are never explicitly mentioned in WGSL source.
* A use of a variable is a value of reference type corresponding
  to the reference memory view of the storage allocated for the
  variable.
* Let-declared constants can be of pointer type, but not reference
  type.
* Function parameter can be of pointer type, but not reference type.
* A variable's store type is never a pointer type, and never a
  reference type.
* The "Load Rule" allows a reference to decay to the underlying
  store type, by issuing a load of the value in the underlying memory.
* For an assignment:
  * The right-hand side evaluates to a non-reference type (atomic-free
    plain type).
  * The left-hand side evaluates to a reference type, whose store
    type is the same as the result of evaluating the right hand side.
* The address-of (unary `&`) operator converts a reference to a
  pointer.
* The dereference (unary `*`) operator converts a pointer to a
  reference.

TODO: Passing textures and samplers to helper functions might be
done by "handler value", or by pointer-to-handle.

## Writing SPIR-V from WGSL

The distinction in WGSL between reference and pointer disappears
at the SPIR-V level.  Both types map into pointer types in SPIR-V.

To translate a valid WGSL program to SPIR-V:

* The dereference operator (unary `*`) is the identity operation.
* The address-of operator (unary `&`) is the identity operation.
* Assignment maps to OpStore.
* The Load Rule translates to OpLoad.

## Reading SPIR-V to create WGSL

The main changes to the SPIR-V reader are:

* When translating a SPIR-V pointer expression, track whether the
  corresponding WGSL expression is of corresponding WGSL pointer
  type or correspoinding WGSL type.
* Insert dereference (unary-`*`) or address-of (unary-`&`) operators
  as needed to generate valid WGSL expressions.

The choices can be made deterministic, as described below.

The SPIR-V reader only supports baseline functionality in Vulkan.
Therefore we assume no VariablePointers or VariablePointersStorageBuffer
capabilities.  All pointers are
[SPIR-V logical pointers](https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#LogicalPointerType).
The [SPIR-V Universal Validation Rules](https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#_universal_validation_rules)
specify where logical pointers can appear as results of instructions
or operands of instructions.

Each SPIR-V pointer result expression is a logical pointer, and
therefore is one of:

* OpVariable: map to the reference type.
* OpFunctionParameter: map to the pointer type.
* OpCopyObject:
   * When these only have one use, then these often fold away.
     Otherwise, they map to a a let-declared constant.
   * Map to the pointer type.
* OpAccessChain, OpInBoundsAccessChain:
   * This could map to either pointer or reference, and adjustments
     in other areas could make it work.  However, we recommend mapping
     this to the reference type.
* OpImageTexelPointer is not supported in WGSL.
   It is used to get a pointer into a storage texture, for use with
   atomic instructions.  But image atomics is not supported in
   WebGPU/WGSL.

Each SPIR-V pointer operand is also a logical pointer, and is an
operand to one of:
* OpLoad Pointer operand:
   * Map to reference, inserting a dereference operator if needed.
* OpStore Pointer operand:
   * Map to reference, inserting a dereference operator if needed.
* OpStore Pointer operand:
* OpAccessChain, OpInBoundsAccessChain Base operand:
   * WGSL array-access and subfield access only works on references.
      * [Gpuweb issue 1530](https://github.com/gpuweb/gpuweb/issues/1530)
        is filed to allow those operations to work on pointers.
   * Map to reference, inserting a dereference operator if needed.
* OpFunctionCall function argument pointer operands
   * Function operands can't be references.
   * Map to pointer, inserting an address-of operator if needed.
* OpAtomic instruction Pointer operand
   * These map to WGSL atomic builtins.
   * Map to pointer, inserting an address-of operator if needed.
   * Note: As of this writing, the atomic instructions are not supported
     by the SPIR-V reader.
* OpCopyObject source operand
   * This could have been mapped either way, but it's easiest to
     map to pointer, to match the choice for OpCopyObject result type.
   * Map to pointer, inserting an address-of operator if needed.
* OpCopyMemory, source and destination operands
   * This acts as an assignment.
   * Map both source and destination to reference, inserting dereference
     operators if needed.
   * Note: As of this writing, OpCopyMemory is not supported by the
     SPIR-V reader.
* Extended instruction set instructions Modf and Frexp
   * These map to builtins.
   * Map the pointer operand to pointer, inserting an address-of
     operator if needed.

---

### Tint Style Guide (`docs/tint/style_guide.md`)

# Tint style guide

* Generally, follow the [Chromium style guide for C++](https://chromium.googlesource.com/chromium/src/+/HEAD/styleguide/c++/c++.md)
  which itself is built on the [Google C++ style guide](https://google.github.io/styleguide/cppguide.html).

* Overall try to use the same style and convention as code around your change.

* Code must be formatted. Use `clang-format` with the provided [.clang-format](../.clang-format)
  file.  The `tools/format` script runs the formatter.

* Code should not have linting errors.
    The `tools/lint` script runs the linter. So does `git cl upload`.

* Do not use C++ exceptions

* Do not use C++ RTTI.
   Instead, use `tint::Castable::As<T>()` from
   [src/castable.h](../src/castable.h)

* Generally, avoid `assert`.  Instead, issue a [diagnostic](../src/diagnostic.h)
  and fail gracefully, possibly by returning an error sentinel value.
  Code that should not be reachable should call `TINT_UNREACHABLE` macro
  and other internal error conditions should call the `TINT_ICE` macro.
  See [src/debug.h](../src/debug.h)

* Use `type` as part of a name only when the name refers to a type
  in WGSL or another shader language processed by Tint.  If the concept you are
  trying to name is about distinguishing between alternatives, use `kind` instead.

* Forward declarations:
  * Use forward declarations where possible, instead of using `#include`'s.
  * Place forward declarations in their own **un-nested** namespace declarations. \
    Example: \
    to forward-declare `struct X` in namespace `A` and `struct Y`
    in namespace `A::B`, you'd write:
    ```c++
    // Forward declarations
    namespace A {
      struct X;
    }  // namespace A
    namespace A::B {
      struct Y;
    }  // namespace A::B

    // rest of the header code is declared below ...
    ```

## Compiler support

Tint requires C++20.

Tint uses the Chromium build system and will stay synchronized with that system.
Compiler configurations beyond that baseline is on a best-effort basis.
We strive to support recent GCC and MSVC compilers.

## Test code

We might relax the above rules for test code, since test code
shouldn't ship to users.

However, test code should still be readable and maintainable.

For test code, the tradeoff between readability and maintainability
and other factors is weighted even more strongly toward readability
and maintainability.

## Infra/Tooling

Tint has infra scripts and tooling written in two primary languages
Python and Go.

The general advice when making changes to existing code is to use the
language that the existing implementation uses, since there is no
ongoing effort to migrate to a single common language, i.e. don't go
rewriting things just for the sake of rewriting things.

If you are looking at writing a new tool or script, where and how this
code is going to be used should guide your choice.

For example if it is a standalone tool that primarily lives in the
Dawn repo, especially for something like a CLI dev tool, etc, the
preference is to use Go, since there is a substantial existing body of
support code already.

If it is something that needs to directly interface with Chromium
tooling, e.g. interacts with GN or other support libraries/tools will
be accessing it, the preference would be to use Python.

That being said for many Chromium infrastructure tasks, i.e. running
in a build recipe, the integration there is via exec, even for Python
scripts. For integration there is no significant difference between
call into a Python script vs a Go one.

The primary driver in this case to using Python instead of Go would be
either something else written in Python that needs to import your code
as a library (and not just treat it like a standalone binary) or your
code needs to import Chromium specific Python code/libraries.

Often tooling for Tint does double duty, being a CLI for devs and also
how the bots do a similar task. Due to the above considerations,
i.e. existing support for CLI in Go and needing to fork regardless of
the language, the preference in these cases tends to be use Go if
possible.

Another way to view the decision is looking at the number of execs or
similar calls that need to occur. If you are designing something and
seeing that your code is going to be ping ponging between the two
languages via execs, it is probably worthwhile seeing if it is
feasible to implement everything as a library in one language.

---

### Tint Testing (`docs/tint/testing.md`)

# Testing Tint

Tint has multiple levels of testing:

* Unit tests, using the Googletest framework.  These are the lowest level tests,
  comprehensively checking functionality of internal functions and classes.
  The test code is inside the Tint source tree in files with names ending in
  `_test.cc`

  To run them, build the `tint_unittests` target to produce the `tint_unittests`
  executable, and then run the executable.

  The `tint_unittests` executable supports all the standard Googletest command
  line options, e.g. using filters to run only a subset of the tests.
  See [Running Test Programs: Advanced
  Options](https://google.github.io/googletest/advanced.html#running-test-programs-advanced-options)

* End-to-end tests. These test the whole compiler flow, translating source
  shaders to output shaders in various languages, and optionally checking
  the text of diagnostics.  See [Tint end-to-end tests](end-to-end-tests.md).

* WebGPU Conformance Test Suite (CTS). The WebGPU CTS has both validation and
  execution tests, in the `webgpu:shader` hierarchy.
  See https://github.com/gpuweb/cts
  All test can be run in Chrome, and many tests can be run via `dawn.node`,
  the [Dawn bindings for NodeJS](../../src/dawn/node/README.md).

---

### Tint Translations (`docs/tint/translations.md`)

# Translations

This document attempts to document how WGSL translates into the various backends
for the cases where the translation is not a direct mapping.

# Access Control

## HLSL
 * ReadOnly -> `ByteAddressBuffer`
 * ReadWrite -> `RWByteAddressBuffer`

## MSL
 * ReadOnly -> `const`

## SPIR-V
There are two ways this can be achieved in SPIR-V. Either the variable can be
decorated with `NonWritable` or each member of the struct can be decorated with
`NonWritable`. We chose to go the struct member route.
 * The read-only becomes part of the type in this case. Otherwise, you are
   treating the readonly type information as part of the variable which is
   confusing.
 * Treating the readonly as part of the variable means we should be
   deduplicating the types behind the access control, which causes confusing
   with the type_names and various tracking systems within Tint.


# Builtin Decorations
| Name | SPIR-V | MSL | HLSL |
|------|--------|-----|------|
| position | SpvBuiltInPosition |position | SV_Position |
| vertex_index | SpvBuiltInVertexIndex |vertex_id | SV_VertexID |
| instance_index | SpvBuiltInInstanceIndex | instance_id| SV_InstanceID |
| front_facing | SpvBuiltInFrontFacing | front_facing | SV_IsFrontFacing |
| frag_coord | SpvBuiltInFragCoord | position | SV_Position |
| frag_depth | SpvBuiltInFragDepth | depth(any) | SV_Depth |
| local_invocation_id | SpvBuiltInLocalInvocationId | thread_position_in_threadgroup | SV_GroupThreadID |
| local_invocation_index | SpvBuiltInLocalInvocationIndex | thread_index_in_threadgroup | SV_GroupIndex |
| global_invocation_id | SpvBuiltInGlobalInvocationId | thread_position_in_grid | SV_DispatchThreadID |


# Builtins Methods
| Name | SPIR-V | MSL | HLSL |
| ------|--------|-----|------ |
| abs | GLSLstd450FAbs or GLSLstd450SAbs| fabs or abs | abs |
| acos | GLSLstd450Acos | acos | acos |
| all | SpvOpAll | all | all |
| any | SpvOpAny | any | any |
| arrayLength | SpvOpArrayLength | | |
| asin | GLSLstd450Asin | asin | asin |
| atan | GLSLstd450Atan | atan | atan |
| atan2 | GLSLstd450Atan2| atan2 | atan2 |
| ceil | GLSLstd450Ceil| ceil | ceil |
| clamp | GLSLstd450NClamp or GLSLstd450UClamp or GLSLstd450SClamp| clamp | clamp |
| cos | GLSLstd450Cos | cos | cos |
| cosh | GLSLstd450Cosh | cosh | cosh |
| countOneBits | SpvOpBitCount | popcount | countbits |
| cross | GLSLstd450Cross | cross | cross |
| determinant | GLSLstd450Determinant | determinant | determinant |
| distance | GLSLstd450Distance | distance | distance |
| dot | SpOpDot | dot | dot |
| dpdx | SpvOpDPdx | dpdx | ddx |
| dpdxCoarse | SpvOpDPdxCoarse | dpdx | ddx_coarse |
| dpdxFine | SpvOpDPdxFine | dpdx | ddx_fine |
| dpdy | SpvOpDPdy | dpdy | ddy |
| dpdyCoarse | SpvOpDPdyCoarse | dpdy | ddy_coarse |
| dpdyFine | SpvOpDPdyFine | dpdy | ddy_fine |
| exp | GLSLstd450Exp | exp |  exp |
| exp2 | GLSLstd450Exp2 | exp2 | exp2 |
| faceForward | GLSLstd450FaceForward | faceforward | faceforward |
| floor | GLSLstd450Floor | floor | floor |
| fma | GLSLstd450Fma | fma | fma |
| fract | GLSLstd450Fract | fract | frac |
| frexp | GLSLstd450Frexp | | |
| fwidth | SpvOpFwidth | fwidth | fwidth |
| fwidthCoarse | SpvOpFwidthCoarse | fwidth | fwidth |
| fwidthFine | SpvOpFwidthFine | fwidth | fwidth |
| inverseSqrt | GLSLstd450InverseSqrt | rsqrt | rsqrt |
| ldexp | GLSLstd450Ldexp | | |
| length | GLSLstd450Length | length | length |
| log | GLSLstd450Log | log | log |
| log2 | GLSLstd450Log2 | log2 | log2 |
| max | GLSLstd450NMax or GLSLstd450SMax or GLSLstd450UMax | fmax or max | max |
| min | GLSLstd450NMin or GLSLstd450SMin or GLSLstd450UMin | fmin or min | min |
| mix | GLSLstd450FMix | mix | mix |
| modf | GLSLstd450Modf | | |
| normalize | GLSLstd450Normalize | normalize | normalize |
| pow | GLSLstd450Pow | pow | pow |
| reflect | GLSLstd450Reflect | reflect | reflect |
| reverseBits | SpvOpBitReverse | reverse_bits | reversebits |
| round | GLSLstd450Round | round | round |
| select | SpvOpSelect | select | |
| sign | GLSLstd450FSign | sign | sign |
| sin | GLSLstd450Sin | sin | sin |
| sinh | GLSLstd450Sinh | sinh | sinh |
| smoothstep | GLSLstd450SmoothStep | smoothstep | smoothstep |
| sqrt | GLSLstd450Sqrt | sqrt | sqrt |
| step | GLSLstd450Step | step | step |
| tan | GLSLstd450Tan | tan | tan |
| tanh | GLSLstd450Tanh | tanh | tanh |
| trunc | GLSLstd450Trunc | trunc | trunc |

# Types
## Sampler Types
| WGSL | SPIR-V | MSL | HLSL |
|------|--------|-----|------|
| sampler | OpTypeSampler | sampler | SamplerState |
| sampler_comparison | OpTypeSampler | sampler | SamplerComparisonState |

## Texture Types
| WGSL | SPIR-V | MSL | HLSL |
|------|--------|-----|------|
| texture_1d&lt;type&gt; | OpTypeImage 1D Sampled=1 | texture1d&lt;type, access::sample&gt; | Texture1D |
| texture_2d&lt;type&gt; | OpTypeImage 2D Sampled=1 | texture2d&lt;type, access::sample&gt; | Texture2D |
| texture_2d_array&lt;type&gt; | OpTypeImage 2D Arrayed=1 Sampled=1 | texture2d_array&lt;type, access::sample&gt; | Texture2DArray |
| texture_3d&lt;type&gt; | OpTypeImage 3D Sampled=1 | texture3d&lt;type, access::sample&gt; | Texture3D |
| texture_cube&lt;type&gt; | OpTypeImage Cube Sampled=1 | texturecube&lt;type, access::sample&gt; | TextureCube |
| texture_cube_array&lt;type&gt; | OpTypeImage Cube Arrayed=1 Sampled=1 | texturecube_array&lt;type, access::sample&gt; | TextureCubeArray |
| | | |
| texture_multisampled_2d&lt;type&gt; | OpTypeImage 2D MS=1 Sampled=1 | texture2d_ms&lt;type, access::sample&gt; | Texture2D |
| | | |
| texture_depth_2d | OpTypeImage 2D Sampled=1 | depth2d&lt;float, access::sample&gt;| Texture2D |
| texture_depth_2d_array | OpTypeImage 2D Arrayed=1 Sampled=1 | depth2d_array&lt;float, access::sample&gt; | Texture2DArray |
| texture_depth_cube | OpTypeImage Cube Sampled=1 | depthcube&lt;float, access::sample&gt; | TextureCube |
| texture_depth_cube_array | OpTypeImage Cube Arrayed=1 Sampled=1 | depthcube_array&lt;float, access::sample&gt; | TextureCubeArray |
| texture_depth_multisampled_2d | OpTypeImage 2D MS=1 Sampled=1 | depth2d&lt;float, access::sample&gt;| Texture2DMSArray |
| | | |
| texture_storage_1d&lt;image_storage_type&gt; | OpTypeImage 1D Sampled=2| texture1d&lt;type, access::read&gt; | RWTexture1D |
| texture_storage_2d&lt;image_storage_type&gt; | OpTypeImage 2D Sampled=2 | texture2d&lt;type, access::read&gt; | RWTexture2D |
| texture_storage_2d_array&lt;image_storage_type&gt; | OpTypeImage 2D Arrayed=1 Sampled=2 | texture2d_array&lt;type, access::read&gt; | RWTexture2DArray |
| texture_storage_3d&lt;image_storage_type&gt; | OpTypeImage 3D Sampled=2 | texture3d&lt;type, access::read&gt; | RWTexture3D |
| | | |
| texture_storage_1d&lt;image_storage_type&gt; | OpTypeImage 1D Sampled=2 | texture1d&lt;type, access::write&gt; | RWTexture1D |
| texture_storage_2d&lt;image_storage_type&gt; | OpTypeImage 2D Sampled=1 | texture2d&lt;type, access::write&gt; | RWTexture2D |
| texture_storage_2d_array&lt;image_storage_type&gt; | OpTypeImage 2D Arrayed=1 Sampled=2 | texture2d_array&lt;type, access::write&gt; | RWTexture2DArray |
| texture_storage_3d&lt;image_storage_type&gt; | OpTypeImage 3D Sampled=2 | texture3d&lt;type, access::write&gt; | RWTexture3D|

# Short-circuting
## HLSL
TODO(dsinclair): Nested if's

## SPIR-V
TODO(dsinclair): Nested if's

# Address spaces
TODO(dsinclair): do ...

# Storage buffers
## HLSL
TODO(dsinclair): Rewriting of accessors to loads

# Loop blocks
## HLSL
TODO(dsinclair): Rewrite with bools

## MSL
TODO(dsinclair): Rewrite with bools

# Input / Output address spaces
## HLSL
TODO(dsinclair): Structs and params

## MSL
TODO(dsinclair): Structs and params

# Discard
## HLSL
 * `discard`

## MSL
 * `discard_fragment()`


# Specialization constants
## HLSL
```
#ifndef WGSL_SPEC_CONSTANT_<id>
-- if default provided
#define WGSL_SPEC_CONSTANT_<id> default value
-- else
#error spec constant required for constant id
--
#endif
static const <type> <name> = WGSL_SPEC_CONSTANT_<id>
```

## MSL
`@function_constant(<id>)`

---

### Tint Uniformity Analysis (`docs/tint/uniformity_analysis.md`)

# Tint: Uniformity Analysis

This document provides a high-level overview of Uniformity Analysis in Tint. It is intended for developers who need to understand or modify the uniformity analysis pass at: [src/tint/lang/wgsl/resolver/uniformity.cc](../../src/tint/lang/wgsl/resolver/uniformity.cc).

## What is Uniformity?

In GPU programming, "uniformity" refers to whether a value is guaranteed to be the same across all threads executed in a group, like a workgroup, subgroup or quad. There are two concepts necessary to understand the analysis of uniformity of a WebGPU program: uniform values and uniform control flow.

### Values

**Uniform Value**: For a given value in a GPU program, if every thread in the workgroup sees the same value, that value is considered **uniform**. Examples include static constants or values from a `uniform` buffer.

**Non-Uniform Value**: If different threads may see different values, that value is considered **non-uniform**. Examples include `@builtin(local_invocation_id)`, a `textureLoad()` from a non-uniform index, or values from a `read_write` storage buffer.

### Control Flow

**Uniform Control Flow**: If every thread in the workgroup is guaranteed to be executing the same statement at the same time, the control flow is **uniform**.

**Non-Uniform Control Flow**: If different threads may be executing different statements, the control flow is **non-uniform**. For example, inside an `if` block where the condition was based on a non-uniform value like the thread id. Some GPU operations, like barriers, must only be executed in uniform control flow.

## What is Uniformity Analysis?

**Uniformity Analysis** is a static compiler pass that proves that certain operations are performed in uniform control flow. If it cannot prove that uniformity is maintained everywhere that uniformity is required, it will result in a compilation error. By necessity, it is a conservative compile-time analysis, meaning it will produce false negatives by rejecting WGSL that would actually be uniform at runtime, but it errs on the side of caution to ensure that all accepted shaders are reasonably guaranteed to be safe and correct.

### Why do we need it?

WebGPU requires uniformity analysis for two main reasons:

1. **Correctness**: Operations like `textureSample()` require the GPU to calculate the differences between neighboring pixels in a quad. If the threads in a quad diverge (i.e., some enter an `if`, others don't), the derivatives become undefined, leading to visual artifacts. Requiring `textureSample()` to be called in uniform control flow helps graphics developers write functionally correct shaders.

2. **Safety**: Synchronization primitives like `workgroupBarrier()` require all threads in the workgroup to reach the barrier together. If the barrier is inside a non-uniform `if` block, some threads might never reach it. In traditional GPU programming models, this would typically result in undefined behavior, and might lead to a GPU hang. WebGPU is designed to execute untrusted web content, so additional hardening against these negative outcomes is strictly necessary because that execution happens in a shared GPU context with less process isolation than other tightly sandboxed web APIs typically have.

## High-Level Implementation

### Graph Construction

Tint implements uniformity analysis by building a **Directed Dependency Graph**.

The analysis uses two main types of graph nodes:

- **Value Nodes**: Represent the uniformity of an expression or variable.
- **Control Flow (CF) Nodes**: Represent the uniformity of the current execution path.

An edge in the uniformity dependency graph from `A -> B` signifies that the uniformity of node `A` is dependent on the uniformity of node `B`. This means if `B` is non-uniform, then `A` must also be considered non-uniform. The uniformity analysis pass builds a graph of these nodes and edges, and when non-uniform values are encountered, it can be determined which parts of control flow they affect.

#### Example

```wgsl
@compute @workgroup_size(8)
fn main(@builtin(local_invocation_id) local_id : vec3u) {
  let u = 1u;
  let n = local_id.x;
  let result = u + n;
}
```

In the example above, `local_id` is a non-uniform value, represented by the edge in the graph below from `main.local_id_ident_expr -> main.MayBeNonUniform`. The `result` is a binary expression dependent on a uniform value (`u`) and a non-uniform value (`n`). This is represented by the edges from `main.binary_expr_result -> main.u_ident_expr` and `main.binary_expr_result -> main.n_ident_expr`.

**Dependency Graph (Tint)**:


You can think of non-uniformity like an "infection". If you reversed the direction of all edges in the graph, it would represent the pathways through which the non-uniformity infection spreads. For illustration purposes, the arrows are reversed in the graph below and infected nodes are highlighted in red, showing how the `MayBeNonUniform` infection spreads from `local_id`, to `n`, to `binary_expr_result`.

**Infection Flow**:


There's nothing wrong with this example shader though, it compiles successfully in Tint. Shaders can have non-uniform values and non-uniform control flow, as long as they're used in a valid way conformant with the WGSL spec.

### Validation

With such a graph built, Tint can now assess the validity of operations like `textureSample` or `workgroupBarrier`. When a function that requires uniform control flow is encountered in the AST, the analyzer checks the current **Control Flow Node**. If that node can be traced back to a non-uniform source, then the analysis cannot guarantee that the operation was called in uniform control flow and validation fails.

#### Example: Valid Control Flow (Uniform)

In this example, the `if` condition is a constant, which is uniform. Every thread in the workgroup is guaranteed to enter the `if` block and reach the barrier together.

```wgsl
@compute @workgroup_size(64)
fn main() {
  const uniform_cond = true;
  if (uniform_cond) {
    workgroupBarrier(); // Valid: Uniform control flow
  }
}
```

This shader passes validation and compiles successfully in Tint. In the graph below, note that the `main.RequiredToBeUniform_Error` node cannot reach any `MayBeNonUniform` values.



#### Example: Invalid Control Flow (Non-Uniform)

In this example, the `if` condition depends on `local_id.x`, which is non-uniform. Threads will diverge, meaning some will reach the barrier while others do not. This is a validation error.

```wgsl
@compute @workgroup_size(64)
fn main(@builtin(local_invocation_id) local_id : vec3u) {
  if (local_id.x > 0u) {
    workgroupBarrier(); // Error: Divergent control flow
  }
}
```

**Tint Compiler Output:**

```text
test.wgsl:4:5 error: 'workgroupBarrier' must only be called from uniform control flow
    workgroupBarrier();
    ^^^^^^^^^^^^^^^^

test.wgsl:3:3 note: control flow depends on possibly non-uniform value
  if (local_id.x > 0u) {
  ^^

test.wgsl:3:7 note: builtin 'local_id' may be non-uniform
  if (local_id.x > 0u) {
      ^^^^^^^^
```

In the graph below, note that the `main.RequiredToBeUniform_Error` node **can** reach `main.MayBeNonUniform`.



## How to Generate Diagrams

Tint has a built-in feature to dump the dependency graph as a Graphviz (DOT) file, which was used to generate the example diagrams in this doc.

To generate these diagrams yourself to help understand the analysis, follow these steps:

1.  Download and install Graphviz: [https://graphviz.org/download/](https://graphviz.org/download/)
2.  Enable the dump flag in [src/tint/lang/wgsl/resolver/uniformity.cc](../../src/tint/lang/wgsl/resolver/uniformity.cc), by changing `#define TINT_DUMP_UNIFORMITY_GRAPH 0` to `1`.
3.  Rebuild Tint.
4.  Since this dumps the DOT file contents to stdout, you should be able to redirect it to a file with the following command:
    ```bash
    ./path/to/tint -o /dev/null shader.wgsl > graph.dot
    ```
5.  Render the image:
    ```bash
    dot -Tsvg graph.dot -o graph.svg
    ```

## Further Reading

The official WGSL specification contains all uniformity rules: [§15.2. Uniformity](https://www.w3.org/TR/WGSL/#uniformity).

---

### Tint Unsafe Buffer Usage (`docs/tint/unsafe_buffer_usage.md`)

# Remaining -Wunsafe-buffer-usage Suppressions in Tint

This document summarizes and explains the remaining
`-Wunsafe-buffer-usage` and `-Wunsafe-buffer-usage-in-container`
suppressions in the Tint codebase.

## Categorization of Suppressions

### 1. Localization via `tint::Copy`
The `tint::Copy` utility in `src/tint/utils/memory/copy.h`
encapsulates `memcpy` calls.
- **Rationale**: `memcpy` is intrinsically unsafe but necessary for
  performance-critical byte copying. By using `tint::Copy`, we
  localize the suppressions to a single file and provide a safer API
  that accepts `std::span`.
- **Affected Files**: `src/tint/utils/memory/copy.h`

### 2. Two-Parameter `std::span` Constructors
Clang's `-Wunsafe-buffer-usage-in-container` warning is triggered by
`std::span(ptr, size)`.
- **Rationale**: This is often unavoidable when interfacing with older
  APIs (like `std::string_view::data()`) or when reinterpreting byte
  buffers (e.g., `std::span<std::byte>` to `std::span<uint8_t>`). We
  prefer this over raw pointer arithmetic as it still provides a
  bound-checked view for subsequent operations.
- **Affected Files**:
  - `src/tint/utils/bytes/buffer_reader.h`
  - `src/tint/utils/text/unicode.cc`

### 3. Tight Loops and Performance-Critical Indexing
In some performance-critical code (like UTF-8 decoding), we use
`UNSAFE_BUFFER_USAGE_IN_CONTAINER` to allow standard indexing after
manual bounds checks.
- **Rationale**: While `span::at()` or other methods might be safer,
  standard indexing `[]` is preferred in certain Tint utilities for
  consistency and to avoid the overhead of redundant checks where we
  have already validated the size.
- **Affected Files**:
  - `src/tint/utils/text/unicode.cc`
  - `src/tint/utils/containers/vector.h`

### 4. External API Interfacing
Code that interfaces with C-style external APIs (like
`std::from_chars` or terminal IO) requires raw pointers.
- **Rationale**: `std::from_chars` requires a pointer to
  one-past-the-end of the buffer. This pattern is inherently flagged
  but necessary to use the standard library feature.
- **Affected Files**:
  - `src/tint/lang/wgsl/reader/parser/lexer.cc` (for `line_end()`)
  - `src/tint/utils/system/terminal_posix.cc`

### 5. Custom Containers
Some foundational Tint containers (`Vector`, `HashMap`,
`BlockAllocator`) still contain suppressions.
- **Rationale**: These are complex, low-level memory management
  utilities where unsafe buffer usage is part of the implementation of
  the container itself. Refactoring these requires deep architectural
  changes beyond the scope of this effort.
- **Affected Files**:
  - `src/tint/utils/containers/vector.h`
  - `src/tint/utils/containers/hashmap_base.h`
  - `src/tint/utils/memory/block_allocator.h`
  - `src/tint/utils/memory/bump_allocator.h`

---

### Dawn Features Adapter Options (`docs/dawn/features/adapter_options.md`)

# Adapter Options (Unstable!)

`wgpu::RequestAdapterOptions` may be passed to `wgpuInstanceRequestAdapter` to request an adapter.

Dawn provides a few chained extension structs on `RequestAdapterOptions` to customize the behavior.
Currently, the `WGPUInstance` doesn't provide a way to query support for these features, so these
features may not be suitable for general use yet. Currently, they are used in Dawn's testing, and
in Chromium-specific integration with Dawn.

`DawnTogglesDescriptor` can also be chained on `RequestAdapterOptions` to provide the required adapter toggles. However, the final adapter toggles state also depends on instance toggles inheritance and adapter toggles validation/default-setting.

`dawn::native::Instance::EnumerateAdapters` is a Dawn native-only API that may be used to synchronously
get a list of adapters according to the RequestAdapterOptions. The members are treated as follows:
 - `RequestAdapterOptions::compatibleSurface` is ignored.
 - `RequestAdapterOptions::featureLevel` all returned adapters must support the features and limits in the requested feature level. Devices created from the adapter will default to the capabilities of this feature level.
 - `RequestAdapterOptions::powerPreference` adapters are sorted according to powerPreference such that
   preferred adapters are at the front of the list. It is a preference - so if
  wgpu::PowerPreference::LowPower is passed, the list may contain only integrated GPUs, fallback adapters, or a mix of everything. Implementations *should* try to avoid returning any discrete GPUs when low power is requested if at least one integrated GPU is available.
 - `RequestAdapterOptions::backendType` filters adapters such that only those on a particular backend are discovered. If `WGPURequestAdapterType_Undefined` is passed, all backends may be discovered.
 - `RequestAdapterOptions::forceFallbackAdapter` all returned adapters must be fallback adapters.

If no options are passed to EnumerateAdapters, then it is as if the default `RequestAdapterOptions` are passed.

### `RequestAdapterOptionsGetGLProc`

When discovering adapters on the GLES backend, Dawn uses the provided `RequestAdapterOptionsGetGLProc::getProc` method to load GL procs. `RequestAdapterOptionsGetGLProc::display` indicates the EGLDisplay on which to create an adapter. If `display` is `EGL_NO_DISPLAY`, the current display will be used. This extension struct does nothing on other backends.

### `RequestAdapterOptionsAngleVirtualizationGroup`

When dawn runs on top of angle, you can set angleVirtualizationGroup to a virtualization group number to be used with EGL_ANGLE_context_virtualization extensions. If this struct isn't provided,
the EGL_DONT_CARE value is used.

### `RequestAdapterOptionsLUID`

When discovering adapters on D3D11 and D3D12, Dawn only discovers adapters matching the provided `RequestAdapterOptionsLUID::adapterLUID`. This extension struct does nothing on other backends.

### `RequestAdapterOptionsD3D11Device`

When discovering adapter on D3D11, Dawn creates an adapter matching the provided `RequestAdapterOptionsD3D11Device::device`, and `wgpu::Device` created from the adapter will share the same D3D11 device from `RequestAdapterOptionsD3D11Device::device`. This extension struct does nothing on other backends.

### `RequestAdapterOptionsVulkanUUIDs`

When discovering adapters on Vulkan, Dawn only discovers adapters matching the provided `RequestAdapterOptionsVulkanUUIDs::driverUUID/deviceUUID`. This extension struct does nothing on other backends.

### `DawnTogglesDescriptor`

When discovering adapters, Dawn will use chained `DawnTogglesDescriptor` as required adapter
toggles. The final toggles state of each result adapter depends on the inheritance of instance
toggles state, the backend-specific validation and the backend-specific default setting.

---

### Dawn Features Adapter Properties (`docs/dawn/features/adapter_properties.md`)

# Adapter Properties

## Memory Heaps

`wgpu::FeatureName::AdapterPropertiesMemoryHeaps` allows querying memory heap information from the adapter.

`wgpu::AdapterPropertiesMemoryHeaps` may be chained on `wgpu::AdapterInfo` in a call to `wgpu::Adapter::GetInfo` or `wgpu::Device::GetAdapterInfo` in order to query information about the memory heaps on the adapter.
The implementation will write out the number of memory heaps and information about each heap.

If `wgpu::FeatureName::AdapterPropertiesMemoryHeaps` is not available, the struct will not be populated.

Adds `wgpu::HeapProperty` which is a bitmask describing the type of memory a heap is. Valid bits:
- DeviceLocal
- HostVisible
- HostCoherent
- HostUncached
- HostCached

Note that both HostUncached and HostCached may be set if a heap can allocate pages with either cache property.

Adds `wgpu::MemoryHeapInfo` which is a struct describing a memory heap.
```
struct MemoryHeapInfo {
    HeapProperty properties;
    uint64_t size;
};
```

`wgpu::MemoryHeapInfo::size` is the size that should be allocated out of this heap. Allocating more than this may result in poor performance or may deterministically run out of memory.

## DRM properties

`wgpu::FeatureName:AdapterPropertiesDRM`, currently only available on Vulkan (but could be added on OpenGL / ES), allows querying Linux DRM information about the adapter.

`wgpu::AdapterPropertiesDRM` may be chained on `wgpu::AdapterInfo` in a call to `wgpu::Adapter::GetInfo` or `wgpu::Device::GetAdapterInfo` in order to query DRM information on the adapter.

## D3D

`wgpu::FeatureName::AdapterPropertiesD3D` allows querying D3D information from the adapter.

`wgpu::AdapterPropertiesD3D` may be chained on `wgpu::AdapterInfo` in a call to `wgpu::Adapter::GetInfo` or `wgpu::Device::GetAdapterInfo` in order to query D3D information on the adapter.

Adds `wgpu::AdapterPropertiesD3D` which is a struct describing the D3D adapter.
```
struct AdapterPropertiesD3D {
    uint32_t shaderModel;  // The D3D shader model
};
```

## Vulkan

`wgpu::FeatureName::AdapterPropertiesVk` allows querying Vulkan information from the adapter.

`wgpu::AdapterPropertiesVk` may be chained on `wgpu::AdapterInfo` in a call to `wgpu::Adapter::GetInfo` or `wgpu::Device::GetAdapterInfo` in order to query Vulkan information on the adapter.

Adds `wgpu::AdapterPropertiesVk` which is a struct describing the Vulkan adapter.
```
struct AdapterPropertiesVk {
    uint32_t driverVersion;  // The Vulkan driver version
};
```

Likewise `dawn::native::vulkan::AdapterPropertiesVulkanUUIDs` may be chained on `wgpu::AdapterInfo` to query the following:

```
struct AdapterPropertiesVulkanUUIDs {
    std::array<uint8_t, VK_UUID_SIZE> deviceUUID;
    std::array<uint8_t, VK_UUID_SIZE> driverUUID;
};
```

## WebGPU

`wgpu::FeatureName::AdapterPropertiesWGPU` allows querying inner backend information of the WebGPUBackend from the adapter.

`wgpu::AdapterPropertiesWGPU` may be chained on `wgpu::AdapterInfo` in a call to `wgpu::Adapter::GetInfo` or `wgpu::Device::GetAdapterInfo` in order to query WebGPU backend information on the adapter.

Adds `wgpu::AdapterPropertiesWGPU` which is a struct describing the WebGPU adapter.
```
struct AdapterPropertiesWGPU {
    wgpu::BackendType backendType;  // The inner backend type
};
```

---

### Dawn Features Angle Texture Sharing (`docs/dawn/features/angle_texture_sharing.md`)

# ANGLE Texture Sharing

Available only on the OpenGL ES backend when running on top of ANGLE.
When a device is created with this extension, it is added to the `EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE` which is a display-wide share context, but only for texturs.

When enabled, `dawn::native::opengl::ExternalImageDescriptorGLTexture` and `dawn::native::opengl::WrapExternalGLTexture` become available which can import a GL texture into a `wgpu::Device` with a GL texture ID.

See the spec for [`EGL_ANGLE_display_texture_share_group`](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/EGL_ANGLE_display_texture_share_group.txt).

---

### Dawn Features Buffer Map Extended Usages (`docs/dawn/features/buffer_map_extended_usages.md`)

# Buffer Map Extended Usages

## Overview:
 - The `wgpu::Feature::BufferMapExtendedUsages` feature allows creating a buffer with `wgpu::BufferUsage::MapRead` and/or `wgpu::BufferUsage::MapWrite` and any other `wgpu::BufferUsage`.
 - `wgpu::Feature::BufferMapExtendedUsages` is a superset of [`BufferMapWriteExtendedUsages`](./buffer_map_write_extended_usages.md). Enabling `BufferMapExtendedUsages` on a device implicitly enables `BufferMapWriteExtendedUsages` as well.

### Example Usage:
```
wgpu::BufferDescriptor descriptor;
descriptor.size = size;
descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::Uniform;
wgpu::Buffer uniformBuffer = device.CreateBuffer(&descriptor);

uniformBuffer.MapAsync(wgpu::MapMode::Write, 0, size,
   [](WGPUBufferMapAsyncStatus status, void* userdata)
   {
      wgpu::Buffer* buffer = static_cast<wgpu::Buffer*>(userdata);
      memcpy(buffer->GetMappedRange(), data, sizeof(data));
   },
   &uniformBuffer);
```

---

### Dawn Features Buffer Map Write Extended Usages (`docs/dawn/features/buffer_map_write_extended_usages.md`)

# Buffer Map Write Extended Usages

## Background

By default WebGPU only allows `wgpu::BufferUsage::MapWrite` to be combined with
`wgpu::BufferUsage::CopySrc`. Uploading data that the GPU consumes as a uniform, vertex, index,
storage or indirect buffer therefore requires a separate staging buffer and an explicit copy on
every update.

The `wgpu::FeatureName::BufferMapWriteExtendedUsages` feature removes this restriction: it allows
creating a buffer with `wgpu::BufferUsage::MapWrite` combined with any other `wgpu::BufferUsage`,
**except** `wgpu::BufferUsage::MapRead`. This makes it possible to write directly into a mappable
buffer that is also GPU-usable, eliminating the staging buffer and the extra copy.

The feature works on the GPUs with either **cache-coherent** or **non-cache-coherent** Unified
Memory Architecture (UMA), where we can effectively upload data from CPU to GPU through mapped
pointer, and directly access the data on GPU without any extra copies.

This is a subset of the more permissive
[`BufferMapExtendedUsages`](./buffer_map_extended_usages.md) feature, which additionally allows
`wgpu::BufferUsage::MapRead` to be combined with arbitrary usages but is only available on a
narrower set of hardware (cache-coherent UMA). Because it is a superset, enabling
`BufferMapExtendedUsages` on a device implicitly enables `BufferMapWriteExtendedUsages` as well.

## Target API support

The table below summarizes the requirement to enable this feature on D3D12, Metal and Vulkan:

| Backend | Requirement to enable | Backing memory for write-only mapping |
| ------- | --------------------- | ------------------------------------- |
| Direct3D 12 | `D3D12_FEATURE_DATA_ARCHITECTURE.UMA == TRUE` | Custom heap (`D3D12_HEAP_TYPE_CUSTOM`) in the L0 system memory pool (`D3D12_MEMORY_POOL_L0`), with CPU page property `D3D12_CPU_PAGE_PROPERTY_WRITE_BACK` on cache-coherent UMA or `D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE` on non-cache-coherent UMA. |
| Metal | `[MTLDevice hasUnifiedMemory] == YES` | `MTLResourceStorageModeShared` allocation with `MTLResourceCPUCacheModeWriteCombined`; no `didModifyRange:` / synchronization needed. |
| Vulkan | A memory type exposing `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT \| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT \| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`. | A `HOST_COHERENT` (write-combined) memory type; `VK_MEMORY_PROPERTY_HOST_CACHED_BIT` is not required, so non-cache-coherent UMA GPUs are supported. |

## Proposal

### API

New feature name `wgpu::FeatureName::BufferMapWriteExtendedUsages`. It is currently experimental
(see [crbug.com/386255678](https://crbug.com/386255678)).

Once enabled, `wgpu::BufferUsage::MapWrite` can be combined with any other `wgpu::BufferUsage`
except `wgpu::BufferUsage::MapRead`.

#### Example Usage

```c++
wgpu::BufferDescriptor descriptor;
descriptor.size = size;
// MapWrite can now be combined with any usage other than MapRead.
descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::Uniform;
wgpu::Buffer uniformBuffer = device.CreateBuffer(&descriptor);

uniformBuffer.MapAsync(wgpu::MapMode::Write, 0, size,
    wgpu::CallbackMode::AllowSpontaneous,
    [uniformBuffer, size](wgpu::MapAsyncStatus status, wgpu::StringView) {
        if (status == wgpu::MapAsyncStatus::Success) {
            memcpy(uniformBuffer.GetMappedRange(), data, size);
            uniformBuffer.Unmap();
        }
    });
```

#### Validation

- The feature must be enabled on the device before creating a buffer
  that combines `wgpu::BufferUsage::MapWrite` with non-`wgpu::BufferUsage::CopySrc` usages.
- `wgpu::BufferUsage::MapRead` cannot be combined with other GPU usages under this feature.

#### Notes and Limitations

- Reading back from a buffer created with this feature on CPU (for example via `memcpy` out of the
  mapped range) may be extremely slow because the underlying memory is typically uncached on CPU.

---

### Dawn Features Core Features And Limits (`docs/dawn/features/core_features_and_limits.md`)

# Core Features And Limits

When enabled on a device, lifts all Compatibility mode restrictions (features and limits) to Core.

Core-defaulting adapters always support the "core-features-and-limits" feature. It is automatically enabled on devices created from such adapters.

Compatibility-defaulting adapters may support the "core-features-and-limits" feature. It may be requested on devices created from such adapters.

For more details see [WebGPU compatibility mode proposal](https://github.com/gpuweb/gpuweb/blob/main/proposals/compatibility-mode.md#core-features-and-limits-feature)

---

### Dawn Features D3d11 Multithread Protected (`docs/dawn/features/d3d11_multithread_protected.md`)

# D3D11 multithread protected

Optional feature available on the D3D11 backend that when enabled makes all calls to the ID3D11DeviceContext happen inside a ID3D11Mulithread scope.
This ensure that the D3D11 device can be used concurrently by other components, in particular ANGLE in Chromium.

The initial tracking bug was crbug.com/dawn/1927.

---

### Dawn Features Dawn Allow Undefined Load Store Op (`docs/dawn/features/dawn_allow_undefined_load_store_op.md`)

# Dawn Allow Undefined Load Store Op
The `dawn-allow-undefined-load-store-op` feature allows `wgpu::LoadOp::Undefined` and
`wgpu::StoreOp::Undefined` to be passed for render pass attachments that would otherwise require
an explicit load/store op:
- `wgpu::LoadOp::Undefined`
  - This will be either Load or Clear depending on what the driver sees as the best performing
    option.
- `wgpu::StoreOp::Undefined`
  - This will be either Store or Discard depending on what the driver sees as the best performing
    option.

---

### Dawn Features Dawn Device Allocator Control (`docs/dawn/features/dawn_device_allocator_control.md`)

# Dawn Device Allocator Control (experimental)

This feature allows `wgpu::DawnDeviceAllocatorControl` to be chained on `wgpu::DeviceDescriptor`. If
`wgpu::DawnDeviceAllocatorControl` is chained and `wgpu::FeatureName::DawnDeviceAllocatorControl` is
not in `requiredFeatures` then creating the device will fail with a validation error.

It is available in the Vulkan backend.

---

### Dawn Features Dawn Internal Usages (`docs/dawn/features/dawn_internal_usages.md`)

# Dawn Internal Usages

The `dawn-internal-usages` feature allows adding additional usage which affects how a texture is allocated, but does not affect normal frontend validation.

Adds `WGPUDawnTextureInternalUsageDescriptor` for specifying additional internal usages to create a texture with.

Example Usage:
```
wgpu::DawnTextureInternalUsageDescriptor internalDesc = {};
internalDesc.internalUsage = wgpu::TextureUsage::CopySrc;

wgpu::TextureDescriptor desc = {};
// set properties of desc.
desc.nextInChain = &internalDesc;

device.CreateTexture(&desc);
```

Adds `WGPUDawnEncoderInternalUsageDescriptor` which may be chained on `WGPUCommandEncoderDescriptor`. Setting `WGPUDawnEncoderInternalUsageDescriptor::useInternalUsages` to `true` means that internal resource usages will be visible during validation. ex.) A texture that has `WGPUTextureUsage_CopySrc` in `WGPUDawnEncoderInternalUsageDescriptor::internalUsage`, but not in `WGPUTextureDescriptor::usage` may be used as the source of a copy command.


Example Usage:
```
wgpu::DawnEncoderInternalUsageDescriptor internalEncoderDesc = { true };
wgpu::CommandEncoderDescriptor encoderDesc = {};
encoderDesc.nextInChain = &internalEncoderDesc;

wgpu::CommandEncoder encoder = device.CreateCommandEncoder(&encoderDesc);

// This will be valid
wgpu::TexelCopyTextureInfo src = {};
src.texture = texture;
encoder.CopyTextureToBuffer(&src, ...);
```

One use case for this is so that Chromium can use an internal copyTextureToTexture command to implement copies from a WebGPU texture-backed canvas to other Web platform primitives when the swapchain texture was not explicitly created with CopySrc usage in Javascript.

---

### Dawn Features Dawn Load Resolve Texture (`docs/dawn/features/dawn_load_resolve_texture.md`)

# Dawn Load Resolve Texture

The `dawn-load-resolve-texture` feature allows a render pass to expand a resolve attachment's pixels into the MSAA attachment's as part of the loading operation.

Additional functionalities:
 - Adds `wgpu::LoadOp::ExpandResolveTexture` enum value to specify that the MSAA attachment will load the pixels from its corresponding resolve texture. This is cheaper than `wgpu::LoadOp::Load` which will load the existing pixels of the MSAA attachment itself.
 - Adds `wgpu::ColorTargetStateExpandResolveTextureDawn` as chained struct for `wgpu::RenderPipelineDescriptor::FragmentState::ColorTargetState`. It has `enabled` flag to indicate that the render pipeline is going to be used in a render pass with `ExpandResolveTexture` load op in the respective color attachment.

Example Usage:
```
/* Detailed source-code truncated for AI context efficiency. */
```

Notes:
 - If a resolve texture is used in a `wgpu::LoadOp::ExpandResolveTexture` operation, it must have `wgpu::TextureUsage::TextureBinding` usage.
 - The `wgpu::ColorTargetStateExpandResolveTextureDawn` chained struct controls the compatibility between a render pipeline and a render pass:
  - If the chained struct is not included in any `wgpu::RenderPipelineDescriptor::FragmentState::ColorTargetState` then the render pipeline **can only** be used on any render pass not using any `ExpandResolveTexture` load op. Whether the render pass has any resolve target doesn't matter.
  - If the chained struct is included in some color targets but **none** of their `enabled` boolean flags are true, then it's the same as above case.
  - If at least one included chained struct has `enabled` = true, then the compatibility's requirements are stricter:
    - If render pipeline's color target `i` has `wgpu::ColorTargetStateExpandResolveTextureDawn.enabled` = true, then the compatible render pass **must** have `ExpandResolveTexture` load op on attachment `i`.
    - If render pipeline's color target `i` has `wgpu::ColorTargetStateExpandResolveTextureDawn.enabled` = false, then the compatible render pass's attachment `i` must have a resolve target and its load op **must not** be `ExpandResolveTexture`.
    - If render pipeline's color target `i` has no `wgpu::ColorTargetStateExpandResolveTextureDawn` included, then the compatible render pass's attachment `i` **must not** have any resolve target.
 - Currently the `ExpandResolveTexture` LoadOp only works on color attachment, this could be changed in future.
 - The texture is not supported if it is not resolvable by WebGPU standard. This means this feature currently doesn't work with integer textures.
 - Using `ExpandResolveTexture` load op on a multiplanar texture's view is not supported currently.

---

### Dawn Features Dawn Native (`docs/dawn/features/dawn_native.md`)

# Dawn Native

The `dawn-native` feature enables additional functionality that is supported only
when the WebGPU implementation is `dawn_native`.

Additional functionality:

 - `dawn::native::DawnInstanceDescriptor` may be chained on `wgpu::InstanceDescriptor` on instance creation to pass in the platform and additional search paths, set backend validation level, and receive instance level errors.

 - `wgpu::DawnWGSLBlocklist` may be chained on `wgpu::InstanceDescriptor` on instance creation to block some WGSL features.

 - `wgpu::DawnCacheDeviceDescriptor` may be chained on `wgpu::DeviceDescriptor` on device creation to enable cache options such as isolation keys and pass in load and store functions.

 - Synchronous `adapter.CreateDevice(const wgpu::DeviceDescriptor*)` may be called.

Notes:
 - Enabling this feature in the `wgpu::DeviceDescriptor` does nothing, but
its presence in the Adapter's set of supported features means that the additional functionality is supported.

---

### Dawn Features Dawn Partial Load Resolve Texture (`docs/dawn/features/dawn_partial_load_resolve_texture.md`)

# Dawn partial Load Resolve Texture

The `dawn-partial-load-resolve-texture` feature is an extension to `dawn-load-resolve-texture`, which in addition allows to specify a rect sub-region of texture, where load and resolve will take effect only. The feature can't be available unless `dawn-load-resolve-texture` is available.

Additional functionalities:
 - Adds `wgpu::RenderPassDescriptorResolveRect` as chained struct for `wgpu::RenderPassDescriptor`. It defines an expanding  rect of {`colorOffsetX`, `colorOffsetY`, `width`, `height`} and a resolving rect {`resolveOffsetX`, `resolveOffsetY`, `width`, `height`} to indicate that expanding and resolving are only performed partially on the texels within this rect region of texture. The texels outside of the rect are not impacted.

Example Usage 1, ExpandResolveTexture and RenderPassDescriptorResolveRect:

When the load operation is set to wgpu::LoadOp::ExpandResolveTexture:
1. Expand the resolve texture's region {resolveOffsetX, resolveOffsetY, width, height} into the MSAA texture's region {colorOffsetX, colorOffsetY, width, height}.
2. Optional user draws to the MSAA texture.
3. Resolve the MSAA texture's region {colorOffsetX, colorOffsetY, width, height} into the resolve texture's region {resolveOffsetX, resolveOffsetY, width, height}.

```
/* Detailed source-code truncated for AI context efficiency. */
```

Example Usage 2, Clear and RenderPassDescriptorResolveRect:

Similar to the above example, but using wgpu::LoadOp::Clear:
1. Clear the entire MSAA texture using the clearValue.
2. Optional user draws to the MSAA texture.
3. Resolve the MSAA texture's region {colorOffsetX, colorOffsetY, width, height} into the resolve texture's region {resolveOffsetX, resolveOffsetY, width, height}.

```
// Create another render pass with "Clear" LoadOp.
wgpu::RenderPassDescriptor renderPassDesc2 = ...;
renderPassDesc2.colorAttachments[0].view = msaaTexture.CreateView();
renderPassDesc2.colorAttachments[0].resolveTarget
 = resolveTexture.CreateView();
renderPassDesc2.colorAttachments[0].loadOp = wgpu::LoadOp::Clear;
renderPassDesc2.colorAttachments[0].clearValue = clearValue;

wgpu::RenderPassDescriptorResolveRect rect{};
rect.colorOffsetX = rect.colorOffsetY = 1;
rect.resolveOffsetX = rect.resolveOffsetY = 1;
rect.width = rect.height = 32;
renderPassDesc2.nextInChain = &rect;
```

Example Usage 3, Load and RenderPassDescriptorResolveRect:

Similar to the first example, but using wgpu::LoadOp::Load:
1. Load the MSAA texture.
2. Optional user draws to the MSAA texture.
3. Resolve the MSAA texture's region {colorOffsetX, colorOffsetY, width, height} into the resolve texture's region {resolveOffsetX, resolveOffsetY, width, height}.

```
// Create another render pass with "Load" LoadOp.
wgpu::RenderPassDescriptor renderPassDesc2 = ...;
renderPassDesc2.colorAttachments[0].view = msaaTexture.CreateView();
renderPassDesc2.colorAttachments[0].resolveTarget
 = resolveTexture.CreateView();
renderPassDesc2.colorAttachments[0].loadOp = wgpu::LoadOp::Load;

wgpu::RenderPassDescriptorResolveRect rect{};
rect.colorOffsetX = rect.colorOffsetY = 1;
rect.resolveOffsetX = rect.resolveOffsetY = 1;
rect.width = rect.height = 32;
renderPassDesc2.nextInChain = &rect;
```

Notes:
 - In case that the target size of a render pass is very large, the cost of using `wgpu::LoadOp::ExpandResolveTexture` can be rather expensive, as it always assumes full-size expand and resolve. More commonly in reality, each frame we only need to re-draw a small damage region, of which UI frameworks usually have the knowledge, instead of the full window, or webpage. This feature aims to eliminate the waste by doing partial expand and resolve with the hint of `wgpu::RenderPassDescriptorResolveRect`, the actual damage region.
 - This feature is also useful to some scenarios where the applications want to use a smaller MSAA texture to render to a larger single sampled texture.
 - If the color attachment's loadOp is Load or Clear, then RenderPassDescriptorResolveRect will be ignored in the loading step. It will still be used in the resolving step to do a partial resolve.
 - The feature currently is only available on dawn d3d11 backend. Internally, both expand and resolve are implemented with a dedicated `wgpu::RenderPipeline`. `wgpu::RenderPassEncoder::APISetScissorRect` is used to set the scissor rect to `wgpu::RenderPassDescriptorResolveRect`, when using the pipeline. The major difference is that expand lives in the original render pass, while resolve requires a separate one.

---

### Dawn Features Dawn Texel Copy Buffer Row Alignment (`docs/dawn/features/dawn_texel_copy_buffer_row_alignment.md`)

# Dawn Texel Copy Buffer Row Alignment

The `dawn-texel-copy-buffer-row-alignment` feature exposes the alignment restriction of `byetsPerRow` in `wgpu::TexelCopyBufferLayout`. Each backend may have its own min alignment value. Without this feature, the alignment must be 256 for all backends.

Additional functionalities:
 - Adds `wgpu::DawnTexelCopyBufferRowAlignmentLimits` as chained struct for `wgpu::SupportedLimits`. It has a member `minTexelCopyBufferRowAlignment` to indicate the alignment limit of the current device.


Notes:
 - Even with this feature enabled, the alignment clients actually use, still needs to respect 'bytes-per-texel-block', and should be the max of them.
 - The feature currently is only available on D3D11 backend.

---

### Dawn Features Flexible Texture Views (`docs/dawn/features/flexible_texture_views.md`)

# Flexible Texture Views
In Compat Mode, a texture by default:
- Must be created with texture view dimension specified.
- Cannot use different views in the same draw call.
- Cannot create a 2D view of a 2DArray texture.

The `FlexibleTextureViews` feature removes the above restrictions.

---

### Dawn Features Format Capabilities (`docs/dawn/features/format_capabilities.md`)

# Format Capabilities

`wgpu::FeatureName::DawnFormatCapabilities`

Allows querying Adapter::GetFormatCapabilities. This API doesn't yet provide WebGPU related
information about the texture format, but will in the future.

See: https://github.com/webgpu-native/webgpu-headers/issues/283

`wgpu::FeatureName::DawnDrmFormatCapabilities`

Allows querying Adapter::GetFormatCapabilities with a DawnDrmFormatCapabilities chained struct, which
will be populated with DRM info including format modifiers and memory plane counts.

Requires the `wgpu::FeatureName::DawnFormatCapabilities` flag to be enabled as well.

---

### Dawn Features Framebuffer Fetch (`docs/dawn/features/framebuffer_fetch.md`)

# Framebuffer Fetch (experimental)

This extension enables support for the
[`chromium_experimental_framebuffer_fetch`](../../tint/extensions/chromium_experimental_framebuffer_fetch.md)
WGSL extension.

The extension is experimental and might change for example to gain new validation rules
(with extension struct) in the future.

It is available on tiler Metal GPUs and Vulkan with the `InputAttachment` capability.

## Validation

In `Device::CreateRenderPipeline` or `Device::CreateRenderPipelineAsync`:
 - For each `@color(N) in : T` fragment in:
   - color target N must exist
   - color target N's format must match T in both component count and base type

## WGSL

### Validation

* Requires `enable chromium_experimental_framebuffer_fetch`
* Requires fragment shader
* Requires T to be `i32`, `u32`, `f32`, `f16` or `vec{2,3,4}{i,u,f,h}`.
* Requires `N` to be unique per `color`
* The `N` value must be in the range of `[0..7]`

### Metal

* `@color(N)` entries translate directly to `[[color(N)]]` annotations on the input data.

### Vulkan

* Requires a `group`, `binding` value provided for each `color` `N` value from Host
* Add the `OpCapability InputAttachment`
* For each `color(N)` entry
  * Create an `OpTypeImage %type SubpassData 0 0 0 2 Unknown` where `%type` is `f32`, `i32` or `u32`. For `f16`,  the `%type` will be `f32` and the `OpImageRead` will be decorated with `RelaxedPrecision`.
  * Create a `OpTypePointer UniformConstant %image`
  * Create a `OpVariable %ptr_uniform_image UniformConstant`
  * Decorate the var with `OpDecorate %inputVar InputAttachmentIndex N`

  * We will then replace the value of the input variable with the value read from the attachment
    * `OpImageRead %v4float %img %zero` where `%zero` is `OpConstantComposite %v2int %int_0 %int_0`
    * For `f16`, the result of the `RelaxedPrecision`-decorated `OpImageRead` is converted to `f16`.

* For Dawn, this would probably map to creating the image in ShaderIO and then calling the
  `image_read` intrinsic for SPIR-V

---

### Dawn Features Host Mapped Pointer (`docs/dawn/features/host_mapped_pointer.md`)

# Host Mapped Pointer (Experimental!)

## Overview

Enable this feature by requesting `wgpu::FeatureName::HostMappedPointer` on device creation.

Allows creation of buffers from host-mapped pointers. These may be pointers from shared memory allocations, memory-mapped files, etc. The created buffer does not own a reference to the underlying memory so any use of it will become undefined if the host memory is unmapped or freed.

## Requirements
 - `wgpu::FeatureName::HostMappedPointer` must be supported and enabled.
 - Both the address of the pointer and the size of the allocation that the pointer refers to must be aligned to OS-specific requirements. This is 4Kb on Mac / Linux and 64Kb on Windows.
 - None of the mapping APIs may actually be called on the buffer since it is effectively persistently mapped.
 - On Windows, the pointer must point to the start of the virtual or mapped memory. It will be invalid to pass a pointer returned from `MapViewOfFile` at a non-zero offset.

## Example Usage
```c++
// Memory map a file.
void* ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);

wgpu::BufferHostMappedPointer hostMappedDesc;
hostMappedDesc.pointer = ptr;

// Use the dispose callback to be notified when the buffer is destroyed and no
// longer in use on the GPU. After this point, it is safe to unmap the memory.
hostMappedDesc.disposeCallback = [](void* userdata) {
  // Unmap and close the file.
  auto* data = reinterpret_cast<std::tuple<int, void*, size_t>*>(userdata);
  munmap(std::get<1>(*data), std::get<2>(*data));
  close(std::get<0>(*data));
  delete data;
};
// Pass the fd, ptr, and size as the userdata so we can free the
// memory later.
hostMappedDesc.userdata = new std::tuple<int, void*, size_t>(fd, ptr, size);

wgpu::BufferDescriptor bufferDesc;
bufferDesc.usage = wgpu::BufferUsage::CopySrc;
bufferDesc.size = size;
bufferDesc.nextInChain = &hostMappedDesc;

wgpu::Buffer buffer = device.CreateBuffer(&bufferDesc);
```

It's expected that the memory pages from wrapped host allocations and mapped files are made resident and then pinned at the time of buffer creation. Thus, the memory should not be paged out when used on the GPU. Explicitly requesting that it be decommitted, for example using `madvise` with `MADV_DONTNEED` or `VirtualFree` with `MEM_DECOMMIT` is undefined behavior.

## TODO(crbug.com/dawn/2018):
 - Investigate adding a chained struct for importing from a Windows file HANDLE
 - Expose alignment as a proper WebGPU limit.
 - Figure out interaction with buffer mapping. These types of buffers are essentially persistently mapped.
 - Flesh out if/when the dispose callback is called when there is a validation, out-of-memory, or internal error on creation.

---

### Dawn Features Implicit Device Synchronization (`docs/dawn/features/implicit_device_synchronization.md`)

# Implicit Device Synchronization

Makes most public API methods implicitly lock the device, making them safe to call in a multi-threaded environment.
All commands for the `wgpu::Device` and child objects are safe, except command encoding which doesn't get implicit synchronization.

Note that this extension is quite heavy-handed in terms of synchronization, and should eventually disappear in favor of proper fine-grained and efficient synchronization in Dawn.
Also there are likely still cases where Dawn's reentrancy is not handled properly and could cause deadlocks with this extension.

The initial tracking bug was https://crbug.com/dawn/1662.

---

### Dawn Features Msaa Render To Single Samples (`docs/dawn/features/msaa_render_to_single_samples.md`)

# MSAA Render To Single Sampled/Multisampled Render To Single Sampled

The `msaa-render-to-single-sampled` feature (MSRTSS) allows a render pass to include single-sampled attachments while rendering is done with a specified number of samples. When this feature is used, the client doesn't need to explicitly allocate any multi-sampled color textures.

Additional functionalities:
 - Adds `wgpu::DawnRenderPassSampleCount` as chained struct for `wgpu::RenderPassDescriptor` to specify number of samples to be rendered for the respective single-sampled attachment.

Example Usage:
```
// Create texture with RenderAttachment usage.
wgpu::TextureDescriptor desc = ...;
desc.sampleCount = 1;
desc.usage = wgpu::TextureUsage::RenderAttachment;

auto texture = device.CreateTexture(&desc);

// Create a render pipeline
wgpu::RenderPipelineDescriptor pipelineDesc = ...;
auto pipeline = device.CreateRenderPipeline(&pipelineDesc);

// Create a render pass with "implicit multi-sampled" enabled.
wgpu::DawnRenderPassSampleCount renderPassSampleCount;
renderPassSampleCount.sampleCount = 4;

wgpu::RenderPassDescriptor renderPassDesc = ...;
renderPassDesc.colorAttachments[0].view = texture.CreateView();
renderPassDesc.nextInChain = &renderPassSampleCount;

auto renderPassEncoder = encoder.BeginRenderPass(&renderPassDesc);

renderPassEncoder.SetPipeline(pipeline);
renderPassEncoder.Draw(3);
renderPassEncoder.End();
```

Notes:
 - To trigger MSRTSS load or store behavior, a single-sampled color attachment must be attached to a render pass with an explicit sample count given by the `wgpu::DawnRenderPassSampleCount` chained struct. The `resolveTarget` field of the the color attachment must be `nullptr`.
 - Only colorAttachment zero may be used.
 - If a multisampled depth/stencil attachment is specified, it must have `TextureUsage::TransientAttachment`, its sample count must match the `sampleCount` specified by the wgpu::DawnRenderPassSampleCount` chained struct and the `resolveTarget` field of the attachment must be `nullptr`
 - If a texture is not resolvable by WebGPU standard it cannot be used as an MSRTSS color attachment. This means this feature currently doesn't work with integer textures.

---

### Dawn Features Multi Draw Indirect (`docs/dawn/features/multi_draw_indirect.md`)

# Multi Draw Indirect

The multi-draw-indirect feature allows issuing multiple draws with a single command. This is useful for rendering thousands if not millions of objects in GPU driven rendering.

It introduces the following new commands:

- `wgpu::CommandEncoder::multiDrawIndirect(wgpu::Buffer indirectBuffer, uint64_t indirectOffset, uint32_t maxDrawCount, wgpu::Buffer countBuffer, uint64_t countOffset)`
- `wgpu::CommandEncoder::multiDrawIndexedIndirect(wgpu::Buffer indirectBuffer, uint64_t indirectOffset, uint32_t maxDrawCount, wgpu::Buffer countBuffer, uint64_t countOffset)`

This feature aims to overcome the shortcomings of `drawIndirect` and `drawIndexedIndirect` commands, which can only issue a single draw call at a time from a certain region of a buffer.
In GPU driven rendering, the compute passes can generate the thousands of draw calls and specify how many draw calls to issue, which can be stored in a buffer and then executed.
With `drawIndirect` and `drawIndexedIndirect`, the CPU has to issue every single draw call and if the number of draw calls is not known ahead of time, the CPU has to read back the number of draw calls from the GPU, which can be a performance bottleneck.
The alternative is to issue maxDrawCount commands and zero out the draw calls that are not needed, but that wastes CPU and GPU resources.
The bottleneck can be avoided by using this feature.


## Usage

`wgpu::FeatureName::MultiDrawIndirect` feature must be enabled to use the commands. Most desktop GPUs support this feature.

```cpp
// Create a buffer to store the draw calls, and the number of draw calls which is a uint32_t.
// Number of draw calls can be stored in another buffer with indirect usage.
wgpu::BufferDescriptor drawCallsBufferDesc = {
	.size = sizeof(DrawCall) * 100 + sizeof(uint32_t),
	.usage = wgpu::BufferUsage::Indirect | wgpu::BufferUsage::Storage,
};
wgpu::Buffer drawCallsBuffer = device.CreateBuffer(&drawCallsBufferDesc);

// Fill the buffer with draw calls and the uint32_t specifying the number of draw calls ...
wgpu::Buffer vertexBuffer = ...;
wgpu::BindGroup bindGroup = ...;
wgpu::CommandEncoder encoder = ...;
wgpu::RenderPassEncoder renderPassEncoder = encoder.BeginRenderPass(...);

// In the render pass, issue the draw calls
{
	...
	renderPassEncoder.SetBindGroup(...);
	renderPassEncoder.SetPipeline(...);
	renderPassEncoder.SetVertexBuffer(0, vertexBuffer);
	renderPassEncoder.MultiDrawIndirect(drawCallsBuffer, 0, 32, drawCallsBuffer, 1024);
	...
}

renderPassEncoder.EndPass();
```

## Restrictions

- The number of draw calls executed is `min(maxDrawCount, countBuffer)`, where countBuffer is the number of draw calls specified in the buffer with the offset.
- The indirect buffer must be created with `wgpu::BufferUsage::Indirect` usage and be large enough to store `maxDrawCount` draw calls.
- If `wgpu::FeatureName::IndirectFirstInstance` is not enabled, the `firstInstance` field of every draw call encoded in the indirect buffer must be `0`, otherwise the draw call with a non-zero `firstInstance` will be skipped.

---

### Dawn Features Multi Planar Formats (`docs/dawn/features/multi_planar_formats.md`)

# Multi Planar Formats

Video is often decoded to formats with a full resolution luminance image and a lower resolution chrominance image.
This is represented in GPU APIs with multi-planar color texture formats where plane 0 is luminance, plane 1 is chrominance and plane 2 an optional alpha channel. It is important to support these formats in Dawn because in combination with `wgpu::ExternalTexture` they can provide 0-copy access to video data with less memory bandwidth than if it were decoded to `RGBA8Unorm` textures.

Dawn has a number of optional features that give access to some of these formats and texture capabilities.
Note however that Dawn doesn't specify the semantics of the planes (luminance vs. chrominance), but instead just exposes multiple planes with varying sizes and formats.

Multi planar formats introduce new `wgpu::TextureAspect` values `Plane0Only`, `Plane1Only` and `Plane2Only` to allow using a single aspect of multi planar textures in `TextureView` creation or copy operations. All multi planar formats support `wgpu::TextureUsage::TextureBinding` and `wgpu::TextureUsage::CopySrc` without any need for another optional feature (and cannot be created directly, only imported with `SharedTextureMemory`).

TODO(dawn:551): It seems these aspects enums are always allowed. Maybe they should be gated on the base feature?

## `DawnMultiPlanarFormats`

This feature adds support for "NV12", a common format for decoded SDR videos.
It is composed of:

 - a full res `R8Unorm` plane 0 (luminance).
 - a 1/4 res `RG8Unorm` plane 1 (chrominance).

For historical reasons the feature is called `DawnMultiPlanarFormats`.
This format is called `wgpu::TextureFormat::R8BG8Biplanar420Unorm`.

TODO(dawn:551): Rename to MultiPlanarFormatNV12 ?

## `MultiPlanarFormatP010`

This feature adds support for "P010", a common format for decoded HDR videos.
It is composed of:

 - a full res `R16Unorm` plane 0 (luminance) with only the first 10 bits used in the decoding of the videos.
 - a 1/4 res `RG16Unorm` plane 1 (chrominance) with only the first 10 bits of each channel used in the decoding of the videos.

This format is called `wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm`.

TODO(dawn:551) is it possible to merge this feature with the nv12 feature?

## `MultiPlanarFormatNV12A` (Experimental!)

This feature adds support for "NV12A", a format for decoded SDR videos with an alpha channel.
It is composed of:

 - a full res `R8Unorm` plane 0 (luminance).
 - a 1/4 res `RG8Unorm` plane 1 (chrominance).
 - a full res `R8Unorm` plane 2 (alpha).

This format is called `wgpu::TextureFormat::R8BG8A8Triplanar420Unorm`.

At the moment it is only available on macOS >= 10.15, iOS >= 13.0.

## `MultiPlanarFormatNV16` (Experimental!)

This feature adds support for "NV16", a common format for 8-bit decoded 4:2:2 videos.
It is composed of:

 - a full res `R8Unorm` plane 0 (luminance).
 - a 1/2 res `RG8Unorm` plane 1 (chrominance).

This format is called `wgpu::TextureFormat::R8BG8Biplanar422Unorm`.

At the moment it is only available on macOS >= 11.0, iOS >= 14.0.

## `MultiPlanarFormatNV24` (Experimental!)

This feature adds support for "NV24", a common format for 8-bit decoded 4:2:2 videos.
It is composed of:

 - a full res `R8Unorm` plane 0 (luminance).
 - a full res `RG8Unorm` plane 1 (chrominance).

This format is called `wgpu::TextureFormat::R8BG8Biplanar444Unorm`.

At the moment it is only available on macOS >= 11.0, iOS >= 14.0.

## `MultiPlanarFormatP210` (Experimental!)

This feature adds support for "P210", a common format for 10-bit decoded 4:2:2 videos.
It is composed of:

 - a full res `R16Unorm` plane 0 (luminance) with only the first 10 bits used in the decoding of the videos.
 - a 1/2 res `RG16Unorm` plane 1 (chrominance) with only the first 10 bits of each channel used in the decoding of the videos.

This format is called `wgpu::TextureFormat::R10X6BG10X6Biplanar422Unorm`.

At the moment it is only available on macOS >= 10.13, iOS >= 11.0.

## `MultiPlanarFormatP410` (Experimental!)

This feature adds support for "P410", a common format for 10-bit decoded 4:4:4 videos.
It is composed of:

 - a full res `R16Unorm` plane 0 (luminance) with only the first 10 bits used in the decoding of the videos.
 - a full res `RG16Unorm` plane 1 (chrominance) with only the first 10 bits of each channel used in the decoding of the videos.

This format is called `wgpu::TextureFormat::R10X6BG10X6Biplanar444Unorm`.

At the moment it is only available on macOS >= 10.13, iOS >= 11.0.

## `MultiPlanarFormatExtendedUsages`

Adds the ability to create multi-planar textures with `device.CreateTexture()` and the `wgpu::TextureUsage::CopyDst`usage.

## `MultiPlanarRenderTargets`

Adds the `wgpu::TextureUsage::RenderAttachment` usage to multi planar formats.

---

### Dawn Features Norm16 Texture Formats (`docs/dawn/features/norm16_texture_formats.md`)

# Norm16 texture formats

Adds support for the following norm16 formats with `CopySrc|CopyDest|RenderAttachment|TextureBinding` usages, multisampling and resolving capabilities:

 - `wgpu::TextureFormat::R16Unorm`
 - `wgpu::TextureFormat::RG16Unorm`
 - `wgpu::TextureFormat::RGBA16Unorm`

The initial tracking bug was https://crbug.com/dawn/1982.

---

### Dawn Features Opaque Ycbcr Android For External Texture (`docs/dawn/features/opaque_ycbcr_android_for_external_texture.md`)

# `OpaqueYCbCrAndroid` for external textures.

[Google-internal design doc](https://docs.google.com/document/d/1sD4Dg1-ZDuPzdDBE6UOtsZuZAr0xOMk6-P8X98wQcfw)

## Context

WebGPU is used on the Web and in native to do GPU-accelerated video processing.
Video streams are usually coming from some system component (HW video decoder, camera hardware) and encoded in some YUV format (as a basic form of compression because the human eye is more sensitive to variation in luminance than chrominance).
For performance and power-efficiency reasons, it is important that Dawn/WebGPU (and the applications using it) be able to directly sample YUV data in the shader instead of first decompressing to RGB as both the decompression and the sampling of expanded RGB data cause additional memory traffic than necessary.

For this purpose, the WebGPU API added the [GPUExternalTexture](https://gpuweb.github.io/gpuweb/#gpuexternaltexture) concept that can be created from a HTML \<video\> element, a WebCodec VideoFrame, etc.
In WGSL shaders, a [sampling builtin](https://gpuweb.github.io/gpuweb/wgsl/#textureSampleBaseClampToEdge) is used that is polyfilled in Tint to [generate](https://source.chromium.org/chromium/chromium/src/+/main:third_party/dawn/src/tint/lang/core/ir/transform/multiplanar_external_texture.h) all the multiplane sampling, YUV to RGB and colorspace conversion, and additional computations needed to sample 0-copy.
On systems where the Y and UV planes of textures can be sampled separately, the generic Tint polyfill works well, but not on Android because videos are AHardwareBuffers with opaque YCbCr formats.

On Android with AHB with [YCbCr formats](https://developer.android.com/ndk/reference/group/a-hardware-buffer#group___a_hardware_buffer_1ga94397844440c1c27b5b454d56ba5ff38) (which is what's given by camera hardware and video decoders), the Vulkan API requires usage of static samplers and more.
Dawn has a large API in [y_cb_cr_vulkan_samplers.md](./y_cb_cr_vulkan_samplers.md) that's  mostly passthrough to Vulkan but it is quite inflexible and difficult to use (future Vulkan extension could improve this but would take time to be available everywhere).

## Creating `ExternalTexture` from `OpaqueYCbCrAndroid`.

This extension `OpaqueYCbCrAndroidForExternalTexture` allows creating an `ExternalTexture` from imported YCbCr AHB, and have sampling "just work".
Internally Dawn recognizes that a pipeline will use an `ExternalTexture` and uses the same mechanisms as [y_cb_cr_vulkan_samplers.md](./y_cb_cr_vulkan_samplers.md) to do 0-copy YUV sampling, recreating `BindGroupLayouts` with static samplers and recompiling the underlying `VkPipeline`.
With this functionality it is possible to 0-copy sample YCbCr AHBs with code like:

```cpp
// Import the AHB in Dawn, wrap it in an STM first (the object doing access control
// and vending indivial wgpu::Texture views of the AHB), then in a wgpu::Texture and
// finally wrap the wgpu::Texture in the wgpu::ExternalTexture object (that is used
// to represent all the other ways to do YUV 0-copy imports).
wgpu::SharedTextureMemory stm = device.CreateSharedTextureMemory(&descFromAHB);
// descForTexture needs to be compatible with Dawn's reflection in stm.GetProperties()
// of what's allowed with that STM (size, usage, format).
wgpu::Texture tex = stm.CreateTexture(&descForTexture);
wgpu::ExternalTexture et = device.CreateExternalTexture(&descWithPlane0Only);

// Start Dawn's access of the AHB, the application needs to pass in the fences required
// to ensure proper ordering of (GPU-timeline) operations on the AHB.
stm.BeginAccess(&beginAccessWithFencesEtc);

// Render, sampling `et` in pipeline to do 0-copy YUV sampling.

// End Dawn's access of the AHB. Dawn will return fences that must be used to
// to synchronize, on the GPU-timeline, the subsequent accesses on the AHB.
stm.EndAccess(&endAccessDesc);

// Destroy stm, tex, et if they are used for a single frame, or keep them around
// if we know that video decoders reuse AHBs internally.
```

## Validation rules

 - This feature allows using `wgpu::TextureFormat::OpaqueYCbCrAndroid` but does not enable any use of them except noted below.
 - `Texture::CreateView` is allowed on `OpaqueYCbCrAndroid` (without a `YCbCrVkDescriptor`).
 - These views can be used in `ExternalTextureDescriptor::plane0` as the single plane specified (views with a `YCbCrVkDescriptor` are rejected).
 - These views *cannot* be used in `BindGroups` even with YCbCr static samplers.

---

### Dawn Features Pixel Local Storage (`docs/dawn/features/pixel_local_storage.md`)

# Pixel Local Storage (PLS)

This features adds support for efficiently reading and writing per-pixel data that's local to a render pass, with correctly synchronized accesses.
On "explicit" tiler GPUs this additional data is allocate in tile storage similarly to attachments, but on other GPUs the same functionality can be implemented using "fragment shader interlock" and read-write storage textures.

Here is the [design doc](https://docs.google.com/document/d/1djJwQLJcVGnDXOA7nhsweppim-hhbqglB4JNyfZcRqQ) for this feature.

## Concepts

A couple new concepts are added:

 - A `pixel_local` storage class in WGSL that can be used with `var<pixel_local> pls : u32/MyStruct` which declares the pixel local data in the shader.
   It requires a WGSL `enable`, see [`chromium_experimental_framebuffer_fetch`](../../tint/extensions/chromium_experimental_pixel_local.md).
 - Pixel local storage descriptors for both `wgpu::PipelineLayoutDescritor` and `wgpu::RenderPassDescriptor` that describe the layout of the PLS inside a render pass, and are part of pipeline / render pass compatibility.
   These descriptors contain both:

   - The total size of the PLS allocation for the render pass.
   - A number of storage attachments which are textures which will have their corresponding texel loaded/stored into a specific offset of the PLS at the start/end of the render pass.
     (note that on explicit tiler GPUs this is exactly what happens, but on others the load/stores are done directly as "storage texture" operations)
   - Implicit PLS slots (the ones not bound to a storage attachment) start with value 0 and are discarded at the end of the pass.

 - A `wgpu::TextureUsage::StorageAttachment` that allows a texture to be used as a storage attachment in a render pass.
 - The `wgpu::RenderPassEncoder::PixelLocalStorageBarrier` method for use in the non-coherent PLS extension.

## Coherency

The feature comes in two flavors depending on coherency.
The coherent version automatically synchronizes fragment shader accesses to the PLS so that no data race happens (as if there is a critical section between the first and last use of the PLS in an invocation), and enforcing that fragment shaders happen in API order (two fragments invocations from two triangles of the same draw happen in the order of the triangles in the draw).
The non-coherent version allows racy access to the PLS during the whole render pass, but provides a `PixelLocalStorageBarrier()` that prevents races between fragment invocations before and after the barrier.
In particular, there is no way to prevent racy access to the PLS in the same draw with the non-coherent version of the feature.

## New API surface

```cpp
// Add to wgpu::FeatureName
wgpu::FeatureName::PixelLocalStorageNonCoherent
wgpu::FeatureName::PixelLocalStorageCoherent

// Add to wgpu::TextureUsage
wgpu::TextureUsage::StorageAttachment

// Can be chained to a RenderPassDescriptor
struct wgpu::RenderPassPixelLocalStorage : wgpu::ChainedStruct {
    uint64_t totalPixelLocalStorageSize;
    size_t storageAttachmentCount;
    wgpu::RenderPassStorageAttachment* storageAttachments;
};
struct wgpu::RenderPassStorageAttachment : wgpu::ChainedStruct {
    wgpu::LoadOp loadOp;
    wgpu::StoreOp storeOp;
    wgpu::Color clearValue;
    wgpu::TextureView storage;
    uint64_t offset;
};

// Can be chained to a PipelineLayoutDescriptor
struct wgpu::PipelineLayoutPixelLocalStorage : wgpu::ChainedStruct {
    uint64_t totalPixelLocalStorageSize;
    size_t storageAttachmentCount;
    wgpu::PipelineLayoutStorageAttachment* storageAttachments;
};
struct wgpu::PipelineLayoutStorageAttachment : wgpu::ChainedStruct {
    wgpu::TextureFormat format;
    uint64_t offset;
};

// Used for non-coherent.
wgpu::RenderPassEncoder::PixelLocalStorageBarrier();
```

## Validation Rules

 - Only the `R32Uint`, `R32Sint` and `R32Float` texture formats can specify `wgpu::TextureUsage::StorageAttachment`.
 - `StorageAttachment` textures must be single-sampled.
 - In a `PixelLocalStorage` descriptor:

    - The totalPixelLocalStorageSize must be less than a small constant (should eventually get into the limits) currently set at 16 and must be a multiple of 4.
    - The offset of each storage attachment must fit in the total size (with the additional size of that PLS slot) and be a multiple of 4.
    - The format of the storage attachments must be one of the texture formats that allows `StorageAttachment`.
    - Storage attachment views must be for a single subresources (and used at most once in `BeginRenderPass`).
    - Storage attachment views must have the same size of any other attachment used in the render pass.
    - The load and store operations on storage attachments follow the same rules as for color attachments.
    - Two storage attachments must not collide.

 - The PLS definition between a pipeline and a render pass must match.
 - A render pipeline must us a `pixel_local` storage class that's compatible with its layout's PLS descriptor.

   - The totalPLSSize must match the size of the `pixel_local` block.
   - Each slot used for a storage attachment must have a WGSL type that matches that attachment's texture format.
   - Each implicit slot must use the `u32` WGSL type.

---

### Dawn Features Render Pass Render Area (`docs/dawn/features/render_pass_render_area.md`)

# Render Pass Render Area

The `render-pass-render-area` feature allows specifying a sub-region of the attachments that the
render pass will use. The "render area" defines the rectangle within which rendering operations,
including load and store operations, must be performed. Rendering operations may still modify texels
outside of the render area.

Additional functionalities:
 - Adds `wgpu::RenderPassRenderAreaRect` as chained struct for `wgpu::RenderPassDescriptor`. It
defines a rect of {`x`, `y`, `width`, `height`} to indicate that rendering operations are only
performed on the texels within this rect region of the attachments.

Example Usage:
```cpp
wgpu::RenderPassRenderAreaRect rect;
rect.x = 10;
rect.y = 10;
rect.width = 100;
rect.height = 100;

wgpu::RenderPassDescriptor renderPassDesc = ...;
renderPassDesc.nextInChain = &rect;

auto renderPassEncoder = encoder.BeginRenderPass(&renderPassDesc);
// Draw commands here...
renderPassEncoder.End();
```

Notes:
 - The render area must be contained within the dimensions of the render pass attachments.
 - The render area cannot be empty.
 - Any scissor rect set with `wgpu::RenderPassEncoder::SetScissorRect()` must be contained by the
render area rect specified in `wgpu::RenderPassRenderAreaRect`.
 - The default scissor rect is the render area.
 - The default viewport is unchanged.
 - The actual render area used by the hardware may be slightly larger than the requested area due to
tile alignment or granularity requirements. Dawn handles this alignment internally by expanding
the render area to meet the backend's granularity requirements while still encompassing the
requested region.

---

### Dawn Features Shader Module Compilation Options (`docs/dawn/features/shader_module_compilation_options.md`)

# Shader Module Compilation Options

Shader module compilation options may be specified to override Dawn's default compilation behavior.

`wgpu::ShaderModuleCompilationOptions` may be chained on `wgpu::ShaderModuleDescriptor`. If it is,
Dawn will use these compilation options instead of its defaults.

### `wgpu::ShaderModuleCompilationOptions::strictMath`
Enables or disables strict math. When strict math is disabled, generally the compiler will:
- Assume no NaNs
- Assume no Inf
- Assume no signed 0
- Use multiplication by reciprocal instead of division
- Allow algebraic transformations according to associative and distribute properties.

It is implemented only on Metal and D3D.

---

### Dawn Features Shared Buffer Memory (`docs/dawn/features/shared_buffer_memory.md`)

# Shared Buffer Memory

## Overview

Shared Buffer Memory refers to a superset of features that allow Dawn to import externally allocated buffers.

- `wgpu::FeatureName::SharedBufferMemoryD3D12Resource`
- `wgpu::FeatureName::SharedBufferMemoryFromWindowsHandle`
- `wgpu::FeatureName::SharedBufferMemoryHostPointer`

```c++
wgpu::SharedBufferMemoryFooBarDescriptor fooBarDesc = {
  .fooBar = ...,
};
wgpu::SharedBufferMemoryDescriptor desc = {};
desc.nextInChain = &fooBarDesc;

wgpu::SharedBufferMemory memory = device.CreateSharedBufferMemory(&desc);
```

After creating the memory, the supported `wgpu::BufferUsage` can be queried.
```c++
wgpu::SharedBufferMemoryProperties properties;
memory.GetProperties(&properties);

switch (properties.usage) {
  // ... handle supported usage
}
```

Then, a buffer can be created from it. For example:
```c++
wgpu::BufferDescriptor bufferDesc = {
  // usage must be a subset of the buffer memory's usage
  .usage = properties.usage
};
wgpu::Buffer buffer = memory.CreateBuffer(&bufferDesc);
```

A buffer can also be created from the shared buffer memory without a `wgpu::BufferDescriptor`. In this case:
- The buffer will be created with the same size as the shared buffer memory
- The buffer will be created with the same buffer usage as the shared buffer memory without `wgpu::BufferUsage::MapRead` and `wgpu::BufferUsage::MapWrite`, which means the buffer will only contain GPU-accessible buffer usages.

```c++
// Create a buffer from shared buffer memory without a descriptor
wgpu::Buffer buffer = memory.CreateBuffer();
```

A buffer created from shared buffer memory is not valid to use inside a queue operation until access to the memory is explicitly started using `BeginAccess`. Access is ended using `EndAccess`. For example:

```c++
wgpu::BufferDescriptor bufferDesc = { ... };
wgpu::Buffer buffer = memory.CreateBuffer(&bufferDesc);

// It is valid to create a bind group and encode commands
// referencing the buffer.
wgpu::BindGroup bg = device.CreateBindGroup({..., buffer, ... });
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::ComputePass pass = encoder.BeginComputePass();
// ...
pass.SetBindGroup(0, bg);
// ...
pass.End();
wgpu::CommandBuffer commandBuffer = encoder.Finish();

// Begin/EndAccess must wrap usage of the buffer on the queue.
wgpu::SharedBufferMemoryBeginAccessDescriptor beginAccessDesc = { ... };
memory.BeginAccess(buffer, &beginAccessDesc);

queue.writeBuffer(buffer, ...);
queue.submit(1, &commandBuffer);

wgpu::SharedBufferMemoryEndAccessState endAccessDesc = { ... };
memory.EndAccess(buffer, &endAccessDesc);
```

# Uniform Usage Restriction

Using wgpu:BufferUsage::Uniform with a buffer created from SharedBufferMemory is not allowed due to an alignment restriction on D3D12 when creating a constant buffer view. It is possible this restriction could be removed in the future if additional alignment restrictions are placed on the provided SharedBufferMemory during import.

# Mappable Buffers

A buffer created from shared buffer memory cannot be mapped until access to the memory is explicitly started using `BeginAccess`. The buffer must be unmapped before calling `EndAccess`.

# Concurrent Access

Multiple buffers may be created from the same SharedBufferMemory, but only one buffer may have access to the memory at a time.

# SharedBufferMemory Types

`SharedBufferMemoryDescriptor` accepts different chained structs corresponding to the memory type.

# Synchronization

The Begin/End access descriptors are used to pass [Shared Fences](./shared_fence.md) necessary to synchronize buffer states between Dawn and external code.

When calling `BeginAccess`, Dawn will wait for any passed shared fences to be signaled before the buffer is used on the GPU. This will ensure any GPU work occurring outside of Dawn is finished before Dawn accesses the memory.

When calling `EndAccess`, the shared fences for the buffer's last usages will be exported. This allows code external to Dawn to be notified when Dawn's GPU work on the shared memory is complete.

---

### Dawn Features Shared Fence (`docs/dawn/features/shared_fence.md`)

# Shared Fence

## Overview

A variety of features may be used to externally synchronize usage of resources on the GPU.

- `wgpu::FeatureName::SharedFenceVkSemaphoreOpaqueFD`
- `wgpu::FeatureName::SharedFenceSyncFD`
- `wgpu::FeatureName::SharedFenceVkSemaphoreZirconHandle`
- `wgpu::FeatureName::SharedFenceDXGISharedHandle`
- `wgpu::FeatureName::SharedFenceMTLSharedEvent`
- `wgpu::FeatureName::SharedFenceEGLSync`

TODO(crbug.com/dawn/1745): additional documentation

Work-in-progress: https://docs.google.com/document/d/1uRGL6vE1mSbpWd2v_KU5--RT5EjTXtruwiC7Ri3ZKz4/edit

---

### Dawn Features Shared Texture Memory (`docs/dawn/features/shared_texture_memory.md`)

# Shared Texture Memory

## Overview

A variety of features may be used to import externally allocated shared texture memory.

- `wgpu::FeatureName::SharedTextureMemoryAHardwareBuffer`
- `wgpu::FeatureName::SharedTextureMemoryDmaBuf`
- `wgpu::FeatureName::SharedTextureMemoryOpaqueFD`
- `wgpu::FeatureName::SharedTextureMemoryZirconHandle`
- `wgpu::FeatureName::SharedTextureMemoryDXGISharedHandle`
- `wgpu::FeatureName::SharedTextureMemoryD3D11Texture2D`
- `wgpu::FeatureName::SharedTextureMemoryIOSurface`

Each memory type uses an extension struct chained on `wgpu::SharedTextureMemoryDescriptor` to create the memory. For example:

```c++
wgpu::SharedTextureMemoryFooBarDescriptor fooBarDesc = {
  .fooBar = ...,
};
wgpu::SharedTextureMemoryDescriptor desc = {};
desc.nextInChain = &fooBarDesc;

wgpu::SharedTextureMemory memory = device.CreateSharedTextureMemory(&desc);
```

After creating the memory, the properties such as the supported `wgpu::TextureUsage` can be queried.
```c++
wgpu::SharedTextureMemoryProperties properties;
memory.GetProperties(&properties);

switch (properties.usage) {
  // ... handle supported usage
}
```

Then, a texture can be created from it. For example:
```c++
wgpu::TextureDescriptor textureDesc = {
  // usage must be a subset of the texture memory's usage
  .usage = properties.usage
};
wgpu::Texture texture = memory.CreateTexture(&textureDesc);
```

Note: There are restrictions on the configuration that can be requested for
textures created from SharedTextureMemory objects:
- they must be single-sampled
- they must be 2D
- they must have a single mip level
- they must have an array layer count of 1

Textures created from shared texture memory are not valid to use inside a queue operation until access to the memory is explicitly started using `BeginAccess`. Access is ended using `EndAccess`. For example:

```c++
wgpu::TextureDescriptor textureDesc = { ... };
wgpu::Texture texture = memory.CreateTexture(&textureDesc);

// It is valid to create a bind group and encode commands
// referencing the texture.
wgpu::BindGroup bg = device.CreateBindGroup({..., texture, ... });
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::ComputePass pass = encoder.BeginComputePass();
// ...
pass.SetBindGroup(0, bg);
// ...
pass.End();
wgpu::CommandBuffer commandBuffer = encoder.Finish();

// Begin/EndAccess must wrap usage of the texture on the queue.
wgpu::SharedTextureMemoryBeginAccessDescriptor beginAccessDesc = { ... };
memory.BeginAccess(texture, &beginAccessDesc);

queue.writeTexture(texture, ...);
queue.submit(1, &commandBuffer);

wgpu::SharedTextureMemoryEndAccessState endAccessDesc = { ... };
memory.EndAccess(texture, &endAccessDesc);
```

Multiple textures may be created from the same SharedTextureMemory, but only one texture may have access to the memory at a time.

The Begin/End access descriptors are used to pass data necessary to update and synchronize texture state since the texture may have been externally used. Chained structs that are supported or required vary depending on the memory type. These access descriptors communicate the texture's initialized state. They are also used to pass [Shared Fences](./shared_fence.md) to synchronize access to the texture. When passed in BeginAccess, the texture will wait for the shared fences to pass before use on the GPU, and on EndAccess, the shared fences for the texture's last usage will be exported. Passing shared fences to BeginAccess allows Dawn to wait on externally-enqueued GPU work, and exporting shared fences from EndAccess then allows other external code to wait for Dawn's GPU work to complete.

TODO(crbug.com/dawn/1745): additional documentation

Work-in-progress: https://docs.google.com/document/d/1uRGL6vE1mSbpWd2v_KU5--RT5EjTXtruwiC7Ri3ZKz4/edit

---

### Dawn Features Static Samplers (`docs/dawn/features/static_samplers.md`)

# Static Samplers

The `static-samplers` feature allows specification of samplers statically as
part of the bind group layout.

TODO(crbug.com/dawn/2364): Expand this documentation with examples and
description of semantics (including constraints/validations) as we build out
support

---

### Dawn Features Subgroup Matrix (`docs/dawn/features/subgroup_matrix.md`)

# Subgroup Matrix

## Background

Subgroup matrices are an abstract matrix data type. Computations on subgroup matrices utilize the SIMD nature of the GPU and are distributed among multiple invocations. Subgroup matrices are a critical primitive for ML workloads: Large matrix-multiply operations can be decomposed into many matrix multiplies over small sub-blocks of the larger matrices.  (Note that 1x1 convolutions are mathematically the same as matrix multiply.)



*   Backgrounder: https://docs.nvidia.com/deeplearning/performance/dl-performance-matrix-multiplication/index.html

Many GPUs now support this feature, with various parameterizations of block sizes, component data types, and result types.  In the following, a typical block matrix multiply operation is:


    Accum = A * B

or

	Accum = A * B + Accum


    where  Accum is M-row, N-column,


    A is M-row K-column,


    B is K-row, N-column.


## Target API support


### SPIR-V/Vulkan

Originally introduced as an Nvidia vendor extension ([SPV_NV_cooperative_matrix](https://github.khronos.org/SPIRV-Registry/extensions/NV/SPV_NV_cooperative_matrix.html) and [VK_NV_cooperative_matrix](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VK_NV_cooperative_matrix)). NVIDIA presented an overview of the feature and its benefits, in a 2019 blog post and a [2022 presentation](https://www.khronos.org/assets/uploads/developers/presentations/Cooperative_Matrix_May22.pdf), and [optimization guide](https://docs.nvidia.com/deeplearning/performance/dl-performance-matrix-multiplication/index.html). The functionality has since been standardized as [SPV_KHR_cooperative_matrix](https://github.khronos.org/SPIRV-Registry/extensions/KHR/SPV_KHR_cooperative_matrix.html) and [VK_KHR_cooperative_matrix](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VK_KHR_cooperative_matrix).

The Vulkan extension has two feature bits:



*   cooperativeMatrix: basic feature support
*   cooperativeMatrixRobustBufferAccess: robust buffer support for cooperative matrix load and store (usual caveats).

There is a supported stages property in <code>[VkPhysicalDeviceCooperativeMatrixPropertiesKHR](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VkPhysicalDeviceCooperativeMatrixPropertiesKHR)</code>, but I’ve only ever seen compute supported.

OpTypeCooperativeMatrixKHR represents the abstract type. It parameterized as follows:



*   Component type: Matrix element type (must be a numerical scalar) (Float16/32/64, S/UInt8/16/32/64 currently)
*   Scope: the scope of operations on the type (Device, QueueFamily, Workgroup, or Subgroup)
*   Rows: Number of rows
*   Columns: Number of columns
*   Use: an enum specifying which kind of matrix this is used as:
    *   MatrixAKHR: LHS of a multiply (M rows x K columns)
    *   MatrixBKHR: RHS of a multiply (K rows x N columns)
    *   MatrixAccumulatorKHR: result of multiply-accumulate (M rows x N columns)

SPIR-V can represent a wide variety of cooperative matrices, but actual devices only support a subset of parameterizations. These are queried from the API by calling [vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR). It returns a linked list of [valid enumerations](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VkCooperativeMatrixPropertiesKHR). Preferred variants are listed earlier in the chain. At least one entry in the list must have power of two values for all of MSize, KSize, and NSize. The enumerations indicate whether they support saturating accumulation. Because the type is abstract it can only be stored in the Function and Private storage classes. Special load and store instructions are used to translate to/from backing memory.

The fundamental operation is OpCooperativeMatrixMulAddKHR. It performs a multiply-accumulate operation. It requires consistent values of scope, M, N, and K for all types. The component types may differ, the operations occur using the result type. Signedness of results and operands is indicated using the Cooperative Matrix Operands (as is saturating accumulation). The result is undefined if saturating accumulation is used, but intermediate results overflow (same as integer dot product).

OpCooperativeMatrixLoadKHR and OpCooperativeMatrixStoreKHR share the following operands:



*   Pointer: a logical pointer into an array (stride is ignored) (limited to Workgroup, StorageBuffer, and PhysicalStorageBuffer storage classes in Vulkan)
*   Memory Layout: an enum for row- or column-majorness
*   Stride: an optional operand indicating the stride between elements (aligned to min(16, align(col/row))).  The stride counts the number of elements in the pointee type; it is not a byte count.  For example, if the pointee type of the Pointer argument is 2xf16 then a stride of 8 translates to a byte stride of 8x (2x2) bytes.

These operations convert to/from the abstract matrix type and memory. The operands **must** be uniform values for the scope.

Additional operations:



*   OpCooperativeMatrixLengthKHR: Number of accessible components from an invocation for a given cooperative matrix type.
*   Conversions: All standard conversions are allowed (FToU, UToF, FToS, SToF, U, S, F, bitcast).
*   Arithmetic: Negate, add, sub, mul, div using the opcode for the appropriate component type. Additionally, OpMatrixTimesScalar is also supported.
*   Constants: OpCompositeConstruct and OpConstantComposite can provide a single fill value for a cooperative matrix.

All operations involving a cooperative matrix **must** only be executed in uniform control flow for scope of the operation.

Cooperative matrices **require** the Vulkan memory model in SPIR-V.

OpTypeCooperativeMatrixKHR can only be instantiated by a variable in the Function or Private storage classes.


### HLSL/Direct3D

Microsoft calls them WaveMatrices, and added them to [SM6.8](https://microsoft.github.io/DirectX-Specs/d3d/HLSL_SM_6_8_WaveMatrix.html) as experimental.

Google provided feedback via https://github.com/microsoft/hlsl-specs/issues/72

This experimental feature was later withdrawn.

Jul 14, 2025 [Chris Bieneman](mailto:cbieneman@microsoft.com) posted a revised proposal for HLSL linalg. See https://github.com/microsoft/hlsl-specs/pull/556


### MSL/Metal

Apple calls them simdgroup matrices and support has existed since MSL 2.3. They are limited to relatively few GPUs though (Apple7+ in the [feature set](https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf)). That is iPhone 12+, some newer iPads, and the M1 and later macs.

MSL support a small number of matrix variants:



*   simdgroup_float8x8
*   simdgroup_half8x8
*   simdgroup_bfloat8x8 (MSL 3.1)

The simdgroup_float8x8 and simdgroup_half8x8 types are equivalent to a SPIR-V 8x8 cooperative matrix with Subgroup scope and corresponding component type. Unlike SPIR-V and HLSL, MSL does not specialize the matrices based on the use.

MSL also has more limited supported operations on simdgroup matrices. For load/store/creation:



*   simdgroup_matrix<_T_, _M_, _N_>: Create diagonal matrix with single value.
*   make_filled_simdgroup_matrix<_T_, _M_, _N_>: Create single value filled matrix.
*   simdgroup_load/store: specify stride, layout (via transpose), and an offset (via origin)

Similar to SPIR-V the device or threadgroup pointer points to a matrix component type.

MSL supports multiply and multiply-accumulate (non-saturating) arithmetic operations.

All functions **must** be called from simdgroup uniform control flow. **Presumably** load and store parameter values **must** be simdgroup uniform though I can’t see this called out in the spec.

MSL allows interfaces to be declared as simdgroup_matrix, but no operators exist for them to make them usable. Effectively, this limits them to function storage.


### SYCL

SYCL is a single-source CPU/GPU compute language, and may reflect support among GPU hardware, at least as exposed through OpenCL.

SYCL has a corresponding proposal, using the terminology “joint matrix”. See https://github.com/intel/llvm/blob/sycl/sycl/doc/extensions/experimental/sycl_ext_oneapi_matrix/sycl_ext_oneapi_matrix.asciidoc


## Proposal


### WGSL


#### Enable Extension

Add a new enable extension, `chromium_experimental_subgroup_matrix`. Enables the declaration of subgroup_matrix types, related built-in functions, and limited use of some scalar types (u8 and i8). Implicitly depends on subgroups. WGSL does not require any subgroups enable, but the API does.


#### Types

Add new types:

*   subgroup_matrix_left<_T_, _K_, _M_>: M rows x K columns matrix of T components.
*   subgroup_matrix_right<_T_, _N_, _K_>: K rows x N columns matrix of T components
*   subgroup_matrix_result<_T_, _N_, _M_>: M rows x N columns matrix of T components
*   M, N, and K are override-expressions of positive integer values
    *   For _initial prototyping and feedback_, we will restrict them to const-expression.  We should finalize const vs. override before full standardization.
    *   Similar to arrays, two subgroup matrices are the same if:
        *   They are the same base type
        *   They have the same component type
        *   The have matching row counts such that:
            *   Both are equal valued const-expressions, or
            *   Both resolve to the same override-declaration
        *   The have matching column counts such that:
            *   Both are equal valued const-expressions, or
            *   Both resolve to the same override-declaration
        *   Note: like arrays, types cannot match if the row or column counts use an override-expression that is not equivalent to override identifier.
*   T is the component type and must be one of the entries in the table
    *   The scalar shader type is the associated type usable in the shader code for scalar operations and data representation in memory
    *   Can be expanded in the future to support more types (e.g. bfloat16) via new enables.
    *   The u8 and i8 cases are predeclared types that are not otherwise usable in WGSL. For layout calculations, they are of size 1 byte and have an alignment requirement of 1 byte.

| Type | Extra Enable | Element Stride (bytes) | Shader Scalar Type | Min Value | Max Value |
| ---- | ------------ | ---------------------- | ------------------ | --------- | --------- |
| f32  |              | 4                      | f32                |           |           |
| f16  | f16          | 2                      | f16                |           |           |
| u32  |              | 4                      | u32                |           |           |
| i32  |              | 4                      | i32                |           |           |
| u8   |              | 1                      | u32                | 0         | 255       |
| i8   |              | 1                      | i32                | -128      | 127       |

These types are not considered “composite” in the WGSL taxonomy, because they are not decomposible.  You can’t reference a sub-vector or a single component. The numeric dimensions must be override-expressions. These types cannot be part of any interface (i.e. they can only be instantiated in Function and Private address spaces). They are plain types (similar to atomics) so that they can be included in composite types.  An important use case is to make arrays of these matrices.

It is a pipeline-creation error if any matrix type is not included in a supported `GPUSubgroupMatrixConfig`, `config`,  on the device:

*   For `subgroup_matrix_left`:
    *   `M` equals `config.M`
    *   `K` equals `config.K`
    *   `T` matches `config.componentType`
*   For `subgroup_matrix_right`:
    *   `K` equals `config.K`
    *   `N` equals `config.N`
    *   `T` matches `config.componentType`
*   For `subgroup_matrix_result`:
    *   `M` equals `config.M`
    *   `N` equals `config.N`
    *   `T` matches `config.resultType`

Why use a “left” and “right” matrix type, instead of a single matrix type like Metal has?



*   D3D and Vulkan both need them.
*   There is no free transpose operation. If you had a free transpose, then you can take advantage of:   A\* B = transpose(transpose(B)\*transpose(A)).  I haven’t seen a transpose operation in APIs or hardware specs.


#### Variables

A variable containing a subgroup matrix can only be instantiated in Function or Private address spaces. (This limitation comes from SPIR-V and Metal). These variables are meant to be used as very temporary scratch space.


#### Loading and storing

Builtin functions are used to load and store subgroup matrix values from variables in workgroup and storage address spaces.  The builtins do two things:

*   Map matrix row and column indices to external memory locations.  This mapping has two parameters: majorness (row-major, or column-major), and an integer Stride.  Let Base be the byte address of the start of the external matrix, i.e. where the [0,0]’th element of the matrix is stored.  Then
    *   For row-major:
        *   Matrix entry [r,c] maps to the sizeof(T) bytes located at Base + Stride\*r\*sizeof(T) + sizeof(T)\*c
        *   Stride >= number of matrix columns.
    *   For column-major:
        *   Matrix entry [r,c] maps to the sizeof(T) bytes located at Base + Stride\*c\*sizeof(T) + sizeof(T)\*r
        *   Stride >= number of matrix rows.
*   Reinterpret data values between the shader scalar type and the external component type T, when those types differ.

For a subgroup_matrix_left/right/result&lt;T, Cols, Rows>, loads and stores are out-of-bounds if the length of the array of the pointer argument is less than Offset + Stride \* Rows\* Cols.


#### Attributes

Add an additional error condition to `workgroup_size`. It is a pipeline-creation error if the x dimension is not a multiple of `GPUAdapterInfo.subgroupMaxSize` and the shader statically accesses any subgroup matrix.

Note: this is where the requirement on the subgroups feature stems from in practice.


#### Expressions

Supported expressions:

*   Parenthesization
*   Identifier expressions
*   Indirection
*   Address-of
*   Function call expression
*   Type expressions

Explicitly not supported:

*   Decomposition expressions

Possible future expansion:

*   Arithmetic expressions


#### Built-in Values

New built-in value: `subgroup_id`.
Input in compute shaders of type `u32`.
It is the current invocation's subgroup's ID within the workgroup.

_Note: This was not included in the subgroups feature because there was insufficient documentation detailing the mapping in HLSL between workgroups and subgroups. This experiment is not implemented on D3D so the built-in value is available on supported platforms. If guaranteed mapping from Microsoft is provided for D3D, subgroup_id could be added as a language feature on top of the subgroups feature._


#### Built-in Functions

Calls to these functions:

*   Must only be used in a compute shader stage.
*   Trigger a `chromium_experimental.subgroup_matrix_uniformity` diagnostic if uniformity analysis cannot prove the call is in subgroup uniform control flow.


##### Value constructors

**Overloads**:
```rust
@must_use fn T(value : S) -> T
@must_use fn T() -> T
```

**Preconditions**:<br>
T is a subgroup matrix type whose shader scalar type is S.

**Description**:<br>
Create a subgroup matrix filled with value.

When no value is provided, use S().

Triggers a `chromium_experimental.subgroup_matrix_uniformity` diagnostic if
uniformity analysis cannot prove value is a subgroup uniform value.

##### Load/store functions

See Loading and Storing above.

**Overload**:
```rust
@must_use fn
subgroupMatrixLoad<T>(p : ptr<AS, SA, AM>,
                      offset : u32,
                      col_major : bool,
                      stride : u32) -> T
```

**Preconditions**:<br>
T is a subgroup matrix type with shader scalar type S.<br>
SA is an array with type S.<br>
AS is storage or workgroup.<br>
AM is read or read_write.

**Description**:<br>
Load a subgroup matrix from p, offset elements from the start of the array.

col_major must be an override-expression.

Triggers a `chromium_experimental.subgroup_matrix_uniformity` diagnostic if
uniformity analysis cannot prove p, offset, or stride are subgroup uniform
values.

offset and stride count elements of the component type of T.
Behavior is undefined if stride is less than:

* The number of rows of T if col_major is true
* The number of columns of T is col_major is false


**Overload**:<br>
```rust
fn subgroupMatrixStore(p : ptr<AS, SA, AM>,
                       offset : u32,
                       value : T,
                       col_major : bool,
                       stride : u32)
```

**Preconditions**:<br>
T is a subgroup matrix type whose scalar shader type is S.<br>
SA is an array with element type S.<br>
AS is storage or workgroup.<br>
AM is write or read_write.

**Description**:<br>
Store the subgroup matrix value into p, offset elements from the start of the array.

col_major must be an override-expression.

Triggers a `chromium_experimental.subgroup_matrix_uniformity` diagnostic if
uniformity analysis cannot prove p, offset, value, or stride are subgroup
uniform values.

offset and stride count elements of the component type of T.
Behavior is undefined if stride is less than:

* The number of rows of T if col_major is true
* The number of columns of T is col_major is false

##### Matrix arithmetic functions

The operands of a subgroup matrix arithmetic function comprise a **supported subgroup matrix configuration** if the device has a `GPUSubgroupMatrixConfig`, `config`, such that all operand types match as below:

*   For `L`:
    *   `M` equals `config.M`
    *   `K` equals `config.K`
    *   `T` matches `config.componentType`
*   For `R`:
    *   `K` equals `config.K`
    *   `N` equals `config.N`
    *   `T` matches `config.componentType`
*   For `RT`:
    *   `M` equals `config.M`
    *   `N` equals `config.N`
    *   `TR` matches `config.resultType`

**Overload**:
```rust
@must_use fn
subgroupMatrixMultiply<RT>(left : L, right : R) -> RT
```

**Preconditions**:<br>
L is a subgroup_matrix_left<T, M, K>.<br>
R is a subgroup_matrix_right<T, K, N>.<br>
RT is a subgroup_matrix_result<TR, M, N>.

**Description**:<br>
Matrix multiply.

It is a pipeline-creation error if L, R, and RT do not comprise a supported
subgroup matrix configuration.

Triggers a `chromium_experimental.subgroup_matrix_uniformity` diagnostic if
uniformity analysis cannot prove left or right are subgroup uniform values.

**Overload**:
```rust
@must_use fn
subgroupMatrixMultiplyAccumulate<RT>(left : L,
                                     right : R,
                                     acc : RT) -> RT
```

**Preconditions**:<br>
L is a subgroup_matrix_left<T, M, K>.<br>
R is a subgroup_matrix_right<T, K, N>.<br>
RT is a subgroup_matrix_result<TR, M, N>.

**Description**:<br>
Matrix multiply add (left * right + acc).

It is a pipeline-creation error if L, R, and RT do not comprise a supported
subgroup matrix configuration.

Triggers a `chromium_experimental.subgroup_matrix_uniformity` diagnostic if
uniformity analysis cannot prove left or right are subgroup uniform values.

##### Scalar arithmetic functions

These functions are useful for operations such as applying biases to a model.

**Overload**:
```rust
@must_use fn
subgroupMatrixScalarAdd(matrix : M, value : S) -> M
```

**Preconditions**:<br>
M is a subgroup matrix type with scalar shader type S.

**Description**:<br>
Scalar addition.

value is clamped to a valid range for the component type of M and then added to
each element of matrix.

Triggers a `chromium_experimental.subgroup_matrix_uniformity` diagnostic if
uniformity analysis cannot prove matrix or value are subgroup uniform values.

**Overload**:
```rust
@must_use fn
subgroupMatrixScalarSubtract(matrix : M, value : S) -> M
```

**Preconditions**:<br>
M is a subgroup matrix type with scalar shader type S.

**Description**:<br>
Scalar subtraction.

value is clamped to a valid range for the component type of M and then added to
each element of matrix.

Triggers a `chromium_experimental.subgroup_matrix_uniformity` diagnostic if
uniformity analysis cannot prove matrix or value are subgroup uniform values.

**Overload**:
```rust
@must_use fn
subgroupMatrixScalarMultiply(matrix : M, value : S) -> M
```

**Preconditions**:<br>
M is a subgroup matrix type with scalar shader type S.

**Description**:<br>
Scalar multiplication.

value is clamped to a valid range for the component type of M and then added to
each element of matrix.

Triggers a `chromium_experimental.subgroup_matrix_uniformity` diagnostic if
uniformity analysis cannot prove matrix or value are subgroup uniform values.


**REMOVED** (in an attempt to be safely forward compatible if Metal adds integral components, but no other operators)

**Overload**:
```rust
@must_use fn
subgroupMatrixScalarDivide(matrix : M, value : S) -> M
```

**Preconditions**:<br>
~~M is a subgroup matrix type with scalar shader type S.~~

**Description**:<br>
~~Scalar division.~~

~~value is clamped to a valid range for the component type of M and then added to
each element of matrix.~~

~~Triggers a `chromium_experimental.subgroup_matrix_uniformity` diagnostic if
uniformity analysis cannot prove matrix or value are subgroup uniform values.~~

#### Uniformity

Subgroup matrices are an abstraction of data spread among the invocations in a subgroup. As such the built-in functions must only be called from subgroup uniform control flow. Additionally, most of the parameter values must also be subgroup uniform values.

WGSL does not currently represent subgroup uniformity and empirical testing shows that implementations do not provide reliable reconvergence. Vulkan backends that implement VK_KHR_shader_subgroup_uniform_control_flow or, even better, VK_shader_maximal_reconvergence would be able to make enabling the analysis at other scopes reasonable, but that is not portable.

For this experimental extension we should add a diagnostic, `chromium_experimental.subgroup_matrix_uniformity`, that defaults to `error`. This diagnostic would be triggered based on workgroup uniformity violations (if the control is not `off`) for all subgroup matrix built-in functions. This allows applications some control. We can gather data about the usefulness of the static analysis for subgroup matrices. It is likely that most use cases would satisfy workgroup uniformity.


#### Floating-point Accuracy

`subgroupMatrixLoad` and `subgroupMatrixStore` should be bit preserving. ULPs are undefined for other operations.


### API

New GPUFeatureName `chromium-experimental-subgroup-matrix`

*   Requires `subgroups` (for maxSubgroupSize)
*   Vulkan:
    *   <code>[vkPhysicalDeviceCooperativeMatrixFeaturesKHR](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VkPhysicalDeviceCooperativeMatrixFeaturesKHR)::cooperativeMatrix</code> is <code>VK_TRUE</code>
    *   <code>[vkPhysicalDeviceCooperativeMatrixPropertiesKHR](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VkPhysicalDeviceCooperativeMatrixPropertiesKHR)::cooperativeMatrixSupportedStages</code> includes <code>VK_SHADER_STAGE_COMPUTE_BIT</code>
    *   The [supported configurations](?tab=t.0#bookmark=id.x7vevmm1vqtf) list is non-empty
    *   <code>[vkPhysicalDeviceVulkanMemoryModelFeatures](https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#VkPhysicalDeviceVulkanMemoryModelFeatures)::vulkanMemoryModel</code> is <code>VK_TRUE</code>
    *   <code>[subgroupSizeControl](https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#features-subgroupSizeControl)</code>feature must be enabled
    *   [computeFullSubgroups](https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#features-computeFullSubgroups) feature must be enabled
    *   Vulkan pipelines will need to be compiled with <code>VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT</code> and <code>VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT</code>, or the SPIR-V module must be version 1.6 or later
*   Metal:
    *   Family is Apple 7+
*   D3D: no support

New immutable array, <code>subgroupMatrixConfigs</code>, added to <code>GPUAdapterInfo</code>.


```json
partial interface GPUAdapterInfo {
  [SameObject] readonly attribute FrozenArray<GPUSubgroupMatrixConfig> subgroupMatrixConfigs;
};

enum GPUSubgroupMatrixComponentType {
  "f32",
  "f16",
  "u32",
  "i32",
  "u8",
  "i8",
};

interface GPUSubgroupMatrixConfig {
  readonly attribute GPUSubgroupMatrixComponentType componentType;
  readonly attribute GPUSubgroupMatrixComponentType resultComponentType;
  readonly attribute unsigned long M;
  readonly attribute unsigned long N;
  readonly attribute unsigned long K;
};
```



#### Validation

No specific API validation is necessary; however, it is likely easier to implement the pipeline-creation error checks in Dawn by adding the subgroup matrix configurations to the shader reflection information.

WGSL pipeline-creation checks (repeated for ease of reference):

*   All subgroup matrix types are part of a `GPUSubgroupMatrixConfig`
*   All subgroup matrix operands in `subgroupMatrixMultiply` and `subgroupMatrixMultipleAccumulate` are part of a single `GPUSubgroupMatrixConfig`
*   The x-dimension of `workgroup_size` is a multiple of `GPUSupportedLimits::maxSubgroupSize`


### Mapping


#### Types

| Type | SPIR-V<sup>1,2</sup> | MSL<sup>3</sup> |
| ---- | -------------------- | --------------- |
| subgroup_matrix_left<T, K, M> | OpTypeCooperativeMatrixKHR<br>MatrixAKHR use<br>M rows<br>K cols<br>T component type | simdgroup_matrix<T, 8, 8> |
| subgroup_matrix_right<T, N, K> | OpTypeCooperativeMatrixKHR<br>MatrixBKHR use<br>K rows<br>N cols<br>T component type | simdgroup_matrix<T, 8, 8> |
| subgroup_matrix_result<T, N, M> | OpTypeCooperativeMatrixKHR<br>MatrixAccumulatorKHR use<br>M rows<br>N cols<br>T component type | simdgroup_matrix<T, 8, 8> |

1. All OpTypeCooperativeMatrixKHR use subgroup scope.
2. Component type enum maps directly to SPIR-V type (e.g. i8 to OpTypeInt 8 1).
3. MSL types use the usual mappings (e.g. f32 to float).


#### Builtin-in Values

| Value | SPIR-V | MSL |
| ----- | ------ | ---- |
| subgroup_id | SubgroupId | simdgroup_index_in_threadgroup |

#### Functions

| Function | SPIR-V | MSL |
| -------- | ------ | ---- |
| Value constructors | OpCompositeConstruct with appropriate value<br>const-/override-expressions could use constant instructions | make_filled_simdgroup_matrix |
| subgroupMatrixLoad | OpCooperativeMatrixLoadKHR<br>Pointer operand is a direct translation of the WGSL pointer | simdgroup_matrix_load<br>The WGSL pointer needs translated into the origin operand in MSL |
| subgroupMatrixStore | OpCooperativeMatrixStoreKHR<br>Pointer operand is a direct translation of the WGSL pointer | simdgroup_matrix_store<br>The WGSL pointer needs translated into the origin operand in MSL |
| subgroupMatrixMultiply | OpCooperativeMatrixMulAddKHR<br>C is a zero value matrix matching the result type | simdgroup_multiply |
| subgroupMatrixMultiplyAccumulate | OpCooperativeMatrixMulAddKHR | simdgroup_multiply_accumulate |
| subgroupMatrixScalarAdd | OpI/FAdd with composite constructed value | simdgroup_multiply_accumulate with diagonal matrix for b and filled matrix scalar value for c |
| subgroupMatrixScalarSubtract | OpI/FSub with composite constructed value | simdgroup_multiply_accumulate with diagonal matrix for b and filled inverted matrix scalar value for c |
| subgroupMatrixScalarMultiply | OpI/FMul with composite constructed value | simdgroup_multiply with diagonal matrix scalar value for b<sup>1</sup> |
| ~~subgroupMatrixScalarDivide~~ | ~~OpI/S/FDiv with composite constructed scalar or OpMatrixTimesScalar (for float components with reciprocal scalar value)~~ | ~~simdgroup_multiply with diagonal matrix reciprocal scalar value for b~~<sup>1</sup> |

1. This works because MSL only supports floating-point component types.


#### Properties


##### Vulkan

Filter the list returned from [vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR) such that:



*   A type and B type match
*   C type and result type match
*   A, B, C, and result types are one of (-> API enum):
    *   VK_COMPONENT_TYPE_FLOAT32_KHR -> f32
    *   VK_COMPONENT_TYPE_FLOAT16_KHR -> f16
    *   VK_COMPONENT_TYPE_SINT32_KHR -> i32
    *   VK_COMPONENT_TYPE_UINT32_KHR -> u32
    *   VK_COMPONENT_TYPE_SINT8_KHR -> i8
    *   VK_COMPONENT_TYPE_UINT8_KHR -> u8
*   saturatingAccumulation is false
*   Scope is `VK_SCOPE_SUBGROUP_KHR`

`VK_COMPONENT_TYPE_FLOAT16` will need to be filtered out of the device properties if the `shader-f16` feature is not requested.


##### Metal

Hardcode the following configurations if the feature is supported:

| componentType | resultComponentType | M | N | K |
| ------------- | ------------------- | ---- | ---- | ---- |
| f32 | f32 | 8 | 8 | 8 |
| f16 | f16 | 8 | 8 | 8 |

1. Filter out f16 from the device properties if `shader-f16` is not requested on the device.


## Future Expansion

It is likely that more component types can be supported in the future. Metal can already support bfloat16 for example.

We could consider exposing saturating accumulation.

---

### Dawn Features Timestamp Query Inside Passes (`docs/dawn/features/timestamp_query_inside_passes.md`)

# Timestamp Query Inside Passes (experimental!)

Adds support for the `writeTimestamp` call inside render and compute passes.
This method might not always make a lot of sense on tiler GPUs (so basically any modern GPU) where the ordering of operations inside render passes is not strictly like the order of commands recorded (each tile being executed independently),

Adds the following methods:

```
voi wgpu::ComputePassEncoder::writeTimestamp(wgpu::QuerySet querySet, uint32_t queryIndex);
voi wgpu::RenderPassEncoder::writeTimestamp(wgpu::QuerySet querySet, uint32_t queryIndex);
```

The validation is the following:

 - `querySet` must be a valid query set.
 - `querySet` must have been created with `wgpu::QueryType::Timestamp`.
 - `queryIndex` must be less than `querySet`'s size.
 - During the `wgpu::Queue::Submit` for these commands, `querySet` must not be destroyed.

The initial tracking bug was https://crbug.com/dawn/434.

---

### Dawn Features Transient Attachments (`docs/dawn/features/transient_attachments.md`)

# Transient Attachments

Transient attachments allow render pass operations to stay in tile memory,
avoiding VRAM traffic and potentially avoiding VRAM allocation for the textures.

Presence of the `transient-attachments` feature on an Adapter is a hint
indicating that the `TransientAttachment` usage will be used by the backend. If
it's not present, the usage is allowed, but it is a no-op.
Enabling this feature on a Device doesn't do anything.

Example Usage:

```cpp
wgpu::TextureDescriptor desc;
desc.format = wgpu::TextureFormat::RGBA8Unorm;
desc.size = {1, 1, 1};
desc.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TransientAttachment;

auto transientTexture = device.CreateTexture(&desc);

// Can now create views from the texture to serve as transient attachments, e.g.
// as color attachments in a render pipeline.
```

Textures with the `TransientAttachment` usage:

- Must have exactly the usages `RenderAttachment | TransientAttachment`.
- May not remove any usages when creating views.
- May not be used with `load`/`store` in render passes.
- May not be used as resolve attachments.

---

### Dawn Features Unorm16 Filterable (`docs/dawn/features/unorm16_filterable.md`)

# Unorm16 filterable

This optional feature makes `R16Unorm`, `RG16Unorm` and `RGBA16Unorm` filterable.
It is needed to allow Graphite to sample (with filtering) individual planes of HDR YUV formats that have planes with formats `R16Unorm` and `RG16Unorm`.

---

### Dawn Features Unorm16 Formats For External Texture (`docs/dawn/features/unorm16_formats_for_external_texture.md`)

# Unorm 16 formats for external textures

Chromium needs to create ExternalTextures on YUV HDR videos for 0-copy interop, using formats such as `wgpu::TextureFormat::R10X6BG10C6Biplanar420Unorm` and friends.
These formats have planes with `R/RG16Unorm` formats that need to work to import `ExternalTextures` from video, even if other texture format extensions haven't been enabled.

This feature loosens validation in two specific places to allow using YUV HDR videos without exposing `R/RG16Unorm` for anything else:

 - Views of planes of YUV HDR textures can be created using the `R/RG16Unorm` formats even if the formats are not otherwise allowed on the device.
 - Validation of `CreateExternalTexture` considers `R/RG16Unorm` to be filterable float, even if they are unfilterable-float (even when enabled with `wgpu::FeatureName::TextureFormatsTier1`).

---

### Dawn Features Y Cb Cr Vulkan Samplers (`docs/dawn/features/y_cb_cr_vulkan_samplers.md`)

# YCbCr Vulkan Samplers

Video playback in Vulkan on Android works via so called YCbCr sampling (note:
this is not the only context in which YCbCr sampling can be used on Android, but
for video playback it *must* be used). In a nutshell:

* The AHardwareBuffer containing the video frame has YCbCr information that can
  be queried. This YCbCr information has an opaque "external format" integer
  that must be supplied when creating a sampler to sample this
  AHardwareBuffer. It's possible for this format or other parts of the YCbCr
  info to change from frame to frame (i.e., between AHBs), and hence the
  information must be queried every frame.
* The client must query that information and use it to create an immutable
  sampler
* The client must then package that sampler together with the information of
  the sampled image into a combined image sampler that is inserted into the
  VkDescriptorSetLayout
* The client must insert the sampled image (i.e., an image wrapping the AHB)
  into the VkDescriptorSet
* The client's SPIR-V must either directly create a SampledImage from the
  combined image sampler or must create sampler and texture variables that both
  point to the combined image sampler entry and then create a SampledImage from
  the sampler and texture variables

# Supporting YCbCr Sampling in Dawn: Semantics

A complicating factor of the challenge here is that combined image samplers are
intentionally not a part of the WebGPU specification. Dawn's support for YCbCr
sampling on Android rests on the following features:

* Static samplers, which allow a client to pass a sampler object
  directly in the BindGroupLayout rather than the BindGroup.
* The ability to query YCbCr info from SharedTextureMemory
  and create YCbCr samplers from this info (details below)
* The ability to specify in the BindGroupLayout that a specific static sampler
  entry and texture entry are paired to one another - that sampler will be used
  to sample only that texture, and that texture will be sampled only by that
  sampler. This is done via adding an optional `sampledTextureBinding` to the
  static sampler entry in the BindGroupLayout.

The `y-cb-cr-vulkan-samplers` feature allows specification of VkSamplerYcbcrConversionCreateInfo as
part of the static vulkan sampler descriptor. For this purpose, clients
can supply `YCbCrVkDescriptor` instances when creating samplers and
texture views. Clients can also obtain the YCbCr info for a
SharedTextureMemory instance that was created from an AHardwareBuffer by
querying its properties. Most properties will be created directly from the
corresponding buffer format properties on the underlying AHardwareBuffer. The
two exceptions are as follows:

* `vkChromaFilter`: Will be set to FilterMode::Linear iff
  `VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT` is
  present in the AHB format features and FilterMode::Nearest otherwise
* `forceExplicitReconstruction`: will be set to true iff
  `VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT`
  is present in the AHB format features

# Implementing YCbCr Sampling via Dawn from the Client's Perspective

Putting the above together, a typical client flow would look like this:

* Obtain the AHB for a video frame
* Create a SharedTextureMemory instance from that AHB
* Query that SharedTextureMemory instance for its YCbCr info
* Create a YCbCr sampler using that YCbCr info
* Create a BindGroupLayout that includes that YCbCr sampler with a
  `sampledTextureBinding` annotation that points to the entry of the texture
  that will be sampled (i.e., where the AHB will be passed). That texture entry
  must have format type EXTERNAL. Note: The client might want to cache created
  BGLs for performance; if so, this caching must be keyed off the YCbCr info.
* Create WGSL that references the sampler and texture via their respective
  indices in the BindGroupLayout, just as if they were an "ordinary" sampler and
  texture.
* Create a BindGroup that passes a texture obtained from the SharedTextureMemory
  wrapping the AHB in the texture entry that had been reserved for that texture
  in the BindGroupLayout.

# Implementation Details

* Static samplers are translated to immutable samplers in the Vulkan backend.
* If the static sampler has a `sampledTextureBinding` annotation, it is
  translated to a combined image sampler rather than a regular sampler entry.
  The texture information for that combined image sampler is that which the
  client passed in the corresponding texture entry.
* In BindGroup->VkDescriptorSet translation, the texture is passed at the index
  for the combined image sampler in the VkDescriptorSetLayout.
* In WGSL->SPIR-V translation, any variables created from the texture entry in
  the BindGroup are translated to point to the combined image sampler in the
  VkDescriptorSet (note that variables created from the sampler entry will
  naturally point to the combined image sampler, since the combined image
  sampler is created at the index for the sampler entry).

# Validation

We validate the following:

* YCbCr samplers must be static samplers
* YCbCr samplers must have a `sampledTextureBinding` annotation
* The `sampledTextureBinding` annotation must point to a valid entry
* No two sampler entries can point at the same texture entry
* Textures sampled by YCbCr samplers must have format type External

---

### Tint Extensions Chromium Experimental Barycentric Coord (`docs/tint/extensions/chromium_experimental_barycentric_coord.md`)

# Chromium Experimental Barycentric Coordinates

The `chromium_experimental_barycentric_coord` is an experimental extension that allows the use of the
`barycentric_coord` builtin in fragment shaders in WGSL.

# Status

This extension is experimental and the syntax is being discussed. No official WebGPU specification
has been written yet.

# Availability

The usage is restricted to `fragment` shaders.

| Backend | Type | Requirements |
|---------|------|--------------|
| SPIR-V | `vec3f` | `VK_KHR_fragment_shader_barycentric`. Available as the `BaryCoordKHR` builtin |
| HLSL | `vec3f` | Requires D3D12, HLSL 6.1. Available as the `SV_Barycentrics` semantic |
| GLSL | `vec3f` | `GLSL_EXT_fragment_shader_barycentric` (Requires GLSL 4.50 and later, ESSL 3.2 and later). Available as the `gl_BaryCoordEXT` builtin |
| Metal | `f32`, `vec2f`, or `vec3f` | Requires Metal 2.2 on MacOS or Metal 2.3 on iOS. Available as `[[barycentric_coord]]`, can test with `supportsShaderBarycentricCoordinates` |

Vulkan support unfortunately [looks pretty low](https://vulkan.gpuinfo.org/displayextensiondetail.php?extension=VK_KHR_fragment_shader_barycentric) with 10.76%	of devices on GPUInfo.org reporting support. The relevant GL extension doesn't even appear on the site.

Due to the limited availability, this will need a `enable` statement to be used. For this
experimental extension it would be `enable chromium_experimental_barycentric_coord`.

Additionally, anywhere that barycentrics are supported they support perspective corrected and non-perspective correct
values are available. Most backends support multiple interpolation modes, but only centroid is available everywhere.

| Backend | Supported Interpolation Modes |
|---------|-------------------------------|
| SPIR-V | `BaryCoordKHR`, `BaryCoordNoPerspKHR`, both can be decorated with the `Centroid` or `Sample` interpolation qualifiers |
| HLSL | `float3 b : SV_Barycentrics` (default, perspective correct), `noperspective float3 b : SV_Barycentrics`. Interpolation modes like `linear`, `centroid`, and `sample` are supported |
| GLSL | `gl_BaryCoordEXT`, `gl_BaryCoordNoPerspEXT`, both can be decorated with the centroid or sample interpolation qualifiers |
| Metal | `[[barycentric_coord, center_perspective]]` (default, can be omitted), ` [[barycentric_coord, center_no_perspective]]` |

# Specification

This extension adds a new `builtin_value_name` entry for `barycentric_coord`.

An entry is added to the _Built-in input and output values_ table:

| Category | Value |
|----------|-------|
| Name | `barycentric_coord` |
| Stage | `fragment` |
| Direction | `input` |
| Type | `vec3` |
| Extension | `chromium_experimental_barycentric_coord` |

* The value contains the relative weights for each vertex in the primitive.
* Fragments in the corners of triangle primitives will have weights of (1,0,0), (0,1,0), and (0,0,1)
* Fragments at the endpoints of line primitives will have weights of (1,0,0), and (0,1,0). The z component will always be 0.
* Support for point primitives is unclear (Metal mentions that they will have yz components of (0, 0), HLSL and GLSL make no mention of points.)
* The value of the three components should add up to approximately 1, but strictly adding up to 1 is not guaranteed.

# Example usage

```wgsl
@fragment fn fs_main(@builtin(barycentric_coord) bary_coord: vec3f) -> @builtin(color) vec4f {
    return vec4f(bary_coord, 1.0);
}
```

# References

* [DirectXShaderCompiler SV_Barycentrics](https://github.com/microsoft/DirectXShaderCompiler/wiki/SV_Barycentrics)
  * [Shader Model 6.1](https://github.com/microsoft/DirectXShaderCompiler/wiki/Shader-Model-6.1)
* [Metal p.119](https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf)
* [MTLDevice supportsShaderBarycentricCoordinates](https://developer.apple.com/documentation/metal/mtldevice/supportsshaderbarycentriccoordinates)
* [Vulkan VK_KHR_fragment_shader_barycentric](https://docs.vulkan.org/refpages/latest/refpages/source/VK_KHR_fragment_shader_barycentric.html)
  * [SPIR-V VK_KHR_fragment_shader_barycentric](https://github.khronos.org/SPIRV-Registry/extensions/KHR/SPV_KHR_fragment_shader_barycentric.html)
* [GLSL_EXT_fragment_shader_barycentric](https://github.com/KhronosGroup/GLSL/blob/main/extensions/ext/GLSL_EXT_fragment_shader_barycentric.txt)

---

### Tint Extensions Chromium Experimental Framebuffer Fetch (`docs/tint/extensions/chromium_experimental_framebuffer_fetch.md`)

# Chromium Experimental Framebuffer Fetch

The `chromium_experimental_framebuffer_fetch` extension adds support for framebuffer inputs to fragment stages in WGSL.
Framebuffer input, or framebuffer fetch, lets a fragment shader read the value of framebuffer attachments for the current fragment.
It synchronizes the read with all previous writes to the same fragment (in API order).

## Status

Framebuffer Fetch support in Tint is experimental and might change as we gain implementation experience.
It will also be updated to follow an upstream specification if one is made at some point.
Specification work in the WebGPU group hasn't started.

## Pseudo-specification

This adds a new builtin attribute `@color` which takes a `u32` argument and is only allowed on `@fragment` entry point scalar / vector inputs.
* Two input variables cannot have the same `@color` attribute.
* The use of `@color(N)` with a multi-sampled color attachment will cause the fragment shader to run once per sample.

## Example usage

```
@fragment fn main(@color(0) vec4f previousColor) -> @location(0) vec4f {
    let color = computeThisFragmentColor();
    return myBlend(previousColor, color);
}
```

## To-dos

 - Are f16 types allowed for the `@color` inputs?

---

### Tint Extensions Chromium Experimental Resource Table (`docs/tint/extensions/chromium_experimental_resource_table.md`)

# Chromium Experimental Resource Table

The `chromium_experimental_resource_table` is an experimental extension that provides different
prototype options to support the WebGPU bindless prototypes.

# Status

This extension is experimental and the syntax is being discussed. No official WebGPU specification
has been written yet.

# Availability

| Platform | Notes |
| SPIR-V | Requires `SPV_EXT_descriptor_indexing` extension and `RuntimeDescriptorArray` capability |
| HLSL | Supported for DXC, not FXC |
| MSL | Supported (TODO determine if there are Metal version restrictions) |
| GLSL | Not supported |

Due to limited availability, this will need an `enable` statement to be used. For this experimental
extension it would be `enable chromium_experimental_resource_table`.

# Specification

## Resource table

The concept of a `Resource Table` is added to WGSL. This is not a type which can be written, or
an address space, but a data table available to use with the `getResource` and `hasResource`
methods. The table is made available by the system and is just accessible.

### `hasResource`
```
@must_use fn hasResource<T>(index: I) -> bool
```
* `I` is an `i32` or `u32`
* `T` is a format-less storage texture (e.g. `texture_storage_2d<f32>`), sampled texture, multisampled
  texture, or depth texture.

`hasResource` returns true if the item a `index` of the resource table is of type `T`.

### `getResource`
```
@must_use fn getResource<T>(index: I) -> T
```
* `I` is an `i32` or `u32`
* `T` is a format-less storage texture (e.g. `texture_storage_2d<f32>`), sampled texture, multisampled
  texture, or depth texture.

`getResource` returns the value in the resource table at `index` of type `T`.

If `index` is outside the bounds of the resource table then a default value of type `T` will be
returned. If the item at `index` is not of type `T` then a default value of type `T` is returned.
Essentially, a value is always returned, it may just be a synthensized default value.

# Example usage

## Resource Table
```
enable chromium_experimental_resource_table;

const kHouseTexture = 0u;

@fragment fn fs() {
    if (hasResource<texture_2d<f32, filterable>>(kHouseTexture)) {
        let tex = textureLoad(getResource<texture_2d<f32, filterable>>(kHouseTexture));
    }
}
```

---

### Tint Extensions Chromium Experimental Static Samplers (`docs/tint/extensions/chromium_experimental_static_samplers.md`)

# Chromium Experimental Static Samplers

The chromium_experimental_static_samplers extension is an experimental extension that allows the use of static samplers in WGSL.
Static samplers translate to immutable samplers in Vulkan, constexpr samplers in Metal Shading Language, and static samplers in Direct3D 12.

# Status

This extension is experimental and the syntax is being discussed. No official WebGPU specification has been written yet.

# Design considerations

## const or var

`@group(X) @binding(Y) const mySampler;`
or
`@group(X) @binding(Y) var mySampler;`


In HLSL and SPIR-V, static samplers need a binding slot, hence the `@group(X) @binding(Y)` syntax.
Vulkan and Direct3D 12 supply static samplers via the API.
Metal Shading Language declares static samplers with `constexpr`, which is ready to be used in the shader without Metal API interaction.

Normal samplers in WGSL are declared with `var` because they have a handle address space, which is read-only.
Using the `const` keyword for static samplers is sensible, as static samplers are immutable objects.
However, `const` does not have a handle address space which is required for HLSL and SPIR-V and would require spec changes to support it.

Using `var` for static samplers is recommended for consistency with the rest of the language and they are already immutable.

## Using a struct

```wgsl
@group(X) @binding(Y) const mySampler = sampler {
  addressModeU: mirror_repeat, // enumerant
  magFilter: linear,
  lodMinClamp: 1, // abstract float
  ...
};
```

The `sampler` is a built-in struct that can be used to define samplers.
Advantages are its readability and similarity to the WebGPU API's `GPUSamplerDescriptor` struct.

This approach would require a new WGSL feature for initializing struct values.

If the new WGSL feature is introduced, this approach is recommended for readability and intuitiveness.
However, for the initial implementation, it is not desirable to introduce a substantial change in WGSL.

## Using a function

```wgsl
const clamp_to_edge = 0;
const mirror_repeat = 1;
...
sampler(mirror_repeat, linear, 0.0, 16);
sampler_comparison(mirror_repeat, linear, 0.0, 16);
```

or

```wgsl
sampler<mirror_repeat, linear>(0.0, 16);
sampler_comparison<mirror_repeat, linear>(0.0, 16);
```

In both cases, `sampler` is a built-in function that returns a static sampler.
The first approach takes a list of arguments. These arguments are predefined constant values and not enums.
The second approach takes a list of enum template parameters and a list of arguments. The arguments are LOD min and max clamps, because they are not enums.

Both approaches are readable and intuitive but due to the large amount of parameters that can be set on a sampler, the approaches may become hard to read and write.
The approaches do not need any new WGSL features and are sufficient for a prototype implementation.

For the prototype implementation, the first function approach is good for its simplicity.

# Pseudo-specification

This extension adds `sampler(...)` and `sampler_comparison(...)` functions to return a sampler type.
The functions take integer values that are predefined constants. If a sampler is initialized with any of the two functions, it is considered a static sampler, otherwise it is a normal sampler.
The predefined constant variable names are similar to the WebGPU API enums.

Function restrictions include:
- The function can only be called to initialize a static sampler.
- Only built-in functions can return a sampler, user-defined functions cannot.
- The function can not be used in an expression, only in a declaration.

Order of arguments:
- addressModeU
- addressModeV
- addressModeW
- magFilter
- minFilter
- mipmapFilter
- compare (only for `sampler_comparison(...)`)
- lodMinClamp
- lodMaxClamp

New Constants:
- Address Mode:
  - WGSL: `clamp_to_edge=0`, `repeat=1`, `mirror_repeat=2`
  - WebGPU equivalent: `"clamp-to-edge"`, `"repeat"`, `"mirror-repeat"`
- Filter Mode:
  - WGSL: `nearest=0`, `linear=1`
  - WebGPU equivalent: `"nearest"`, `"linear"`
- Mipmap Filter Mode:
  - WGSL: `mipmap_nearest=0`, `mipmap_linear=1`
  - WebGPU equivalent: `"nearest"`, `"linear"`
- Comparison Function:
  - WGSL: `never=0`, `less=1`, `equal=2`, `less_equal=3`, `greater=4`, `not_equal=5`, `greater_equal=6`, `always=7`
  - WebGPU equivalent: `"never"`, `"less"`, `"equal"`, `"less-equal"`, `"greater"`, `"not-equal"`, `"greater-equal"`, `"always"`

# Example usage

```wgsl
@group(0) @binding(0) var mySampler = sampler(mirror_repeat, clamp_to_edge, mirror_repeat, linear, linear, mipmap_linear, 0.0, 16.0)

@group(0) @binding(1) var myComparisonSampler = sampler_comparison(mirror_repeat, clamp_to_edge, mirror_repeat, linear, linear, mipmap_linear, less, 0.0, 16.0)

```

---

### Tint Extensions Chromium Internal Input Attachments (`docs/tint/extensions/chromium_internal_input_attachments.md`)

# Chromium Internal Input Attachments

The `chromium_internal_input_attachments` extension adds support for input attachment global variables to WGSL.
This is similar to Vulkan's `subpassInput` variable.

## Status

Input attachments support in Tint is highly experimental and only meant to be used in internal transforms at this stage.
This extension is only relevant to SPIRV backend atm.
Specification work in the WebGPU group hasn't started.

## Pseudo-specification

This extension adds:
- A new `input_attachment<T>` type in the `handle` address space, where `T` is `u32`, `i32` or `f32`. It's only allowed on module-scoped variable declarations.
  - This will be mapped to `OpTypeImage` with Dim=`SubpassData` in SPIRV.
- A new `input_attachment_index(n)` attribute where `n` is a `u32` or `i32` with a positive value. Unlike `binding` which specifies the binding point in a BindGroup, this attribute specifies the index of the input attachment in the render pass descriptor. This is required for `input_attachment<T>` type.
  - This is equivalent to `InputAttachmentIndex` decoration in SPIRV.
- A new `inputAttachmentLoad` builtin function with signature:
  `inputAttachmentLoad(input_attachment<T>) -> vec4<T>`
  - This will emit `OpImageRead` function call in SPIRV.
- All of the above are only available in fragment state.

## Example usage

```
@group(0) @binding(0) @input_attachment_index(0)
    var input_tex : input_attachment<f32>;

@fragment fn main() -> @location(0) vec4f {
    return inputAttachmentLoad(input_tex);
}
```

---

### Tint Extensions Fragment Depth (`docs/tint/extensions/fragment_depth.md`)

# Fragment depth (less, greater)

Issue: https://github.com/gpuweb/gpuweb/issues/5342

Proposal: https://github.com/gpuweb/gpuweb/blob/main/proposals/fragment-depth.md

## Motivation

The ability for fragment shaders to explicitly write to the depth buffer via the `@builtin(frag_depth)` attribute is critical for many modern rendering techniques, including custom depth biasing for shadows, precise depth fog, and complex depth-based effects.

In the current WGSL specification, the mere act of writing to `@builtin(frag_depth)` often incurs a significant performance penalty because driver heuristics cannot guarantee that the fragment shader output will adhere to the depth written by the rasterizer's interpolated depth. Consequently, writing to `frag_depth` typically forces the GPU to disable crucial early-Z optimizations for the entire draw call.

The introduction of a new `depth_mode` built-in parameter for the `@builtin(frag_depth)` with modes `less`, and `greater` directly addresses this performance limitation by letting the developer express their intent to the hardware.

*   The `less` and `greater` modes are used when a fragment shader declares it will only write depth values that are guaranteed to be less than (or greater than) the existing depth. The driver can be assured that any fragment whose interpolated depth already fails the declared comparison cannot possibly satisfy the conditional write, allowing it to be rejected early.

## HLSL

*   `SV_Depth`: Depth buffer data. Can be written by pixel shader
*   `SV_DepthGreaterEqual`: In a pixel shader, allows outputting depth, as long as it is greater than or equal to the value determined by the rasterizer. Enables adjusting depth without disabling early Z.
*   `SV_DepthLessEqual`: In a pixel shader, allows outputting depth, as long as it is less than or equal to the value determined by the rasterizer. Enables adjusting depth without disabling early Z.

Source: https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics

## Metal

If a fragment function writes a depth value, the `depth_argument` needs to be specified with one of the following values: `any`, `greater`, `less`.

Source: https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf

## OpenGL

The built-in fragment shader variable `gl_FragDepth` may be redeclared using one of the following layout qualifiers: `depth_any`, `depth_greater`, `depth_less`,  `depth_unchanged`.

Source: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_conservative_depth.txt

## Vulkan

In cases where you know that the fragment shader will move the depth value in only one direction (toward or away from the viewer), you can apply the SPIR-V `DepthGreater` or `DepthLess` execution modes to the fragment shader’s entry point with the `OpExecutionMode` instruction.

When `DepthGreater` is applied, then Vulkan knows that no matter what your shader does, the resulting depth values produced by the fragment shader will only be greater than the values produced by interpolation. Therefore, if the depth test is `VK_COMPARE_OP_GREATER` or `VK_COMPARE_OP_GREATER_OR_EQUAL`, then the fragment shader cannot negate the result of a depth test that’s already passed.

Likewise, when `DepthLess` is applied, then Vulkan knows that the fragment shader will only make the resulting depth value less than it would have been and therefore cannot negate the result of a passing `VK_COMPARE_OP_LESS` or `VK_COMPARE_OP_LESS_OR_EQUAL` test.

Source: https://usermanual.wiki/Document/Vulkan2BProgramming2BGuide2BThe2BOfficial2BGuide2Bto2BLearning2BVulkan.1140066211.pdf

## WGSL

### Language extension

| WGSL language extension | Description |
| --- | --- |
| `fragment_depth` | Adds built-in `depth_mode` parameter support for fragment's depth |

### Built-in `depth_mode` parameter to `frag_depth`

An optional `depth_mode` parameter declared after the built-in `frag_depth` name is allowed and defaults to `any`.

It can be either `@builtin(frag_depth)`, `@builtin(frag_depth, greater)`, or `@builtin(frag_depth, less)`

This applies `depth_mode` on the `frag_depth` built-in output value in a fragment shader.

### Example usage

```wgsl
requires fragment_depth;

@fragment
fn main() -> @builtin(frag_depth, greater) f32 {
  return 1.0f;
}
```

### Built-in Value Mappings

| Built-in | SPIR-V | MSL | HLSL | GLSL |
|----------|--------|-----|------|------|
| frag_depth | SpvBuiltInFragDepth | depth(any) | SV_Depth | depth_any |
| frag_depth, greater | SpvBuiltInFragDepth + OpExecutionMode DepthGreater | depth(greater) | SV_DepthGreaterEqual | depth_greater |
| frag_depth, less | SpvBuiltInFragDepth + OpExecutionMode DepthLess | depth(less) | SV_DepthLessEqual | depth_less |

## Open sub-issues

- https://github.com/search?q=parent-issue%3Agpuweb%2Fgpuweb%235342+state%3Aopen+&type=issues

## Resources

- https://github.com/gfx-rs/wgpu/pull/7676
- https://github.com/gpuweb/gpuweb/issues/4891#issuecomment-3341897141
- https://therealmjp.github.io/posts/to-earlyz-or-not-to-earlyz/

---

### Tint Extensions Multisampled Array Textures (`docs/tint/extensions/multisampled_array_textures.md`)

# Multisampled Array Textures

The `multisampled_array_textures` language feature adds the
`texture_multisampled_2d_array<T>` WGSL type. The texel type `T` must be `f32`, `i32`, or `u32`.

## Status

This is an experimental language feature based on the
[multisampled array textures proposal](https://github.com/gpuweb/gpuweb/blob/main/proposals/multisampled-array-textures.md).

## Syntax

The `requires` directive documents the module's dependency on the language feature. It does not
enable the feature; the implementation must support it.

```wgsl
requires multisampled_array_textures;

@group(0) @binding(0)
var texture : texture_multisampled_2d_array<f32>;
```

The language feature adds overloads for:

- `textureDimensions(texture)`
- `textureNumLayers(texture)`
- `textureNumSamples(texture)`
- `textureLoad(texture, coordinates, array_index, sample_index)`

For example:

```wgsl
requires multisampled_array_textures;

@group(0) @binding(0)
var texture : texture_multisampled_2d_array<f32>;

fn loadSample(coordinates : vec2<i32>, layer : i32, sample : i32) -> vec4<f32> {
    return textureLoad(texture, coordinates, layer, sample);
}
```

---

### Tint Extensions Subgroup Size Control (`docs/tint/extensions/subgroup_size_control.md`)

# Subgroup Size Control

The `subgroup_size_control` is an extension that allows the use of the
`subgroup_size` attribute in compute shaders in WGSL. When the attribute `subgroup_size` is declared, the compute shader will only be executed with the specified subgroup size, and the built-in value `subgroup_size` will be the the value of the attribute `subgroup_size`. Otherwise, the compute shader may be executed with a subgroup size chosen by the GPU driver, which is unknown before the execution of the compute shader.

# Status

This extension has been accepted as an extension in the [WebGPU specification](https://gpuweb.github.io/gpuweb/#dom-gpufeaturename-subgroup-size-control).

# Availability

The usage is restricted to `compute` shaders.

|Backend|Implementation|Requirements|
|---|---|---|
|D3D12|`[WaveSize(<numLanes>)]` in HLSL|Shader Model 6.6 and Wave Operations support|
|Vulkan|`requiredSubgroupSize` in `VkPipelineShaderStageRequiredSubgroupSizeCreateInfo` on the API side|`subgroupSizeControl` and `computeFullSubgroups` in `VK_EXT_subgroup_size_control` or Vulkan 1.3|
|Metal|Enabled when `mSubgroupMinSize == mSubgroupMaxSize` (only available on Apple GPUs)|`mSubgroupMinSize == mSubgroupMaxSize`|

- On D3D12, `SubgroupSizeControl` is enabled when wave operations are supported and the highest supported shader model is at least 6.6 (which provides the `[WaveSize]` attribute in HLSL).
- On Vulkan, `SubgroupSizeControl` is enabled when `VkPhysicalDeviceSubgroupSizeControlFeatures.subgroupSizeControl` and `VkPhysicalDeviceSubgroupSizeControlFeatures.computeFullSubgroups` are both `VK_TRUE`.
- On Metal, `SubgroupSizeControl` is enabled when `SubgroupMinSize == SubgroupMaxSize`. Although Metal doesn't support selecting a subgroup size, currently all the Apple GPUs have a fixed subgroup size of 32, so controlling the subgroup size is trivially satisfied. Non-Apple Metal GPUs (e.g. Intel/AMD Macs) have different min/max sizes and do not support this feature.

# Specification

This extension adds a new `subgroup_size` attribute for `compute` shaders.

Enabling this extension on the API side will implicitly enable the `subgroups` extension.

```
subgroup_size_attr :
 '@' 'subgroup_size' '(' expression ',' ? ')'
```

Below are the validation rules against the extension `subgroup_size_control` and the attribute `subgroup_size`:
- Enabling `subgroup_size_control` must also enable `subgroups`.
- `subgroup_size` cannot be used when `subgroup_size_control` is not enabled.
- `subgroup_size` can only be used in the compute stage.
- `subgroup_size` must be a const-expression or an override-expression that resolves to an `i32` or `u32`.
- `subgroup_size` must be a power-of-two.
- `subgroup_size` must be greater than or equal to `SubgroupMinSize` on the current `adapter`.
- `subgroup_size` must be less than or equal to `SubgroupMaxSize` on the current `adapter`.
- The total compute invocations per workgroup must be a multiple of the declared `subgroup_size`.

# Example usage

```wgsl
enable subgroups;
enable subgroup_size_control;

@subgroup_size(32)
@compute @workgroup_size(64, 1, 1)
fn main(@builtin(subgroup_invocation_id) sg_id : u32,
        @builtin(subgroup_size) sg_size : u32) {

}
```

# Notes

1. `maxComputeWorkgroupSubgroups` on Vulkan

`maxComputeWorkgroupSubgroups` is a limitation defined in the Vulkan extension `VK_EXT_subgroup_size_control` and defined in `VkPhysicalDeviceSubgroupSizeControlProperties.maxComputeWorkgroupSubgroups`. Note that there is no such limitation on D3D12.

| Limit name | Type | Limit class | Default | Compatibility Mode Default |
|------------|------|-------------|---------|----------------------------|
| `maxComputeWorkgroupSubgroups` | `GPUSize32` | `maximum` | - | Not Supported |

- On Vulkan the `workgroup_size` attribute, `subgroup_size` attribute and `maxComputeWorkgroupSubgroups` limit must follow below requirement:
```
workgroup_size.x * workgroup_size.y * workgroup_size.z <= subgroup_size * maxComputeWorkgroupSubgroups
```

We decide not to expose `maxComputeWorkgroupSubgroups` in Dawn because it is not a limit on D3D12 and it is not a limit on Metal. Instead, we will validate the `subgroup_size` attribute against the `workgroup_size` attribute and the `maxComputeWorkgroupSubgroups` limit on Vulkan.

2. `SubgroupMinSize` and `SubgroupMaxSize` (in `wgpu::AdapterInfo`)

The `subgroup_size` attribute is validated against `SubgroupMinSize` and `SubgroupMaxSize` directly. The separate `minExplicitComputeSubgroupSize` and `maxExplicitComputeSubgroupSize` limits are no longer needed.

- On D3D12, `SubgroupMinSize` and `SubgroupMaxSize` are sourced from `D3D12_FEATURE_DATA_D3D12_OPTIONS1::waveLaneCountMin` and `waveLaneCountMax`. Microsoft has [clarified](https://github.com/microsoft/DirectXShaderCompiler/issues/8535) that `WaveLaneCountMax` is exactly the maximum wave lane count that can be used with the HLSL `[WaveSize]` attribute.
  - On some Intel GPUs, pixel shaders may run with wave lane count 8 while `waveLaneCountMin` reports 16. This is treated as a driver issue.
- On Vulkan, `SubgroupMinSize` and `SubgroupMaxSize` are sourced from `VkPhysicalDeviceSubgroupSizeControlPropertiesEXT::minSubgroupSize` and `maxSubgroupSize`.

# References

* [DirectX Specs HLSL Wave Size](https://microsoft.github.io/DirectX-Specs/d3d/HLSL_SM_6_6_WaveSize.html#allowed-wave-sizes)
* [Vulkan VK_EXT_subgroup_size_control](https://docs.vulkan.org/refpages/latest/refpages/source/VK_EXT_subgroup_size_control.html)

---

### Tint Extensions Texel Buffers (`docs/tint/extensions/texel_buffers.md`)

# Chromium Experimental Texel Buffers

The `texel_buffers` language feature allows to store WebGPU plain
color format data with a `GPUBuffer`, supporting format conversion on load and store
operations. Texel buffers roughly translate to buffer textures in Vulkan
(`Dim = Buffer`), `texture_buffer<format>` in Metal Shading Language, and typed
UAV/SRV buffer views in HLSL/D3D12.

## Status

The feature is actively under review for adoption in WGSL. Although a [proposal]
(https://github.com/gpuweb/gpuweb/blob/main/proposals/texel-buffers.md) has been
accepted, implementations must not expose the feature
without an explicit draft-enable flag. Discussion is ongoing in a
[Texel Buffers Issue] (https://github.com/gpuweb/gpuweb/issues/162).

## Syntax

A new `texel_buffer` type, parameterized by the color format and the access mode:
`texel_buffer<Format, Access>`.

*   `Format` must be a WebGPU plain color format.
*   `Access` is either read or read_write.

Texel buffers overload `textureDimensions`, `textureLoad` and `textureStore`
built-in functions.

## Example usage

```wgsl
@group(0) @binding(0) var myBuffer : texel_buffer<rgba8unorm, read_write>;

@compute @workgroup_size(32)
fn computeMain(@builtin(global_invocation_id) threadId : vec3<u32>) {
    let length = textureDimensions(myBuffer);
    if (threadId.x >= length) {
        return;
    }

    var value = textureLoad(myBuffer, threadId.x);
    value = value * value;
    textureStore(myBuffer, threadId.x, value);
}
```

---

--- METRICS ---
- Files Extracted: 94
- Estimated Token Budget: ~107136 tokens
- Canonical Reference: https://codewiki.google/github.com/google/dawn
