11import 'dart:convert' ;
22import 'dart:io' ;
3+ import 'dart:isolate' ;
34
45/// Bundle a shader ('name'.frag & 'name'.vert) into a single shader bundle and
56/// store it in the assets directory.
@@ -8,23 +9,68 @@ import 'dart:io';
89/// Flutter might support auto-bundling themselves but until then we have to
910/// do it manually.
1011///
11- /// Note: this script should be run from the root of the package:
12- /// packages/flame_3d
12+ /// Run from the package root whose shaders are being built. When invoked
13+ /// from a consumer package via `dart run flame_3d:build_shaders` , the
14+ /// consumer's own `shaders/` is bundled. Every Dart dependency that ships a
15+ /// top-level `shaders/` directory is added to impellerc's include path under
16+ /// its package name, so shaders can `#include <pkg_name/foo.glsl>` against
17+ /// any of them. `<flutter/...>` resolves to the engine builtins.
1318void main (List <String > arguments) async {
1419 final root = Directory .current;
1520 final assets = Directory .fromUri (root.uri.resolve ('assets/shaders' ));
1621 final shaders = Directory .fromUri (root.uri.resolve ('shaders' ));
22+ final packageShaderDirs = await _resolvePackageShaderDirs ();
1723
18- await compute (assets, shaders);
24+ await compute (assets, shaders, packageShaderDirs );
1925 if (arguments.contains ('watch' )) {
2026 stdout.writeln ('Running in watch mode' );
2127 shaders.watch (recursive: true ).listen ((event) {
22- compute (assets, shaders);
28+ compute (assets, shaders, packageShaderDirs );
2329 });
2430 }
2531}
2632
27- Future <void > compute (Directory assets, Directory shaders) async {
33+ /// Returns every Dart dependency's top-level `shaders/` directory, so an
34+ /// `#include <pkg_name/foo.glsl>` can resolve to
35+ /// `<pkg-root>/shaders/pkg_name/foo.glsl` .
36+ Future <List <Directory >> _resolvePackageShaderDirs () async {
37+ final configUri = await Isolate .packageConfig;
38+ if (configUri == null ) {
39+ throw Exception (
40+ 'Unable to locate package_config.json. Run `dart pub get` first.' ,
41+ );
42+ }
43+
44+ final configFile = File .fromUri (configUri);
45+ final config =
46+ jsonDecode (configFile.readAsStringSync ()) as Map <String , dynamic >;
47+ final packages = (config['packages' ] as List ).cast <Map <String , dynamic >>();
48+ final result = < Directory > [];
49+ for (final package in packages) {
50+ final name = package['name' ] as String ;
51+ if (name == 'flutter' ) {
52+ // `flutter` ships no shader chunks; its includes come from the engine.
53+ continue ;
54+ }
55+
56+ final rootUriRaw = package['rootUri' ] as String ;
57+ final rootUri = configUri.resolve (
58+ rootUriRaw.endsWith ('/' ) ? rootUriRaw : '$rootUriRaw /' ,
59+ );
60+
61+ final shaderDir = Directory .fromUri (rootUri.resolve ('shaders/' ));
62+ if (shaderDir.existsSync ()) {
63+ result.add (shaderDir);
64+ }
65+ }
66+ return result;
67+ }
68+
69+ Future <void > compute (
70+ Directory assets,
71+ Directory shaders,
72+ List <Directory > packageShaderDirs,
73+ ) async {
2874 // Delete all the bundled shaders so we can replace them with new ones.
2975 if (assets.existsSync ()) {
3076 assets.deleteSync (recursive: true );
@@ -44,6 +90,9 @@ Future<void> compute(Directory assets, Directory shaders) async {
4490 .map ((f) => f.path.split (Platform .pathSeparator).last.split ('.' ).first)
4591 .toSet ();
4692
93+ final impellerC = await findImpellerC ();
94+ final engineShaderLib = impellerC.resolve ('./shader_lib/' ).toFilePath ();
95+
4796 for (final name in uniqueShaders) {
4897 final bundle = {
4998 'TextureFragment' : {
@@ -57,14 +106,17 @@ Future<void> compute(Directory assets, Directory shaders) async {
57106 };
58107
59108 stdout.writeln ('Computing shader "$name "' );
60- final impellerC = await findImpellerC ();
61109 final result = await Process .run (impellerC.toFilePath (), [
62110 '--sl=${assets .path }${Platform .pathSeparator }$name .shaderbundle' ,
63111 '--shader-bundle=${jsonEncode (bundle )}' ,
112+ '--include=${shaders .path }' ,
113+ for (final dir in packageShaderDirs) '--include=${dir .path }' ,
114+ '--include=$engineShaderLib ' ,
64115 ]);
65116
66117 if (result.exitCode != 0 ) {
67- return stderr.writeln (result.stderr);
118+ stderr.writeln ('Failed to compile shader "$name ":\n ${result .stderr }' );
119+ exitCode = 1 ;
68120 }
69121 }
70122}
@@ -126,7 +178,7 @@ Future<Uri> findImpellerC() async {
126178 // ignore: do_not_use_environment
127179 const impellercEnvVar = String .fromEnvironment ('IMPELLERC' );
128180 if (impellercEnvVar != '' ) {
129- if (! doesFileExist (impellercEnvVar)) {
181+ if (! File (impellercEnvVar). existsSync ( )) {
130182 throw Exception (
131183 'IMPELLERC environment variable is set, '
132184 "but it doesn't point to a valid file!" ,
@@ -147,7 +199,7 @@ Future<Uri> findImpellerC() async {
147199 final tried = < Uri > [];
148200 for (final variant in _impellercLocations) {
149201 final impellercPath = engineArtifactsDir.resolve (variant);
150- if (doesFileExist (impellercPath.toFilePath ())) {
202+ if (File (impellercPath.toFilePath ()). existsSync ( )) {
151203 found = impellercPath;
152204 break ;
153205 }
@@ -161,7 +213,3 @@ Future<Uri> findImpellerC() async {
161213
162214 return found;
163215}
164-
165- bool doesFileExist (String path) {
166- return File (path).existsSync ();
167- }
0 commit comments