addMaterial method

Registration addMaterial(
  1. String source, {
  2. required String from,
})

Compiles source for this engine's backend and adds it to the renderer's materials once loaded. from names it in every message.

The registration takes it out of the renderer again.

In the plugin's namespace. Through the view of a plugin from outside the engine, the bare name the source writes (material Ink) is compiled as <namespace>__Ink (materialStageIn), the source renamed to match, so its lighting model is <namespace>.Ink whatever the file wrote; a name in another plugin's namespace is a PluginException.

Implementation

Registration addMaterial(String source, {required String from}) {
  final String name;
  // [source], in the plugin's namespace.
  final String named;
  final ShaderBundle bundle;
  try {
    final parsed = parseMaterial(source);
    final manifest = _scope?.manifest;
    final stage = manifest == null || manifest.isEngine
        ? parsed.name
        : materialStageIn(manifest.namespace, parsed.name);
    if (stage == null) {
      throw PluginException(
        '${_owner()} brought the material "${parsed.name}", which is named '
        'in another plugin\'s namespace: a plugin writes the bare name, '
        'which it compiles as "${manifest!.namespace}__<Name>"',
      );
    }
    named = stage == parsed.name
        ? source
        : renameMaterialSource(source, stage);
    final program = stage == parsed.name ? parsed : parseMaterial(named);
    name = program.name;
    if (program.kind != MaterialStageKind.surface) {
      throw PluginException(
        '${_owner()} brought "$name", a ${program.kind} stage, as a '
        'material: a full-screen stage is a render step\'s, under its '
        '"shaders" as "material"',
      );
    }
  } on MaterialSyntaxException catch (error, stack) {
    // A source that does not parse is the author's mistake on every
    // backend, so it fails the install rather than switching anything off.
    Error.throwWithStackTrace(
      PluginException(
        '${_owner()} brought a material that does not parse: '
        '$from:${error.line}:${error.column}: ${error.message}',
      ),
      stack,
    );
  }
  if (_store.backend == RuntimeBackends.impeller) {
    return _prebuiltMaterial(named, name);
  }
  try {
    bundle = runtimeMaterialBundle(
      named,
      backend: _store.backend,
      from: from,
      engineSources: _store.engineSources,
      webGpuHost: _store.webGpuHost,
      webGpuFullscreenUvLocation: _store.webGpuFullscreenUvLocation,
      compilers: _store.compilers,
    );
  } on RuntimeShadersException catch (refused) {
    _store.statuses[name] = refused.message;
    return _tracked(Registration(() => _store.statuses.remove(name)));
  }
  _store.statuses[name] = null;
  Registration? added;
  var cancelled = false;
  _store.load(bundle, name, (library) {
    if (cancelled) return;
    added = _store.addMaterials(library);
    _store.lighting[name] = describeMaterial(
      parseMaterial(named),
    ).lightingModel(label: name, shaderName: name);
  });
  return _tracked(
    Registration(() {
      cancelled = true;
      added?.cancel();
      _store.lighting.remove(name);
      _store.statuses.remove(name);
    }),
  );
}