diff --git a/CHANGELOG.md b/CHANGELOG.md index 94a3065..5080e1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/lib/build.dart b/lib/build.dart index 9cfed84..f7dca34 100644 --- a/lib/build.dart +++ b/lib/build.dart @@ -13,6 +13,7 @@ Future _buildShaderBundleJson({ required Uri inputManifestFilePath, required Uri outputBundleFilePath, required BuildOutputBuilder buildOutput, + required List includeDirectories, }) async { ///////////////////////////////////////////////////////////////////////////// /// 1. Parse the manifest file. @@ -47,12 +48,13 @@ Future _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(), @@ -94,6 +96,33 @@ List 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 shaderBundleImpellercArguments({ + required Uri outputBundleFilePath, + required String manifestJson, + required Uri manifestDirectory, + required Uri shaderLibDirectory, + List 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. @@ -111,6 +140,14 @@ List 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 @@ -141,6 +178,7 @@ Future buildShaderBundleJson({ required BuildInput buildInput, required BuildOutputBuilder buildOutput, required String manifestFileName, + List includeDirectories = const [], }) async { String outputFileName = Uri(path: manifestFileName).pathSegments.last; if (!outputFileName.endsWith('.shaderbundle.json')) { @@ -178,5 +216,6 @@ Future buildShaderBundleJson({ inputManifestFilePath: inFile, outputBundleFilePath: outFile, buildOutput: buildOutput, + includeDirectories: includeDirectories, ); } diff --git a/pubspec.yaml b/pubspec.yaml index f960903..955984a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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: diff --git a/test/build_test.dart b/test/build_test.dart index 102206e..16bf9bc 100644 --- a/test/build_test.dart +++ b/test/build_test.dart @@ -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 build({List? includeDirectories}) => + shaderBundleImpellercArguments( + outputBundleFilePath: out, + manifestJson: '{}', + manifestDirectory: manifestDir, + shaderLibDirectory: shaderLib, + includeDirectories: includeDirectories ?? const [], + ); + expect(build(includeDirectories: const []), build()); + }); + }); }