fullscreenMaterialWgsl function

PackedStage fullscreenMaterialWgsl(
  1. MaterialProgram program, {
  2. required int uvLocation,
  3. required String from,
})

A full-screen stage written in the language — version 2 — as WGSL, for a plugin's render step on WebGPU while the game runs.

Written whole rather than spliced: a full-screen stage is the picture in, its colour out, through the shared FullscreenVertex, with nothing of the engine's surface header in it, so there is no host stage to keep. What it has to agree with the engine on is the one varying, v_uv, at uvLocation — the location the engine's own full-screen stages read it at, webGpuFullscreenUvLocation() from flutter3d_webgpu — and the fragment stage's bind group, numbered as the build numbers it: blocks by name, then samplers by name, a texture and a sampler each.

program must be specialised. Throws a RuntimeShadersException for a source that is not a full-screen stage.

Implementation

PackedStage fullscreenMaterialWgsl(
  MaterialProgram program, {
  required int uvLocation,
  required String from,
}) {
  if (program.kind != MaterialStageKind.fullscreen) {
    throw RuntimeShadersException(
      '$from: "${program.name}" is a ${program.kind} stage, not a full-screen '
      'one',
    );
  }
  // A full-screen stage's textures are its own by nature and bound by name;
  // a block of its own is what WebGPU cannot number at run time.
  if (program.parameters.any(
    (p) => p.uniform && p.block != materialParametersBlock,
  )) {
    throw RuntimeShadersException(
      '$from: a full-screen stage with a uniform block of its own is compiled '
      'for WebGPU by the build (assets_src/); at run time its uniforms are '
      'one MaterialParams block',
    );
  }
  const group = 1;
  final uniforms = <MaterialParameter>[
    for (final parameter in program.parameters)
      if (parameter.uniform) parameter,
  ];
  final readsDepth = program.inputsUsed.contains('sceneDepth');
  final params = uniforms.isEmpty ? null : _paramsBlock(uniforms, group, 0);
  var binding = params == null ? 0 : 1;
  final samplers = <PreparedSampler>[
    for (final name in <String>[
      for (final slot in program.textures) slot.bindingName,
      if (readsDepth) 'surface_texture',
    ]..sort())
      (
        name: name,
        group: group,
        textureBinding: binding++,
        samplerBinding: binding++,
        dimension: 'twoDimensional',
      ),
  ];

  final out = StringBuffer()
    ..writeln(
      '// The full-screen stage "${program.name}", written at run time.',
    );
  if (params != null) {
    out
      ..writeln('struct F3dMaterialParams {')
      ..writeAll(<String>[
        for (final p in uniforms) '    m_${p.name}: ${_type(p.type)},\n',
      ])
      ..writeln('}')
      ..writeln()
      ..writeln('@group($group) @binding(0) ')
      ..writeln('var<uniform> f3d_material_params: F3dMaterialParams;');
  }
  for (final sampler in samplers) {
    out
      ..writeln('@group($group) @binding(${sampler.textureBinding}) ')
      ..writeln('var ${sampler.name}_tex: texture_2d<f32>;')
      ..writeln('@group($group) @binding(${sampler.samplerBinding}) ')
      ..writeln('var ${sampler.name}_smp: sampler;');
  }
  final writer = _WgslWriter(
    surface: 'f3d_s',
    varying: (glsl) => glsl,
    params: 'f3d_material_params',
  );
  out
    ..writeln()
    ..writeln('@fragment ')
    ..writeln(
      'fn main(@location($uvLocation) v_uv: vec2<f32>) -> '
      '@location(0) vec4<f32> {',
    );
  if (readsDepth) {
    out
      ..writeln(
        '    let f3d_stored: f32 = textureSampleLevel(surface_texture_tex, '
        'surface_texture_smp, v_uv, 0.0).w;',
      )
      ..writeln(
        '    let f3d_scene_depth: f32 = select(1000000.0, f3d_stored, '
        'f3d_stored > 0.0);',
      );
  }
  for (final statement in program.body) {
    out.write(
      statement.accept(
        _WgslStatements(
          writer,
          returns: (value) => '    return $value;\n',
          output: () => throw RuntimeShadersException(
            '$from: a full-screen stage has no outputs',
          ),
          unknown: (String what) =>
              throw RuntimeShadersException('$from: $what'),
        ),
      ),
    );
  }
  out.writeln('}');
  return (
    wgsl: out.toString(),
    prepared: PreparedStage(
      glsl: emitMaterialFragment(program),
      attributes: const <PreparedAttribute>[],
      blocks: <PreparedBlock>[?params],
      samplers: samplers,
    ),
  );
}