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 {
_cache = cache,
_processManager = processManager,
_platform = platform,
_operatingSystemUtils = operatingSystemUtils;
_operatingSystemUtils = operatingSystemUtils,
_backupCache = CachedArtifacts(fileSystem: fileSystem, platform: platform, cache: cache, operatingSystemUtils: operatingSystemUtils);
@override
final String engineOutPath;
......@@ -765,7 +766,7 @@ class CachedLocalEngineArtifacts implements LocalEngineArtifacts {
final ProcessManager _processManager;
final Platform _platform;
final OperatingSystemUtils _operatingSystemUtils;
final CachedArtifacts _backupCache;
@override
FileSystemEntity getHostArtifact(HostArtifact artifact) {
......@@ -838,7 +839,11 @@ class CachedLocalEngineArtifacts implements LocalEngineArtifacts {
case HostArtifact.impellerc:
case HostArtifact.libtessellator:
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 {
final List<_Asset> materialAssets = <_Asset>[
if (flutterManifest.usesMaterialDesign)
..._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
// code and not by the Flutter manifest.
if (targetPlatform != TargetPlatform.web_javascript)
..._getMaterialShaders(),
..._getMaterialShaders(),
];
for (final _Asset asset in materialAssets) {
final File assetFile = asset.lookupAssetFile(_fileSystem);
......@@ -754,23 +753,19 @@ class ManifestAssetBundle implements AssetBundle {
}
}
// No shader compilation for the web.
if (targetPlatform != TargetPlatform.web_javascript) {
for (final Uri shaderUri in flutterManifest.shaders) {
_parseAssetFromFile(
packageConfig,
flutterManifest,
assetBase,
cache,
result,
shaderUri,
packageName: packageName,
attributedPackage: attributedPackage,
assetKind: AssetKind.shader,
);
}
for (final Uri shaderUri in flutterManifest.shaders) {
_parseAssetFromFile(
packageConfig,
flutterManifest,
assetBase,
cache,
result,
shaderUri,
packageName: packageName,
attributedPackage: attributedPackage,
assetKind: AssetKind.shader,
);
}
// Add assets referenced in the fonts section of the manifest.
for (final Font font in flutterManifest.fonts) {
for (final FontAsset fontAsset in font.fontAssets) {
......
......@@ -128,6 +128,7 @@ Future<Depfile> copyAssets(
input: content.file as File,
outputPath: file.path,
target: shaderTarget,
json: targetPlatform == TargetPlatform.web_javascript,
);
break;
}
......
......@@ -47,6 +47,7 @@ class DevelopmentShaderCompiler {
late ShaderTarget _shaderTarget;
bool _debugConfigured = false;
bool _jsonMode = false;
/// Configure the output format of the shader compiler for a particular
/// flutter device.
......@@ -69,9 +70,13 @@ class DevelopmentShaderCompiler {
case TargetPlatform.fuchsia_arm64:
case TargetPlatform.fuchsia_x64:
case TargetPlatform.tester:
assert(!enableImpeller);
_shaderTarget = ShaderTarget.sksl;
break;
case TargetPlatform.web_javascript:
assert(!enableImpeller);
_shaderTarget = ShaderTarget.sksl;
_jsonMode = true;
break;
case null:
return;
......@@ -102,6 +107,7 @@ class DevelopmentShaderCompiler {
outputPath: output.path,
target: _shaderTarget,
fatal: false,
json: _jsonMode,
);
if (!success) {
return null;
......@@ -157,6 +163,7 @@ class ShaderCompiler {
required String outputPath,
required ShaderTarget target,
bool fatal = true,
required bool json,
}) async {
final File impellerc = _fs.file(
_artifacts.getHostArtifact(HostArtifact.impellerc),
......@@ -172,6 +179,8 @@ class ShaderCompiler {
impellerc.path,
target.target,
'--iplr',
if (json)
'--json',
'--sl=$outputPath',
'--spirv=$outputPath.spirv',
'--input=${input.path}',
......
......@@ -134,9 +134,10 @@ Future<AssetBundle?> buildAssets({
Future<void> writeBundle(
Directory bundleDir,
Map<String, DevFSContent> assetEntries,
Map<String, AssetKind> entryKinds,
{ Logger? loggerOverride }
) async {
Map<String, AssetKind> entryKinds, {
Logger? loggerOverride,
required TargetPlatform targetPlatform,
}) async {
loggerOverride ??= globals.logger;
if (bundleDir.existsSync()) {
try {
......@@ -185,6 +186,7 @@ Future<void> writeBundle(
input: input,
outputPath: file.path,
target: ShaderTarget.sksl, // TODO(zanderso): configure impeller target when enabled.
json: targetPlatform == TargetPlatform.web_javascript,
);
break;
}
......
......@@ -502,8 +502,12 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
throwToolExit('Error: Failed to build asset bundle');
}
if (_needRebuild(assetBundle.entries)) {
await writeBundle(globals.fs.directory(globals.fs.path.join('build', 'unit_test_assets')),
assetBundle.entries, assetBundle.entryKinds);
await writeBundle(
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 {
globals.fs.directory(getAssetBuildDirectory()),
bundle.entries,
bundle.entryKinds,
targetPlatform: TargetPlatform.web_javascript,
);
}
}
......
......@@ -322,6 +322,13 @@ void main() {
fileSystem.path.join('/out', 'host_debug_unopt', 'dart-sdk', 'bin',
'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(
artifacts.getHostArtifact(HostArtifact.impellerc).path,
fileSystem.path.join('/out', 'host_debug_unopt', 'impellerc'),
......@@ -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', () {
final String failureMessage = 'Unable to find a built dart sdk at:'
' "${fileSystem.path.join('/out', 'host_debug_unopt', 'dart-sdk')}"'
......
......@@ -313,6 +313,7 @@ flutter:
<String, DevFSContent>{},
<String, AssetKind>{},
loggerOverride: testLogger,
targetPlatform: TargetPlatform.android,
);
expect(testLogger.warningText, contains('Expected Error Text'));
......@@ -434,6 +435,7 @@ flutter:
bundle.entries,
bundle.entryKinds,
loggerOverride: testLogger,
targetPlatform: TargetPlatform.android,
);
}, overrides: <Type, Generator>{
......@@ -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('pubspec.yaml')
..createSync()
......@@ -478,18 +480,34 @@ flutter:
bundle.entries,
bundle.entryKinds,
loggerOverride: testLogger,
targetPlatform: TargetPlatform.web_javascript,
);
}, overrides: <Type, Generator>{
Artifacts: () => artifacts,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
// No impeller commands are expected here because shader compilation is
// not supposed to happen for the web.
FakeCommand(
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();
final String materialIconsPath = fileSystem.path.join(
......@@ -508,6 +526,25 @@ flutter:
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')
..createSync()
..writeAsStringSync(r'''
......@@ -524,15 +561,13 @@ flutter:
bundle.entries,
bundle.entryKinds,
loggerOverride: testLogger,
targetPlatform: TargetPlatform.web_javascript,
);
}, overrides: <Type, Generator>{
Artifacts: () => artifacts,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
// No impeller commands are expected here because shader compilation is
// not supposed to happen for the web.
]),
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[]),
});
});
......
......@@ -69,6 +69,7 @@ void main() {
input: fileSystem.file(fragPath),
outputPath: outputPath,
target: ShaderTarget.sksl,
json: false,
),
true,
);
......@@ -106,6 +107,7 @@ void main() {
input: fileSystem.file(fragPath),
outputPath: outputPath,
target: ShaderTarget.impelleriOS,
json: false,
),
true,
);
......@@ -142,6 +144,7 @@ void main() {
input: fileSystem.file(fragPath),
outputPath: outputPath,
target: ShaderTarget.impellerAndroid,
json: false,
),
true,
);
......@@ -179,6 +182,7 @@ void main() {
input: fileSystem.file(notFragPath),
outputPath: outputPath,
target: ShaderTarget.sksl,
json: false,
),
true,
);
......@@ -216,6 +220,7 @@ void main() {
input: fileSystem.file(notFragPath),
outputPath: outputPath,
target: ShaderTarget.sksl,
json: false,
);
fail('unreachable');
} on ShaderCompilerException catch (e) {
......@@ -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'), 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() {
input: globals.fs.file(inkSparklePath),
outputPath: inkSparkleOutputPath,
target: ShaderTarget.sksl,
json: false,
);
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