-
Notifications
You must be signed in to change notification settings - Fork 3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Impeller Scene] Add SceneC asset importing (#118157)
- Loading branch information
Showing
14 changed files
with
330 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
156 changes: 156 additions & 0 deletions
156
packages/flutter_tools/lib/src/build_system/targets/scene_importer.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
// 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'; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.