Combine one value from each invocation of a workgroup into one result, or into what comes before each.
kit.workgroup
shader
workgroup.sum(schema)→ Monoid
workgroup.min(schema)→ Monoid
workgroup.max(schema)→ Monoid
workgroup.bounds(schema)→ Monoid
and workgroup.reduce, workgroup.scan
schematakes
a numeric scalar or vector, or a struct or array of them
Monoidreturns
a monoid for reduce and scan
schematakes
a scalar or vector of numbers
Monoidreturns
a monoid of { low, high } for reduce and scan
types
MonoidOrdered
Tap a value in the last row, or use the arrow keys, to light the values that reach it. Each row is one step between barriers. A reduce halves the invocations that add at each step, then every invocation reads the first. A scan adds the value reach invocations back, then each invocation reads the one before it.
The invocations of a workgroup often need one answer from all of them: the average brightness of their samples, the box around their points, or where each writes in a packed list. Each answer takes an array in workgroup memory, a loop of steps and a barrier between them. workgroup.reduce and workgroup.scan write that loop once, for any monoid: a schema, an identity value and a combine function.
Keeps the box around points: low is their min and high their max. Give each invocation's
point as schema({ low: point, high: point }).
@param ― schema a scalar or vector of numbers
@returns ― a monoid of { low, high } for reduce and scan
bounds(
import d
d.
constvec3f:d.Vec3f
vec3f);
const
constenclose:(value:{
low:d.v3f;
high:d.v3f;
},lid:number)=>{
low:d.v3f;
high:d.v3f;
}
enclose=
import workgroup
workgroup.
functionreduce<d.WgslStruct<{
low:d.Vec3f;
high:d.Vec3f;
}>>(monoid:workgroup.Monoid<d.WgslStruct<{
low:d.Vec3f;
high:d.Vec3f;
}>>,size:number):(value:{
low:d.v3f;
high:d.v3f;
},lid:number)=>{
low:d.v3f;
high:d.v3f;
}
Returns a 'use gpu' function (value, lid) that combines value from each of the size
invocations of a workgroup and returns the result to every one of them. lid is the
invocation's localInvocationIndex. Every invocation must call it, before any of them returns:
one with nothing to add passes monoid.identity(). Each call to reduce declares its own
workgroup array, and the function can run several times in one kernel.
@param ― monoid how to combine values, such as workgroup.sum(d.f32)
@param ― size the workgroup's invocation count, a power of two
@returns ― a 'use gpu' function for kernels of that workgroup size
Returns a 'use gpu' function (value, lid) that gives each of the size invocations of a
workgroup the combination of the values of the invocations before it in lid order, and the
first monoid.identity(). Combine the result with value to include the invocation's own.
Calls follow the rules of reduce.
@param ― monoid how to combine values, such as workgroup.sum(d.u32) for offsets to write at
@param ― size the workgroup's invocation count, a power of two
@returns ― a 'use gpu' function for kernels of that workgroup size
sum(schema) adds from zero. A struct or an array adds field by field, so one reduction sums several quantities.
min(schema) and max(schema) compare scalars, and vectors per component, from the highest or lowest value the schema holds.
bounds(schema) keeps { low, high }, the min and max of the points it is given.
Write your own as { schema, identity, combine }, with 'use gpu' functions. This one finds the brightest sample, and the lowest index on a tie:
const
constSample:d.WgslStruct<{
value:d.F32;
at:d.U32;
}>
Sample=
import d
d.
struct<{
value:d.F32;
at:d.U32;
}>(props:{
value:d.F32;
at:d.U32;
}):d.WgslStruct<{
value:d.F32;
at:d.U32;
}>
export struct
Creates a struct schema that can be used to construct GPU buffers.
Ensures proper alignment and padding of properties (as opposed to a d.unstruct schema).
The order of members matches the passed in properties object.
This type combines values of one schema. combine must be associative, and also commutative
for reduce. Combining a value with identity() must leave it unchanged. Both functions are
'use gpu' and also run on the CPU, so a test can fold the same values.
combine must be associative, and also commutative for reduce, which combines the invocations out of order. identity and combine also run on the CPU, so a test can fold the values the kernel combined and compare.
reduce(monoid, size) and scan(monoid, size) return a function of (value, lid) for kernels of size invocations, with lid the localInvocationIndex. reduce returns the combination of every value to every invocation. scan returns the combination of the values before the invocation, and identity() to the first. Combine it with value to include the invocation’s own.
Every invocation of the workgroup calls the function, before any of them returns. Barriers wait for all of them, so one that skips the call stalls or corrupts the rest. An invocation with nothing to add passes monoid.identity().
Each call to reduce or scan declares its own array of size values in workgroup memory. The function can run several times in one kernel.
The array takes size values of the schema from the workgroup memory WebGPU guarantees, 16 384 bytes. sum over 64 invocations of nine vec4f takes 9 216.
The functions combine within one workgroup. Combine the results of several workgroups in a later dispatch, or with atomics.