checkStageAgreement function

List<StageMismatch> checkStageAgreement({
  1. required StageInterface vertex,
  2. required StageInterface fragment,
  3. List<StageVariable>? attributes,
})

Whether vertex and fragment, the stages of one pipeline, agree — and with attributes, the vertex layout the pipeline is drawn with, when it is known. Every mismatch, in the order found; empty when they agree.

The check the bundle writer runs over every pipeline it writes (amendment 5): before this, a fragment stage reading a varying its vertex stage never wrote compiled on every backend and drew zeros on one, garbage on another, and failed to link on a third.

What it holds:

  • every input of fragment is an output of vertex with the same type, and the same location where both state one (StageMismatchKind.varying); an output nobody reads is not a mismatch;
  • every input of vertex is in attributes with the same type, where attributes is given (StageMismatchKind.attribute);
  • a uniform block both stages declare has the same members, in the same order, with the same types (StageMismatchKind.block).

Implementation

List<StageMismatch> checkStageAgreement({
  required StageInterface vertex,
  required StageInterface fragment,
  List<StageVariable>? attributes,
}) {
  final found = <StageMismatch>[];
  final pair = '"${vertex.name}" and "${fragment.name}"';
  for (final input in fragment.inputs) {
    final output = vertex.outputs.where((o) => o.name == input.name);
    if (output.isEmpty) {
      found.add(
        StageMismatch(
          kind: StageMismatchKind.varying,
          name: input.name,
          says:
              '"${fragment.name}" reads the varying ${input.name}, which '
              '"${vertex.name}" does not write',
        ),
      );
      continue;
    }
    final written = output.first;
    if (written.type != input.type ||
        (written.location != null &&
            input.location != null &&
            written.location != input.location)) {
      found.add(
        StageMismatch(
          kind: StageMismatchKind.varying,
          name: input.name,
          says:
              '$pair disagree about the varying ${input.name}: '
              '"${vertex.name}" writes $written, "${fragment.name}" reads $input',
        ),
      );
    }
  }
  if (attributes != null) {
    for (final input in vertex.inputs) {
      final given = attributes.where((a) => a.name == input.name);
      if (given.isEmpty) {
        found.add(
          StageMismatch(
            kind: StageMismatchKind.attribute,
            name: input.name,
            says:
                '"${vertex.name}" reads the attribute ${input.name}, which the '
                'vertex layout does not give it',
          ),
        );
      } else if (given.first.type != input.type) {
        found.add(
          StageMismatch(
            kind: StageMismatchKind.attribute,
            name: input.name,
            says:
                '"${vertex.name}" reads ${input.name} as ${input.type}, and the '
                'vertex layout gives it as ${given.first.type}',
          ),
        );
      }
    }
  }
  for (final MapEntry(key: block, value: members) in vertex.blocks.entries) {
    final other = fragment.blocks[block];
    if (other == null || _sameMembers(members, other)) continue;
    found.add(
      StageMismatch(
        kind: StageMismatchKind.block,
        name: block,
        says:
            '$pair declare the block $block differently: '
            '{${members.join('; ')}} against {${other.join('; ')}}',
      ),
    );
  }
  return found;
}