buildShaderBundleJson function

Future<ShaderBundleBuildResult> buildShaderBundleJson({
  1. required BuildInput buildInput,
  2. required BuildOutputBuilder buildOutput,
  3. required String manifestFileName,
  4. List<Uri> includeDirectories = const [],
  5. ShaderBundleAssetMode assetMode = ShaderBundleAssetMode.legacyOnly,
  6. String? dataAssetName,
})

Build a Flutter GPU shader bundle/library from a JSON manifest file.

The buildInput and buildOutput are provided by the build hook system.

The manifestFileName is the path to the JSON manifest file, which is relative to the package root where the build hook resides.

The manifestFileName must end with ".shaderbundle.json".

The built shader bundle will be written to build/shaderbundles/[name].shaderbundle, relative to the package root where the build hook resides.

The hook declares the manifest and every shader source file it references as build-system dependencies, so the bundle is rebuilt when any input changes. When the resolved impellerc supports --depfile in --shader-bundle mode, the hook also declares transitive #include dependencies from the generated depfile. Older impellerc builds fall back to the manifest scan.

The optional includeDirectories are added to impellerc's #include search path, after the manifest's directory and impellerc's built-in shader_lib. Use this to compile shaders that #include reusable GLSL shipped by another package: resolve that package's shader directory and pass it here. Files resolved through these directories are not declared as dependencies automatically; add them via buildOutput if edits to them should retrigger the build.

Set assetMode to opt in to DataAssets registration. The default ShaderBundleAssetMode.legacyOnly preserves the historical behavior and requires listing the generated .shaderbundle in flutter.assets. ShaderBundleAssetMode.dataAssetsIfAvailable registers the bundle when the current toolchain supports Dart DataAssets and otherwise falls back to the legacy output. ShaderBundleAssetMode.dataAssetsRequired fails early with setup guidance when DataAssets are unavailable.

Example usage:

hook/build.dart

import 'package:hooks/hooks.dart';
import 'package:flutter_gpu_shaders/build.dart';

void main(List<String> args) async {
  await build(args, (config, output) async {
    await buildShaderBundleJson(
        buildInput: config,
        buildOutput: output,
        manifestFileName: 'my_cool_bundle.shaderbundle.json');
  });
}

my_cool_bundle.shaderbundle.json

{
    "SimpleVertex": {
        "type": "vertex",
        "file": "shaders/my_cool_shader.vert"
    }
}

Implementation

Future<ShaderBundleBuildResult> buildShaderBundleJson({
  required BuildInput buildInput,
  required BuildOutputBuilder buildOutput,
  required String manifestFileName,
  List<Uri> includeDirectories = const [],
  ShaderBundleAssetMode assetMode = ShaderBundleAssetMode.legacyOnly,
  String? dataAssetName,
}) async {
  String outputFileName = Uri(path: manifestFileName).pathSegments.last;
  if (!outputFileName.endsWith('.shaderbundle.json')) {
    throw Exception(
      'Shader bundle manifest file names must end with ".shaderbundle.json".',
    );
  }
  if (outputFileName.length <= '.shaderbundle.json'.length) {
    throw Exception(
      'Invalid shader bundle manifest file name: $outputFileName',
    );
  }
  if (outputFileName.endsWith('.json')) {
    outputFileName = outputFileName.substring(0, outputFileName.length - 5);
  }

  final outDir = Directory.fromUri(
    buildInput.packageRoot.resolve('build/shaderbundles/'),
  );
  await outDir.create(recursive: true);
  final packageRoot = buildInput.packageRoot;

  final inFile = packageRoot.resolve(manifestFileName);
  final outFile = outDir.uri.resolve(outputFileName);
  final legacyAssetKey = 'build/shaderbundles/$outputFileName';

  if (assetMode == ShaderBundleAssetMode.dataAssetsRequired &&
      !buildInput.config.buildDataAssets) {
    _throwDataAssetsUnavailable(legacyAssetKey);
  }

  await _buildShaderBundleJson(
    packageRoot: packageRoot,
    inputManifestFilePath: inFile,
    outputBundleFilePath: outFile,
    buildOutput: buildOutput,
    includeDirectories: includeDirectories,
  );

  return registerShaderBundleDataAsset(
        buildInput: buildInput,
        buildOutput: buildOutput,
        outputBundleFile: outFile,
        legacyAssetKey: legacyAssetKey,
        assetMode: assetMode,
        dataAssetName: dataAssetName,
      ) ??
      ShaderBundleBuildResult(
        outputFile: outFile,
        legacyAssetKey: legacyAssetKey,
      );
}