Unverified Commit 700fe3d2 authored by Brandon DeRosier's avatar Brandon DeRosier Committed by GitHub

[Impeller Scene] Add SceneC asset importing (#118157)

parent 583a8122
...@@ -107,6 +107,8 @@ enum HostArtifact { ...@@ -107,6 +107,8 @@ enum HostArtifact {
// The Impeller shader compiler. // The Impeller shader compiler.
impellerc, impellerc,
// The Impeller Scene 3D model importer.
scenec,
// Impeller's tessellation library. // Impeller's tessellation library.
libtessellator, libtessellator,
} }
...@@ -252,6 +254,8 @@ String _hostArtifactToFileName(HostArtifact artifact, Platform platform) { ...@@ -252,6 +254,8 @@ String _hostArtifactToFileName(HostArtifact artifact, Platform platform) {
return 'dart_sdk.js.map'; return 'dart_sdk.js.map';
case HostArtifact.impellerc: case HostArtifact.impellerc:
return 'impellerc$exe'; return 'impellerc$exe';
case HostArtifact.scenec:
return 'scenec$exe';
case HostArtifact.libtessellator: case HostArtifact.libtessellator:
return 'libtessellator$dll'; return 'libtessellator$dll';
} }
...@@ -432,6 +436,7 @@ class CachedArtifacts implements Artifacts { ...@@ -432,6 +436,7 @@ class CachedArtifacts implements Artifacts {
final String artifactFileName = _hostArtifactToFileName(artifact, _platform); final String artifactFileName = _hostArtifactToFileName(artifact, _platform);
return _cache.getArtifactDirectory('usbmuxd').childFile(artifactFileName); return _cache.getArtifactDirectory('usbmuxd').childFile(artifactFileName);
case HostArtifact.impellerc: case HostArtifact.impellerc:
case HostArtifact.scenec:
case HostArtifact.libtessellator: case HostArtifact.libtessellator:
final String artifactFileName = _hostArtifactToFileName(artifact, _platform); final String artifactFileName = _hostArtifactToFileName(artifact, _platform);
final String engineDir = _getEngineArtifactsPath(_currentHostPlatform(_platform, _operatingSystemUtils))!; final String engineDir = _getEngineArtifactsPath(_currentHostPlatform(_platform, _operatingSystemUtils))!;
...@@ -866,6 +871,7 @@ class CachedLocalEngineArtifacts implements Artifacts { ...@@ -866,6 +871,7 @@ class CachedLocalEngineArtifacts implements Artifacts {
final String artifactFileName = _hostArtifactToFileName(artifact, _platform); final String artifactFileName = _hostArtifactToFileName(artifact, _platform);
return _cache.getArtifactDirectory('usbmuxd').childFile(artifactFileName); return _cache.getArtifactDirectory('usbmuxd').childFile(artifactFileName);
case HostArtifact.impellerc: case HostArtifact.impellerc:
case HostArtifact.scenec:
case HostArtifact.libtessellator: case HostArtifact.libtessellator:
final String artifactFileName = _hostArtifactToFileName(artifact, _platform); final String artifactFileName = _hostArtifactToFileName(artifact, _platform);
final File file = _fileSystem.file(_fileSystem.path.join(_hostEngineOutPath, artifactFileName)); final File file = _fileSystem.file(_fileSystem.path.join(_hostEngineOutPath, artifactFileName));
...@@ -1151,6 +1157,7 @@ class CachedLocalWebSdkArtifacts implements Artifacts { ...@@ -1151,6 +1157,7 @@ class CachedLocalWebSdkArtifacts implements Artifacts {
case HostArtifact.iproxy: case HostArtifact.iproxy:
case HostArtifact.skyEnginePath: case HostArtifact.skyEnginePath:
case HostArtifact.impellerc: case HostArtifact.impellerc:
case HostArtifact.scenec:
case HostArtifact.libtessellator: case HostArtifact.libtessellator:
return _parent.getHostArtifact(artifact); return _parent.getHostArtifact(artifact);
} }
......
...@@ -70,6 +70,7 @@ enum AssetKind { ...@@ -70,6 +70,7 @@ enum AssetKind {
regular, regular,
font, font,
shader, shader,
model,
} }
abstract class AssetBundle { abstract class AssetBundle {
...@@ -772,6 +773,20 @@ class ManifestAssetBundle implements AssetBundle { ...@@ -772,6 +773,20 @@ class ManifestAssetBundle implements AssetBundle {
} }
} }
for (final Uri modelUri in flutterManifest.models) {
_parseAssetFromFile(
packageConfig,
flutterManifest,
assetBase,
cache,
result,
modelUri,
packageName: packageName,
attributedPackage: attributedPackage,
assetKind: AssetKind.model,
);
}
// 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) {
......
...@@ -14,6 +14,7 @@ import '../build_system.dart'; ...@@ -14,6 +14,7 @@ import '../build_system.dart';
import '../depfile.dart'; import '../depfile.dart';
import 'common.dart'; import 'common.dart';
import 'icon_tree_shaker.dart'; import 'icon_tree_shaker.dart';
import 'scene_importer.dart';
import 'shader_compiler.dart'; import 'shader_compiler.dart';
/// A helper function to copy an asset bundle into an [environment]'s output /// A helper function to copy an asset bundle into an [environment]'s output
...@@ -84,6 +85,12 @@ Future<Depfile> copyAssets( ...@@ -84,6 +85,12 @@ Future<Depfile> copyAssets(
fileSystem: environment.fileSystem, fileSystem: environment.fileSystem,
artifacts: environment.artifacts, artifacts: environment.artifacts,
); );
final SceneImporter sceneImporter = SceneImporter(
processManager: environment.processManager,
logger: environment.logger,
fileSystem: environment.fileSystem,
artifacts: environment.artifacts,
);
final Map<String, DevFSContent> assetEntries = <String, DevFSContent>{ final Map<String, DevFSContent> assetEntries = <String, DevFSContent>{
...assetBundle.entries, ...assetBundle.entries,
...@@ -131,6 +138,12 @@ Future<Depfile> copyAssets( ...@@ -131,6 +138,12 @@ Future<Depfile> copyAssets(
json: targetPlatform == TargetPlatform.web_javascript, json: targetPlatform == TargetPlatform.web_javascript,
); );
break; break;
case AssetKind.model:
doCopy = !await sceneImporter.importScene(
input: content.file as File,
outputPath: file.path,
);
break;
} }
if (doCopy) { if (doCopy) {
await (content.file as File).copy(file.path); await (content.file as File).copy(file.path);
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math' as math;
import 'dart:typed_data';
import 'package:meta/meta.dart';
import 'package:pool/pool.dart';
import 'package:process/process.dart';
import '../../artifacts.dart';
import '../../base/error_handling_io.dart';
import '../../base/file_system.dart';
import '../../base/io.dart';
import '../../base/logger.dart';
import '../../convert.dart';
import '../../devfs.dart';
import '../build_system.dart';
/// A wrapper around [SceneImporter] to support hot reload of 3D models.
class DevelopmentSceneImporter {
DevelopmentSceneImporter({
required SceneImporter sceneImporter,
required FileSystem fileSystem,
@visibleForTesting math.Random? random,
}) : _sceneImporter = sceneImporter,
_fileSystem = fileSystem,
_random = random ?? math.Random();
final SceneImporter _sceneImporter;
final FileSystem _fileSystem;
final Pool _compilationPool = Pool(4);
final math.Random _random;
/// Recompile the input ipscene and return a devfs content that should be
/// synced to the attached device in its place.
Future<DevFSContent?> reimportScene(DevFSContent inputScene) async {
final File output = _fileSystem.systemTempDirectory.childFile('${_random.nextDouble()}.temp');
late File inputFile;
bool cleanupInput = false;
Uint8List result;
PoolResource? resource;
try {
resource = await _compilationPool.request();
if (inputScene is DevFSFileContent) {
inputFile = inputScene.file as File;
} else {
inputFile = _fileSystem.systemTempDirectory.childFile('${_random.nextDouble()}.temp');
inputFile.writeAsBytesSync(await inputScene.contentsAsBytes());
cleanupInput = true;
}
final bool success = await _sceneImporter.importScene(
input: inputFile,
outputPath: output.path,
fatal: false,
);
if (!success) {
return null;
}
result = output.readAsBytesSync();
} finally {
resource?.release();
ErrorHandlingFileSystem.deleteIfExists(output);
if (cleanupInput) {
ErrorHandlingFileSystem.deleteIfExists(inputFile);
}
}
return DevFSByteContent(result);
}
}
/// A class the wraps the functionality of the Impeller Scene importer scenec.
class SceneImporter {
SceneImporter({
required ProcessManager processManager,
required Logger logger,
required FileSystem fileSystem,
required Artifacts artifacts,
}) : _processManager = processManager,
_logger = logger,
_fs = fileSystem,
_artifacts = artifacts;
final ProcessManager _processManager;
final Logger _logger;
final FileSystem _fs;
final Artifacts _artifacts;
/// The [Source] inputs that targets using this should depend on.
///
/// See [Target.inputs].
static const List<Source> inputs = <Source>[
Source.pattern(
'{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/scene_importer.dart'),
Source.hostArtifact(HostArtifact.scenec),
];
/// Calls scenec, which transforms the [input] 3D model into an imported
/// ipscene at [outputPath].
///
/// All parameters are required.
///
/// If the scene importer subprocess fails, it will print the stdout and
/// stderr to the log and throw a [SceneImporterException]. Otherwise, it
/// will return true.
Future<bool> importScene({
required File input,
required String outputPath,
bool fatal = true,
}) async {
final File scenec = _fs.file(
_artifacts.getHostArtifact(HostArtifact.scenec),
);
if (!scenec.existsSync()) {
throw SceneImporterException._(
'The scenec utility is missing at "${scenec.path}". '
'Run "flutter doctor".',
);
}
final List<String> cmd = <String>[
scenec.path,
'--input=${input.path}',
'--output=$outputPath',
];
_logger.printTrace('scenec command: $cmd');
final Process scenecProcess = await _processManager.start(cmd);
final int code = await scenecProcess.exitCode;
if (code != 0) {
final String stdout = await utf8.decodeStream(scenecProcess.stdout);
final String stderr = await utf8.decodeStream(scenecProcess.stderr);
_logger.printTrace(stdout);
_logger.printError(stderr);
if (fatal) {
throw SceneImporterException._(
'Scene import of "${input.path}" to "$outputPath" '
'failed with exit code $code.\n'
'scenec stdout:\n$stdout\n'
'scenec stderr:\n$stderr',
);
}
return false;
}
return true;
}
}
class SceneImporterException implements Exception {
SceneImporterException._(this.message);
final String message;
@override
String toString() => 'SceneImporterException: $message\n\n';
}
...@@ -13,6 +13,7 @@ import 'build_info.dart'; ...@@ -13,6 +13,7 @@ import 'build_info.dart';
import 'build_system/build_system.dart'; import 'build_system/build_system.dart';
import 'build_system/depfile.dart'; import 'build_system/depfile.dart';
import 'build_system/targets/common.dart'; import 'build_system/targets/common.dart';
import 'build_system/targets/scene_importer.dart';
import 'build_system/targets/shader_compiler.dart'; import 'build_system/targets/shader_compiler.dart';
import 'bundle.dart'; import 'bundle.dart';
import 'cache.dart'; import 'cache.dart';
...@@ -158,6 +159,13 @@ Future<void> writeBundle( ...@@ -158,6 +159,13 @@ Future<void> writeBundle(
artifacts: globals.artifacts!, artifacts: globals.artifacts!,
); );
final SceneImporter sceneImporter = SceneImporter(
processManager: globals.processManager,
logger: globals.logger,
fileSystem: globals.fs,
artifacts: globals.artifacts!,
);
// Limit number of open files to avoid running out of file descriptors. // Limit number of open files to avoid running out of file descriptors.
final Pool pool = Pool(64); final Pool pool = Pool(64);
await Future.wait<void>( await Future.wait<void>(
...@@ -189,6 +197,12 @@ Future<void> writeBundle( ...@@ -189,6 +197,12 @@ Future<void> writeBundle(
json: targetPlatform == TargetPlatform.web_javascript, json: targetPlatform == TargetPlatform.web_javascript,
); );
break; break;
case AssetKind.model:
doCopy = !await sceneImporter.importScene(
input: input,
outputPath: file.path,
);
break;
} }
if (doCopy) { if (doCopy) {
input.copySync(file.path); input.copySync(file.path);
......
...@@ -15,6 +15,7 @@ import 'base/logger.dart'; ...@@ -15,6 +15,7 @@ import 'base/logger.dart';
import 'base/net.dart'; import 'base/net.dart';
import 'base/os.dart'; import 'base/os.dart';
import 'build_info.dart'; import 'build_info.dart';
import 'build_system/targets/scene_importer.dart';
import 'build_system/targets/shader_compiler.dart'; import 'build_system/targets/shader_compiler.dart';
import 'compile.dart'; import 'compile.dart';
import 'convert.dart' show base64, utf8; import 'convert.dart' show base64, utf8;
...@@ -483,6 +484,7 @@ class DevFS { ...@@ -483,6 +484,7 @@ class DevFS {
final Directory? rootDirectory; final Directory? rootDirectory;
final Set<String> assetPathsToEvict = <String>{}; final Set<String> assetPathsToEvict = <String>{};
final Set<String> shaderPathsToEvict = <String>{}; final Set<String> shaderPathsToEvict = <String>{};
final Set<String> scenePathsToEvict = <String>{};
// A flag to indicate whether we have called `setAssetDirectory` on the target device. // A flag to indicate whether we have called `setAssetDirectory` on the target device.
bool hasSetAssetDirectory = false; bool hasSetAssetDirectory = false;
...@@ -582,6 +584,7 @@ class DevFS { ...@@ -582,6 +584,7 @@ class DevFS {
required PackageConfig packageConfig, required PackageConfig packageConfig,
required String dillOutputPath, required String dillOutputPath,
required DevelopmentShaderCompiler shaderCompiler, required DevelopmentShaderCompiler shaderCompiler,
DevelopmentSceneImporter? sceneImporter,
DevFSWriter? devFSWriter, DevFSWriter? devFSWriter,
String? target, String? target,
AssetBundle? bundle, AssetBundle? bundle,
...@@ -600,8 +603,8 @@ class DevFS { ...@@ -600,8 +603,8 @@ class DevFS {
// Update modified files // Update modified files
final Map<Uri, DevFSContent> dirtyEntries = <Uri, DevFSContent>{}; final Map<Uri, DevFSContent> dirtyEntries = <Uri, DevFSContent>{};
final List<Future<void>> pendingShaderCompiles = <Future<void>>[]; final List<Future<void>> pendingAssetBuilds = <Future<void>>[];
bool shaderCompilationFailed = false; bool assetBuildFailed = false;
int syncedBytes = 0; int syncedBytes = 0;
if (fullRestart) { if (fullRestart) {
generator.reset(); generator.reset();
...@@ -656,26 +659,48 @@ class DevFS { ...@@ -656,26 +659,48 @@ class DevFS {
didUpdateFontManifest = true; didUpdateFontManifest = true;
} }
if (bundle.entryKinds[archivePath] == AssetKind.shader) { switch (bundle.entryKinds[archivePath]) {
final Future<DevFSContent?> pending = shaderCompiler.recompileShader(content); case AssetKind.shader:
pendingShaderCompiles.add(pending); final Future<DevFSContent?> pending = shaderCompiler.recompileShader(content);
pending.then((DevFSContent? content) { pendingAssetBuilds.add(pending);
if (content == null) { pending.then((DevFSContent? content) {
shaderCompilationFailed = true; if (content == null) {
return; assetBuildFailed = true;
return;
}
dirtyEntries[deviceUri] = content;
syncedBytes += content.size;
if (archivePath != null && !bundleFirstUpload) {
shaderPathsToEvict.add(archivePath);
}
});
break;
case AssetKind.model:
if (sceneImporter == null) {
break;
} }
final Future<DevFSContent?> pending = sceneImporter.reimportScene(content);
pendingAssetBuilds.add(pending);
pending.then((DevFSContent? content) {
if (content == null) {
assetBuildFailed = true;
return;
}
dirtyEntries[deviceUri] = content;
syncedBytes += content.size;
if (archivePath != null && !bundleFirstUpload) {
scenePathsToEvict.add(archivePath);
}
});
break;
case AssetKind.regular:
case AssetKind.font:
case null:
dirtyEntries[deviceUri] = content; dirtyEntries[deviceUri] = content;
syncedBytes += content.size; syncedBytes += content.size;
if (archivePath != null && !bundleFirstUpload) { if (archivePath != null && !bundleFirstUpload) {
shaderPathsToEvict.add(archivePath); assetPathsToEvict.add(archivePath);
} }
});
} else {
dirtyEntries[deviceUri] = content;
syncedBytes += content.size;
if (archivePath != null && !bundleFirstUpload) {
assetPathsToEvict.add(archivePath);
}
} }
}); });
...@@ -707,8 +732,8 @@ class DevFS { ...@@ -707,8 +732,8 @@ class DevFS {
_logger.printTrace('Updating files.'); _logger.printTrace('Updating files.');
final Stopwatch transferTimer = _stopwatchFactory.createStopwatch('transfer')..start(); final Stopwatch transferTimer = _stopwatchFactory.createStopwatch('transfer')..start();
await Future.wait(pendingShaderCompiles); await Future.wait(pendingAssetBuilds);
if (shaderCompilationFailed) { if (assetBuildFailed) {
return UpdateFSReport(); return UpdateFSReport();
} }
......
...@@ -13,6 +13,9 @@ import 'base/user_messages.dart'; ...@@ -13,6 +13,9 @@ import 'base/user_messages.dart';
import 'base/utils.dart'; import 'base/utils.dart';
import 'plugins.dart'; import 'plugins.dart';
/// Whether or not Impeller Scene 3D model import is enabled.
const bool kIs3dSceneSupported = true;
const Set<String> _kValidPluginPlatforms = <String>{ const Set<String> _kValidPluginPlatforms = <String>{
'android', 'ios', 'web', 'windows', 'linux', 'macos', 'android', 'ios', 'web', 'windows', 'linux', 'macos',
}; };
...@@ -370,28 +373,28 @@ class FlutterManifest { ...@@ -370,28 +373,28 @@ class FlutterManifest {
return fonts; return fonts;
} }
late final List<Uri> shaders = _extractAssetUris('shaders', 'Shader');
late final List<Uri> models = kIs3dSceneSupported ? _extractAssetUris('models', 'Model') : <Uri>[];
late final List<Uri> shaders = _extractShaders(); List<Uri> _extractAssetUris(String key, String singularName) {
if (!_flutterDescriptor.containsKey(key)) {
List<Uri> _extractShaders() {
if (!_flutterDescriptor.containsKey('shaders')) {
return <Uri>[]; return <Uri>[];
} }
final List<Object?>? shaders = _flutterDescriptor['shaders'] as List<Object?>?; final List<Object?>? items = _flutterDescriptor[key] as List<Object?>?;
if (shaders == null) { if (items == null) {
return const <Uri>[]; return const <Uri>[];
} }
final List<Uri> results = <Uri>[]; final List<Uri> results = <Uri>[];
for (final Object? shader in shaders) { for (final Object? item in items) {
if (shader is! String || shader == null || shader == '') { if (item is! String || item == null || item == '') {
_logger.printError('Shader manifest contains a null or empty uri.'); _logger.printError('$singularName manifest contains a null or empty uri.');
continue; continue;
} }
try { try {
results.add(Uri(pathSegments: shader.split('/'))); results.add(Uri(pathSegments: item.split('/')));
} on FormatException { } on FormatException {
_logger.printError('Shader manifest contains invalid uri: $shader.'); _logger.printError('$singularName manifest contains invalid uri: $item.');
} }
} }
return results; return results;
...@@ -545,6 +548,17 @@ void _validateFlutter(YamlMap? yaml, List<String> errors) { ...@@ -545,6 +548,17 @@ void _validateFlutter(YamlMap? yaml, List<String> errors) {
); );
} }
break; break;
case 'models':
if (yamlValue is! YamlList) {
errors.add('Expected "$yamlKey" to be a list, but got $yamlValue (${yamlValue.runtimeType}).');
} else if (yamlValue.isEmpty) {
break;
} else if (yamlValue[0] is! String) {
errors.add(
'Expected "$yamlKey" to be a list of strings, but the first element is $yamlValue (${yamlValue.runtimeType}).',
);
}
break;
case 'fonts': case 'fonts':
if (yamlValue is! YamlList) { if (yamlValue is! YamlList) {
errors.add('Expected "$yamlKey" to be a list, but got $yamlValue (${yamlValue.runtimeType}).'); errors.add('Expected "$yamlKey" to be a list, but got $yamlValue (${yamlValue.runtimeType}).');
......
...@@ -27,6 +27,7 @@ import '../base/logger.dart'; ...@@ -27,6 +27,7 @@ import '../base/logger.dart';
import '../base/net.dart'; import '../base/net.dart';
import '../base/platform.dart'; import '../base/platform.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../build_system/targets/scene_importer.dart';
import '../build_system/targets/shader_compiler.dart'; import '../build_system/targets/shader_compiler.dart';
import '../build_system/targets/web.dart'; import '../build_system/targets/web.dart';
import '../bundle_builder.dart'; import '../bundle_builder.dart';
...@@ -816,6 +817,7 @@ class WebDevFS implements DevFS { ...@@ -816,6 +817,7 @@ class WebDevFS implements DevFS {
required PackageConfig packageConfig, required PackageConfig packageConfig,
required String dillOutputPath, required String dillOutputPath,
required DevelopmentShaderCompiler shaderCompiler, required DevelopmentShaderCompiler shaderCompiler,
DevelopmentSceneImporter? sceneImporter,
DevFSWriter? devFSWriter, DevFSWriter? devFSWriter,
String? target, String? target,
AssetBundle? bundle, AssetBundle? bundle,
...@@ -968,6 +970,9 @@ class WebDevFS implements DevFS { ...@@ -968,6 +970,9 @@ class WebDevFS implements DevFS {
@override @override
Set<String> get shaderPathsToEvict => <String>{}; Set<String> get shaderPathsToEvict => <String>{};
@override
Set<String> get scenePathsToEvict => <String>{};
} }
class ReleaseAssetServer { class ReleaseAssetServer {
......
...@@ -26,6 +26,7 @@ import 'build_info.dart'; ...@@ -26,6 +26,7 @@ import 'build_info.dart';
import 'build_system/build_system.dart'; import 'build_system/build_system.dart';
import 'build_system/targets/dart_plugin_registrant.dart'; import 'build_system/targets/dart_plugin_registrant.dart';
import 'build_system/targets/localizations.dart'; import 'build_system/targets/localizations.dart';
import 'build_system/targets/scene_importer.dart';
import 'build_system/targets/shader_compiler.dart'; import 'build_system/targets/shader_compiler.dart';
import 'bundle.dart'; import 'bundle.dart';
import 'cache.dart'; import 'cache.dart';
...@@ -51,6 +52,7 @@ class FlutterDevice { ...@@ -51,6 +52,7 @@ class FlutterDevice {
ResidentCompiler? generator, ResidentCompiler? generator,
this.userIdentifier, this.userIdentifier,
required this.developmentShaderCompiler, required this.developmentShaderCompiler,
this.developmentSceneImporter,
}) : assert(buildInfo.trackWidgetCreation != null), }) : assert(buildInfo.trackWidgetCreation != null),
generator = generator ?? ResidentCompiler( generator = generator ?? ResidentCompiler(
globals.artifacts!.getArtifactPath( globals.artifacts!.getArtifactPath(
...@@ -99,6 +101,16 @@ class FlutterDevice { ...@@ -99,6 +101,16 @@ class FlutterDevice {
fileSystem: globals.fs, fileSystem: globals.fs,
); );
final DevelopmentSceneImporter sceneImporter = DevelopmentSceneImporter(
sceneImporter: SceneImporter(
artifacts: globals.artifacts!,
logger: globals.logger,
processManager: globals.processManager,
fileSystem: globals.fs,
),
fileSystem: globals.fs,
);
// For both web and non-web platforms we initialize dill to/from // For both web and non-web platforms we initialize dill to/from
// a shared location for faster bootstrapping. If the compiler fails // a shared location for faster bootstrapping. If the compiler fails
// due to a kernel target or version mismatch, no error is reported // due to a kernel target or version mismatch, no error is reported
...@@ -200,6 +212,7 @@ class FlutterDevice { ...@@ -200,6 +212,7 @@ class FlutterDevice {
buildInfo: buildInfo, buildInfo: buildInfo,
userIdentifier: userIdentifier, userIdentifier: userIdentifier,
developmentShaderCompiler: shaderCompiler, developmentShaderCompiler: shaderCompiler,
developmentSceneImporter: sceneImporter,
); );
} }
...@@ -209,6 +222,7 @@ class FlutterDevice { ...@@ -209,6 +222,7 @@ class FlutterDevice {
final BuildInfo buildInfo; final BuildInfo buildInfo;
final String? userIdentifier; final String? userIdentifier;
final DevelopmentShaderCompiler developmentShaderCompiler; final DevelopmentShaderCompiler developmentShaderCompiler;
final DevelopmentSceneImporter? developmentSceneImporter;
DevFSWriter? devFSWriter; DevFSWriter? devFSWriter;
Stream<Uri?>? observatoryUris; Stream<Uri?>? observatoryUris;
...@@ -584,6 +598,7 @@ class FlutterDevice { ...@@ -584,6 +598,7 @@ class FlutterDevice {
packageConfig: packageConfig, packageConfig: packageConfig,
devFSWriter: devFSWriter, devFSWriter: devFSWriter,
shaderCompiler: developmentShaderCompiler, shaderCompiler: developmentShaderCompiler,
sceneImporter: developmentSceneImporter,
dartPluginRegistrant: FlutterProject.current().dartPluginRegistrant, dartPluginRegistrant: FlutterProject.current().dartPluginRegistrant,
); );
} on DevFSException { } on DevFSException {
......
...@@ -507,6 +507,7 @@ class HotRunner extends ResidentRunner { ...@@ -507,6 +507,7 @@ class HotRunner extends ResidentRunner {
} }
devFS.assetPathsToEvict.clear(); devFS.assetPathsToEvict.clear();
devFS.shaderPathsToEvict.clear(); devFS.shaderPathsToEvict.clear();
devFS.scenePathsToEvict.clear();
} }
} }
...@@ -1044,7 +1045,9 @@ class HotRunner extends ResidentRunner { ...@@ -1044,7 +1045,9 @@ class HotRunner extends ResidentRunner {
Future<void> evictDirtyAssets() async { Future<void> evictDirtyAssets() async {
final List<Future<void>> futures = <Future<void>>[]; final List<Future<void>> futures = <Future<void>>[];
for (final FlutterDevice? device in flutterDevices) { for (final FlutterDevice? device in flutterDevices) {
if (device!.devFS!.assetPathsToEvict.isEmpty && device.devFS!.shaderPathsToEvict.isEmpty) { if (device!.devFS!.assetPathsToEvict.isEmpty &&
device.devFS!.shaderPathsToEvict.isEmpty &&
device.devFS!.scenePathsToEvict.isEmpty) {
continue; continue;
} }
final List<FlutterView> views = await device.vmService!.getFlutterViews(); final List<FlutterView> views = await device.vmService!.getFlutterViews();
...@@ -1096,8 +1099,18 @@ class HotRunner extends ResidentRunner { ...@@ -1096,8 +1099,18 @@ class HotRunner extends ResidentRunner {
) )
); );
} }
for (final String assetPath in device.devFS!.scenePathsToEvict) {
futures.add(
device.vmService!
.flutterEvictScene(
assetPath,
isolateId: views.first.uiIsolate!.id!,
)
);
}
device.devFS!.assetPathsToEvict.clear(); device.devFS!.assetPathsToEvict.clear();
device.devFS!.shaderPathsToEvict.clear(); device.devFS!.shaderPathsToEvict.clear();
device.devFS!.scenePathsToEvict.clear();
} }
await Future.wait<void>(futures); await Future.wait<void>(futures);
} }
......
...@@ -739,6 +739,18 @@ class FlutterVmService { ...@@ -739,6 +739,18 @@ class FlutterVmService {
); );
} }
Future<Map<String, Object?>?> flutterEvictScene(String assetPath, {
required String isolateId,
}) {
return invokeFlutterExtensionRpcRaw(
'ext.ui.window.reinitializeScene',
isolateId: isolateId,
args: <String, Object?>{
'assetKey': assetPath,
},
);
}
/// Exit the application by calling [exit] from `dart:io`. /// Exit the application by calling [exit] from `dart:io`.
/// ///
......
...@@ -571,6 +571,9 @@ class FakeDevFs extends Fake implements DevFS { ...@@ -571,6 +571,9 @@ class FakeDevFs extends Fake implements DevFS {
@override @override
Set<String> shaderPathsToEvict= <String>{}; Set<String> shaderPathsToEvict= <String>{};
@override
Set<String> scenePathsToEvict= <String>{};
@override @override
Uri? baseUri; Uri? baseUri;
} }
......
...@@ -18,6 +18,7 @@ import 'package:flutter_tools/src/base/io.dart' as io; ...@@ -18,6 +18,7 @@ import 'package:flutter_tools/src/base/io.dart' as io;
import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_system/targets/scene_importer.dart';
import 'package:flutter_tools/src/build_system/targets/shader_compiler.dart'; import 'package:flutter_tools/src/build_system/targets/shader_compiler.dart';
import 'package:flutter_tools/src/compile.dart'; import 'package:flutter_tools/src/compile.dart';
import 'package:flutter_tools/src/convert.dart'; import 'package:flutter_tools/src/convert.dart';
...@@ -2694,6 +2695,9 @@ class FakeDevFS extends Fake implements DevFS { ...@@ -2694,6 +2695,9 @@ class FakeDevFS extends Fake implements DevFS {
@override @override
Set<String> shaderPathsToEvict = <String>{}; Set<String> shaderPathsToEvict = <String>{};
@override
Set<String> scenePathsToEvict = <String>{};
@override @override
bool didUpdateFontManifest = false; bool didUpdateFontManifest = false;
...@@ -2722,6 +2726,7 @@ class FakeDevFS extends Fake implements DevFS { ...@@ -2722,6 +2726,7 @@ class FakeDevFS extends Fake implements DevFS {
required PackageConfig packageConfig, required PackageConfig packageConfig,
required String dillOutputPath, required String dillOutputPath,
required DevelopmentShaderCompiler shaderCompiler, required DevelopmentShaderCompiler shaderCompiler,
DevelopmentSceneImporter? sceneImporter,
DevFSWriter? devFSWriter, DevFSWriter? devFSWriter,
String? target, String? target,
AssetBundle? bundle, AssetBundle? bundle,
......
...@@ -16,6 +16,7 @@ import 'package:flutter_tools/src/base/logger.dart'; ...@@ -16,6 +16,7 @@ import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/time.dart'; import 'package:flutter_tools/src/base/time.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_system/targets/scene_importer.dart';
import 'package:flutter_tools/src/build_system/targets/shader_compiler.dart'; import 'package:flutter_tools/src/build_system/targets/shader_compiler.dart';
import 'package:flutter_tools/src/compile.dart'; import 'package:flutter_tools/src/compile.dart';
import 'package:flutter_tools/src/devfs.dart'; import 'package:flutter_tools/src/devfs.dart';
...@@ -1401,6 +1402,7 @@ class FakeWebDevFS extends Fake implements WebDevFS { ...@@ -1401,6 +1402,7 @@ class FakeWebDevFS extends Fake implements WebDevFS {
required PackageConfig packageConfig, required PackageConfig packageConfig,
required String dillOutputPath, required String dillOutputPath,
required DevelopmentShaderCompiler shaderCompiler, required DevelopmentShaderCompiler shaderCompiler,
DevelopmentSceneImporter? sceneImporter,
DevFSWriter? devFSWriter, DevFSWriter? devFSWriter,
String? target, String? target,
AssetBundle? bundle, AssetBundle? bundle,
......
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