public static bool BuildAssetBundle(string manifestPath, GUID assetGuid, string cacheFilePath, BuildTarget target, HashSet <Entities.Hash128> dependencies, HashSet <System.Type> types, long fileIdent) { using (new BuildInterfacesWrapper()) { Directory.CreateDirectory(k_TempBuildPath); // Used for naming var fixedGUID = assetGuid; if (fileIdent != -1) { GUIDHelper.PackBuiltinExtraWithFileIdent(ref fixedGUID, fileIdent); } string assetGUIDString = fixedGUID.ToString(); // Deterministic ID Generator var generator = new Unity5PackedIdentifiers(); // Target platform settings & script information var settings = new BuildSettings { buildFlags = ContentBuildFlags.None, target = target, group = BuildPipeline.GetBuildTargetGroup(target), typeDB = null }; if (assetGuid == GUIDHelper.UnityBuiltinResources) { FilterBuiltinResourcesObjectManifest(manifestPath); } if (assetGuid == GUIDHelper.UnityBuiltinExtraResources) { FilterBuiltinExtraResourcesObjectManifest(manifestPath); } // Collect all the objects we need for this asset & bundle (returned array order is deterministic) var manifestObjects = ContentBuildInterface.GetPlayerObjectIdentifiersInSerializedFile(manifestPath, settings.target); // Collect all the objects we need to reference for this asset (returned array order is deterministic) var manifestDependencies = ContentBuildInterface.GetPlayerDependenciesForObjects(manifestObjects, settings.target, settings.typeDB); // Scene / Project lighting information var globalUsage = ContentBuildInterface.GetGlobalUsageFromGraphicsSettings(); globalUsage = ForceKeepInstancingVariants(globalUsage); // Inter-asset feature usage (shader features, used mesh channels) var usageSet = new BuildUsageTagSet(); ContentBuildInterface.CalculateBuildUsageTags(manifestDependencies, manifestDependencies, globalUsage, usageSet); // TODO: Cache & Append to the assets that are influenced by this usageTagSet, ideally it would be a nice api to extract just the data for a given asset or object from the result // Bundle all the needed write parameters var writeParams = new WriteParameters { // Target platform settings & script information settings = settings, // Scene / Project lighting information globalUsage = globalUsage, // Inter-asset feature usage (shader features, used mesh channels) usageSet = usageSet, // Serialized File Layout writeCommand = new WriteCommand { fileName = generator.GenerateInternalFileName(assetGUIDString), internalName = generator.GenerateAssetBundleInternalFileName(fixedGUID), serializeObjects = new List <SerializationInfo>() // Populated Below }, // External object references referenceMap = new BuildReferenceMap(), // Populated Below // Asset Bundle object layout bundleInfo = new AssetBundleInfo { bundleName = assetGUIDString, // What is loadable from this bundle bundleAssets = new List <AssetLoadInfo> { // The manifest object and it's dependencies new AssetLoadInfo { address = assetGUIDString, asset = assetGuid, // TODO: Remove this as it is unused in C++ includedObjects = manifestObjects.ToList(), // TODO: In our effort to modernize the public API design we over complicated it trying to take List or return ReadOnlyLists. Should have just stuck with Arrays[] in all places referencedObjects = manifestDependencies.ToList() } } } }; // The requirement is that a single asset bundle only contains the ObjectManifest and the objects that are directly part of the asset, objects for external assets will be in their own bundles. IE: 1 asset per bundle layout // So this means we need to take manifestObjects & manifestDependencies and filter storing them into writeCommand.serializeObjects and/or referenceMap based on if they are this asset or other assets // For the Manifest Objects, we only have the ScriptableObject `AssetObjectManifest` which is not referenced outside of this bundle. // This creates a deterministic ID for it, regardless of the path of the manifest file var linearGenerator = new LinearPackedIdentifiers(2); foreach (var obj in manifestObjects) { var index = linearGenerator.SerializationIndexFromObjectIdentifier(obj); writeParams.writeCommand.serializeObjects.Add(new SerializationInfo { serializationObject = obj, serializationIndex = index }); writeParams.referenceMap.AddMapping(writeParams.writeCommand.internalName, index, obj); } foreach (var obj in manifestDependencies) { // MonoScripts need to live beside the MonoBehavior (in this case ScriptableObject) and loaded first. We could move it to it's own bundle, but this is safer and it's a lightweight object var type = ContentBuildInterface.GetTypeForObject(obj); if (obj.guid == GUIDHelper.UnityBuiltinResources) { // For Builtin Resources, we can reference them directly // TODO: Once we switch to using GlobalObjectId for SBP, we will need a mapping for certain special cases of GUID <> FilePath for Builtin Resources writeParams.referenceMap.AddMapping(obj.filePath, obj.localIdentifierInFile, obj); } else if (type == typeof(MonoScript)) { writeParams.writeCommand.serializeObjects.Add(new SerializationInfo { serializationObject = obj, serializationIndex = generator.SerializationIndexFromObjectIdentifier(obj) }); writeParams.referenceMap.AddMapping(writeParams.writeCommand.internalName, generator.SerializationIndexFromObjectIdentifier(obj), obj); } else if (obj.guid == assetGuid) { // If we are a specific built-in asset, only add the built-in asset if (fileIdent != -1 && obj.localIdentifierInFile != fileIdent) { continue; } writeParams.writeCommand.serializeObjects.Add(new SerializationInfo { serializationObject = obj, serializationIndex = generator.SerializationIndexFromObjectIdentifier(obj) }); writeParams.referenceMap.AddMapping(writeParams.writeCommand.internalName, generator.SerializationIndexFromObjectIdentifier(obj), obj); } else if (obj.guid == GUIDHelper.UnityBuiltinExtraResources) { var convGUID = obj.guid; GUIDHelper.PackBuiltinExtraWithFileIdent(ref convGUID, obj.localIdentifierInFile); writeParams.referenceMap.AddMapping(generator.GenerateAssetBundleInternalFileName(convGUID), generator.SerializationIndexFromObjectIdentifier(obj), obj); dependencies.Add(convGUID); } else if (!obj.guid.Empty()) { writeParams.referenceMap.AddMapping(generator.GenerateAssetBundleInternalFileName(obj.guid), generator.SerializationIndexFromObjectIdentifier(obj), obj); dependencies.Add(obj.guid); } else { throw new Exception($"Invalid dependency! GUID={obj.guid}, type={type}"); } if (type != null) { types.Add(type); } } // Write the serialized file var result = ContentBuildInterface.WriteSerializedFile(k_TempBuildPath, writeParams); // Archive and compress the serialized & resource files for the previous operation var crc = ContentBuildInterface.ArchiveAndCompress(result.resourceFiles.ToArray(), cacheFilePath, UnityEngine.BuildCompression.Uncompressed); // Because the shader compiler progress bar hooks are absolute shit EditorUtility.ClearProgressBar(); //Debug.Log($"Wrote '{writeParams.writeCommand.fileName}' to '{k_TempBuildPath}/{writeParams.writeCommand.internalName}' resulting in {result.serializedObjects.Count} objects in the serialized file."); //Debug.Log($"Archived '{k_TempBuildPath}/{writeParams.writeCommand.internalName}' to '{cacheFilePath}' resulting in {crc} CRC."); return(crc != 0); } }
internal static BuildUsageTagGlobal GetGlobalUsage() { return(ContentBuildInterface.GetGlobalUsageFromGraphicsSettings()); }
public static void BuildSubSceneBundle(string manifestPath, string bundleName, string bundlePath, BuildTarget target, HashSet <Entities.Hash128> dependencies) { using (new BuildInterfacesWrapper()) { Directory.CreateDirectory(k_TempBuildPath); // Deterministic ID Generator var generator = new Unity5PackedIdentifiers(); // Target platform settings & script information var buildSettings = new BuildSettings { buildFlags = ContentBuildFlags.None, target = target, group = BuildPipeline.GetBuildTargetGroup(target), typeDB = null }; #if UNITY_2020_1_OR_NEWER // Collect all the objects we need for this asset & bundle (returned array order is deterministic) var manifestObjects = ContentBuildInterface.GetPlayerObjectIdentifiersInSerializedFile(manifestPath, buildSettings.target); #else var method = typeof(ContentBuildInterface).GetMethod("GetPlayerObjectIdentifiersInSerializedFile", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); // Collect all the objects we need for this asset & bundle (returned array order is deterministic) var manifestObjects = (ObjectIdentifier[])method.Invoke(null, new object[] { manifestPath, buildSettings.target }); #endif // Collect all the objects we need to reference for this asset (returned array order is deterministic) var manifestDependencies = ContentBuildInterface.GetPlayerDependenciesForObjects(manifestObjects, buildSettings.target, buildSettings.typeDB); // Scene / Project lighting information var globalUsage = ContentBuildInterface.GetGlobalUsageFromGraphicsSettings(); globalUsage = ForceKeepInstancingVariants(globalUsage); // Inter-asset feature usage (shader features, used mesh channels) var usageSet = new BuildUsageTagSet(); ContentBuildInterface.CalculateBuildUsageTags(manifestDependencies, manifestDependencies, globalUsage, usageSet); // TODO: Cache & Append to the assets that are influenced by this usageTagSet, ideally it would be a nice api to extract just the data for a given asset or object from the result // Bundle all the needed write parameters var writeParams = new WriteParameters { // Target platform settings & script information settings = buildSettings, // Scene / Project lighting information globalUsage = globalUsage, // Inter-asset feature usage (shader features, used mesh channels) usageSet = usageSet, // Serialized File Layout writeCommand = new WriteCommand { fileName = generator.GenerateInternalFileName(bundleName), internalName = generator.GenerateAssetBundleInternalFileName(bundleName), serializeObjects = new List <SerializationInfo>() // Populated Below }, // External object references referenceMap = new BuildReferenceMap(), // Populated Below // Asset Bundle object layout bundleInfo = new AssetBundleInfo { bundleName = bundleName, // What is loadable from this bundle bundleAssets = new List <AssetLoadInfo> { // The manifest object and it's dependencies new AssetLoadInfo { address = bundleName, asset = new GUID(), // TODO: Remove this as it is unused in C++ includedObjects = manifestObjects .ToList(), // TODO: In our effort to modernize the public API design we over complicated it trying to take List or return ReadOnlyLists. Should have just stuck with Arrays[] in all places referencedObjects = manifestDependencies.ToList() } } } }; // The requirement is that a single asset bundle only contains the ObjectManifest and the objects that are directly part of the asset, objects for external assets will be in their own bundles. IE: 1 asset per bundle layout // So this means we need to take manifestObjects & manifestDependencies and filter storing them into writeCommand.serializeObjects and/or referenceMap based on if they are this asset or other assets foreach (var obj in manifestObjects) { writeParams.writeCommand.serializeObjects.Add(new SerializationInfo { serializationObject = obj, serializationIndex = generator.SerializationIndexFromObjectIdentifier(obj) }); writeParams.referenceMap.AddMapping(writeParams.writeCommand.internalName, generator.SerializationIndexFromObjectIdentifier(obj), obj); } foreach (var obj in manifestDependencies) { // MonoScripts need to live beside the MonoBehavior (in this case ScriptableObject) and loaded first. We could move it to it's own bundle, but this is safer and it's a lightweight object var type = ContentBuildInterface.GetTypeForObject(obj); if (obj.guid == k_UnityBuiltinResources) { // For Builtin Resources, we can reference them directly // TODO: Once we switch to using GlobalObjectId for SBP, we will need a mapping for certain special cases of GUID <> FilePath for Builtin Resources writeParams.referenceMap.AddMapping(obj.filePath, obj.localIdentifierInFile, obj); } else if (type == typeof(MonoScript)) { writeParams.writeCommand.serializeObjects.Add(new SerializationInfo { serializationObject = obj, serializationIndex = generator.SerializationIndexFromObjectIdentifier(obj) }); writeParams.referenceMap.AddMapping(writeParams.writeCommand.internalName, generator.SerializationIndexFromObjectIdentifier(obj), obj); } else if (!obj.guid.Empty()) { writeParams.referenceMap.AddMapping( generator.GenerateAssetBundleInternalFileName(obj.guid.ToString()), generator.SerializationIndexFromObjectIdentifier(obj), obj); } // This will be solvable after we move SBP into the asset pipeline as importers. if (!obj.guid.Empty() && type != typeof(MonoScript)) { var convGUID = obj.guid; if (obj.guid == k_UnityBuiltinExtraResources) { PackBuiltinExtraWithFileIdent(ref convGUID, obj.localIdentifierInFile); } dependencies?.Add(convGUID); } } // Write the serialized file var result = ContentBuildInterface.WriteSerializedFile(k_TempBuildPath, writeParams); // Archive and compress the serialized & resource files for the previous operation var crc = ContentBuildInterface.ArchiveAndCompress(result.resourceFiles.ToArray(), bundlePath, UnityEngine.BuildCompression.Uncompressed); // Because the shader compiler progress bar hooks are absolute shit EditorUtility.ClearProgressBar(); //Debug.Log($"Wrote '{writeParams.writeCommand.fileName}' to '{k_TempBuildPath}/{writeParams.writeCommand.internalName}' resulting in {result.serializedObjects.Count} objects in the serialized file."); //Debug.Log($"Archived '{k_TempBuildPath}/{writeParams.writeCommand.internalName}' to '{cacheFilePath}' resulting in {crc} CRC."); Directory.Delete(k_TempBuildPath, true); } }
public static NPath BuildSlimGameManagers(BuildTarget target, string[] scenes, bool development, string typeDBDirectory) { NPath tempPath; WriteManagerParameters mParams; WriteParameters wParams; WriteCommand[] writeCommands; PreloadInfo preloadInfo; // Setup { tempPath = TempDataPath; tempPath.CreateDirectory(); tempPath.Combine("Resources").MakeAbsolute().CreateDirectory(); //work around bug in unity's TypeDBHelper where it assumes Library/Type will exist new NPath("Library/Type").EnsureDirectoryExists(); mParams = new WriteManagerParameters { settings = new UnityEditor.Build.Content.BuildSettings { target = target, group = UnityEditor.BuildPipeline.GetBuildTargetGroup(target), buildFlags = development ? ContentBuildFlags.DevelopmentBuild : ContentBuildFlags.None, typeDB = TypeDbHelper.GetForPlayer(typeDBDirectory) }, globalUsage = ContentBuildInterface.GetGlobalUsageFromGraphicsSettings(), referenceMap = new BuildReferenceMap() }; wParams = new WriteParameters { settings = mParams.settings, globalUsage = mParams.globalUsage, referenceMap = mParams.referenceMap, usageSet = new BuildUsageTagSet() }; writeCommands = new[] { new WriteCommand { fileName = "unity_builtin_extra", internalName = "Resources/unity_builtin_extra", serializeObjects = new List <SerializationInfo>() }, new WriteCommand { fileName = "globalgamemanagers.assets", internalName = "globalgamemanagers.assets", serializeObjects = new List <SerializationInfo>() } }; preloadInfo = new PreloadInfo { preloadObjects = new List <ObjectIdentifier>() }; } // Dependency Calculation { var dependencyResults = ContentBuildInterface.CalculatePlayerDependenciesForGameManagers(mParams.settings, mParams.globalUsage, wParams.usageSet); var referencedObjects = dependencyResults.referencedObjects.ToArray(); var types = ContentBuildInterface.GetTypeForObjects(referencedObjects); for (int i = 0; i < referencedObjects.Length; i++) { if (referencedObjects[i].guid == k_UnityBuiltinResources) { // unity default resources contain scripts that need to be preloaded if (types[i] == typeof(MonoScript)) { preloadInfo.preloadObjects.Add(referencedObjects[i]); } // Prebuild player specific default resources file, don't remap local identifiers mParams.referenceMap.AddMapping("Library/unity default resources", referencedObjects[i].localIdentifierInFile, referencedObjects[i]); } else if (referencedObjects[i].guid == k_UnityBuiltinExtraResources) { if (types[i] == typeof(Shader)) { var command = writeCommands[0]; // Resources/unity_builtin_extra // Don't remap local identifiers var info = new SerializationInfo { serializationObject = referencedObjects[i], serializationIndex = referencedObjects[i].localIdentifierInFile }; command.serializeObjects.Add(info); mParams.referenceMap.AddMapping(command.internalName, info.serializationIndex, info.serializationObject); } else { var command = writeCommands[1]; // globalgamemanagers.assets // Squash / Remap local identifiers, starting at 2 (PreloadData 1) var info = new SerializationInfo { serializationObject = referencedObjects[i], serializationIndex = command.serializeObjects.Count + 2 }; command.serializeObjects.Add(info); mParams.referenceMap.AddMapping(command.internalName, info.serializationIndex, info.serializationObject); } } else if (types[i] == typeof(MonoScript)) { //globalgamemanagers.assets //error because we can't support all the ggm features in slim builds } else { //globalgamemanagers.assets //error because we can't support all the ggm features in slim builds } } } // Writing globalgamemanagers { var writeResults = ContentBuildInterface.WriteGameManagersSerializedFile(tempPath.ToString(), mParams); EditorUtility.ClearProgressBar(); //if (writeResults.serializedObjects.Count == 0) return "FAIL"; } // Writing globalgamemanagers.assets { wParams.writeCommand = writeCommands[1]; wParams.preloadInfo = preloadInfo; var writeResults = ContentBuildInterface.WriteSerializedFile(tempPath.ToString(), wParams); EditorUtility.ClearProgressBar(); //if (writeResults.serializedObjects.Count == 0) return "FAIL"; } // Writing unity_builtin_extra" { wParams.writeCommand = writeCommands[0]; wParams.preloadInfo = null; // unity_builtin_extras requires absolutepath writing, so fixup the internalName and referenceMap for this wParams.writeCommand.internalName = tempPath.Combine(wParams.writeCommand.internalName).MakeAbsolute().ToString(); wParams.referenceMap.AddMappings(wParams.writeCommand.internalName, wParams.writeCommand.serializeObjects.ToArray(), true); var writeResults = ContentBuildInterface.WriteSerializedFile(tempPath.Combine("Resources").ToString(), wParams); EditorUtility.ClearProgressBar(); //if (writeResults.serializedObjects.Count == 0) return "FAIL"; } { var parameters = new BundleBuildParameters(mParams.settings.target, mParams.settings.group, TempStreamingAssetsPath.ToString()); parameters.BundleCompression = UnityEngine.BuildCompression.Uncompressed; parameters.ScriptInfo = mParams.settings.typeDB; // Writing scenes.bundle { WriteSceneBundles(parameters, scenes); //if (???) return "FAIL"; } // Writing resources.bundle { WriteResourcesBundles(parameters); //if (???) return "FAIL"; } { WriteRenderPipelineBundles(parameters); //if (???) return "FAIL"; } } return(tempPath); }