Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Commit

Permalink
[Impeller Scene] Add SceneC asset importing (#118157)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdero authored Jan 10, 2023
1 parent 583a812 commit 700fe3d
Show file tree
Hide file tree
Showing 14 changed files with 330 additions and 31 deletions.
7 changes: 7 additions & 0 deletions packages/flutter_tools/lib/src/artifacts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ enum HostArtifact {

// The Impeller shader compiler.
impellerc,
// The Impeller Scene 3D model importer.
scenec,
// Impeller's tessellation library.
libtessellator,
}
Expand Down Expand Up @@ -252,6 +254,8 @@ String _hostArtifactToFileName(HostArtifact artifact, Platform platform) {
return 'dart_sdk.js.map';
case HostArtifact.impellerc:
return 'impellerc$exe';
case HostArtifact.scenec:
return 'scenec$exe';
case HostArtifact.libtessellator:
return 'libtessellator$dll';
}
Expand Down Expand Up @@ -432,6 +436,7 @@ class CachedArtifacts implements Artifacts {
final String artifactFileName = _hostArtifactToFileName(artifact, _platform);
return _cache.getArtifactDirectory('usbmuxd').childFile(artifactFileName);
case HostArtifact.impellerc:
case HostArtifact.scenec:
case HostArtifact.libtessellator:
final String artifactFileName = _hostArtifactToFileName(artifact, _platform);
final String engineDir = _getEngineArtifactsPath(_currentHostPlatform(_platform, _operatingSystemUtils))!;
Expand Down Expand Up @@ -866,6 +871,7 @@ class CachedLocalEngineArtifacts implements Artifacts {
final String artifactFileName = _hostArtifactToFileName(artifact, _platform);
return _cache.getArtifactDirectory('usbmuxd').childFile(artifactFileName);
case HostArtifact.impellerc:
case HostArtifact.scenec:
case HostArtifact.libtessellator:
final String artifactFileName = _hostArtifactToFileName(artifact, _platform);
final File file = _fileSystem.file(_fileSystem.path.join(_hostEngineOutPath, artifactFileName));
Expand Down Expand Up @@ -1151,6 +1157,7 @@ class CachedLocalWebSdkArtifacts implements Artifacts {
case HostArtifact.iproxy:
case HostArtifact.skyEnginePath:
case HostArtifact.impellerc:
case HostArtifact.scenec:
case HostArtifact.libtessellator:
return _parent.getHostArtifact(artifact);
}
Expand Down
15 changes: 15 additions & 0 deletions packages/flutter_tools/lib/src/asset.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ enum AssetKind {
regular,
font,
shader,
model,
}

abstract class AssetBundle {
Expand Down Expand Up @@ -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.
for (final Font font in flutterManifest.fonts) {
for (final FontAsset fontAsset in font.fontAssets) {
Expand Down
13 changes: 13 additions & 0 deletions packages/flutter_tools/lib/src/build_system/targets/assets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import '../build_system.dart';
import '../depfile.dart';
import 'common.dart';
import 'icon_tree_shaker.dart';
import 'scene_importer.dart';
import 'shader_compiler.dart';

/// A helper function to copy an asset bundle into an [environment]'s output
Expand Down Expand Up @@ -84,6 +85,12 @@ Future<Depfile> copyAssets(
fileSystem: environment.fileSystem,
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>{
...assetBundle.entries,
Expand Down Expand Up @@ -131,6 +138,12 @@ Future<Depfile> copyAssets(
json: targetPlatform == TargetPlatform.web_javascript,
);
break;
case AssetKind.model:
doCopy = !await sceneImporter.importScene(
input: content.file as File,
outputPath: file.path,
);
break;
}
if (doCopy) {
await (content.file as File).copy(file.path);
Expand Down
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';
}
14 changes: 14 additions & 0 deletions packages/flutter_tools/lib/src/bundle_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'build_info.dart';
import 'build_system/build_system.dart';
import 'build_system/depfile.dart';
import 'build_system/targets/common.dart';
import 'build_system/targets/scene_importer.dart';
import 'build_system/targets/shader_compiler.dart';
import 'bundle.dart';
import 'cache.dart';
Expand Down Expand Up @@ -158,6 +159,13 @@ Future<void> writeBundle(
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.
final Pool pool = Pool(64);
await Future.wait<void>(
Expand Down Expand Up @@ -189,6 +197,12 @@ Future<void> writeBundle(
json: targetPlatform == TargetPlatform.web_javascript,
);
break;
case AssetKind.model:
doCopy = !await sceneImporter.importScene(
input: input,
outputPath: file.path,
);
break;
}
if (doCopy) {
input.copySync(file.path);
Expand Down
Loading

0 comments on commit 700fe3d

Please sign in to comment.