-
Notifications
You must be signed in to change notification settings - Fork 345
Description
The "auto" layout algorithm currently generates layouts which cannot be shared with other pipelines. The reason for this is that these layouts are derived from static-use analysis of WGSL shaders, which makes the "auto" generated layout sensitive to small changes in the WGSL source (e.g. removing a call to a helper function that uses a binding). This makes the layouts fragile - difficult to share between pipelines, and potentially difficult to debug due to "spooky action at a distance" (making a small code change in one shader makes an entirely different pipeline creation fail).
However, "auto" layouts are extremely convenient. It's annoying to have to fall back to explicit layouts as soon as you need to share the layout - which happens quickly in most non-trivial graphics projects - as you suddenly have to duplicate the entire layout on the JS side and store the GPUBindGroupLayout/GPUPipelineLayout objects off somewhere.
To fix this I want to propose two things: #4956, and adding an explicit layout syntax to WGSL. "auto" layouts created from this syntax would be very tightly defined in a single @interface annotation on the entrypoint(s) of the pipeline (the vertex and fragment layouts would be merged). All bindings referenced from @interface would also require explicit filtering/non-filtering and float/unfilterable-float/depth information from #4956.
Rough mock-up with compute:
@group(0) @binding(0) var<uniform> params: SimParams;
@group(1) @binding(1) var<storage, read> particlesA: Particles;
@group(1) @binding(2) var<storage, read_write> particlesB: Particles;
@interface(params, particlesA, particlesB)
@compute @workgroup_size(64) fn main() { /* ... params ... particlesA ... particlesB ... */ }Bind points could optionally be deferrable until @interface time:
@group() @binding() var<uniform> params: SimParams;
@group() @binding(1) var<storage, read> particlesA: Particles;
@group() @binding(2) var<storage, read_write> particlesB: Particles;
@interface(@group(0) @binding(0) params, @group(1) particlesA, @group(1) particlesB)
@compute @workgroup_size(64) fn main() { /* ... params ... particlesA ... particlesB ... */ }Or better, even batched into entire bind groups with a new type of "struct":
@group() @binding() var<uniform> params: SimParams;
bind_group ParticleBindings {
@binding(1) var<storage, read> a: Particles;
@binding(2) var<storage, read_write> b: Particles;
}
@group() var<bind_group> particles: ParticleBindings;
@interface(@group(0) @binding(0) params, @group(1) particles)
@compute @workgroup_size(64) fn main() { /* ... params ... particles.a ... particles.b ... */ }Thanks to informal design discussion with teammates after the F2F, especially @dj2 :)