bundleVaryingLocations function

Map<String, int> bundleVaryingLocations(
  1. ShaderSet engine,
  2. Iterable<BundleStageSource> stages
)

A location per varying name, agreeing with engine's.

Computed over both manifests, then held against the engine's alone. Throws WgslPrepareException when the bundle would move one of the engine's varyings: the failure is a refusal to pack rather than a section that would compile and read the wrong slot.

Implementation

Map<String, int> bundleVaryingLocations(
  ShaderSet engine,
  Iterable<BundleStageSource> stages,
) {
  final theirs = <Set<String>>[
    for (final entry in engine.stages.values)
      scanVaryings(
        _resolveEngine(engine, entry.file),
        from: entry.file,
        fragment: entry.fragment,
      ),
  ];
  final ours = <Set<String>>[
    for (final stage in stages)
      scanVaryings(stage.resolved, from: stage.file, fragment: stage.fragment),
  ];

  final engineOnly = assignVaryingLocations(theirs);
  final together = assignVaryingLocations(<Set<String>>[...theirs, ...ours]);

  for (final entry in engineOnly.entries) {
    if (together[entry.key] == entry.value) continue;
    throw WgslPrepareException(
      'this bundle moves the engine\'s varying "${entry.key}" from location '
      '${entry.value} to ${together[entry.key]}. A stage packed here is paired '
      'with a stage the engine already compiled, and WebGPU joins the two by '
      'location alone, so the numbering cannot be renegotiated by a bundle. '
      'A varying declared beside the engine\'s in one stage joins their '
      'family and renumbers it — give it a name of its own, or declare it in '
      'a stage that shares nothing with the engine\'s.',
    );
  }
  return together;
}