Skip to content

Commit 4f19a9f

Browse files
author
Jonah Williams
authored
[flutter_tools] Add support for compiling shaders to JSON bundle for web (#114295)
1 parent 37beffe commit 4f19a9f

File tree

11 files changed

+156
-35
lines changed

11 files changed

+156
-35
lines changed

packages/flutter_tools/lib/src/artifacts.dart

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,8 @@ class CachedLocalEngineArtifacts implements LocalEngineArtifacts {
751751
_cache = cache,
752752
_processManager = processManager,
753753
_platform = platform,
754-
_operatingSystemUtils = operatingSystemUtils;
754+
_operatingSystemUtils = operatingSystemUtils,
755+
_backupCache = CachedArtifacts(fileSystem: fileSystem, platform: platform, cache: cache, operatingSystemUtils: operatingSystemUtils);
755756

756757
@override
757758
final String engineOutPath;
@@ -765,7 +766,7 @@ class CachedLocalEngineArtifacts implements LocalEngineArtifacts {
765766
final ProcessManager _processManager;
766767
final Platform _platform;
767768
final OperatingSystemUtils _operatingSystemUtils;
768-
769+
final CachedArtifacts _backupCache;
769770

770771
@override
771772
FileSystemEntity getHostArtifact(HostArtifact artifact) {
@@ -838,7 +839,11 @@ class CachedLocalEngineArtifacts implements LocalEngineArtifacts {
838839
case HostArtifact.impellerc:
839840
case HostArtifact.libtessellator:
840841
final String artifactFileName = _hostArtifactToFileName(artifact, _platform);
841-
return _fileSystem.file(_fileSystem.path.join(_hostEngineOutPath, artifactFileName));
842+
final File file = _fileSystem.file(_fileSystem.path.join(_hostEngineOutPath, artifactFileName));
843+
if (!file.existsSync()) {
844+
return _backupCache.getHostArtifact(artifact);
845+
}
846+
return file;
842847
}
843848
}
844849

packages/flutter_tools/lib/src/asset.dart

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -409,11 +409,10 @@ class ManifestAssetBundle implements AssetBundle {
409409
final List<_Asset> materialAssets = <_Asset>[
410410
if (flutterManifest.usesMaterialDesign)
411411
..._getMaterialFonts(),
412-
// For non-web platforms, include the shaders unconditionally. They are
412+
// For all platforms, include the shaders unconditionally. They are
413413
// small, and whether they're used is determined only by the app source
414414
// code and not by the Flutter manifest.
415-
if (targetPlatform != TargetPlatform.web_javascript)
416-
..._getMaterialShaders(),
415+
..._getMaterialShaders(),
417416
];
418417
for (final _Asset asset in materialAssets) {
419418
final File assetFile = asset.lookupAssetFile(_fileSystem);
@@ -754,23 +753,19 @@ class ManifestAssetBundle implements AssetBundle {
754753
}
755754
}
756755

757-
// No shader compilation for the web.
758-
if (targetPlatform != TargetPlatform.web_javascript) {
759-
for (final Uri shaderUri in flutterManifest.shaders) {
760-
_parseAssetFromFile(
761-
packageConfig,
762-
flutterManifest,
763-
assetBase,
764-
cache,
765-
result,
766-
shaderUri,
767-
packageName: packageName,
768-
attributedPackage: attributedPackage,
769-
assetKind: AssetKind.shader,
770-
);
771-
}
756+
for (final Uri shaderUri in flutterManifest.shaders) {
757+
_parseAssetFromFile(
758+
packageConfig,
759+
flutterManifest,
760+
assetBase,
761+
cache,
762+
result,
763+
shaderUri,
764+
packageName: packageName,
765+
attributedPackage: attributedPackage,
766+
assetKind: AssetKind.shader,
767+
);
772768
}
773-
774769
// Add assets referenced in the fonts section of the manifest.
775770
for (final Font font in flutterManifest.fonts) {
776771
for (final FontAsset fontAsset in font.fontAssets) {

packages/flutter_tools/lib/src/build_system/targets/assets.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ Future<Depfile> copyAssets(
128128
input: content.file as File,
129129
outputPath: file.path,
130130
target: shaderTarget,
131+
json: targetPlatform == TargetPlatform.web_javascript,
131132
);
132133
break;
133134
}

packages/flutter_tools/lib/src/build_system/targets/shader_compiler.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class DevelopmentShaderCompiler {
4747

4848
late ShaderTarget _shaderTarget;
4949
bool _debugConfigured = false;
50+
bool _jsonMode = false;
5051

5152
/// Configure the output format of the shader compiler for a particular
5253
/// flutter device.
@@ -69,9 +70,13 @@ class DevelopmentShaderCompiler {
6970
case TargetPlatform.fuchsia_arm64:
7071
case TargetPlatform.fuchsia_x64:
7172
case TargetPlatform.tester:
73+
assert(!enableImpeller);
74+
_shaderTarget = ShaderTarget.sksl;
75+
break;
7276
case TargetPlatform.web_javascript:
7377
assert(!enableImpeller);
7478
_shaderTarget = ShaderTarget.sksl;
79+
_jsonMode = true;
7580
break;
7681
case null:
7782
return;
@@ -102,6 +107,7 @@ class DevelopmentShaderCompiler {
102107
outputPath: output.path,
103108
target: _shaderTarget,
104109
fatal: false,
110+
json: _jsonMode,
105111
);
106112
if (!success) {
107113
return null;
@@ -157,6 +163,7 @@ class ShaderCompiler {
157163
required String outputPath,
158164
required ShaderTarget target,
159165
bool fatal = true,
166+
required bool json,
160167
}) async {
161168
final File impellerc = _fs.file(
162169
_artifacts.getHostArtifact(HostArtifact.impellerc),
@@ -172,6 +179,8 @@ class ShaderCompiler {
172179
impellerc.path,
173180
target.target,
174181
'--iplr',
182+
if (json)
183+
'--json',
175184
'--sl=$outputPath',
176185
'--spirv=$outputPath.spirv',
177186
'--input=${input.path}',

packages/flutter_tools/lib/src/bundle_builder.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,10 @@ Future<AssetBundle?> buildAssets({
134134
Future<void> writeBundle(
135135
Directory bundleDir,
136136
Map<String, DevFSContent> assetEntries,
137-
Map<String, AssetKind> entryKinds,
138-
{ Logger? loggerOverride }
139-
) async {
137+
Map<String, AssetKind> entryKinds, {
138+
Logger? loggerOverride,
139+
required TargetPlatform targetPlatform,
140+
}) async {
140141
loggerOverride ??= globals.logger;
141142
if (bundleDir.existsSync()) {
142143
try {
@@ -185,6 +186,7 @@ Future<void> writeBundle(
185186
input: input,
186187
outputPath: file.path,
187188
target: ShaderTarget.sksl, // TODO(zanderso): configure impeller target when enabled.
189+
json: targetPlatform == TargetPlatform.web_javascript,
188190
);
189191
break;
190192
}

packages/flutter_tools/lib/src/commands/test.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -502,8 +502,12 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
502502
throwToolExit('Error: Failed to build asset bundle');
503503
}
504504
if (_needRebuild(assetBundle.entries)) {
505-
await writeBundle(globals.fs.directory(globals.fs.path.join('build', 'unit_test_assets')),
506-
assetBundle.entries, assetBundle.entryKinds);
505+
await writeBundle(
506+
globals.fs.directory(globals.fs.path.join('build', 'unit_test_assets')),
507+
assetBundle.entries,
508+
assetBundle.entryKinds,
509+
targetPlatform: TargetPlatform.tester,
510+
);
507511
}
508512
}
509513

packages/flutter_tools/lib/src/isolated/devfs_web.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,7 @@ class WebDevFS implements DevFS {
868868
globals.fs.directory(getAssetBuildDirectory()),
869869
bundle.entries,
870870
bundle.entryKinds,
871+
targetPlatform: TargetPlatform.web_javascript,
871872
);
872873
}
873874
}

packages/flutter_tools/test/general.shard/artifacts_test.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,13 @@ void main() {
322322
fileSystem.path.join('/out', 'host_debug_unopt', 'dart-sdk', 'bin',
323323
'snapshots', 'frontend_server.dart.snapshot')
324324
);
325+
326+
327+
fileSystem.file(fileSystem.path.join('/out', 'host_debug_unopt', 'impellerc'))
328+
.createSync(recursive: true);
329+
fileSystem.file(fileSystem.path.join('/out', 'host_debug_unopt', 'libtessellator.so'))
330+
.createSync(recursive: true);
331+
325332
expect(
326333
artifacts.getHostArtifact(HostArtifact.impellerc).path,
327334
fileSystem.path.join('/out', 'host_debug_unopt', 'impellerc'),
@@ -332,6 +339,17 @@ void main() {
332339
);
333340
});
334341

342+
testWithoutContext('falls back to bundled impeller artifacts if the files do not exist in the local engine', () {
343+
expect(
344+
artifacts.getHostArtifact(HostArtifact.impellerc).path,
345+
fileSystem.path.join('root', 'bin', 'cache', 'artifacts', 'engine', 'linux-x64', 'impellerc'),
346+
);
347+
expect(
348+
artifacts.getHostArtifact(HostArtifact.libtessellator).path,
349+
fileSystem.path.join('root', 'bin', 'cache', 'artifacts', 'engine', 'linux-x64', 'libtessellator.so'),
350+
);
351+
});
352+
335353
testWithoutContext('falls back to prebuilt dart sdk', () {
336354
final String failureMessage = 'Unable to find a built dart sdk at:'
337355
' "${fileSystem.path.join('/out', 'host_debug_unopt', 'dart-sdk')}"'

packages/flutter_tools/test/general.shard/asset_bundle_test.dart

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ flutter:
313313
<String, DevFSContent>{},
314314
<String, AssetKind>{},
315315
loggerOverride: testLogger,
316+
targetPlatform: TargetPlatform.android,
316317
);
317318

318319
expect(testLogger.warningText, contains('Expected Error Text'));
@@ -434,6 +435,7 @@ flutter:
434435
bundle.entries,
435436
bundle.entryKinds,
436437
loggerOverride: testLogger,
438+
targetPlatform: TargetPlatform.android,
437439
);
438440

439441
}, overrides: <Type, Generator>{
@@ -459,7 +461,7 @@ flutter:
459461
]),
460462
});
461463

462-
testUsingContext('Included shaders are not compiled for the web', () async {
464+
testUsingContext('Included shaders are compiled for the web', () async {
463465
fileSystem.file('.packages').createSync();
464466
fileSystem.file('pubspec.yaml')
465467
..createSync()
@@ -478,18 +480,34 @@ flutter:
478480
bundle.entries,
479481
bundle.entryKinds,
480482
loggerOverride: testLogger,
483+
targetPlatform: TargetPlatform.web_javascript,
481484
);
482485

483486
}, overrides: <Type, Generator>{
484487
Artifacts: () => artifacts,
485488
FileSystem: () => fileSystem,
486489
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
487-
// No impeller commands are expected here because shader compilation is
488-
// not supposed to happen for the web.
490+
FakeCommand(
491+
command: <String>[
492+
impellerc,
493+
'--sksl',
494+
'--iplr',
495+
'--json',
496+
'--sl=$outputPath',
497+
'--spirv=$outputPath.spirv',
498+
'--input=/$shaderPath',
499+
'--input-type=frag',
500+
'--include=/$assetsPath',
501+
],
502+
onRun: () {
503+
fileSystem.file(outputPath).createSync(recursive: true);
504+
fileSystem.file('$outputPath.spirv').createSync(recursive: true);
505+
},
506+
),
489507
]),
490508
});
491509

492-
testUsingContext('Material shaders are not compiled for the web', () async {
510+
testUsingContext('Material shaders are compiled for the web', () async {
493511
fileSystem.file('.packages').createSync();
494512

495513
final String materialIconsPath = fileSystem.path.join(
@@ -508,6 +526,25 @@ flutter:
508526
materialDir.childFile(shader).createSync(recursive: true);
509527
}
510528

529+
(globals.processManager as FakeProcessManager)
530+
.addCommand(FakeCommand(
531+
command: <String>[
532+
impellerc,
533+
'--sksl',
534+
'--iplr',
535+
'--json',
536+
'--sl=${fileSystem.path.join(output.path, 'shaders', 'ink_sparkle.frag')}',
537+
'--spirv=${fileSystem.path.join(output.path, 'shaders', 'ink_sparkle.frag.spirv')}',
538+
'--input=${fileSystem.path.join(materialDir.path, 'shaders', 'ink_sparkle.frag')}',
539+
'--input-type=frag',
540+
'--include=${fileSystem.path.join(materialDir.path, 'shaders')}',
541+
],
542+
onRun: () {
543+
fileSystem.file(outputPath).createSync(recursive: true);
544+
fileSystem.file('$outputPath.spirv').createSync(recursive: true);
545+
},
546+
));
547+
511548
fileSystem.file('pubspec.yaml')
512549
..createSync()
513550
..writeAsStringSync(r'''
@@ -524,15 +561,13 @@ flutter:
524561
bundle.entries,
525562
bundle.entryKinds,
526563
loggerOverride: testLogger,
564+
targetPlatform: TargetPlatform.web_javascript,
527565
);
528566

529567
}, overrides: <Type, Generator>{
530568
Artifacts: () => artifacts,
531569
FileSystem: () => fileSystem,
532-
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
533-
// No impeller commands are expected here because shader compilation is
534-
// not supposed to happen for the web.
535-
]),
570+
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[]),
536571
});
537572
});
538573

0 commit comments

Comments
 (0)