Unverified Commit 4f19a9fa authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] Add support for compiling shaders to JSON bundle for web (#114295)

parent 37beffe2
...@@ -751,7 +751,8 @@ class CachedLocalEngineArtifacts implements LocalEngineArtifacts { ...@@ -751,7 +751,8 @@ class CachedLocalEngineArtifacts implements LocalEngineArtifacts {
_cache = cache, _cache = cache,
_processManager = processManager, _processManager = processManager,
_platform = platform, _platform = platform,
_operatingSystemUtils = operatingSystemUtils; _operatingSystemUtils = operatingSystemUtils,
_backupCache = CachedArtifacts(fileSystem: fileSystem, platform: platform, cache: cache, operatingSystemUtils: operatingSystemUtils);
@override @override
final String engineOutPath; final String engineOutPath;
...@@ -765,7 +766,7 @@ class CachedLocalEngineArtifacts implements LocalEngineArtifacts { ...@@ -765,7 +766,7 @@ class CachedLocalEngineArtifacts implements LocalEngineArtifacts {
final ProcessManager _processManager; final ProcessManager _processManager;
final Platform _platform; final Platform _platform;
final OperatingSystemUtils _operatingSystemUtils; final OperatingSystemUtils _operatingSystemUtils;
final CachedArtifacts _backupCache;
@override @override
FileSystemEntity getHostArtifact(HostArtifact artifact) { FileSystemEntity getHostArtifact(HostArtifact artifact) {
...@@ -838,7 +839,11 @@ class CachedLocalEngineArtifacts implements LocalEngineArtifacts { ...@@ -838,7 +839,11 @@ class CachedLocalEngineArtifacts implements LocalEngineArtifacts {
case HostArtifact.impellerc: case HostArtifact.impellerc:
case HostArtifact.libtessellator: case HostArtifact.libtessellator:
final String artifactFileName = _hostArtifactToFileName(artifact, _platform); final String artifactFileName = _hostArtifactToFileName(artifact, _platform);
return _fileSystem.file(_fileSystem.path.join(_hostEngineOutPath, artifactFileName)); final File file = _fileSystem.file(_fileSystem.path.join(_hostEngineOutPath, artifactFileName));
if (!file.existsSync()) {
return _backupCache.getHostArtifact(artifact);
}
return file;
} }
} }
......
...@@ -409,11 +409,10 @@ class ManifestAssetBundle implements AssetBundle { ...@@ -409,11 +409,10 @@ class ManifestAssetBundle implements AssetBundle {
final List<_Asset> materialAssets = <_Asset>[ final List<_Asset> materialAssets = <_Asset>[
if (flutterManifest.usesMaterialDesign) if (flutterManifest.usesMaterialDesign)
..._getMaterialFonts(), ..._getMaterialFonts(),
// For non-web platforms, include the shaders unconditionally. They are // For all platforms, include the shaders unconditionally. They are
// small, and whether they're used is determined only by the app source // small, and whether they're used is determined only by the app source
// code and not by the Flutter manifest. // code and not by the Flutter manifest.
if (targetPlatform != TargetPlatform.web_javascript) ..._getMaterialShaders(),
..._getMaterialShaders(),
]; ];
for (final _Asset asset in materialAssets) { for (final _Asset asset in materialAssets) {
final File assetFile = asset.lookupAssetFile(_fileSystem); final File assetFile = asset.lookupAssetFile(_fileSystem);
...@@ -754,23 +753,19 @@ class ManifestAssetBundle implements AssetBundle { ...@@ -754,23 +753,19 @@ class ManifestAssetBundle implements AssetBundle {
} }
} }
// No shader compilation for the web. for (final Uri shaderUri in flutterManifest.shaders) {
if (targetPlatform != TargetPlatform.web_javascript) { _parseAssetFromFile(
for (final Uri shaderUri in flutterManifest.shaders) { packageConfig,
_parseAssetFromFile( flutterManifest,
packageConfig, assetBase,
flutterManifest, cache,
assetBase, result,
cache, shaderUri,
result, packageName: packageName,
shaderUri, attributedPackage: attributedPackage,
packageName: packageName, assetKind: AssetKind.shader,
attributedPackage: attributedPackage, );
assetKind: AssetKind.shader,
);
}
} }
// Add assets referenced in the fonts section of the manifest. // Add assets referenced in the fonts section of the manifest.
for (final Font font in flutterManifest.fonts) { for (final Font font in flutterManifest.fonts) {
for (final FontAsset fontAsset in font.fontAssets) { for (final FontAsset fontAsset in font.fontAssets) {
......
...@@ -128,6 +128,7 @@ Future<Depfile> copyAssets( ...@@ -128,6 +128,7 @@ Future<Depfile> copyAssets(
input: content.file as File, input: content.file as File,
outputPath: file.path, outputPath: file.path,
target: shaderTarget, target: shaderTarget,
json: targetPlatform == TargetPlatform.web_javascript,
); );
break; break;
} }
......
...@@ -47,6 +47,7 @@ class DevelopmentShaderCompiler { ...@@ -47,6 +47,7 @@ class DevelopmentShaderCompiler {
late ShaderTarget _shaderTarget; late ShaderTarget _shaderTarget;
bool _debugConfigured = false; bool _debugConfigured = false;
bool _jsonMode = false;
/// Configure the output format of the shader compiler for a particular /// Configure the output format of the shader compiler for a particular
/// flutter device. /// flutter device.
...@@ -69,9 +70,13 @@ class DevelopmentShaderCompiler { ...@@ -69,9 +70,13 @@ class DevelopmentShaderCompiler {
case TargetPlatform.fuchsia_arm64: case TargetPlatform.fuchsia_arm64:
case TargetPlatform.fuchsia_x64: case TargetPlatform.fuchsia_x64:
case TargetPlatform.tester: case TargetPlatform.tester:
assert(!enableImpeller);
_shaderTarget = ShaderTarget.sksl;
break;
case TargetPlatform.web_javascript: case TargetPlatform.web_javascript:
assert(!enableImpeller); assert(!enableImpeller);
_shaderTarget = ShaderTarget.sksl; _shaderTarget = ShaderTarget.sksl;
_jsonMode = true;
break; break;
case null: case null:
return; return;
...@@ -102,6 +107,7 @@ class DevelopmentShaderCompiler { ...@@ -102,6 +107,7 @@ class DevelopmentShaderCompiler {
outputPath: output.path, outputPath: output.path,
target: _shaderTarget, target: _shaderTarget,
fatal: false, fatal: false,
json: _jsonMode,
); );
if (!success) { if (!success) {
return null; return null;
...@@ -157,6 +163,7 @@ class ShaderCompiler { ...@@ -157,6 +163,7 @@ class ShaderCompiler {
required String outputPath, required String outputPath,
required ShaderTarget target, required ShaderTarget target,
bool fatal = true, bool fatal = true,
required bool json,
}) async { }) async {
final File impellerc = _fs.file( final File impellerc = _fs.file(
_artifacts.getHostArtifact(HostArtifact.impellerc), _artifacts.getHostArtifact(HostArtifact.impellerc),
...@@ -172,6 +179,8 @@ class ShaderCompiler { ...@@ -172,6 +179,8 @@ class ShaderCompiler {
impellerc.path, impellerc.path,
target.target, target.target,
'--iplr', '--iplr',
if (json)
'--json',
'--sl=$outputPath', '--sl=$outputPath',
'--spirv=$outputPath.spirv', '--spirv=$outputPath.spirv',
'--input=${input.path}', '--input=${input.path}',
......
...@@ -134,9 +134,10 @@ Future<AssetBundle?> buildAssets({ ...@@ -134,9 +134,10 @@ Future<AssetBundle?> buildAssets({
Future<void> writeBundle( Future<void> writeBundle(
Directory bundleDir, Directory bundleDir,
Map<String, DevFSContent> assetEntries, Map<String, DevFSContent> assetEntries,
Map<String, AssetKind> entryKinds, Map<String, AssetKind> entryKinds, {
{ Logger? loggerOverride } Logger? loggerOverride,
) async { required TargetPlatform targetPlatform,
}) async {
loggerOverride ??= globals.logger; loggerOverride ??= globals.logger;
if (bundleDir.existsSync()) { if (bundleDir.existsSync()) {
try { try {
...@@ -185,6 +186,7 @@ Future<void> writeBundle( ...@@ -185,6 +186,7 @@ Future<void> writeBundle(
input: input, input: input,
outputPath: file.path, outputPath: file.path,
target: ShaderTarget.sksl, // TODO(zanderso): configure impeller target when enabled. target: ShaderTarget.sksl, // TODO(zanderso): configure impeller target when enabled.
json: targetPlatform == TargetPlatform.web_javascript,
); );
break; break;
} }
......
...@@ -502,8 +502,12 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts { ...@@ -502,8 +502,12 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
throwToolExit('Error: Failed to build asset bundle'); throwToolExit('Error: Failed to build asset bundle');
} }
if (_needRebuild(assetBundle.entries)) { if (_needRebuild(assetBundle.entries)) {
await writeBundle(globals.fs.directory(globals.fs.path.join('build', 'unit_test_assets')), await writeBundle(
assetBundle.entries, assetBundle.entryKinds); globals.fs.directory(globals.fs.path.join('build', 'unit_test_assets')),
assetBundle.entries,
assetBundle.entryKinds,
targetPlatform: TargetPlatform.tester,
);
} }
} }
......
...@@ -868,6 +868,7 @@ class WebDevFS implements DevFS { ...@@ -868,6 +868,7 @@ class WebDevFS implements DevFS {
globals.fs.directory(getAssetBuildDirectory()), globals.fs.directory(getAssetBuildDirectory()),
bundle.entries, bundle.entries,
bundle.entryKinds, bundle.entryKinds,
targetPlatform: TargetPlatform.web_javascript,
); );
} }
} }
......
...@@ -322,6 +322,13 @@ void main() { ...@@ -322,6 +322,13 @@ void main() {
fileSystem.path.join('/out', 'host_debug_unopt', 'dart-sdk', 'bin', fileSystem.path.join('/out', 'host_debug_unopt', 'dart-sdk', 'bin',
'snapshots', 'frontend_server.dart.snapshot') 'snapshots', 'frontend_server.dart.snapshot')
); );
fileSystem.file(fileSystem.path.join('/out', 'host_debug_unopt', 'impellerc'))
.createSync(recursive: true);
fileSystem.file(fileSystem.path.join('/out', 'host_debug_unopt', 'libtessellator.so'))
.createSync(recursive: true);
expect( expect(
artifacts.getHostArtifact(HostArtifact.impellerc).path, artifacts.getHostArtifact(HostArtifact.impellerc).path,
fileSystem.path.join('/out', 'host_debug_unopt', 'impellerc'), fileSystem.path.join('/out', 'host_debug_unopt', 'impellerc'),
...@@ -332,6 +339,17 @@ void main() { ...@@ -332,6 +339,17 @@ void main() {
); );
}); });
testWithoutContext('falls back to bundled impeller artifacts if the files do not exist in the local engine', () {
expect(
artifacts.getHostArtifact(HostArtifact.impellerc).path,
fileSystem.path.join('root', 'bin', 'cache', 'artifacts', 'engine', 'linux-x64', 'impellerc'),
);
expect(
artifacts.getHostArtifact(HostArtifact.libtessellator).path,
fileSystem.path.join('root', 'bin', 'cache', 'artifacts', 'engine', 'linux-x64', 'libtessellator.so'),
);
});
testWithoutContext('falls back to prebuilt dart sdk', () { testWithoutContext('falls back to prebuilt dart sdk', () {
final String failureMessage = 'Unable to find a built dart sdk at:' final String failureMessage = 'Unable to find a built dart sdk at:'
' "${fileSystem.path.join('/out', 'host_debug_unopt', 'dart-sdk')}"' ' "${fileSystem.path.join('/out', 'host_debug_unopt', 'dart-sdk')}"'
......
...@@ -313,6 +313,7 @@ flutter: ...@@ -313,6 +313,7 @@ flutter:
<String, DevFSContent>{}, <String, DevFSContent>{},
<String, AssetKind>{}, <String, AssetKind>{},
loggerOverride: testLogger, loggerOverride: testLogger,
targetPlatform: TargetPlatform.android,
); );
expect(testLogger.warningText, contains('Expected Error Text')); expect(testLogger.warningText, contains('Expected Error Text'));
...@@ -434,6 +435,7 @@ flutter: ...@@ -434,6 +435,7 @@ flutter:
bundle.entries, bundle.entries,
bundle.entryKinds, bundle.entryKinds,
loggerOverride: testLogger, loggerOverride: testLogger,
targetPlatform: TargetPlatform.android,
); );
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
...@@ -459,7 +461,7 @@ flutter: ...@@ -459,7 +461,7 @@ flutter:
]), ]),
}); });
testUsingContext('Included shaders are not compiled for the web', () async { testUsingContext('Included shaders are compiled for the web', () async {
fileSystem.file('.packages').createSync(); fileSystem.file('.packages').createSync();
fileSystem.file('pubspec.yaml') fileSystem.file('pubspec.yaml')
..createSync() ..createSync()
...@@ -478,18 +480,34 @@ flutter: ...@@ -478,18 +480,34 @@ flutter:
bundle.entries, bundle.entries,
bundle.entryKinds, bundle.entryKinds,
loggerOverride: testLogger, loggerOverride: testLogger,
targetPlatform: TargetPlatform.web_javascript,
); );
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Artifacts: () => artifacts, Artifacts: () => artifacts,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[ ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
// No impeller commands are expected here because shader compilation is FakeCommand(
// not supposed to happen for the web. command: <String>[
impellerc,
'--sksl',
'--iplr',
'--json',
'--sl=$outputPath',
'--spirv=$outputPath.spirv',
'--input=/$shaderPath',
'--input-type=frag',
'--include=/$assetsPath',
],
onRun: () {
fileSystem.file(outputPath).createSync(recursive: true);
fileSystem.file('$outputPath.spirv').createSync(recursive: true);
},
),
]), ]),
}); });
testUsingContext('Material shaders are not compiled for the web', () async { testUsingContext('Material shaders are compiled for the web', () async {
fileSystem.file('.packages').createSync(); fileSystem.file('.packages').createSync();
final String materialIconsPath = fileSystem.path.join( final String materialIconsPath = fileSystem.path.join(
...@@ -508,6 +526,25 @@ flutter: ...@@ -508,6 +526,25 @@ flutter:
materialDir.childFile(shader).createSync(recursive: true); materialDir.childFile(shader).createSync(recursive: true);
} }
(globals.processManager as FakeProcessManager)
.addCommand(FakeCommand(
command: <String>[
impellerc,
'--sksl',
'--iplr',
'--json',
'--sl=${fileSystem.path.join(output.path, 'shaders', 'ink_sparkle.frag')}',
'--spirv=${fileSystem.path.join(output.path, 'shaders', 'ink_sparkle.frag.spirv')}',
'--input=${fileSystem.path.join(materialDir.path, 'shaders', 'ink_sparkle.frag')}',
'--input-type=frag',
'--include=${fileSystem.path.join(materialDir.path, 'shaders')}',
],
onRun: () {
fileSystem.file(outputPath).createSync(recursive: true);
fileSystem.file('$outputPath.spirv').createSync(recursive: true);
},
));
fileSystem.file('pubspec.yaml') fileSystem.file('pubspec.yaml')
..createSync() ..createSync()
..writeAsStringSync(r''' ..writeAsStringSync(r'''
...@@ -524,15 +561,13 @@ flutter: ...@@ -524,15 +561,13 @@ flutter:
bundle.entries, bundle.entries,
bundle.entryKinds, bundle.entryKinds,
loggerOverride: testLogger, loggerOverride: testLogger,
targetPlatform: TargetPlatform.web_javascript,
); );
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Artifacts: () => artifacts, Artifacts: () => artifacts,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[ ProcessManager: () => FakeProcessManager.list(<FakeCommand>[]),
// No impeller commands are expected here because shader compilation is
// not supposed to happen for the web.
]),
}); });
}); });
......
...@@ -69,6 +69,7 @@ void main() { ...@@ -69,6 +69,7 @@ void main() {
input: fileSystem.file(fragPath), input: fileSystem.file(fragPath),
outputPath: outputPath, outputPath: outputPath,
target: ShaderTarget.sksl, target: ShaderTarget.sksl,
json: false,
), ),
true, true,
); );
...@@ -106,6 +107,7 @@ void main() { ...@@ -106,6 +107,7 @@ void main() {
input: fileSystem.file(fragPath), input: fileSystem.file(fragPath),
outputPath: outputPath, outputPath: outputPath,
target: ShaderTarget.impelleriOS, target: ShaderTarget.impelleriOS,
json: false,
), ),
true, true,
); );
...@@ -142,6 +144,7 @@ void main() { ...@@ -142,6 +144,7 @@ void main() {
input: fileSystem.file(fragPath), input: fileSystem.file(fragPath),
outputPath: outputPath, outputPath: outputPath,
target: ShaderTarget.impellerAndroid, target: ShaderTarget.impellerAndroid,
json: false,
), ),
true, true,
); );
...@@ -179,6 +182,7 @@ void main() { ...@@ -179,6 +182,7 @@ void main() {
input: fileSystem.file(notFragPath), input: fileSystem.file(notFragPath),
outputPath: outputPath, outputPath: outputPath,
target: ShaderTarget.sksl, target: ShaderTarget.sksl,
json: false,
), ),
true, true,
); );
...@@ -216,6 +220,7 @@ void main() { ...@@ -216,6 +220,7 @@ void main() {
input: fileSystem.file(notFragPath), input: fileSystem.file(notFragPath),
outputPath: outputPath, outputPath: outputPath,
target: ShaderTarget.sksl, target: ShaderTarget.sksl,
json: false,
); );
fail('unreachable'); fail('unreachable');
} on ShaderCompilerException catch (e) { } on ShaderCompilerException catch (e) {
...@@ -313,4 +318,49 @@ void main() { ...@@ -313,4 +318,49 @@ void main() {
expect(fileSystem.file('/.tmp_rand0/0.8255140718871702.temp.spirv'), isNot(exists)); expect(fileSystem.file('/.tmp_rand0/0.8255140718871702.temp.spirv'), isNot(exists));
expect(fileSystem.file('/.tmp_rand0/0.8255140718871702.temp'), isNot(exists)); expect(fileSystem.file('/.tmp_rand0/0.8255140718871702.temp'), isNot(exists));
}); });
testWithoutContext('DevelopmentShaderCompiler can compile JSON for web targts', () async {
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
FakeCommand(
command: <String>[
impellerc,
'--sksl',
'--iplr',
'--json',
'--sl=/.tmp_rand0/0.8255140718871702.temp',
'--spirv=/.tmp_rand0/0.8255140718871702.temp.spirv',
'--input=$fragPath',
'--input-type=frag',
'--include=$fragDir',
],
onRun: () {
fileSystem.file('/.tmp_rand0/0.8255140718871702.temp.spirv').createSync();
fileSystem.file('/.tmp_rand0/0.8255140718871702.temp')
..createSync()
..writeAsBytesSync(<int>[1, 2, 3, 4]);
}
),
]);
fileSystem.file(fragPath).writeAsBytesSync(<int>[1, 2, 3, 4]);
final ShaderCompiler shaderCompiler = ShaderCompiler(
processManager: processManager,
logger: logger,
fileSystem: fileSystem,
artifacts: artifacts,
);
final DevelopmentShaderCompiler developmentShaderCompiler = DevelopmentShaderCompiler(
shaderCompiler: shaderCompiler,
fileSystem: fileSystem,
random: math.Random(0),
);
developmentShaderCompiler.configureCompiler(TargetPlatform.web_javascript, enableImpeller: false);
final DevFSContent? content = await developmentShaderCompiler
.recompileShader(DevFSFileContent(fileSystem.file(fragPath)));
expect(await content!.contentsAsBytes(), <int>[1, 2, 3, 4]);
expect(fileSystem.file('/.tmp_rand0/0.8255140718871702.temp.spirv'), isNot(exists));
expect(fileSystem.file('/.tmp_rand0/0.8255140718871702.temp'), isNot(exists));
});
} }
...@@ -43,6 +43,7 @@ void main() { ...@@ -43,6 +43,7 @@ void main() {
input: globals.fs.file(inkSparklePath), input: globals.fs.file(inkSparklePath),
outputPath: inkSparkleOutputPath, outputPath: inkSparkleOutputPath,
target: ShaderTarget.sksl, target: ShaderTarget.sksl,
json: false,
); );
final File resultFile = globals.fs.file(inkSparkleOutputPath); final File resultFile = globals.fs.file(inkSparkleOutputPath);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment