wgpu/documentation/extensions/
cooperative_matrices.rs

1/*!
2# 🧪Experimental🧪 Cooperative Matrix Extensions
3
4`wgpu` supports an experimental cooperative matrix feature when [`Features::EXPERIMENTAL_COOPERATIVE_MATRIX`] is enabled.
5This exposes hardware-accelerated matrix multiply-accumulate (MMA) operations (for example, NVIDIA tensor cores,
6Metal SIMD-group matrices, and Vulkan `VK_KHR_cooperative_matrix`).
7
8**Note**: The features documented here may have bugs and are subject to breaking changes. The API and shader
9semantics are expected to evolve. Please refer to the GitHub issue tracker for the latest status and discussions.
10
11---
12
13## Overview
14
15Cooperative matrices allow a **workgroup** (or equivalent execution group) to collectively:
16
17- load small matrix tiles from memory,
18- perform matrix multiply-accumulate operations on those tiles, and
19- store the results back to memory.
20
21Conceptually, this is specialized hardware that evaluates:
22
23> `C = A * B + C`
24
25for relatively small tiles, but at very high throughput compared to composing the same operation from
26scalar/vector instructions.
27
28Cooperative matrix operations are most useful in workloads such as:
29
30- machine learning and inference,
31- dense linear algebra and scientific computing,
32- image processing, filtering, and transforms.
33
34The cooperative nature means that all lanes in the cooperating execution group must participate in
35the operations; individual invocations cannot diverge.
36
37Typical example:
38
39- `A` is an M×K matrix.
40- `B` is a K×N matrix.
41- `C` is an M×N matrix, acting as the accumulator and result.
42
43---
44
45## Querying hardware support (host side)
46
47Before using cooperative matrices in shaders, you must query what configurations your hardware and backend support.
48
49On the `Adapter`, `wgpu` exposes:
50
51- `Adapter::cooperative_matrix_properties() -> Vec<CooperativeMatrixProperties>`
52
53Each `CooperativeMatrixProperties` describes a single supported configuration. Fields are:
54
55- `m_size`: height of matrices A and C (type: `naga::CooperativeSize`)
56- `n_size`: width of matrices B and C (type: `naga::CooperativeSize`)
57- `k_size`: shared inner dimension of A and B (type: `naga::CooperativeSize`)
58- `ab_type`: scalar element type for A and B (type: `naga::Scalar`)
59- `cr_type`: scalar element type for C and the result (type: `naga::Scalar`)
60- `saturating_accumulation`: `bool` indicating whether overflow clamping on accumulation
61  is supported for this configuration
62
63Example usage:
64
65```ignore
66let coop_props = adapter.cooperative_matrix_properties();
67for prop in &coop_props {
68    println!(
69        "{:?}x{:?}x{:?} - AB: {:?}, CR: {:?}, saturating: {}",
70        prop.m_size, prop.n_size, prop.k_size,
71        prop.ab_type, prop.cr_type,
72        prop.saturating_accumulation,
73    );
74}
75```
76
77You **must**:
78
791. Enable [`Features::EXPERIMENTAL_COOPERATIVE_MATRIX`] on the `Device`.
802. Query `adapter.cooperative_matrix_properties()` and ensure that the configuration(s) you intend
81   to use in WGSL are actually available on the running adapter/backend.
823. Treat the sizes and types as a contract between your shaders and the underlying hardware implementation.
83   Using unsupported configurations is an error.
84
85---
86
87## Feature and backend requirements
88
89### `wgpu` feature
90
91- Using cooperative matrices requires enabling:
92  - [`Features::EXPERIMENTAL_COOPERATIVE_MATRIX`]
93
94This feature may be restricted to certain backends and hardware.
95
96### Hardware / backend notes
97
98These are general guidelines, not a complete compatibility matrix:
99
100- **Metal**:
101  - Requires Apple7+ (A14) or Mac2+ (M1) GPU with MSL 2.3+.
102  - Strong support for 8×8 `f32`, 8×8 `f16`, and mixed-precision modes (e.g. `f16` A/B and `f32` accumulator C).
103  - Implementation is based on SIMD-group matrix operations.
104
105- **Vulkan**:
106  - Requires the `VK_KHR_cooperative_matrix` extension.
107  - Many NVIDIA and AMD GPUs support `f16` at 16×16 tile sizes and similar.
108  - 8×8 `f32` support is hardware-dependent.
109  - Exact configurations are enumerated by [`Adapter::cooperative_matrix_properties()`].
110
111- **Other backends**:
112  - May not support cooperative matrices at all. In that case the feature will not be exposed, and
113    `adapter.cooperative_matrix_properties()` will return an empty list.
114
115> Always treat the properties returned at runtime as the source of truth.
116
117---
118
119## `wgpu` API surface
120
121This section summarizes the host-side API elements related to cooperative matrices.
122(For exact signatures and details, refer to the Rust documentation.)
123
124### Adapter
125
126- `Adapter::cooperative_matrix_properties() -> Vec<CooperativeMatrixProperties>`
127
128Returns all cooperative matrix configurations supported by the adapter/backend.
129
130### Structures
131
132- `CooperativeMatrixProperties`
133  - `m_size: naga::CooperativeSize`
134  - `n_size: naga::CooperativeSize`
135  - `k_size: naga::CooperativeSize`
136  - `ab_type: naga::Scalar`
137  - `cr_type: naga::Scalar`
138  - `saturating_accumulation: bool`
139
140The `naga` types (`CooperativeSize`, `Scalar`) are part of the shader translation layer and
141determine the legal WGSL/cooperative matrix combinations.
142
143There are currently no dedicated `wgpu` buffer or texture types for cooperative matrices; they are
144expressed in WGSL as special value types accessed via pointers into ordinary `var<storage>` /
145`var<workgroup>` / `var<private>` / etc.
146
147---
148
149## WGSL extension specification
150
151Cooperative matrices are enabled and accessed via WGSL extensions. The exact extension spelling
152may change; the details below describe the intended semantics.
153
154### Enabling cooperative matrices in WGSL
155
156Any WGSL program using cooperative matrices must declare an extension at the top of the shader, for example:
157
158```wgsl
159enable wgpu_cooperative_matrix;
160```
161
162The shader is invalid if any cooperative matrix types or builtins are used without enabling this extension.
163
164### Cooperative matrix types
165
166A cooperative matrix is a value type parameterized by:
167
168- tile size (M×N),
169- scalar element type `T`, and
170- role `R` indicating how the matrix participates in the multiply-accumulate:
171  - `A`: left operand
172  - `B`: right operand
173  - `C`: accumulator / result
174
175Conceptually:
176
177```wgsl
178// A: MxK, B: KxN, C: MxN
179type coop_matMxN<T, A>;
180type coop_matMxN<T, B>;
181type coop_matMxN<T, C>;
182```
183
184Concrete examples (sizes and types must match a supported configuration from
185`Adapter::cooperative_matrix_properties`):
186
187```wgsl
188// 8x8 single-precision tiles
189alias CoopMatA = coop_mat8x8<f32, A>;
190alias CoopMatB = coop_mat8x8<f32, B>;
191alias CoopMatC = coop_mat8x8<f32, C>;
192
193// 16x16 half-precision inputs, 16x16 f32 accumulator (mixed precision)
194alias CoopMat16x16A = coop_mat16x16<f16, A>;
195alias CoopMat16x16B = coop_mat16x16<f16, B>;
196alias CoopMat16x16C = coop_mat16x16<f32, C>;
197```
198
199The actual set of legal `(M, N, T, R)` combinations is defined by the cooperative matrix
200properties returned at runtime; shaders must not use arbitrary combinations.
201
202### Roles and semantics
203
204- `A` role:
205  - Treated as the left operand in the multiplication. Has shape M×K.
206  - Participates as `A` in `A * B + C`.
207
208- `B` role:
209  - Treated as the right operand in the multiplication. Has shape K×N.
210  - Participates as `B` in `A * B + C`.
211
212- `C` role:
213  - Treated as accumulator and result. Has shape M×N.
214  - Participates as `C` in `A * B + C`.
215
216These roles are part of the type; they are not interchangeable.
217
218### Cooperative matrix operations
219
220WGSL provides built-in functions for operating on cooperative matrices. The exact spelling may
221change; the semantics are:
222
223#### `coopLoad` / `coopLoadT`
224
225Collectively load a tile from memory into a cooperative matrix. Two variants
226select the memory layout:
227
228- `coopLoad` — matrix is stored **column-major** in memory; `stride` is the
229  number of elements between adjacent columns.
230- `coopLoadT` — matrix is stored **row-major** in memory (i.e. transposed
231  relative to the canonical column-major layout used by `coopLoad`);
232  `stride` is the number of elements between adjacent rows. This is the
233  natural fit for C-style `ptr[i * num_cols + j]` storage.
234
235```wgsl
236fn coopLoad<T, R>(
237    ptr: ptr<STORAGE_CLASS, T>, // base pointer to scalar or vector elements
238    stride: u32                  // elements between adjacent columns
239) -> coop_matMxN<T, R>;
240
241fn coopLoadT<T, R>(
242    ptr: ptr<STORAGE_CLASS, T>, // base pointer to scalar or vector elements
243    stride: u32                  // elements between adjacent rows
244) -> coop_matMxN<T, R>;
245```
246
247- Loads an M×N tile (or M×K / K×N, depending on role and operation) from memory pointed to by `ptr`.
248- All invocations in the cooperative group must call the chosen variant in a converged fashion.
249- Memory address range must be valid and properly aligned for the scalar type.
250
251> Implementation note: Each lane contributes to filling the tile based on an implementation-defined mapping from
252> invocation/lane ID to sub-fragment of the matrix.
253
254#### `coopStore` / `coopStoreT`
255
256Collectively store a cooperative matrix tile back to memory. Variant
257selection mirrors the load builtins:
258
259- `coopStore` — writes **column-major**; `stride` between columns.
260- `coopStoreT` — writes **row-major**; `stride` between rows.
261
262```wgsl
263fn coopStore<T, R>(
264    value: coop_matMxN<T, R>,
265    ptr: ptr<STORAGE_CLASS, T>,
266    stride: u32
267);
268
269fn coopStoreT<T, R>(
270    value: coop_matMxN<T, R>,
271    ptr: ptr<STORAGE_CLASS, T>,
272    stride: u32
273);
274```
275
276- Stores `value` into the memory region addressed by `ptr` with given `stride`.
277- All invocations in the cooperative group must participate.
278- The store must not alias overlapping tiles in undefined ways.
279
280#### `coopMultiplyAdd`
281
282Perform a matrix multiply-accumulate operation on cooperative matrices:
283
284```wgsl
285fn coopMultiplyAdd<Tab, Tcr, MA, KA, KB, NB>(
286    a: coop_matMAxKA<Tab, A>, // A: MAxKA tile
287    b: coop_matKBxNB<Tab, B>, // B: KBxNB tile (KB == KA)
288    c: coop_matMAxNB<Tcr, C>  // C: MAxNB accumulator/result
289) -> coop_matMAxNB<Tcr, C>;
290```
291
292Semantics:
293
294- Computes `C' = A * B + C`.
295- Returns the resulting accumulator tile `C'`.
296- Implies:
297  - `KA == KB` (inner dimension must match).
298  - Types `(Tab, Tcr)` must be one of the supported AB/CR combinations given by
299    `CooperativeMatrixProperties`.
300  - Sizes `(MA, NB, KA)` must match a supported `(m_size, n_size, k_size)` triple.
301
302For example, with a supported configuration:
303
304```wgsl
305enable wgpu_cooperative_matrix;
306
307alias MatA = coop_mat8x8<f32, A>;
308alias MatB = coop_mat8x8<f32, B>;
309alias MatC = coop_mat8x8<f32, C>;
310
311// Assumes each tile is stored column-major in memory (the plain `coopLoad`
312// / `coopStore` form); use `coopLoadT` / `coopStoreT` for row-major storage.
313fn matmul_tile(
314    ptr_a: ptr<storage, f32>,
315    ptr_b: ptr<storage, f32>,
316    ptr_c: ptr<storage, f32>,
317    stride: u32,
318) {
319    let a: MatA = coopLoad<_, A>(ptr_a, stride);
320    let b: MatB = coopLoad<_, B>(ptr_b, stride);
321    let c: MatC = coopLoad<_, C>(ptr_c, stride);
322
323    let result: MatC = coopMultiplyAdd(a, b, c);
324    coopStore(result, ptr_c, stride);
325}
326```
327
328If `saturating_accumulation` is true for the chosen configuration, then overflow during accumulation
329is clamped (e.g. saturating arithmetic). If false, overflow behavior for the accumulator follows the
330underlying scalar type semantics (e.g. IEEE-754 for floats).
331
332### Workgroup cooperation and execution model
333
334Cooperative matrix operations are **collective**:
335
336- All invocations in the relevant execution group must execute each cooperative operation in uniform control flow:
337  - Using `coopLoad` / `coopLoadT`, `coopStore` / `coopStoreT`, or `coopMultiplyAdd` in divergent control flow
338    (e.g. some lanes taking a branch, others not) is undefined behavior.
339  - The exact execution group may be a workgroup, a SIMD-group / subgroup, or another backend-specific
340    granularity; shaders must treat it abstractly.
341
342- The workgroup (or cooperating group) size is constrained by both:
343  - the cooperative matrix configuration, and
344  - backend-specific implementation details.
345
346For portable code:
347
348- Choose a workgroup size that is known to be supported efficiently on your target backends, for example:
349  - `@workgroup_size(8, 8, 1)` to operate on an 8×8 tile, or
350  - a multiple of the tile size where each subgroup handles a tile.
351
352- Avoid control-flow divergence around cooperative operations.
353
354Example:
355
356```wgsl
357enable wgpu_cooperative_matrix;
358
359struct Matrices {
360    // Row-major tiles for A, B, C — use the `…T` load/store variants.
361    data: array<f32>,
362};
363
364@group(0) @binding(0)
365var<storage, read>  buf_a: Matrices;
366@group(0) @binding(1)
367var<storage, read>  buf_b: Matrices;
368@group(0) @binding(2)
369var<storage, read_write> buf_c: Matrices;
370
371alias MatA = coop_mat8x8<f32, A>;
372alias MatB = coop_mat8x8<f32, B>;
373alias MatC = coop_mat8x8<f32, C>;
374
375@compute @workgroup_size(8, 8, 1)
376fn main(
377    @builtin(workgroup_id) wg_id: vec3<u32>,
378    @builtin(local_invocation_id) lid: vec3<u32>,
379) {
380    // Compute tile offset; this is one of many possible mappings.
381    let tile_index = wg_id.x; // 1D tiling in this simple example
382    let tile_offset = tile_index * 64u; // 8x8 tile has 64 elements
383
384    // Base pointers for tiles of A, B, C.
385    let base_a = &buf_a.data[tile_offset];
386    let base_b = &buf_b.data[tile_offset];
387    let base_c = &buf_c.data[tile_offset];
388
389    let a: MatA = coopLoadT<f32, A>(base_a, 8u);
390    let b: MatB = coopLoadT<f32, B>(base_b, 8u);
391    let c: MatC = coopLoadT<f32, C>(base_c, 8u);
392
393    let result: MatC = coopMultiplyAdd(a, b, c);
394    coopStoreT(result, base_c, 8u);
395}
396```
397
398---
399
400## Validation rules and undefined behavior
401
402Implementations must validate the following where possible:
403
404- The `wgpu_cooperative_matrix` WGSL extension is enabled if any cooperative matrix types
405  or builtins are used.
406- Tile sizes `(M, N, K)` and scalar types `(ab_type, cr_type)` match at least one
407  [`CooperativeMatrixProperties`] entry for the current adapter/backend.
408- Workgroup size, shader stage, and other pipeline configuration constraints required
409  by the backend are satisfied.
410
411The following are examples of **undefined behavior** (non-exhaustive):
412
413- Using cooperative matrix operations without enabling the WGSL extension.
414- Using a cooperative matrix type `(M, N, T, R)` not supported by
415  [`Adapter::cooperative_matrix_properties()`].
416- Mismatching sizes or roles in `coopMultiplyAdd` (e.g. incompatible M/N/K, or incorrect roles).
417- Executing `coopLoad` / `coopLoadT`, `coopStore` / `coopStoreT`, or `coopMultiplyAdd` in divergent
418  control flow within the cooperating execution group.
419- Providing invalid, misaligned, or out-of-bounds pointers to any of the load/store builtins.
420- Using a load/store variant (`coopLoad` vs `coopLoadT`, `coopStore` vs `coopStoreT`) whose memory
421  layout does not match how the tile is actually stored.
422- Overlapping `coopStore` / `coopStoreT` targets in a way that creates data races or aliasing that
423  the memory model does not allow.
424
425---
426
427## Example: 64×64 matrix multiply using 8×8 tiles
428
429The example in `examples/features/src/cooperative_matrix` demonstrates using cooperative matrices to
430compute:
431
432- `C = A * B + C` where:
433  - `A` is 64×64,
434  - `B` is 64×64,
435  - `C` is 64×64.
436
437A high-level tiling strategy:
438
4391. Partition A, B, and C into 8×8 tiles.
4402. Launch one workgroup per output tile of C (i.e. 8×8 tiles for a 64×64 matrix = 8×8 = 64 tiles).
4413. Within each workgroup:
442   - Loop over K-dimension tiles.
443   - For each `k` tile:
444     - Load an 8×8 tile of A (`MatA`).
445     - Load an 8×8 tile of B (`MatB`).
446     - Maintain an 8×8 accumulator tile (`MatC`) and repeatedly apply `coopMultiplyAdd`.
4474. After the K loop, store the final accumulator tile back to C.
448
449Key points from the example:
450
451- Workgroup size is chosen so that all cooperative operations are well-defined and efficient for 8×8 tiles.
452- Host-side code:
453  - Enables [`Features::EXPERIMENTAL_COOPERATIVE_MATRIX`].
454  - Queries `cooperative_matrix_properties` and verifies that 8×8 `f32` or chosen configuration is supported.
455  - Dispatches the compute pipeline with appropriate grid dimensions.
456
457---
458
459## Notes and best practices
460
461- Always query `adapter.cooperative_matrix_properties()` and check that the configuration your shaders use exists.
462  Do not hard-code assumptions about available tile sizes or element types.
463- Treat the cooperative execution group as an abstract concept; avoid making assumptions about how
464  tiles are mapped to lanes beyond what is guaranteed by the spec.
465- Avoid divergent control flow around cooperative operations.
466- Consider providing a fallback non-cooperative implementation for devices that do not support the feature.
467- This is an experimental extension; API and semantics may change across versions of `wgpu` and `naga`.
468
469*/
470
471use crate::{Adapter, CooperativeMatrixProperties, Features};