Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
## 0.4.4

* `buildShaderBundleJson` accepts an `includeDirectories` parameter:
extra directories appended to `impellerc`'s `#include` search path,
after the manifest directory and the bundled `shader_lib`. This lets a
build hook compile shaders that `#include` reusable GLSL shipped by
another package, by resolving that package's shader directory and
passing it through. Files resolved this way are not auto-declared as
build dependencies; declare them via the build output if edits to them
should retrigger the build.
* New `shaderBundleImpellercArguments(...)` helper returns the exact
`impellerc` argument list, exposed for inspection from custom build
hooks and tests.

## 0.4.3

* `buildShaderBundleJson` now declares the manifest and every shader
Expand Down
51 changes: 45 additions & 6 deletions lib/build.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Future<void> _buildShaderBundleJson({
required Uri inputManifestFilePath,
required Uri outputBundleFilePath,
required BuildOutputBuilder buildOutput,
required List<Uri> includeDirectories,
}) async {
/////////////////////////////////////////////////////////////////////////////
/// 1. Parse the manifest file.
Expand Down Expand Up @@ -47,12 +48,13 @@ Future<void> _buildShaderBundleJson({

final impellercExec = await findImpellerC();
final shaderLibPath = impellercExec.resolve('./shader_lib');
final impellercArgs = [
'--sl=${outputBundleFilePath.toFilePath()}',
'--shader-bundle=$reconstitutedManifest',
'--include=${inputManifestFilePath.resolve('./').toFilePath()}',
'--include=${shaderLibPath.toFilePath()}',
];
final impellercArgs = shaderBundleImpellercArguments(
outputBundleFilePath: outputBundleFilePath,
manifestJson: reconstitutedManifest,
manifestDirectory: inputManifestFilePath.resolve('./'),
shaderLibDirectory: shaderLibPath,
includeDirectories: includeDirectories,
);

final impellerc = Process.runSync(
impellercExec.toFilePath(),
Expand Down Expand Up @@ -94,6 +96,33 @@ List<Uri> collectShaderBundleDependencies(
return result;
}

/// Builds the `impellerc` argument list for a shader-bundle compile.
///
/// The first two `--include` directories are always the manifest's own
/// directory and `impellerc`'s bundled `shader_lib`. Any [includeDirectories]
/// are appended after them, so a package that ships reusable GLSL (for example
/// framework shaders that generated bundles `#include`) can put its shader
/// directory on the search path.
///
/// Exposed so users authoring custom build hooks (and tests) can inspect the
/// exact arguments [buildShaderBundleJson] passes to `impellerc`.
List<String> shaderBundleImpellercArguments({
required Uri outputBundleFilePath,
required String manifestJson,
required Uri manifestDirectory,
required Uri shaderLibDirectory,
List<Uri> includeDirectories = const [],
}) {
return [
'--sl=${outputBundleFilePath.toFilePath()}',
'--shader-bundle=$manifestJson',
'--include=${manifestDirectory.toFilePath()}',
'--include=${shaderLibDirectory.toFilePath()}',
for (final directory in includeDirectories)
'--include=${directory.toFilePath()}',
];
}

/// Build a Flutter GPU shader bundle/library from a JSON manifest file.
///
/// The [buildInput] and [buildOutput] are provided by the build hook system.
Expand All @@ -111,6 +140,14 @@ List<Uri> collectShaderBundleDependencies(
/// references as build-system dependencies, so the bundle is rebuilt
/// when any input changes.
///
/// 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.
///
/// Example usage:
///
/// hook/build.dart
Expand Down Expand Up @@ -141,6 +178,7 @@ Future<void> buildShaderBundleJson({
required BuildInput buildInput,
required BuildOutputBuilder buildOutput,
required String manifestFileName,
List<Uri> includeDirectories = const [],
}) async {
String outputFileName = Uri(path: manifestFileName).pathSegments.last;
if (!outputFileName.endsWith('.shaderbundle.json')) {
Expand Down Expand Up @@ -178,5 +216,6 @@ Future<void> buildShaderBundleJson({
inputManifestFilePath: inFile,
outputBundleFilePath: outFile,
buildOutput: buildOutput,
includeDirectories: includeDirectories,
);
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: flutter_gpu_shaders
description: 'Build tools for Flutter GPU shader bundles/libraries.'
version: 0.4.3
version: 0.4.4
homepage: https://github.com/bdero/flutter_gpu_shaders

environment:
Expand Down
54 changes: 54 additions & 0 deletions test/build_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,58 @@ void main() {
expect(deps, [manifestUri]);
});
});

group('shaderBundleImpellercArguments', () {
final out = Uri.parse(
'file:///pkg/build/shaderbundles/bundle.shaderbundle',
);
final manifestDir = Uri.parse('file:///pkg/shaders/');
final shaderLib = Uri.parse('file:///sdk/shader_lib/');

test('emits sl, shader-bundle, and the two default includes', () {
final args = shaderBundleImpellercArguments(
outputBundleFilePath: out,
manifestJson: '{}',
manifestDirectory: manifestDir,
shaderLibDirectory: shaderLib,
);
expect(args, [
'--sl=${out.toFilePath()}',
'--shader-bundle={}',
'--include=${manifestDir.toFilePath()}',
'--include=${shaderLib.toFilePath()}',
]);
});

test('appends an --include for each extra directory, in order', () {
final depA = Uri.parse('file:///dep_a/shaders/');
final depB = Uri.parse('file:///dep_b/glsl/');
final args = shaderBundleImpellercArguments(
outputBundleFilePath: out,
manifestJson: '{}',
manifestDirectory: manifestDir,
shaderLibDirectory: shaderLib,
includeDirectories: [depA, depB],
);
final includes = args.where((a) => a.startsWith('--include=')).toList();
expect(includes, [
'--include=${manifestDir.toFilePath()}',
'--include=${shaderLib.toFilePath()}',
'--include=${depA.toFilePath()}',
'--include=${depB.toFilePath()}',
]);
});

test('an empty include list matches the default behaviour', () {
List<String> build({List<Uri>? includeDirectories}) =>
shaderBundleImpellercArguments(
outputBundleFilePath: out,
manifestJson: '{}',
manifestDirectory: manifestDir,
shaderLibDirectory: shaderLib,
includeDirectories: includeDirectories ?? const [],
);
expect(build(includeDirectories: const []), build());
});
});
}
Loading