initial commit

This commit is contained in:
Jo 2025-01-07 02:06:59 +01:00
parent 6715289efe
commit 788c3389af
37645 changed files with 2526849 additions and 80 deletions

View file

@ -0,0 +1,46 @@
using System.IO;
using System.Linq;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Append a hash to each bundle name.
/// </summary>
public class AppendBundleHash : IBuildTask
{
/// <inheritdoc />
public int Version { get { return 1; } }
#pragma warning disable 649
[InjectContext(ContextUsage.In)]
IBundleBuildParameters m_Parameters;
[InjectContext]
IBundleBuildResults m_Results;
#pragma warning restore 649
/// <inheritdoc />
public ReturnCode Run()
{
if (!m_Parameters.AppendHash)
return ReturnCode.SuccessNotRun;
string[] bundles = m_Results.BundleInfos.Keys.ToArray();
foreach (string bundle in bundles)
{
var details = m_Results.BundleInfos[bundle];
var oldFileName = details.FileName;
var newFileName = string.Format("{0}_{1}", details.FileName, details.Hash.ToString());
details.FileName = newFileName;
m_Results.BundleInfos[bundle] = details;
File.Delete(newFileName);
File.Move(oldFileName, newFileName);
}
return ReturnCode.Success;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f4af5ab96cd890342912b92e54ac2c3e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,442 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
using UnityEngine;
using UnityEngine.Build.Pipeline;
namespace UnityEditor.Build.Pipeline.Tasks
{
#if UNITY_2018_3_OR_NEWER
using BuildCompression = UnityEngine.BuildCompression;
#else
using BuildCompression = UnityEditor.Build.Content.BuildCompression;
#endif
/// <summary>
/// Archives and compresses all asset bundles.
/// </summary>
public class ArchiveAndCompressBundles : IBuildTask
{
private const int kVersion = 2;
/// <inheritdoc />
public int Version { get { return kVersion; } }
#pragma warning disable 649
[InjectContext(ContextUsage.In)]
IBuildParameters m_Parameters;
[InjectContext(ContextUsage.In)]
IBundleWriteData m_WriteData;
#if UNITY_2019_3_OR_NEWER
[InjectContext(ContextUsage.In)]
IBundleBuildContent m_Content;
#endif
[InjectContext]
IBundleBuildResults m_Results;
[InjectContext(ContextUsage.In, true)]
IProgressTracker m_Tracker;
[InjectContext(ContextUsage.In, true)]
IBuildCache m_Cache;
[InjectContext(ContextUsage.In, true)]
IBuildLogger m_Log;
#pragma warning restore 649
internal static void CopyFileWithTimestampIfDifferent(string srcPath, string destPath, IBuildLogger log)
{
if (srcPath == destPath)
return;
srcPath = Path.GetFullPath(srcPath);
destPath = Path.GetFullPath(destPath);
#if UNITY_EDITOR_WIN
// Max path length per MS Path code.
const int MaxPath = 260;
if (srcPath.Length > MaxPath)
throw new PathTooLongException(srcPath);
if (destPath.Length > MaxPath)
throw new PathTooLongException(destPath);
#endif
DateTime time = File.GetLastWriteTime(srcPath);
DateTime destTime = File.Exists(destPath) ? File.GetLastWriteTime(destPath) : new DateTime();
if (destTime == time)
return;
using (log.ScopedStep(LogLevel.Verbose, "Copying From Cache", $"{srcPath} -> {destPath}"))
{
var directory = Path.GetDirectoryName(destPath);
Directory.CreateDirectory(directory);
File.Copy(srcPath, destPath, true);
}
}
static CacheEntry GetCacheEntry(IBuildCache cache, string bundleName, IEnumerable<ResourceFile> resources, BuildCompression compression, List<SerializedFileMetaData> hashes)
{
var entry = new CacheEntry();
entry.Type = CacheEntry.EntryType.Data;
entry.Guid = HashingMethods.Calculate("ArchiveAndCompressBundles", bundleName).ToGUID();
List<object> toHash = new List<object> { kVersion, compression };
foreach (var resource in resources)
{
toHash.Add(resource.serializedFile);
toHash.Add(resource.fileAlias);
}
toHash.AddRange(hashes.Select(x => (object)x.RawFileHash));
entry.Hash = HashingMethods.Calculate(toHash).ToHash128();
entry.Version = kVersion;
return entry;
}
static CachedInfo GetCachedInfo(IBuildCache cache, CacheEntry entry, IEnumerable<ResourceFile> resources, BundleDetails details)
{
var info = new CachedInfo();
info.Asset = entry;
info.Dependencies = new CacheEntry[0];
info.Data = new object[] { details };
return info;
}
internal static Hash128 CalculateHashVersion(ArchiveWorkItem item, string[] dependencies)
{
List<Hash128> hashes = new List<Hash128>();
hashes.AddRange(item.SeriliazedFileMetaDatas.Select(x => x.ContentHash));
return HashingMethods.Calculate(hashes, dependencies).ToHash128();
}
internal class ArchiveWorkItem
{
public int Index;
public string BundleName;
public string OutputFilePath;
public string CachedArtifactPath;
public List<ResourceFile> ResourceFiles;
public BuildCompression Compression;
public BundleDetails ResultDetails;
public List<SerializedFileMetaData> SeriliazedFileMetaDatas = new List<SerializedFileMetaData>();
}
internal struct TaskInput
{
public Dictionary<string, WriteResult> InternalFilenameToWriteResults;
public Dictionary<string, SerializedFileMetaData> InternalFilenameToWriteMetaData;
#if UNITY_2019_3_OR_NEWER
public Dictionary<string, List<ResourceFile>> BundleNameToAdditionalFiles;
#endif
public Dictionary<string, string> InternalFilenameToBundleName;
public Func<string, BuildCompression> GetCompressionForIdentifier;
public Func<string, string> GetOutputFilePathForIdentifier;
public IBuildCache BuildCache;
public Dictionary<GUID, List<string>> AssetToFilesDependencies;
public IProgressTracker ProgressTracker;
public string TempOutputFolder;
public bool Threaded;
public List<string> OutCachedBundles;
public IBuildLogger Log;
public bool StripUnityVersion;
}
internal struct TaskOutput
{
public Dictionary<string, BundleDetails> BundleDetails;
}
/// <inheritdoc />
public ReturnCode Run()
{
TaskInput input = new TaskInput();
input.InternalFilenameToWriteResults = m_Results.WriteResults;
#if UNITY_2019_3_OR_NEWER
input.BundleNameToAdditionalFiles = m_Content.AdditionalFiles;
#endif
input.InternalFilenameToBundleName = m_WriteData.FileToBundle;
input.GetCompressionForIdentifier = (x) => m_Parameters.GetCompressionForIdentifier(x);
input.GetOutputFilePathForIdentifier = (x) => m_Parameters.GetOutputFilePathForIdentifier(x);
input.BuildCache = m_Parameters.UseCache ? m_Cache : null;
input.ProgressTracker = m_Tracker;
input.TempOutputFolder = m_Parameters.TempOutputFolder;
input.AssetToFilesDependencies = m_WriteData.AssetToFiles;
input.InternalFilenameToWriteMetaData = m_Results.WriteResultsMetaData;
input.Log = m_Log;
input.StripUnityVersion = (m_Parameters.GetContentBuildSettings().buildFlags & ContentBuildFlags.StripUnityVersion) != 0;
input.Threaded = ReflectionExtensions.SupportsMultiThreadedArchiving && ScriptableBuildPipeline.threadedArchiving;
TaskOutput output;
ReturnCode code = Run(input, out output);
if (code == ReturnCode.Success)
{
foreach (var item in output.BundleDetails)
m_Results.BundleInfos.Add(item.Key, item.Value);
}
return code;
}
internal static Dictionary<string, string[]> CalculateBundleDependencies(List<List<string>> assetFileList, Dictionary<string, string> filenameToBundleName)
{
var bundleDependencies = new Dictionary<string, string[]>();
Dictionary<string, HashSet<string>> bundleDependenciesHash = new Dictionary<string, HashSet<string>>();
foreach (var files in assetFileList)
{
if (files.IsNullOrEmpty())
continue;
string bundle = filenameToBundleName[files.First()];
HashSet<string> dependencies;
bundleDependenciesHash.GetOrAdd(bundle, out dependencies);
dependencies.UnionWith(files.Select(x => filenameToBundleName[x]));
dependencies.Remove(bundle);
// Ensure we create mappings for all encountered files
foreach (var file in files)
bundleDependenciesHash.GetOrAdd(filenameToBundleName[file], out dependencies);
}
// Recursively combine dependencies
foreach (var dependencyPair in bundleDependenciesHash)
{
List<string> dependencies = dependencyPair.Value.ToList();
for (int i = 0; i < dependencies.Count; i++)
{
if (!bundleDependenciesHash.TryGetValue(dependencies[i], out var recursiveDependencies))
continue;
foreach (var recursiveDependency in recursiveDependencies)
{
if (dependencyPair.Value.Add(recursiveDependency))
dependencies.Add(recursiveDependency);
}
}
}
foreach (var dep in bundleDependenciesHash)
{
string[] ret = dep.Value.ToArray();
Array.Sort(ret);
bundleDependencies.Add(dep.Key, ret);
}
return bundleDependencies;
}
static void PostArchiveProcessing(List<ArchiveWorkItem> items, List<List<string>> assetFileList, Dictionary<string, string> filenameToBundleName, IBuildLogger log)
{
using (log.ScopedStep(LogLevel.Info, "PostArchiveProcessing"))
{
Dictionary<string, string[]> bundleDependencies = CalculateBundleDependencies(assetFileList, filenameToBundleName);
foreach (ArchiveWorkItem item in items)
{
// apply bundle dependencies
item.ResultDetails.Dependencies = bundleDependencies.ContainsKey(item.BundleName) ? bundleDependencies[item.BundleName] : new string[0];
item.ResultDetails.Hash = CalculateHashVersion(item, item.ResultDetails.Dependencies);
}
}
}
static ArchiveWorkItem GetOrCreateWorkItem(TaskInput input, string bundleName, Dictionary<string, ArchiveWorkItem> bundleToWorkItem)
{
if (!bundleToWorkItem.TryGetValue(bundleName, out ArchiveWorkItem item))
{
item = new ArchiveWorkItem();
item.BundleName = bundleName;
item.Compression = input.GetCompressionForIdentifier(bundleName);
item.OutputFilePath = input.GetOutputFilePathForIdentifier(bundleName);
item.ResourceFiles = new List<ResourceFile>();
bundleToWorkItem[bundleName] = item;
}
return item;
}
static RawHash HashResourceFiles(List<ResourceFile> files)
{
return HashingMethods.Calculate(files.Select((x) => HashingMethods.CalculateFile(x.fileName)));
}
static List<ArchiveWorkItem> CreateWorkItems(TaskInput input)
{
using (input.Log.ScopedStep(LogLevel.Info, "CreateWorkItems"))
{
Dictionary<string, ArchiveWorkItem> bundleNameToWorkItem = new Dictionary<string, ArchiveWorkItem>();
foreach (var pair in input.InternalFilenameToWriteResults)
{
string internalName = pair.Key;
string bundleName = input.InternalFilenameToBundleName[internalName];
ArchiveWorkItem item = GetOrCreateWorkItem(input, bundleName, bundleNameToWorkItem);
if (input.InternalFilenameToWriteMetaData.TryGetValue(pair.Key, out SerializedFileMetaData md))
item.SeriliazedFileMetaDatas.Add(md);
else
throw new Exception($"Archive {bundleName} with internal name {internalName} does not have associated SerializedFileMetaData");
item.ResourceFiles.AddRange(pair.Value.resourceFiles);
#if UNITY_2019_3_OR_NEWER
if (input.BundleNameToAdditionalFiles.TryGetValue(bundleName, out List<ResourceFile> additionalFiles))
{
RawHash hash = HashResourceFiles(additionalFiles);
item.SeriliazedFileMetaDatas.Add(new SerializedFileMetaData() { ContentHash = hash.ToHash128(), RawFileHash = hash.ToHash128() });
item.ResourceFiles.AddRange(additionalFiles);
}
#endif
}
List<ArchiveWorkItem> allItems = bundleNameToWorkItem.Select((x, index) => { x.Value.Index = index; return x.Value; }).ToList();
return allItems;
}
}
static internal ReturnCode Run(TaskInput input, out TaskOutput output)
{
output = new TaskOutput();
output.BundleDetails = new Dictionary<string, BundleDetails>();
List<ArchiveWorkItem> allItems = CreateWorkItems(input);
IList<CacheEntry> cacheEntries = null;
IList<CachedInfo> cachedInfo = null;
List<ArchiveWorkItem> cachedItems = new List<ArchiveWorkItem>();
List<ArchiveWorkItem> nonCachedItems = allItems;
if (input.BuildCache != null)
{
using (input.Log.ScopedStep(LogLevel.Info, "Creating Cache Entries"))
cacheEntries = allItems.Select(x => GetCacheEntry(input.BuildCache, x.BundleName, x.ResourceFiles, x.Compression, x.SeriliazedFileMetaDatas)).ToList();
using (input.Log.ScopedStep(LogLevel.Info, "Load Cached Data"))
input.BuildCache.LoadCachedData(cacheEntries, out cachedInfo);
cachedItems = allItems.Where(x => cachedInfo[x.Index] != null).ToList();
nonCachedItems = allItems.Where(x => cachedInfo[x.Index] == null).ToList();
foreach (ArchiveWorkItem i in allItems)
i.CachedArtifactPath = string.Format("{0}/{1}", input.BuildCache.GetCachedArtifactsDirectory(cacheEntries[i.Index]), HashingMethods.Calculate(i.BundleName));
}
using (input.Log.ScopedStep(LogLevel.Info, "CopyingCachedFiles"))
{
foreach (ArchiveWorkItem item in cachedItems)
{
if (!input.ProgressTracker.UpdateInfoUnchecked(string.Format("{0} (Cached)", item.BundleName)))
return ReturnCode.Canceled;
item.ResultDetails = (BundleDetails)cachedInfo[item.Index].Data[0];
item.ResultDetails.FileName = item.OutputFilePath;
CopyFileWithTimestampIfDifferent(item.CachedArtifactPath, item.ResultDetails.FileName, input.Log);
}
}
// Write all the files that aren't cached
if (!ArchiveItems(nonCachedItems, input.TempOutputFolder, input.ProgressTracker, input.Threaded, input.Log, input.StripUnityVersion))
return ReturnCode.Canceled;
PostArchiveProcessing(allItems, input.AssetToFilesDependencies.Values.ToList(), input.InternalFilenameToBundleName, input.Log);
// Put everything into the cache
if (input.BuildCache != null)
{
using (input.Log.ScopedStep(LogLevel.Info, "Copying To Cache"))
{
List<CachedInfo> uncachedInfo = nonCachedItems.Select(x => GetCachedInfo(input.BuildCache, cacheEntries[x.Index], x.ResourceFiles, x.ResultDetails)).ToList();
input.BuildCache.SaveCachedData(uncachedInfo);
}
}
output.BundleDetails = allItems.ToDictionary((x) => x.BundleName, (x) => x.ResultDetails);
if (input.OutCachedBundles != null)
input.OutCachedBundles.AddRange(cachedItems.Select(x => x.BundleName));
return ReturnCode.Success;
}
static private void ArchiveSingleItem(ArchiveWorkItem item, string tempOutputFolder, IBuildLogger log, bool stripUnityVersion)
{
using (log.ScopedStep(LogLevel.Info, "ArchiveSingleItem", item.BundleName))
{
item.ResultDetails = new BundleDetails();
string writePath = string.Format("{0}/{1}", tempOutputFolder, item.BundleName);
if (!string.IsNullOrEmpty(item.CachedArtifactPath))
writePath = item.CachedArtifactPath;
Directory.CreateDirectory(Path.GetDirectoryName(writePath));
item.ResultDetails.FileName = item.OutputFilePath;
item.ResultDetails.Crc = ContentBuildInterface.ArchiveAndCompress(item.ResourceFiles.ToArray(), writePath, item.Compression, stripUnityVersion);
CopyFileWithTimestampIfDifferent(writePath, item.ResultDetails.FileName, log);
}
}
static private bool ArchiveItems(List<ArchiveWorkItem> items, string tempOutputFolder, IProgressTracker tracker, bool threaded, IBuildLogger log, bool stripUnityVersion)
{
using (log.ScopedStep(LogLevel.Info, "ArchiveItems", threaded))
{
log?.AddEntry(LogLevel.Info, $"Archiving {items.Count} Bundles");
if (threaded)
return ArchiveItemsThreaded(items, tempOutputFolder, tracker, log, stripUnityVersion);
foreach (ArchiveWorkItem item in items)
{
if (tracker != null && !tracker.UpdateInfoUnchecked(item.BundleName))
return false;
ArchiveSingleItem(item, tempOutputFolder, log, stripUnityVersion);
}
return true;
}
}
static private bool ArchiveItemsThreaded(List<ArchiveWorkItem> items, string tempOutputFolder, IProgressTracker tracker, IBuildLogger log, bool stripUnityVersion)
{
CancellationTokenSource srcToken = new CancellationTokenSource();
SemaphoreSlim semaphore = new SemaphoreSlim(0);
List<Task> tasks = new List<Task>(items.Count);
foreach (ArchiveWorkItem item in items)
{
tasks.Add(Task.Run(() =>
{
try { ArchiveSingleItem(item, tempOutputFolder, log, stripUnityVersion); }
finally { semaphore.Release(); }
}, srcToken.Token));
}
for (int i = 0; i < items.Count; i++)
{
semaphore.Wait(srcToken.Token);
if (tracker != null && !tracker.UpdateInfoUnchecked($"Archive {i + 1}/{items.Count}"))
{
srcToken.Cancel();
break;
}
}
Task.WaitAny(Task.WhenAll(tasks));
int count = 0;
foreach (var task in tasks)
{
if (task.Exception == null)
continue;
Debug.LogException(task.Exception);
count++;
}
if (count > 0)
throw new BuildFailedException($"ArchiveAndCompressBundles encountered {count} exception(s). See console for logged exceptions.");
return !srcToken.Token.IsCancellationRequested;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b7bdc0bcfab4e714b88a12d6b22bac9b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,50 @@
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Utilities;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Player;
using System.IO;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Compiles all player scripts.
/// </summary>
public class BuildPlayerScripts : IBuildTask
{
/// <inheritdoc />
public int Version { get { return 1; } }
#pragma warning disable 649
[InjectContext]
IBuildParameters m_Parameters;
[InjectContext]
IBuildResults m_Results;
#pragma warning restore 649
/// <inheritdoc />
public ReturnCode Run()
{
if (m_Parameters.ScriptInfo != null)
{
BuildCacheUtility.SetTypeDB(m_Parameters.ScriptInfo);
return ReturnCode.SuccessNotRun;
}
// We need to ensure the directory is empty so prior results or other artifacts in this directory do not influence the build result
if (Directory.Exists(m_Parameters.ScriptOutputFolder))
{
Directory.Delete(m_Parameters.ScriptOutputFolder, true);
Directory.CreateDirectory(m_Parameters.ScriptOutputFolder);
}
m_Results.ScriptResults = PlayerBuildInterface.CompilePlayerScripts(m_Parameters.GetScriptCompilationSettings(), m_Parameters.ScriptOutputFolder);
m_Parameters.ScriptInfo = m_Results.ScriptResults.typeDB;
BuildCacheUtility.SetTypeDB(m_Parameters.ScriptInfo);
if (m_Results.ScriptResults.assemblies.IsNullOrEmpty() && m_Results.ScriptResults.typeDB == null)
return ReturnCode.Error;
return ReturnCode.Success;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c42a8e1e1d1b874e95761dab5bdfbc8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,646 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
using UnityEditor.Build.Player;
using UnityEngine;
namespace UnityEditor.Build.Pipeline.Tasks
{
[Serializable]
internal class ObjectDependencyInfo
{
public ObjectIdentifier Object;
public List<ObjectIdentifier> Dependencies = new List<ObjectIdentifier>();
}
#if !UNITY_2020_2_OR_NEWER
internal class CalculateAssetDependencyHooks
{
public virtual UnityEngine.Object[] LoadAllAssetRepresentationsAtPath(string assetPath)
{
return AssetDatabase.LoadAllAssetRepresentationsAtPath(assetPath);
}
}
#endif
/// <summary>
/// Calculates the dependency data for all assets.
/// </summary>
public class CalculateAssetDependencyData : IBuildTask
{
internal const int kVersion = 6;
/// <inheritdoc />
public int Version { get { return kVersion; } }
#pragma warning disable 649
[InjectContext(ContextUsage.In)]
IBundleBuildParameters m_Parameters;
[InjectContext(ContextUsage.In)]
IBuildContent m_Content;
[InjectContext]
IDependencyData m_DependencyData;
[InjectContext]
IObjectDependencyData m_ObjectDependencyData;
[InjectContext(ContextUsage.InOut, true)]
IBuildResults m_Results;
[InjectContext(ContextUsage.InOut, true)]
IBuildSpriteData m_SpriteData;
[InjectContext(ContextUsage.InOut, true)]
IBuildExtendedAssetData m_ExtendedAssetData;
[InjectContext(ContextUsage.In, true)]
IProgressTracker m_Tracker;
[InjectContext(ContextUsage.In, true)]
IBuildCache m_Cache;
[InjectContext(ContextUsage.In, true)]
IBuildLogger m_Log;
#pragma warning restore 649
internal struct TaskInput
{
public IBuildCache BuildCache;
public BuildTarget Target;
public TypeDB TypeDB;
public List<GUID> Assets;
public IProgressTracker ProgressTracker;
public BuildUsageTagGlobal GlobalUsage;
public BuildUsageCache DependencyUsageCache;
#if !UNITY_2020_2_OR_NEWER
public CalculateAssetDependencyHooks EngineHooks;
#endif
public bool NonRecursiveDependencies;
public IBuildLogger Logger;
}
internal struct AssetOutput
{
public GUID asset;
public Hash128 Hash;
public AssetLoadInfo assetInfo;
public List<ObjectDependencyInfo> objectDependencyInfo;
public BuildUsageTagSet usageTags;
public SpriteImporterData spriteData;
public ExtendedAssetData extendedData;
public List<ObjectTypes> objectTypes;
}
internal struct TaskOutput
{
public AssetOutput[] AssetResults;
public int CachedAssetCount;
}
static CacheEntry GetCacheEntry(GUID asset, TaskInput input)
{
if (input.BuildCache == null)
return default;
#if NONRECURSIVE_DEPENDENCY_DATA
CacheEntry entry = input.BuildCache.GetCacheEntry(asset, input.NonRecursiveDependencies ? -kVersion : kVersion);
#else
CacheEntry entry = input.BuildCache.GetCacheEntry(asset, Version);
#endif
return entry;
}
static CachedInfo GetCachedInfo(TaskInput input, GUID asset, AssetLoadInfo assetInfo, List<ObjectDependencyInfo> objectDependencies, BuildUsageTagSet usageTags, SpriteImporterData importerData, ExtendedAssetData assetData)
{
var info = new CachedInfo();
info.Asset = GetCacheEntry(asset, input);
var uniqueTypes = new HashSet<System.Type>();
var objectTypes = new List<ObjectTypes>();
var dependencies = new HashSet<CacheEntry>();
ExtensionMethods.ExtractCommonCacheData(input.BuildCache, assetInfo.includedObjects, assetInfo.referencedObjects, uniqueTypes, objectTypes, dependencies);
info.Dependencies = dependencies.ToArray();
info.Data = new object[] { assetInfo, usageTags, importerData, assetData, objectTypes, objectDependencies };
return info;
}
/// <inheritdoc />
public ReturnCode Run()
{
TaskInput input = new TaskInput();
input.Target = m_Parameters.Target;
input.TypeDB = m_Parameters.ScriptInfo;
input.BuildCache = m_Parameters.UseCache ? m_Cache : null;
#if NONRECURSIVE_DEPENDENCY_DATA
input.NonRecursiveDependencies = m_Parameters.NonRecursiveDependencies;
#else
input.NonRecursiveDependencies = false;
#endif
input.Assets = m_Content.Assets;
input.ProgressTracker = m_Tracker;
input.DependencyUsageCache = m_DependencyData.DependencyUsageCache;
input.GlobalUsage = m_DependencyData.GlobalUsage;
input.Logger = m_Log;
foreach (SceneDependencyInfo sceneInfo in m_DependencyData.SceneInfo.Values)
input.GlobalUsage |= sceneInfo.globalUsage;
ReturnCode code = RunInternal(input, out TaskOutput output);
if (code == ReturnCode.Success)
{
foreach (AssetOutput o in output.AssetResults)
{
m_DependencyData.AssetInfo.Add(o.asset, o.assetInfo);
foreach (var objectDependencyInfo in o.objectDependencyInfo)
m_ObjectDependencyData.ObjectDependencyMap[objectDependencyInfo.Object] = objectDependencyInfo.Dependencies;
}
foreach (AssetOutput assetOutput in output.AssetResults)
{
#if NONRECURSIVE_DEPENDENCY_DATA
if (!input.NonRecursiveDependencies)
ExpandReferences(assetOutput, m_ObjectDependencyData.ObjectDependencyMap);
#endif
m_DependencyData.AssetUsage.Add(assetOutput.asset, assetOutput.usageTags);
Dictionary<ObjectIdentifier, System.Type[]> objectTypes = new Dictionary<ObjectIdentifier, Type[]>();
foreach (var objectType in assetOutput.objectTypes)
objectTypes.Add(objectType.ObjectID, objectType.Types);
if (m_Results != null)
{
AssetResultData resultData = new AssetResultData
{
Guid = assetOutput.asset,
Hash = assetOutput.Hash,
IncludedObjects = assetOutput.assetInfo.includedObjects,
ReferencedObjects = assetOutput.assetInfo.referencedObjects,
ObjectTypes = objectTypes
};
m_Results.AssetResults.Add(assetOutput.asset, resultData);
}
if (assetOutput.spriteData != null)
{
if (m_SpriteData == null)
m_SpriteData = new BuildSpriteData();
m_SpriteData.ImporterData.Add(assetOutput.asset, assetOutput.spriteData);
}
if (!m_Parameters.DisableVisibleSubAssetRepresentations && assetOutput.extendedData != null)
{
if (m_ExtendedAssetData == null)
m_ExtendedAssetData = new BuildExtendedAssetData();
m_ExtendedAssetData.ExtendedData.Add(assetOutput.asset, assetOutput.extendedData);
}
if (assetOutput.objectTypes != null)
BuildCacheUtility.SetTypeForObjects(assetOutput.objectTypes);
}
}
return code;
}
// expand dependencies to explicit assets, results in the same output as recursive dependency calculation
private void ExpandReferences(AssetOutput assetOutput, Dictionary<ObjectIdentifier, List<ObjectIdentifier>> objectDependencyMap)
{
HashSet<ObjectIdentifier> processed = new HashSet<ObjectIdentifier>();
HashSet<ObjectIdentifier> referencedObjects = new HashSet<ObjectIdentifier>(assetOutput.assetInfo.referencedObjects);
Stack<ObjectIdentifier> processStack = new Stack<ObjectIdentifier>(1024);
if (assetOutput.objectDependencyInfo != null && assetOutput.objectDependencyInfo.Count > 0)
{
foreach (ObjectDependencyInfo info in assetOutput.objectDependencyInfo)
{
foreach (ObjectIdentifier dependency in info.Dependencies)
{
if (processed.Contains(dependency) || dependency.guid == assetOutput.asset)
continue;
processStack.Push(dependency);
referencedObjects.Add(dependency);
}
// internal object to asset o
processed.Add(info.Object);
}
List<ObjectIdentifier> dependencies;
while (processStack.Count > 0)
{
var dep = processStack.Pop();
processed.Add(dep);
if (!objectDependencyMap.TryGetValue(dep, out dependencies) || dependencies.Count == 0)
continue;
foreach (ObjectIdentifier dependency in dependencies)
{
if (processed.Contains(dependency) || dependency.guid == assetOutput.asset)
continue;
processStack.Push(dependency);
referencedObjects.Add(dependency);
}
}
// go through all object dependencies that are not to self, add to stack
// loop through external dependencies
var refs = new List<ObjectIdentifier>(referencedObjects.ToArray());
// to mimic recursive dependency calculation
assetOutput.assetInfo.referencedObjects = refs;
}
}
#if !UNITY_2020_2_OR_NEWER
static internal void GatherAssetRepresentations(string assetPath, System.Func<string, UnityEngine.Object[]> loadAllAssetRepresentations, ObjectIdentifier[] includedObjects, out ExtendedAssetData extendedData)
{
extendedData = null;
var representations = loadAllAssetRepresentations(assetPath);
if (representations.IsNullOrEmpty())
return;
var resultData = new ExtendedAssetData();
for (int j = 0; j < representations.Length; j++)
{
if (representations[j] == null)
{
BuildLogger.LogWarning($"SubAsset {j} inside {assetPath} is null. It will not be included in the build.");
continue;
}
if (AssetDatabase.IsMainAsset(representations[j]))
continue;
string guid;
long localId;
if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(representations[j], out guid, out localId))
continue;
resultData.Representations.AddRange(includedObjects.Where(x => x.localIdentifierInFile == localId));
}
if (resultData.Representations.Count > 0)
extendedData = resultData;
}
#else
static internal void GatherAssetRepresentations(GUID asset, BuildTarget target, ObjectIdentifier[] includedObjects, out ExtendedAssetData extendedData)
{
extendedData = null;
var includeSet = new HashSet<ObjectIdentifier>(includedObjects);
// GetPlayerAssetRepresentations can return editor only objects, filter out those to only include what is in includedObjects
ObjectIdentifier[] representations = ContentBuildInterface.GetPlayerAssetRepresentations(asset, target);
var filteredRepresentations = representations.Where(includeSet.Contains);
// Main Asset always returns at index 0, we only want representations, so check for greater than 1 length
if (representations.IsNullOrEmpty() || filteredRepresentations.Count() < 2)
return;
extendedData = new ExtendedAssetData();
extendedData.Representations.AddRange(filteredRepresentations.Skip(1));
}
#endif
static internal ReturnCode RunInternal(TaskInput input, out TaskOutput output)
{
#if !UNITY_2020_2_OR_NEWER
input.EngineHooks = input.EngineHooks != null ? input.EngineHooks : new CalculateAssetDependencyHooks();
#endif
output = new TaskOutput();
output.AssetResults = new AssetOutput[input.Assets.Count];
IList<CachedInfo> cachedInfo = null;
using (input.Logger.ScopedStep(LogLevel.Info, "Gathering Cache Entries to Load"))
{
if (input.BuildCache != null)
{
IList<CacheEntry> entries = input.Assets.Select(x => GetCacheEntry(x, input)).ToList();
input.BuildCache.LoadCachedData(entries, out cachedInfo);
}
}
HashSet<GUID> explicitAssets = new HashSet<GUID>(input.Assets);
Dictionary<GUID, AssetOutput> implicitAssetsOutput = new Dictionary<GUID, AssetOutput>();
var textureImporters = new Dictionary<GUID, TextureImporter>();
for (int i = 0; i < input.Assets.Count; i++)
{
using (input.Logger.ScopedStep(LogLevel.Info, "Calculate Asset Dependencies"))
{
AssetOutput assetResult = new AssetOutput();
assetResult.asset = input.Assets[i];
if (cachedInfo != null && cachedInfo[i] != null)
{
var objectTypes = cachedInfo[i].Data[4] as List<ObjectTypes>;
var assetInfos = cachedInfo[i].Data[0] as AssetLoadInfo;
var objectDependencyInfo = cachedInfo[i].Data[5] as List<ObjectDependencyInfo>;
bool useCachedData = true;
foreach (var objectType in objectTypes)
{
//Sprite association to SpriteAtlas might have changed since last time data was cached, this might
//imply that we have stale data in our cache, if so ensure we regenerate the data.
if (objectType.Types[0] == typeof(UnityEngine.Sprite))
{
var referencedObjectOld = assetInfos.referencedObjects.ToArray();
ObjectIdentifier[] referencedObjectsNew = null;
#if NONRECURSIVE_DEPENDENCY_DATA
referencedObjectsNew = GetPlayerDependenciesForAsset(input.Assets[i], assetInfos.includedObjects.ToArray(), input, assetResult, explicitAssets, implicitAssetsOutput, textureImporters);
#else
referencedObjectsNew = ContentBuildInterface.GetPlayerDependenciesForObjects(assetInfos.includedObjects.ToArray(), input.Target, input.TypeDB);
#endif
if (Enumerable.SequenceEqual(referencedObjectOld, referencedObjectsNew) == false)
{
useCachedData = false;
}
break;
}
}
if (useCachedData)
{
assetResult.assetInfo = assetInfos;
assetResult.objectDependencyInfo = objectDependencyInfo;
assetResult.usageTags = cachedInfo[i].Data[1] as BuildUsageTagSet;
assetResult.spriteData = cachedInfo[i].Data[2] as SpriteImporterData;
assetResult.extendedData = cachedInfo[i].Data[3] as ExtendedAssetData;
assetResult.objectTypes = objectTypes;
output.AssetResults[i] = assetResult;
output.CachedAssetCount++;
input.Logger.AddEntrySafe(LogLevel.Info, $"{assetResult.asset} (cached)");
continue;
}
}
GUID asset = input.Assets[i];
string assetPath = AssetDatabase.GUIDToAssetPath(asset.ToString());
if (!input.ProgressTracker.UpdateInfoUnchecked(assetPath))
return ReturnCode.Canceled;
input.Logger.AddEntrySafe(LogLevel.Info, $"{assetResult.asset}");
assetResult.assetInfo = new AssetLoadInfo();
assetResult.objectDependencyInfo = new List<ObjectDependencyInfo>();
assetResult.usageTags = new BuildUsageTagSet();
assetResult.assetInfo.asset = asset;
var includedObjects = ContentBuildInterface.GetPlayerObjectIdentifiersInAsset(asset, input.Target);
assetResult.assetInfo.includedObjects = new List<ObjectIdentifier>(includedObjects);
ObjectIdentifier[] referencedObjects;
#if NONRECURSIVE_DEPENDENCY_DATA
referencedObjects = GetPlayerDependenciesForAsset(asset, includedObjects, input, assetResult, explicitAssets, implicitAssetsOutput, textureImporters);
#else
referencedObjects = ContentBuildInterface.GetPlayerDependenciesForObjects(includedObjects, input.Target, input.TypeDB);
#endif
assetResult.assetInfo.referencedObjects = new List<ObjectIdentifier>(referencedObjects);
var allObjects = new List<ObjectIdentifier>(includedObjects);
allObjects.AddRange(referencedObjects);
ContentBuildInterface.CalculateBuildUsageTags(allObjects.ToArray(), includedObjects, input.GlobalUsage, assetResult.usageTags, input.DependencyUsageCache);
TextureImporter importer = null;
bool isSprite = false;
if (textureImporters.ContainsKey(asset))
{
importer = textureImporters[asset];
isSprite = true;
}
else
{
importer = AssetImporter.GetAtPath(assetPath) as TextureImporter;
isSprite = importer != null && importer.textureType == TextureImporterType.Sprite;
if (isSprite)
textureImporters.Add(asset, importer);
}
if (isSprite)
{
assetResult.spriteData = new SpriteImporterData();
assetResult.spriteData.PackedSprite = false;
assetResult.spriteData.SourceTexture = includedObjects.FirstOrDefault();
if (EditorSettings.spritePackerMode != SpritePackerMode.Disabled)
assetResult.spriteData.PackedSprite = referencedObjects.Length > 0;
#if !UNITY_2020_1_OR_NEWER
if (EditorSettings.spritePackerMode == SpritePackerMode.AlwaysOn || EditorSettings.spritePackerMode == SpritePackerMode.BuildTimeOnly)
assetResult.spriteData.PackedSprite = !string.IsNullOrEmpty(importer.spritePackingTag);
#endif
}
#if !UNITY_2020_2_OR_NEWER
GatherAssetRepresentations(assetPath, input.EngineHooks.LoadAllAssetRepresentationsAtPath, includedObjects, out assetResult.extendedData);
#else
GatherAssetRepresentations(asset, input.Target, includedObjects, out assetResult.extendedData);
#endif
output.AssetResults[i] = assetResult;
}
}
using (input.Logger.ScopedStep(LogLevel.Info, "Gathering Cache Entries to Save"))
{
if (input.BuildCache != null)
{
List<CachedInfo> toCache = new List<CachedInfo>();
for (int i = 0; i < input.Assets.Count; i++)
{
AssetOutput r = output.AssetResults[i];
CachedInfo info = cachedInfo[i];
if (info == null)
{
info = GetCachedInfo(input, input.Assets[i], r.assetInfo, r.objectDependencyInfo, r.usageTags, r.spriteData, r.extendedData);
toCache.Add(info);
}
r.Hash = info.Asset.Hash;
if (r.objectTypes == null && info.Data.Length > 4)
{
List<ObjectTypes> types = info.Data[4] as List<ObjectTypes>;
r.objectTypes = types;
}
output.AssetResults[i] = r;
}
input.BuildCache.SaveCachedData(toCache);
}
else
{
for (int i = 0; i < input.Assets.Count; i++)
{
AssetOutput r = output.AssetResults[i];
if (r.objectTypes != null)
continue;
var info = BuildCacheUtility.GetCacheEntry(input.Assets[i], input.NonRecursiveDependencies ? -kVersion : kVersion);
r.Hash = info.Hash;
r.objectTypes = new List<ObjectTypes>(r.assetInfo.includedObjects.Count);
foreach (var objectId in r.assetInfo.includedObjects)
{
var types = BuildCacheUtility.GetSortedUniqueTypesForObject(objectId);
r.objectTypes.Add(new ObjectTypes(objectId, types));
}
output.AssetResults[i] = r;
}
}
}
return ReturnCode.Success;
}
private static ObjectIdentifier[] GetPlayerDependenciesForAsset(GUID inputAssetGuid, ObjectIdentifier[] includedObjects, TaskInput input, AssetOutput assetResult, HashSet<GUID> explicitAssets,
in Dictionary<GUID, AssetOutput> implicitAssetsOutput, Dictionary<GUID, TextureImporter> textureImporters)
{
HashSet<ObjectIdentifier> otherReferencedAssetObjectsHashSet = new HashSet<ObjectIdentifier>();
ObjectIdentifier[] singleObjectIdArray = new ObjectIdentifier[1];
foreach (ObjectIdentifier subObject in includedObjects)
{
singleObjectIdArray[0] = subObject;
ObjectIdentifier[] objs = ContentBuildInterface.GetPlayerDependenciesForObjects(singleObjectIdArray, input.Target, input.TypeDB, DependencyType.ValidReferences);
foreach (ObjectIdentifier objRef in objs)
{
// inputAssetGuid is an explicit build asset, so it is all objects in the asset and do not need to include them
if (objRef.guid == inputAssetGuid)
continue;
otherReferencedAssetObjectsHashSet.Add(objRef);
}
if (objs.Length > 0)
{
if (assetResult.objectDependencyInfo == null)
assetResult.objectDependencyInfo = new List<ObjectDependencyInfo>();
assetResult.objectDependencyInfo.Add(new ObjectDependencyInfo()
{
Object = subObject,
Dependencies = new List<ObjectIdentifier>(objs)
});
}
}
var collectedImmediateReferences = new HashSet<ObjectIdentifier>();
var encounteredExplicitAssetDependencies = new HashSet<ObjectIdentifier>();
var mainRepresentationNeeded = new HashSet<GUID>();
string assetPath = AssetDatabase.GUIDToAssetPath(inputAssetGuid.ToString());
bool isSpriteAtlas = AssetDatabase.GetMainAssetTypeAtPath(assetPath) == typeof(UnityEngine.U2D.SpriteAtlas);
Stack<ObjectIdentifier> objectLookingAt = new Stack<ObjectIdentifier>(otherReferencedAssetObjectsHashSet);
while (objectLookingAt.Count > 0)
{
var obj = objectLookingAt.Pop();
// Track which roots we encounter to do dependency pruning
if (obj.guid != inputAssetGuid && explicitAssets.Contains(obj.guid))
{
encounteredExplicitAssetDependencies.Add(obj); // might just be able to add to collected
// Don't include source textures for sprites included in an atlas
if (isSpriteAtlas)
{
string explicitAssetPath = AssetDatabase.GUIDToAssetPath(obj.guid.ToString());
TextureImporter importer = null;
bool isSprite = false;
if (textureImporters.ContainsKey(obj.guid))
{
importer = textureImporters[obj.guid];
isSprite = true;
}
else
{
importer = AssetImporter.GetAtPath(explicitAssetPath) as TextureImporter;
isSprite = importer != null && importer.textureType == TextureImporterType.Sprite;
if (isSprite)
textureImporters.Add(obj.guid, importer);
}
if (isSprite)
continue;
}
mainRepresentationNeeded.Add(obj.guid);
}
// looking for implicit assets we have not visited yet
else if (!explicitAssets.Contains(obj.guid) && !collectedImmediateReferences.Contains(obj))
{
collectedImmediateReferences.Add(obj);
ObjectIdentifier[] referencedObjects = null;
if (!implicitAssetsOutput.TryGetValue(obj.guid, out var implicitOutput))
{
implicitOutput = new AssetOutput()
{
asset = obj.guid,
objectDependencyInfo = new List<ObjectDependencyInfo>()
};
implicitAssetsOutput[obj.guid] = implicitOutput;
}
else // implicit player dependencies for asset is cached, check for specific object
{
List<ObjectIdentifier> foundObjectDependencies = null;
foreach (ObjectDependencyInfo dependencyInfo in implicitOutput.objectDependencyInfo)
{
if (dependencyInfo.Object == obj)
{
foundObjectDependencies = dependencyInfo.Dependencies;
break;
}
}
if (foundObjectDependencies != null)
{
foreach (ObjectIdentifier o in foundObjectDependencies)
objectLookingAt.Push(o);
if (foundObjectDependencies.Count > 0)
{
assetResult.objectDependencyInfo.Add(new ObjectDependencyInfo()
{
Object = obj,
Dependencies = foundObjectDependencies
});
}
continue;
}
}
// have not got the object references for this object yet
singleObjectIdArray[0] = obj;
referencedObjects = ContentBuildInterface.GetPlayerDependenciesForObjects(singleObjectIdArray, input.Target, input.TypeDB, DependencyType.ValidReferences);
List<ObjectIdentifier> referencedObjectList = new List<ObjectIdentifier>(referencedObjects);
implicitOutput.objectDependencyInfo.Add(new ObjectDependencyInfo()
{
Object = obj,
Dependencies = referencedObjectList
});
if (referencedObjectList.Count > 0)
{
assetResult.objectDependencyInfo.Add(new ObjectDependencyInfo()
{
Object = obj,
Dependencies = referencedObjectList
});
}
foreach (ObjectIdentifier o in referencedObjects)
objectLookingAt.Push(o);
}
}
// We need to ensure that we have a reference to a visible representation so our runtime dependency appending process
// can find something that can be appended, otherwise the necessary data will fail to load correctly in all cases. (EX: prefab A has reference to component on prefab B)
foreach (var dependency in mainRepresentationNeeded)
{
// For each dependency, add just the main representation as a reference
var representations = ContentBuildInterface.GetPlayerAssetRepresentations(dependency, input.Target);
collectedImmediateReferences.Add(representations[0]);
}
collectedImmediateReferences.UnionWith(encounteredExplicitAssetDependencies);
return collectedImmediateReferences.ToArray();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 40e93d78541d2664e8424875bca4e041
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,207 @@
#if UNITY_2019_3_OR_NEWER
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Build Task that calculates teh included objects and references objects for custom assets not tracked by the AssetDatabase.
/// <seealso cref="IBuildTask"/>
/// </summary>
public class CalculateCustomDependencyData : IBuildTask
{
/// <inheritdoc />
public int Version { get { return 2; } }
#pragma warning disable 649
[InjectContext(ContextUsage.In)]
IBuildParameters m_Parameters;
[InjectContext(ContextUsage.InOut)]
IBundleBuildContent m_Content;
[InjectContext(ContextUsage.InOut)]
IDependencyData m_DependencyData;
[InjectContext(ContextUsage.Out, true)]
ICustomAssets m_CustomAssets;
[InjectContext(ContextUsage.In, true)]
IProgressTracker m_Tracker;
[InjectContext(ContextUsage.In, true)]
IBuildCache m_Cache;
[InjectContext(ContextUsage.In, true)]
IBuildLogger m_Log;
#pragma warning restore 649
BuildUsageTagGlobal m_GlobalUsage;
BuildUsageTagGlobal m_CustomUsage;
Dictionary<string, AssetLoadInfo> m_AssetInfo = new Dictionary<string, AssetLoadInfo>();
Dictionary<string, BuildUsageTagSet> m_BuildUsage = new Dictionary<string, BuildUsageTagSet>();
/// <inheritdoc />
public ReturnCode Run()
{
m_CustomAssets = new CustomAssets();
m_GlobalUsage = m_DependencyData.GlobalUsage;
foreach (SceneDependencyInfo sceneInfo in m_DependencyData.SceneInfo.Values)
m_GlobalUsage |= sceneInfo.globalUsage;
foreach (CustomContent info in m_Content.CustomAssets)
{
if (!m_Tracker.UpdateInfoUnchecked(info.Asset.ToString()))
return ReturnCode.Canceled;
using (m_Log.ScopedStep(LogLevel.Verbose, "CustomAssetDependency", info.Asset.ToString()))
info.Processor(info.Asset, this);
}
// Add all the additional global usage for custom assets back into the dependency data result
// for use in the write serialized file build task
m_DependencyData.GlobalUsage |= m_CustomUsage;
return ReturnCode.Success;
}
CacheEntry GetCacheEntry(string path, BuildUsageTagGlobal additionalGlobalUsage)
{
var entry = new CacheEntry();
entry.Type = CacheEntry.EntryType.Data;
entry.Guid = HashingMethods.Calculate("CalculateCustomDependencyData", path).ToGUID();
entry.Hash = HashingMethods.Calculate(HashingMethods.CalculateFile(path), additionalGlobalUsage).ToHash128();
entry.Version = Version;
return entry;
}
CachedInfo GetCachedInfo(CacheEntry entry, AssetLoadInfo assetInfo, BuildUsageTagSet usageTags)
{
var info = new CachedInfo();
info.Asset = entry;
var uniqueTypes = new HashSet<Type>();
var objectTypes = new List<ObjectTypes>();
var dependencies = new HashSet<CacheEntry>();
ExtensionMethods.ExtractCommonCacheData(m_Cache, assetInfo.includedObjects, assetInfo.referencedObjects, uniqueTypes, objectTypes, dependencies);
info.Dependencies = dependencies.ToArray();
info.Data = new object[] { assetInfo, usageTags, objectTypes };
return info;
}
bool LoadCachedData(string path, out AssetLoadInfo assetInfo, out BuildUsageTagSet buildUsage, BuildUsageTagGlobal globalUsage)
{
assetInfo = default;
buildUsage = default;
if (!m_Parameters.UseCache || m_Cache == null)
return false;
CacheEntry entry = GetCacheEntry(path, globalUsage);
m_Cache.LoadCachedData(new List<CacheEntry> { entry }, out IList<CachedInfo> cachedInfos);
var cachedInfo = cachedInfos[0];
if (cachedInfo != null)
{
assetInfo = (AssetLoadInfo)cachedInfo.Data[0];
buildUsage = (BuildUsageTagSet)cachedInfo.Data[1];
var objectTypes = (List<ObjectTypes>)cachedInfo.Data[2];
BuildCacheUtility.SetTypeForObjects(objectTypes);
}
else
{
GatherAssetData(path, out assetInfo, out buildUsage, globalUsage);
cachedInfo = GetCachedInfo(entry, assetInfo, buildUsage);
m_Cache.SaveCachedData(new List<CachedInfo> { cachedInfo });
}
return true;
}
void GatherAssetData(string path, out AssetLoadInfo assetInfo, out BuildUsageTagSet buildUsage, BuildUsageTagGlobal globalUsage)
{
assetInfo = new AssetLoadInfo();
buildUsage = new BuildUsageTagSet();
var includedObjects = ContentBuildInterface.GetPlayerObjectIdentifiersInSerializedFile(path, m_Parameters.Target);
var referencedObjects = ContentBuildInterface.GetPlayerDependenciesForObjects(includedObjects, m_Parameters.Target, m_Parameters.ScriptInfo);
assetInfo.includedObjects = new List<ObjectIdentifier>(includedObjects);
assetInfo.referencedObjects = new List<ObjectIdentifier>(referencedObjects);
ContentBuildInterface.CalculateBuildUsageTags(referencedObjects, includedObjects, globalUsage, buildUsage, m_DependencyData.DependencyUsageCache);
}
/// <summary>
/// Returns the Object Identifiers and Types in a raw Unity Serialized File. The resulting arrays will be empty if a non-serialized file path was used.
/// </summary>
/// <param name="path">Path to the Unity Serialized File</param>
/// <param name="objectIdentifiers">Object Identifiers for all the objects in the serialized file</param>
/// <param name="types">Types for all the objects in the serialized file</param>
public void GetObjectIdentifiersAndTypesForSerializedFile(string path, out ObjectIdentifier[] objectIdentifiers, out Type[] types)
{
GetObjectIdentifiersAndTypesForSerializedFile(path, out objectIdentifiers, out types, default);
}
/// <summary>
/// Returns the Object Identifiers and Types in a raw Unity Serialized File. The resulting arrays will be empty if a non-serialized file path was used.
/// </summary>
/// <param name="path">Path to the Unity Serialized File</param>
/// <param name="objectIdentifiers">Object Identifiers for all the objects in the serialized file</param>
/// <param name="types">Types for all the objects in the serialized file</param>
/// <param name="additionalGlobalUsage">Additional global lighting usage information to include with this custom asset</param>
public void GetObjectIdentifiersAndTypesForSerializedFile(string path, out ObjectIdentifier[] objectIdentifiers, out Type[] types, BuildUsageTagGlobal additionalGlobalUsage)
{
// Additional global usage is local to the custom asset, so we are using a local copy of this additional data to avoid influencing the calcualtion
// of other custom assets. Additionally we store all the addtional global usage for later copying back into the dependency data result for the final write build task.
var globalUsage = m_GlobalUsage | additionalGlobalUsage;
m_CustomUsage = m_CustomUsage | additionalGlobalUsage;
if (!LoadCachedData(path, out var assetInfo, out var buildUsage, globalUsage))
GatherAssetData(path, out assetInfo, out buildUsage, globalUsage);
// Local cache to reuse data from this function in the next function
m_AssetInfo[path] = assetInfo;
m_BuildUsage[path] = buildUsage;
objectIdentifiers = assetInfo.includedObjects.ToArray();
types = BuildCacheUtility.GetSortedUniqueTypesForObjects(objectIdentifiers);
}
/// <summary>
/// Adds mapping and bundle information for a custom asset that contains a set of unity objects.
/// </summary>
/// <param name="includedObjects">Object Identifiers that belong to this custom asset</param>
/// <param name="path">Path on disk for this custom asset</param>
/// <param name="bundleName">Asset Bundle name where to add this custom asset</param>
/// <param name="address">Load address to used to load this asset from the Asset Bundle</param>
/// <param name="mainAssetType">Type of the main object for this custom asset</param>
public void CreateAssetEntryForObjectIdentifiers(ObjectIdentifier[] includedObjects, string path, string bundleName, string address, Type mainAssetType)
{
AssetLoadInfo assetInfo = m_AssetInfo[path];
BuildUsageTagSet buildUsage = m_BuildUsage[path];
assetInfo.asset = HashingMethods.Calculate(address).ToGUID();
assetInfo.address = address;
if (m_DependencyData.AssetInfo.ContainsKey(assetInfo.asset))
throw new ArgumentException(string.Format("Custom Asset '{0}' already exists. Building duplicate asset entries is not supported.", address));
SetOutputInformation(bundleName, assetInfo, buildUsage);
}
void SetOutputInformation(string bundleName, AssetLoadInfo assetInfo, BuildUsageTagSet usageTags)
{
List<GUID> assets;
m_Content.BundleLayout.GetOrAdd(bundleName, out assets);
assets.Add(assetInfo.asset);
m_Content.Addresses.Add(assetInfo.asset, assetInfo.address);
m_DependencyData.AssetInfo.Add(assetInfo.asset, assetInfo);
m_DependencyData.AssetUsage.Add(assetInfo.asset, usageTags);
m_CustomAssets.Assets.Add(assetInfo.asset);
}
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5f0270e64b3580847a1adbb01f58852b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,266 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
using UnityEngine;
#if !UNITY_2019_3_OR_NEWER
using System.IO;
#endif
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Calculates the dependency data for all scenes.
/// </summary>
public class CalculateSceneDependencyData : IBuildTask
{
/// <inheritdoc />
public int Version { get { return 6; } }
#pragma warning disable 649
[InjectContext(ContextUsage.In)]
IBuildParameters m_Parameters;
[InjectContext(ContextUsage.In)]
IBuildContent m_Content;
[InjectContext]
IDependencyData m_DependencyData;
[InjectContext(ContextUsage.InOut, true)]
IBuildResults m_Results;
[InjectContext(ContextUsage.In, true)]
IProgressTracker m_Tracker;
[InjectContext(ContextUsage.In, true)]
IBuildCache m_Cache;
[InjectContext(ContextUsage.In, true)]
IBuildLogger m_Log;
#pragma warning restore 649
CacheEntry GetCacheEntry(GUID asset)
{
CacheEntry entry;
#if NONRECURSIVE_DEPENDENCY_DATA
entry = m_Cache.GetCacheEntry(asset, m_Parameters.NonRecursiveDependencies ? -Version : Version);
#else
entry = m_Cache.GetCacheEntry(asset, Version);
#endif
return entry;
}
CacheEntry GetCacheEntry(string asset)
{
#if UNITY_2020_1_OR_NEWER
GUID guid = AssetDatabase.AssetPathToGUID_Internal(asset);
#else
string stringGUID = AssetDatabase.AssetPathToGUID(asset);
if (GUID.TryParse(stringGUID, out GUID guid))
return GetCacheEntry(guid);
#endif
if (!guid.Empty())
return GetCacheEntry(guid);
#if NONRECURSIVE_DEPENDENCY_DATA
return m_Cache.GetCacheEntry(asset, m_Parameters.NonRecursiveDependencies ? -Version : Version);
#else
return m_Cache.GetCacheEntry(asset, Version);
#endif
}
CachedInfo GetCachedInfo(GUID scene, IEnumerable<ObjectIdentifier> references, SceneDependencyInfo sceneInfo, BuildUsageTagSet usageTags, IEnumerable<CacheEntry> prefabEntries, Hash128 prefabDependency)
{
var info = new CachedInfo();
info.Asset = GetCacheEntry(scene);
#if ENABLE_TYPE_HASHING || UNITY_2020_1_OR_NEWER
var uniqueTypes = new HashSet<System.Type>(sceneInfo.includedTypes);
#else
var uniqueTypes = new HashSet<System.Type>();
#endif
var objectTypes = new List<ObjectTypes>();
var dependencies = new HashSet<CacheEntry>(prefabEntries);
ExtensionMethods.ExtractCommonCacheData(m_Cache, null, references, uniqueTypes, objectTypes, dependencies);
info.Dependencies = dependencies.ToArray();
info.Data = new object[] { sceneInfo, usageTags, prefabDependency, objectTypes };
return info;
}
/// <inheritdoc />
public ReturnCode Run()
{
if (m_Content.Scenes.IsNullOrEmpty())
return ReturnCode.SuccessNotRun;
IList<CachedInfo> cachedInfo = null;
IList<CachedInfo> uncachedInfo = null;
if (m_Parameters.UseCache && m_Cache != null)
{
IList<CacheEntry> entries = m_Content.Scenes.Select(x => GetCacheEntry(x)).ToList();
m_Cache.LoadCachedData(entries, out cachedInfo);
uncachedInfo = new List<CachedInfo>();
}
List<CachedInfo> info = new List<CachedInfo>(m_Content.Scenes.Count);
BuildSettings settings = m_Parameters.GetContentBuildSettings();
for (int i = 0; i < m_Content.Scenes.Count; i++)
{
using (m_Log.ScopedStep(LogLevel.Info, "Calculate Scene Dependencies"))
{
GUID scene = m_Content.Scenes[i];
string scenePath = AssetDatabase.GUIDToAssetPath(scene.ToString());
SceneDependencyInfo sceneInfo;
BuildUsageTagSet usageTags;
Hash128 prefabDependency = new Hash128();
bool useUncachedScene = true;
if (cachedInfo != null && cachedInfo[i] != null)
{
info.Add(cachedInfo[i]);
useUncachedScene = false;
if (!m_Tracker.UpdateInfoUnchecked(string.Format("{0} (Cached)", scenePath)))
return ReturnCode.Canceled;
m_Log.AddEntrySafe(LogLevel.Info, $"{scene} (cached)");
sceneInfo = (SceneDependencyInfo)cachedInfo[i].Data[0];
// case 1288677: Update scenePath in case scene moved, but didn't change
sceneInfo.SetScene(scenePath);
usageTags = cachedInfo[i].Data[1] as BuildUsageTagSet;
prefabDependency = (Hash128)cachedInfo[i].Data[2];
var objectTypes = cachedInfo[i].Data[3] as List<ObjectTypes>;
if (objectTypes != null)
{
BuildCacheUtility.SetTypeForObjects(objectTypes);
foreach (var objectType in objectTypes)
{
//Sprite association to SpriteAtlas might have changed since last time data was cached, this might
//imply that we have stale data in our cache, if so ensure we regenerate the data.
if (objectType.Types[0] == typeof(UnityEngine.Sprite))
{
ObjectIdentifier[] filteredReferences = sceneInfo.referencedObjects.ToArray();
ObjectIdentifier[] filteredReferencesNew = null;
#if NONRECURSIVE_DEPENDENCY_DATA
if (m_Parameters.NonRecursiveDependencies)
{
var sceneInfoNew = ContentBuildInterface.CalculatePlayerDependenciesForScene(scenePath, settings, usageTags, m_DependencyData.DependencyUsageCache, DependencyType.ValidReferences);
filteredReferencesNew = sceneInfoNew.referencedObjects.ToArray();
filteredReferencesNew = ExtensionMethods.FilterReferencedObjectIDs(scene, filteredReferencesNew, m_Parameters.Target, m_Parameters.ScriptInfo, new HashSet<GUID>(m_Content.Assets));
}
else
#endif
{
var sceneInfoNew = ContentBuildInterface.CalculatePlayerDependenciesForScene(scenePath, settings, usageTags, m_DependencyData.DependencyUsageCache);
filteredReferencesNew = sceneInfoNew.referencedObjects.ToArray();
}
if (Enumerable.SequenceEqual(filteredReferences,filteredReferencesNew) == false)
{
useUncachedScene = true;
}
break;
}
}
}
if (!useUncachedScene)
SetOutputInformation(scene, sceneInfo, usageTags, prefabDependency);
}
if(useUncachedScene)
{
if (!m_Tracker.UpdateInfoUnchecked(scenePath))
return ReturnCode.Canceled;
m_Log.AddEntrySafe(LogLevel.Info, $"{scene}");
usageTags = new BuildUsageTagSet();
#if UNITY_2019_3_OR_NEWER
#if NONRECURSIVE_DEPENDENCY_DATA
if ( m_Parameters.NonRecursiveDependencies)
{
sceneInfo = ContentBuildInterface.CalculatePlayerDependenciesForScene(scenePath, settings, usageTags, m_DependencyData.DependencyUsageCache, DependencyType.ValidReferences);
ObjectIdentifier[] filteredReferences = sceneInfo.referencedObjects.ToArray();
filteredReferences = ExtensionMethods.FilterReferencedObjectIDs(scene, filteredReferences, m_Parameters.Target, m_Parameters.ScriptInfo, new HashSet<GUID>(m_Content.Assets));
ContentBuildInterface.CalculateBuildUsageTags(filteredReferences, filteredReferences, sceneInfo.globalUsage, usageTags);
sceneInfo.SetReferencedObjects(filteredReferences);
}
else
#endif
{
sceneInfo = ContentBuildInterface.CalculatePlayerDependenciesForScene(scenePath, settings, usageTags, m_DependencyData.DependencyUsageCache);
}
#else
string outputFolder = m_Parameters.TempOutputFolder;
if (m_Parameters.UseCache && m_Cache != null)
outputFolder = m_Cache.GetCachedArtifactsDirectory(GetSceneCacheEntry(scene, Version));
System.IO.Directory.CreateDirectory(outputFolder);
sceneInfo = ContentBuildInterface.PrepareScene(scenePath, settings, usageTags, m_DependencyData.DependencyUsageCache, outputFolder);
#endif
if (uncachedInfo != null)
{
// We only need to gather prefab dependencies and calculate the hash if we are using caching, otherwise we can skip it
string[] sceneDependencyPaths = AssetDatabase.GetDependencies(scenePath);
List<CacheEntry> prefabEntries = new List<CacheEntry>();
foreach (string assetPath in sceneDependencyPaths)
{
if (assetPath.EndsWith(".prefab", StringComparison.Ordinal))
prefabEntries.Add(GetCacheEntry(assetPath));
}
prefabDependency = HashingMethods.Calculate(prefabEntries).ToHash128();
var cacheInfo = GetCachedInfo(scene, sceneInfo.referencedObjects, sceneInfo, usageTags, prefabEntries, prefabDependency);
uncachedInfo.Add(cacheInfo);
info.Add(cacheInfo);
}
SetOutputInformation(scene, sceneInfo, usageTags, prefabDependency);
}
}
}
if (m_Parameters.UseCache && m_Cache != null)
m_Cache.SaveCachedData(uncachedInfo);
if (info != null && m_Results != null)
{
foreach (CachedInfo sceneFileInfo in info)
{
Dictionary<ObjectIdentifier, System.Type[]> objectTypes = new Dictionary<ObjectIdentifier, System.Type[]>();
List<ObjectTypes> types = sceneFileInfo.Data[3] as List<ObjectTypes>;
foreach (var objectType in types)
objectTypes.Add(objectType.ObjectID, objectType.Types);
AssetResultData resultData = new AssetResultData
{
Guid = sceneFileInfo.Asset.Guid,
Hash = sceneFileInfo.Asset.Hash,
IncludedObjects = new List<ObjectIdentifier>(),
ReferencedObjects = null,
ObjectTypes = objectTypes
};
m_Results.AssetResults.Add(resultData.Guid, resultData);
}
}
return ReturnCode.Success;
}
void SetOutputInformation(GUID asset, SceneDependencyInfo sceneInfo, BuildUsageTagSet usageTags, Hash128 prefabDependency)
{
// Add generated scene information to BuildDependencyData
m_DependencyData.SceneInfo.Add(asset, sceneInfo);
m_DependencyData.SceneUsage.Add(asset, usageTags);
m_DependencyData.DependencyHash.Add(asset, prefabDependency);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e2d5a57757fc0cc4eb4f2fade47b59dd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,158 @@
#if UNITY_2022_2_OR_NEWER
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
using UnityEditor.Build.Pipeline.WriteTypes;
using UnityEditor.Build.Utilities;
using UnityEngine;
namespace UnityEditor.Build.Pipeline.Tasks
{
public interface IClusterOutput : IContextObject
{
Dictionary<ObjectIdentifier, Hash128> ObjectToCluster { get; }
Dictionary<ObjectIdentifier, long> ObjectToLocalID { get; }
}
public class ClusterOutput : IClusterOutput
{
private Dictionary<ObjectIdentifier, Hash128> m_ObjectToCluster = new Dictionary<ObjectIdentifier, Hash128>();
private Dictionary<ObjectIdentifier, long> m_ObjectToLocalID = new Dictionary<ObjectIdentifier, long>();
public Dictionary<ObjectIdentifier, Hash128> ObjectToCluster { get { return m_ObjectToCluster; } }
public Dictionary<ObjectIdentifier, long> ObjectToLocalID { get { return m_ObjectToLocalID; } }
}
/// <summary>
/// Build task for creating content archives based asset co-location.
/// </summary>
public class ClusterBuildLayout : IBuildTask
{
private static void GetOrAdd<TKey, TValue>(IDictionary<TKey, TValue> dictionary, TKey key, out TValue value) where TValue : new()
{
if (dictionary.TryGetValue(key, out value))
return;
value = new TValue();
dictionary.Add(key, value);
}
/// <inheritdoc />
public int Version { get { return 1; } }
#pragma warning disable 649
[InjectContext(ContextUsage.In)]
IDependencyData m_DependencyData;
[InjectContext]
IBundleWriteData m_WriteData;
[InjectContext(ContextUsage.In)]
IDeterministicIdentifiers m_PackingMethod;
[InjectContext]
IBuildResults m_Results;
[InjectContext]
IClusterOutput m_ClusterResult;
#pragma warning restore 649
/// <inheritdoc />
public ReturnCode Run()
{
// Create usage maps
Dictionary<ObjectIdentifier, HashSet<GUID>> objectToAssets = new Dictionary<ObjectIdentifier, HashSet<GUID>>();
foreach (var pair in m_DependencyData.AssetInfo)
{
ExtractAssets(objectToAssets, pair.Key, pair.Value.includedObjects);
ExtractAssets(objectToAssets, pair.Key, pair.Value.referencedObjects);
}
foreach (var pair in m_DependencyData.SceneInfo)
{
ExtractAssets(objectToAssets, pair.Key, pair.Value.referencedObjects);
}
// Using usage maps, create clusters
Dictionary<Hash128, HashSet<ObjectIdentifier>> clusterToObjects = new Dictionary<Hash128, HashSet<ObjectIdentifier>>();
foreach (var pair in objectToAssets)
{
HashSet<GUID> assets = pair.Value;
Hash128 cluster = HashingMethods.Calculate(assets.OrderBy(x => x)).ToHash128();
GetOrAdd(clusterToObjects, cluster, out var objectIds);
objectIds.Add(pair.Key);
m_ClusterResult.ObjectToCluster.Add(pair.Key, cluster);
}
// From clusters, create the final write data
BuildUsageTagSet usageSet = new BuildUsageTagSet();
foreach (var pair in m_DependencyData.AssetUsage)
usageSet.UnionWith(pair.Value);
foreach (var pair in m_DependencyData.SceneUsage)
usageSet.UnionWith(pair.Value);
// Generates Content Archive Files from Clusters
BuildReferenceMap referenceMap = new BuildReferenceMap();
foreach (var pair in clusterToObjects)
{
var cluster = pair.Key.ToString();
m_WriteData.FileToObjects.Add(cluster, pair.Value.ToList());
#pragma warning disable CS0618 // Type or member is obsolete
var op = new RawWriteOperation();
#pragma warning restore CS0618 // Type or member is obsolete
m_WriteData.WriteOperations.Add(op);
op.Command = new WriteCommand();
op.Command.fileName = cluster;
op.Command.internalName = cluster;
op.Command.serializeObjects = new List<SerializationInfo>();
foreach (var objectId in pair.Value)
{
var lfid = m_PackingMethod.SerializationIndexFromObjectIdentifier(objectId);
op.Command.serializeObjects.Add(new SerializationInfo { serializationObject = objectId, serializationIndex = lfid });
referenceMap.AddMapping(cluster, lfid, objectId);
m_ClusterResult.ObjectToLocalID.Add(objectId, lfid);
}
op.ReferenceMap = referenceMap;
op.UsageSet = usageSet;
m_WriteData.FileToBundle.Add(cluster, cluster);
}
// Generates Content Scene Archive Files from Scene Input
foreach (var pair in m_DependencyData.SceneInfo)
{
#pragma warning disable CS0618 // Type or member is obsolete
var op = new SceneRawWriteOperation();
#pragma warning restore CS0618 // Type or member is obsolete
m_WriteData.WriteOperations.Add(op);
op.Command = new WriteCommand();
op.Command.fileName = pair.Key.ToString();
op.Command.internalName = pair.Key.ToString();
op.Command.serializeObjects = new List<SerializationInfo>();
op.ReferenceMap = referenceMap;
op.UsageSet = usageSet;
op.Scene = pair.Value.scene;
m_WriteData.FileToBundle.Add(pair.Key.ToString(), pair.Key.ToString());
}
return ReturnCode.Success;
}
private static void ExtractAssets(Dictionary<ObjectIdentifier, HashSet<GUID>> objectToAssets, GUID asset, IEnumerable<ObjectIdentifier> objectIds)
{
foreach (var objectId in objectIds)
{
if (objectId.filePath.Equals(CommonStrings.UnityDefaultResourcePath, StringComparison.OrdinalIgnoreCase))
continue;
GetOrAdd(objectToAssets, objectId, out var assets);
assets.Add(asset);
}
}
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2fb47cf44ffdf4da8a7725b796217e67
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Utilities;
using UnityEngine;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Optional build task that extracts Unity's built in shaders and assigns them to the specified bundle
/// </summary>
public class CreateBuiltInShadersBundle : IBuildTask
{
static readonly GUID k_BuiltInGuid = new GUID(CommonStrings.UnityBuiltInExtraGuid);
/// <inheritdoc />
public int Version { get { return 1; } }
#pragma warning disable 649
[InjectContext(ContextUsage.In)]
IDependencyData m_DependencyData;
[InjectContext(ContextUsage.InOut, true)]
IBundleExplictObjectLayout m_Layout;
#pragma warning restore 649
/// <summary>
/// Stores the name for the built-in shaders bundle.
/// </summary>
public string ShaderBundleName { get; set; }
/// <summary>
/// Create the built-in shaders bundle.
/// </summary>
/// <param name="bundleName">The name of the bundle.</param>
public CreateBuiltInShadersBundle(string bundleName)
{
ShaderBundleName = bundleName;
}
/// <inheritdoc />
public ReturnCode Run()
{
HashSet<ObjectIdentifier> buildInObjects = new HashSet<ObjectIdentifier>();
foreach (AssetLoadInfo dependencyInfo in m_DependencyData.AssetInfo.Values)
buildInObjects.UnionWith(dependencyInfo.referencedObjects.Where(x => x.guid == k_BuiltInGuid));
foreach (SceneDependencyInfo dependencyInfo in m_DependencyData.SceneInfo.Values)
buildInObjects.UnionWith(dependencyInfo.referencedObjects.Where(x => x.guid == k_BuiltInGuid));
ObjectIdentifier[] usedSet = buildInObjects.ToArray();
Type[] usedTypes = BuildCacheUtility.GetMainTypeForObjects(usedSet);
if (m_Layout == null)
m_Layout = new BundleExplictObjectLayout();
Type shader = typeof(Shader);
for (int i = 0; i < usedTypes.Length; i++)
{
if (usedTypes[i] != shader)
continue;
m_Layout.ExplicitObjectLocation.Add(usedSet[i], ShaderBundleName);
}
if (m_Layout.ExplicitObjectLocation.Count == 0)
m_Layout = null;
return ReturnCode.Success;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7b9ded651292a474fad227fce94f4c79
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Utilities;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Optional build task that extracts all referenced MonoScripts and assigns them to the specified bundle
/// </summary>
public class CreateMonoScriptBundle : IBuildTask
{
static readonly GUID k_DefaultGuid = new GUID(CommonStrings.UnityDefaultResourceGuid);
/// <inheritdoc />
public int Version { get { return 1; } }
#pragma warning disable 649
[InjectContext(ContextUsage.In)]
IDependencyData m_DependencyData;
[InjectContext(ContextUsage.InOut, true)]
IBundleExplictObjectLayout m_Layout;
#pragma warning restore 649
/// <summary>
/// Stores the name for the MonoScript bundle.
/// </summary>
public string MonoScriptBundleName { get; set; }
/// <summary>
/// Create the MonoScript bundle.
/// </summary>
/// <param name="bundleName">The name of the bundle.</param>
public CreateMonoScriptBundle(string bundleName)
{
MonoScriptBundleName = bundleName;
}
/// <inheritdoc />
public ReturnCode Run()
{
HashSet<ObjectIdentifier> buildInObjects = new HashSet<ObjectIdentifier>();
foreach (AssetLoadInfo dependencyInfo in m_DependencyData.AssetInfo.Values)
buildInObjects.UnionWith(dependencyInfo.referencedObjects);
foreach (SceneDependencyInfo dependencyInfo in m_DependencyData.SceneInfo.Values)
buildInObjects.UnionWith(dependencyInfo.referencedObjects);
ObjectIdentifier[] usedSet = buildInObjects.ToArray();
Type[] usedTypes = BuildCacheUtility.GetMainTypeForObjects(usedSet);
if (m_Layout == null)
m_Layout = new BundleExplictObjectLayout();
Type monoScript = typeof(MonoScript);
for (int i = 0; i < usedTypes.Length; i++)
{
if (usedTypes[i] != monoScript || usedSet[i].guid == k_DefaultGuid)
continue;
m_Layout.ExplicitObjectLocation.Add(usedSet[i], MonoScriptBundleName);
}
if (m_Layout.ExplicitObjectLocation.Count == 0)
m_Layout = null;
return ReturnCode.Success;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 96b169e370585d7489d37bc46ef5e3bf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,317 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
using UnityEditor.Build.Pipeline.WriteTypes;
using UnityEditor.Build.Utilities;
using UnityEngine;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Creates bundle commands for assets and scenes.
/// </summary>
public class GenerateBundleCommands : IBuildTask
{
/// <inheritdoc />
public int Version { get { return 1; } }
#pragma warning disable 649
[InjectContext(ContextUsage.In)]
IBuildParameters m_Parameters;
[InjectContext(ContextUsage.In)]
IBundleBuildContent m_BuildContent;
[InjectContext(ContextUsage.In)]
IDependencyData m_DependencyData;
[InjectContext]
IBundleWriteData m_WriteData;
[InjectContext(ContextUsage.In)]
IDeterministicIdentifiers m_PackingMethod;
#if UNITY_2019_3_OR_NEWER
[InjectContext(ContextUsage.In, true)]
ICustomAssets m_CustomAssets;
#endif
#pragma warning restore 649
static bool ValidAssetBundle(List<GUID> assets, HashSet<GUID> customAssets)
{
// Custom Valid Asset Bundle function that tests if every asset is known by the asset database, is an asset (not a scene), or is a user driven custom asset
return assets.All(x => ValidationMethods.ValidAsset(x) == ValidationMethods.Status.Asset || customAssets.Contains(x));
}
/// <inheritdoc />
public ReturnCode Run()
{
HashSet<GUID> customAssets = new HashSet<GUID>();
#if UNITY_2019_3_OR_NEWER
if (m_CustomAssets != null)
customAssets.UnionWith(m_CustomAssets.Assets);
#endif
Dictionary<GUID, string> assetToMainFile = new Dictionary<GUID, string>();
foreach (var pair in m_WriteData.AssetToFiles)
assetToMainFile.Add(pair.Key, pair.Value[0]);
foreach (var bundlePair in m_BuildContent.BundleLayout)
{
if (ValidAssetBundle(bundlePair.Value, customAssets))
{
// Use generated internalName here as we could have an empty asset bundle used for raw object storage (See CreateStandardShadersBundle)
var internalName = string.Format(CommonStrings.AssetBundleNameFormat, m_PackingMethod.GenerateInternalFileName(bundlePair.Key));
CreateAssetBundleCommand(bundlePair.Key, internalName, bundlePair.Value);
}
else if (ValidationMethods.ValidSceneBundle(bundlePair.Value))
{
var firstScene = bundlePair.Value[0];
CreateSceneBundleCommand(bundlePair.Key, assetToMainFile[firstScene], firstScene, bundlePair.Value, assetToMainFile);
for (int i = 1; i < bundlePair.Value.Count; ++i)
{
var additionalScene = bundlePair.Value[i];
CreateSceneDataCommand(assetToMainFile[additionalScene], additionalScene);
}
}
}
return ReturnCode.Success;
}
internal static WriteCommand CreateWriteCommand(string internalName, List<ObjectIdentifier> objects, IDeterministicIdentifiers packingMethod)
{
var command = new WriteCommand();
command.internalName = internalName;
command.fileName = Path.GetFileName(internalName);
command.serializeObjects = new List<SerializationInfo>();
var consumedIds = new Dictionary<long, ObjectIdentifier>();
foreach (var obj in objects)
{
var serializationInfo = new SerializationInfo
{
serializationObject = obj,
serializationIndex = packingMethod.SerializationIndexFromObjectIdentifier(obj)
};
if (consumedIds.TryGetValue(serializationInfo.serializationIndex, out var priorObject))
{
string msg = string.Format(@"File '{0}' contains a file identifier collision between {1} ({2}) and {3} ({4}) at id {5}. Objects will be missing from the bundle!
You can work around this issue by changing the 'FileID Generator Seed' found in the Scriptable Build Pipeline Preferences window.",
internalName, obj, BuildCacheUtility.GetMainTypeForObject(obj), priorObject, BuildCacheUtility.GetMainTypeForObject(priorObject), serializationInfo.serializationIndex);
throw new BuildFailedException(msg);
}
consumedIds.Add(serializationInfo.serializationIndex, obj);
command.serializeObjects.Add(serializationInfo);
}
return command;
}
// Sort function to ensure Asset Bundle's path container is ordered deterministically everytime
internal static int AssetLoadInfoCompare(AssetLoadInfo x, AssetLoadInfo y)
{
if (x.asset != y.asset)
return x.asset.CompareTo(y.asset);
if (x.includedObjects.IsNullOrEmpty() && !y.includedObjects.IsNullOrEmpty())
return -1;
if (!x.includedObjects.IsNullOrEmpty() && y.includedObjects.IsNullOrEmpty())
return 1;
if (!x.includedObjects.IsNullOrEmpty() && !y.includedObjects.IsNullOrEmpty())
{
if (x.includedObjects[0] < y.includedObjects[0])
return -1;
if (x.includedObjects[0] > y.includedObjects[0])
return 1;
}
if (string.IsNullOrEmpty(x.address) && !string.IsNullOrEmpty(y.address))
return -1;
if (!string.IsNullOrEmpty(x.address) && string.IsNullOrEmpty(y.address))
return 1;
if (!string.IsNullOrEmpty(x.address) && !string.IsNullOrEmpty(y.address))
return x.address.CompareTo(y.address);
return 0;
}
void CreateAssetBundleCommand(string bundleName, string internalName, List<GUID> assets)
{
var command = CreateWriteCommand(internalName, m_WriteData.FileToObjects[internalName], m_PackingMethod);
var usageSet = new BuildUsageTagSet();
var referenceMap = new BuildReferenceMap();
var dependencyHashes = new List<Hash128>();
var bundleAssets = new List<AssetLoadInfo>();
referenceMap.AddMappings(command.internalName, command.serializeObjects.ToArray());
foreach (var asset in assets)
{
usageSet.UnionWith(m_DependencyData.AssetUsage[asset]);
if (m_DependencyData.DependencyHash.TryGetValue(asset, out var hash))
dependencyHashes.Add(hash);
AssetLoadInfo assetInfo = m_DependencyData.AssetInfo[asset];
assetInfo.address = m_BuildContent.Addresses[asset];
bundleAssets.Add(assetInfo);
}
bundleAssets.Sort(AssetLoadInfoCompare);
var operation = new AssetBundleWriteOperation();
operation.Command = command;
operation.UsageSet = usageSet;
operation.ReferenceMap = referenceMap;
operation.DependencyHash = !dependencyHashes.IsNullOrEmpty() ? HashingMethods.Calculate(dependencyHashes).ToHash128() : new Hash128();
operation.Info = new AssetBundleInfo();
operation.Info.bundleName = bundleName;
operation.Info.bundleAssets = bundleAssets;
m_WriteData.WriteOperations.Add(operation);
m_WriteData.FileToUsageSet.Add(command.internalName, usageSet);
m_WriteData.FileToReferenceMap.Add(command.internalName, referenceMap);
}
#if !UNITY_2019_1_OR_NEWER || NONRECURSIVE_DEPENDENCY_DATA
static int GetSortIndex(System.Type type)
{
// Closely matches CalculateSortIndex in C++, not 100% as the same info is not available to C#, doesn't need to be 100%
if (type == null)
return int.MaxValue - 5;
if (type == typeof(MonoScript))
return int.MinValue;
if (typeof(ScriptableObject).IsAssignableFrom(type))
return int.MaxValue - 4;
if (typeof(MonoBehaviour).IsAssignableFrom(type))
return int.MaxValue - 3;
if (typeof(TerrainData).IsAssignableFrom(type))
return int.MaxValue - 2;
return System.BitConverter.ToInt32(HashingMethods.Calculate(type.Name).ToBytes(), 0);
}
struct SortObject
{
public ObjectIdentifier objectId;
public int sortIndex;
}
internal static List<ObjectIdentifier> GetSortedSceneObjectIdentifiers(List<ObjectIdentifier> objects)
{
var types = new List<System.Type>(BuildCacheUtility.GetMainTypeForObjects(objects));
var sortedObjects = new List<SortObject>();
for (int i = 0; i < objects.Count; i++)
sortedObjects.Add(new SortObject { sortIndex = GetSortIndex(types[i]), objectId = objects[i] });
return sortedObjects.OrderBy(x => x.sortIndex).Select(x => x.objectId).ToList();
}
#endif
List<ObjectIdentifier> GetFileObjectsForScene(string internalName)
{
var fileObjects = m_WriteData.FileToObjects[internalName];
#if !UNITY_2019_1_OR_NEWER || NONRECURSIVE_DEPENDENCY_DATA
// ContentBuildInterface.PrepareScene was not returning stable sorted references, causing a indeterminism and loading errors in some cases
// Add correct sorting here until patch lands to fix the API.
// Additionally, if we are using Non-Recursive build mode, we still need to sort by type.
#if NONRECURSIVE_DEPENDENCY_DATA
if (m_Parameters.NonRecursiveDependencies)
#endif
{
fileObjects = GetSortedSceneObjectIdentifiers(fileObjects);
}
#endif
return fileObjects;
}
void CreateSceneBundleCommand(string bundleName, string internalName, GUID scene, List<GUID> bundledScenes, Dictionary<GUID, string> assetToMainFile)
{
var fileObjects = GetFileObjectsForScene(internalName);
var command = CreateWriteCommand(internalName, fileObjects, new LinearPackedIdentifiers(3)); // Start at 3: PreloadData = 1, AssetBundle = 2
var usageSet = new BuildUsageTagSet();
var referenceMap = new BuildReferenceMap();
var preloadObjects = new List<ObjectIdentifier>();
var bundleScenes = new List<SceneLoadInfo>();
var dependencyInfo = m_DependencyData.SceneInfo[scene];
var fileObjectSet = new HashSet<ObjectIdentifier>(fileObjects);
usageSet.UnionWith(m_DependencyData.SceneUsage[scene]);
referenceMap.AddMappings(command.internalName, command.serializeObjects.ToArray());
foreach (var referencedObject in dependencyInfo.referencedObjects)
{
if (fileObjectSet.Contains(referencedObject))
continue;
preloadObjects.Add(referencedObject);
}
foreach (var bundledScene in bundledScenes)
{
var loadInfo = new SceneLoadInfo();
loadInfo.asset = bundledScene;
loadInfo.internalName = Path.GetFileNameWithoutExtension(assetToMainFile[bundledScene]);
loadInfo.address = m_BuildContent.Addresses[bundledScene];
bundleScenes.Add(loadInfo);
}
var operation = new SceneBundleWriteOperation();
operation.Command = command;
operation.UsageSet = usageSet;
operation.ReferenceMap = referenceMap;
operation.DependencyHash = m_DependencyData.DependencyHash.TryGetValue(scene, out var hash) ? hash : new Hash128();
operation.Scene = dependencyInfo.scene;
#if !UNITY_2019_3_OR_NEWER
operation.ProcessedScene = dependencyInfo.processedScene;
#endif
operation.PreloadInfo = new PreloadInfo();
operation.PreloadInfo.preloadObjects = preloadObjects;
operation.Info = new SceneBundleInfo();
operation.Info.bundleName = bundleName;
operation.Info.bundleScenes = bundleScenes;
m_WriteData.WriteOperations.Add(operation);
m_WriteData.FileToUsageSet.Add(command.internalName, usageSet);
m_WriteData.FileToReferenceMap.Add(command.internalName, referenceMap);
}
void CreateSceneDataCommand(string internalName, GUID scene)
{
var fileObjects = GetFileObjectsForScene(internalName);
var command = CreateWriteCommand(internalName, fileObjects, new LinearPackedIdentifiers(2)); // Start at 3: PreloadData = 1
var usageSet = new BuildUsageTagSet();
var referenceMap = new BuildReferenceMap();
var preloadObjects = new List<ObjectIdentifier>();
var dependencyInfo = m_DependencyData.SceneInfo[scene];
var fileObjectSet = new HashSet<ObjectIdentifier>(fileObjects);
usageSet.UnionWith(m_DependencyData.SceneUsage[scene]);
referenceMap.AddMappings(command.internalName, command.serializeObjects.ToArray());
foreach (var referencedObject in dependencyInfo.referencedObjects)
{
if (fileObjectSet.Contains(referencedObject))
continue;
preloadObjects.Add(referencedObject);
}
var operation = new SceneDataWriteOperation();
operation.Command = command;
operation.UsageSet = usageSet;
operation.ReferenceMap = referenceMap;
operation.DependencyHash = m_DependencyData.DependencyHash.TryGetValue(scene, out var hash) ? hash : new Hash128();
operation.Scene = dependencyInfo.scene;
#if !UNITY_2019_3_OR_NEWER
operation.ProcessedScene = dependencyInfo.processedScene;
#endif
operation.PreloadInfo = new PreloadInfo();
operation.PreloadInfo.preloadObjects = preloadObjects;
m_WriteData.FileToReferenceMap.Add(command.internalName, referenceMap);
m_WriteData.FileToUsageSet.Add(command.internalName, usageSet);
m_WriteData.WriteOperations.Add(operation);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f8f85552081e62e47abaaba497a138d7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,124 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Generates reference maps and usage sets for asset bundles.
/// </summary>
public class GenerateBundleMaps : IBuildTask
{
/// <inheritdoc />
public int Version { get { return 1; } }
#pragma warning disable 649
[InjectContext(ContextUsage.In)]
IDependencyData m_DependencyData;
[InjectContext]
IBundleWriteData m_WriteData;
[InjectContext(ContextUsage.In, true)]
IBuildLogger m_Log;
#pragma warning restore 649
/// <inheritdoc />
public ReturnCode Run()
{
Dictionary<string, WriteCommand> fileToCommand;
Dictionary<string, HashSet<ObjectIdentifier>> forwardObjectDependencies;
Dictionary<string, HashSet<string>> forwardFileDependencies;
Dictionary<string, HashSet<GUID>> reverseAssetDependencies;
// BuildReferenceMap details what objects exist in other bundles that objects in a source bundle depend upon (forward dependencies)
// BuildUsageTagSet details the conditional data needed to be written by objects in a source bundle that is in used by objects in other bundles (reverse dependencies)
using (m_Log.ScopedStep(LogLevel.Info, $"Temporary Map Creations"))
{
fileToCommand = m_WriteData.WriteOperations.ToDictionary(x => x.Command.internalName, x => x.Command);
forwardObjectDependencies = new Dictionary<string, HashSet<ObjectIdentifier>>();
forwardFileDependencies = new Dictionary<string, HashSet<string>>();
reverseAssetDependencies = new Dictionary<string, HashSet<GUID>>();
foreach (var pair in m_WriteData.AssetToFiles)
{
GUID asset = pair.Key;
List<string> files = pair.Value;
// The includes for an asset live in the first file, references could live in any file
forwardObjectDependencies.GetOrAdd(files[0], out HashSet<ObjectIdentifier> objectDependencies);
forwardFileDependencies.GetOrAdd(files[0], out HashSet<string> fileDependencies);
// Grab the list of object references for the asset or scene and add them to the forward dependencies hash set for this file (write command)
if (m_DependencyData.AssetInfo.TryGetValue(asset, out AssetLoadInfo assetInfo))
objectDependencies.UnionWith(assetInfo.referencedObjects);
if (m_DependencyData.SceneInfo.TryGetValue(asset, out SceneDependencyInfo sceneInfo))
objectDependencies.UnionWith(sceneInfo.referencedObjects);
// Grab the list of file references for the asset or scene and add them to the forward dependencies hash set for this file (write command)
// While doing so, also add the asset to the reverse dependencies hash set for all the other files it depends upon.
// We already ensure BuildReferenceMap & BuildUsageTagSet contain the objects in this write command in GenerateBundleCommands. So skip over the first file (self)
for (int i = 1; i < files.Count; i++)
{
fileDependencies.Add(files[i]);
reverseAssetDependencies.GetOrAdd(files[i], out HashSet<GUID> reverseDependencies);
reverseDependencies.Add(asset);
}
}
}
// Using the previously generated forward dependency maps, update the BuildReferenceMap per WriteCommand to contain just the references that we care about
using (m_Log.ScopedStep(LogLevel.Info, $"Populate BuildReferenceMaps"))
{
foreach (var operation in m_WriteData.WriteOperations)
{
var internalName = operation.Command.internalName;
BuildReferenceMap referenceMap = m_WriteData.FileToReferenceMap[internalName];
if (!forwardObjectDependencies.TryGetValue(internalName, out var objectDependencies))
continue; // this bundle has no external dependencies
if (!forwardFileDependencies.TryGetValue(internalName, out var fileDependencies))
continue; // this bundle has no external dependencies
foreach (string file in fileDependencies)
{
WriteCommand dependentCommand = fileToCommand[file];
foreach (var serializedObject in dependentCommand.serializeObjects)
{
// Only add objects we are referencing. This ensures that new/removed objects to files we depend upon will not cause a rebuild
// of this file, unless are referencing the new/removed objects.
if (!objectDependencies.Contains(serializedObject.serializationObject))
continue;
referenceMap.AddMapping(file, serializedObject.serializationIndex, serializedObject.serializationObject);
}
}
}
}
// Using the previously generate reverse dependency map, create the BuildUsageTagSet per WriteCommand to contain just the data that we care about
using (m_Log.ScopedStep(LogLevel.Info, $"Populate BuildUsageTagSet"))
{
foreach (var operation in m_WriteData.WriteOperations)
{
var internalName = operation.Command.internalName;
BuildUsageTagSet fileUsage = m_WriteData.FileToUsageSet[internalName];
if (reverseAssetDependencies.TryGetValue(internalName, out var assetDependencies))
{
foreach (GUID asset in assetDependencies)
{
if (m_DependencyData.AssetUsage.TryGetValue(asset, out var assetUsage))
fileUsage.UnionWith(assetUsage);
if (m_DependencyData.SceneUsage.TryGetValue(asset, out var sceneUsage))
fileUsage.UnionWith(sceneUsage);
}
}
if (ReflectionExtensions.SupportsFilterToSubset)
fileUsage.FilterToSubset(m_WriteData.FileToObjects[internalName].ToArray());
}
}
return ReturnCode.Success;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9d24a7995f0e1ed4c81c185bcc48a695
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,223 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
using UnityEditor.Build.Utilities;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Packs each asset bundle and calculates the asset load file dependency list.
/// </summary>
public class GenerateBundlePacking : IBuildTask
{
/// <inheritdoc />
public int Version { get { return 1; } }
#pragma warning disable 649
[InjectContext(ContextUsage.In)]
IBundleBuildContent m_BuildContent;
[InjectContext(ContextUsage.In)]
IDependencyData m_DependencyData;
[InjectContext]
IBundleWriteData m_WriteData;
[InjectContext(ContextUsage.In)]
IDeterministicIdentifiers m_PackingMethod;
#if UNITY_2019_3_OR_NEWER
[InjectContext(ContextUsage.In, true)]
ICustomAssets m_CustomAssets;
#endif
#pragma warning restore 649
static bool ValidAssetBundle(List<GUID> assets, HashSet<GUID> customAssets)
{
// Custom Valid Asset Bundle function that tests if every asset is known by the asset database, is an asset (not a scene), or is a user driven custom asset
return assets.All(x => ValidationMethods.ValidAsset(x) == ValidationMethods.Status.Asset || customAssets.Contains(x));
}
/// <inheritdoc />
public ReturnCode Run()
{
Dictionary<GUID, List<GUID>> assetToReferences = new Dictionary<GUID, List<GUID>>();
HashSet<GUID> customAssets = new HashSet<GUID>();
#if UNITY_2019_3_OR_NEWER
if (m_CustomAssets != null)
customAssets.UnionWith(m_CustomAssets.Assets);
#endif
// Pack each asset bundle
foreach (var bundle in m_BuildContent.BundleLayout)
{
if (ValidAssetBundle(bundle.Value, customAssets))
PackAssetBundle(bundle.Key, bundle.Value, assetToReferences);
else if (ValidationMethods.ValidSceneBundle(bundle.Value))
PackSceneBundle(bundle.Key, bundle.Value, assetToReferences);
}
// Calculate Asset file load dependency list
foreach (var bundle in m_BuildContent.BundleLayout)
{
foreach (var asset in bundle.Value)
{
List<string> files = m_WriteData.AssetToFiles[asset];
List<GUID> references = assetToReferences[asset];
foreach (var reference in references)
{
List<string> referenceFiles = m_WriteData.AssetToFiles[reference];
if (!files.Contains(referenceFiles[0]))
files.Add(referenceFiles[0]);
}
}
}
return ReturnCode.Success;
}
void PackAssetBundle(string bundleName, List<GUID> includedAssets, Dictionary<GUID, List<GUID>> assetToReferences)
{
var internalName = string.Format(CommonStrings.AssetBundleNameFormat, m_PackingMethod.GenerateInternalFileName(bundleName));
var allObjects = new HashSet<ObjectIdentifier>();
Dictionary<GUID, HashSet<ObjectIdentifier>> assetObjectIdentifierHashSets = new Dictionary<GUID, HashSet<ObjectIdentifier>>();
foreach (var asset in includedAssets)
{
AssetLoadInfo assetInfo = m_DependencyData.AssetInfo[asset];
allObjects.UnionWith(assetInfo.includedObjects);
var references = new List<ObjectIdentifier>();
references.AddRange(assetInfo.referencedObjects);
assetToReferences[asset] = FilterReferencesForAsset(m_DependencyData, asset, references, null, null, assetObjectIdentifierHashSets);
allObjects.UnionWith(references);
m_WriteData.AssetToFiles[asset] = new List<string> { internalName };
}
m_WriteData.FileToBundle.Add(internalName, bundleName);
m_WriteData.FileToObjects.Add(internalName, allObjects.ToList());
}
void PackSceneBundle(string bundleName, List<GUID> includedScenes, Dictionary<GUID, List<GUID>> assetToReferences)
{
if (includedScenes.IsNullOrEmpty())
return;
string firstFileName = "";
HashSet<ObjectIdentifier> previousSceneObjects = new HashSet<ObjectIdentifier>();
HashSet<GUID> previousSceneAssets = new HashSet<GUID>();
List<string> sceneInternalNames = new List<string>();
Dictionary<GUID, HashSet<ObjectIdentifier>> assetObjectIdentifierHashSets = new Dictionary<GUID, HashSet<ObjectIdentifier>>();
foreach (var scene in includedScenes)
{
var scenePath = AssetDatabase.GUIDToAssetPath(scene.ToString());
var internalSceneName = m_PackingMethod.GenerateInternalFileName(scenePath);
if (string.IsNullOrEmpty(firstFileName))
firstFileName = internalSceneName;
var internalName = string.Format(CommonStrings.SceneBundleNameFormat, firstFileName, internalSceneName);
SceneDependencyInfo sceneInfo = m_DependencyData.SceneInfo[scene];
var references = new List<ObjectIdentifier>();
references.AddRange(sceneInfo.referencedObjects);
assetToReferences[scene] = FilterReferencesForAsset(m_DependencyData, scene, references, previousSceneObjects, previousSceneAssets, assetObjectIdentifierHashSets);
previousSceneObjects.UnionWith(references);
previousSceneAssets.UnionWith(assetToReferences[scene]);
m_WriteData.FileToObjects.Add(internalName, references);
m_WriteData.FileToBundle.Add(internalName, bundleName);
var files = new List<string> { internalName };
files.AddRange(sceneInternalNames);
m_WriteData.AssetToFiles[scene] = files;
sceneInternalNames.Add(internalName);
}
}
static HashSet<ObjectIdentifier> GetRefObjectIdLookup(AssetLoadInfo referencedAsset, Dictionary<GUID, HashSet<ObjectIdentifier>> assetObjectIdentifierHashSets)
{
HashSet<ObjectIdentifier> refObjectIdLookup;
if ((assetObjectIdentifierHashSets == null) || (!assetObjectIdentifierHashSets.TryGetValue(referencedAsset.asset, out refObjectIdLookup)))
{
refObjectIdLookup = new HashSet<ObjectIdentifier>(referencedAsset.referencedObjects);
assetObjectIdentifierHashSets?.Add(referencedAsset.asset, refObjectIdLookup);
}
return refObjectIdLookup;
}
internal static List<GUID> FilterReferencesForAsset(IDependencyData dependencyData, GUID asset, List<ObjectIdentifier> references, HashSet<ObjectIdentifier> previousSceneObjects = null, HashSet<GUID> previousSceneReferences = null, Dictionary<GUID, HashSet<ObjectIdentifier>> assetObjectIdentifierHashSets = null)
{
var referencedAssets = new HashSet<AssetLoadInfo>();
var referencedAssetsGuids = new List<GUID>(referencedAssets.Count);
var referencesPruned = new List<ObjectIdentifier>(references.Count);
// Remove Default Resources and Includes for Assets assigned to Bundles
foreach (ObjectIdentifier reference in references)
{
if (reference.filePath.Equals(CommonStrings.UnityDefaultResourcePath, StringComparison.OrdinalIgnoreCase))
continue;
if (dependencyData.AssetInfo.TryGetValue(reference.guid, out AssetLoadInfo referenceInfo))
{
if (referencedAssets.Add(referenceInfo))
referencedAssetsGuids.Add(referenceInfo.asset);
continue;
}
referencesPruned.Add(reference);
}
references.Clear();
references.AddRange(referencesPruned);
// Remove References also included by non-circular Referenced Assets
// Remove References also included by circular Referenced Assets if Asset's GUID is higher than Referenced Asset's GUID
foreach (AssetLoadInfo referencedAsset in referencedAssets)
{
if ((asset > referencedAsset.asset) || (asset == referencedAsset.asset))
{
references.RemoveAll(GetRefObjectIdLookup(referencedAsset, assetObjectIdentifierHashSets).Contains);
}
else
{
bool exists = false;
foreach (ObjectIdentifier referencedObject in referencedAsset.referencedObjects)
{
if (referencedObject.guid == asset)
{
exists = true;
break;
}
}
if (!exists)
{
references.RemoveAll(GetRefObjectIdLookup(referencedAsset, assetObjectIdentifierHashSets).Contains);
}
}
}
// Special path for scenes, they can reference the same assets previously references
if (!previousSceneReferences.IsNullOrEmpty())
{
foreach (GUID reference in previousSceneReferences)
{
if (!dependencyData.AssetInfo.TryGetValue(reference, out AssetLoadInfo referencedAsset))
continue;
var refObjectIdLookup = GetRefObjectIdLookup(referencedAsset, assetObjectIdentifierHashSets);
// NOTE: It's impossible for an asset to depend on a scene, thus no need for circular reference checks
// So just remove and add a dependency on the asset if there is a need to depend upon it.
if (references.RemoveAll(refObjectIdLookup.Contains) > 0)
referencedAssetsGuids.Add(referencedAsset.asset);
}
}
// Special path for scenes, they can use data from previous sharedAssets in the same bundle
if (!previousSceneObjects.IsNullOrEmpty())
references.RemoveAll(previousSceneObjects.Contains);
return referencedAssetsGuids;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8de0232a33280c9479a3db23d266ef17
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,49 @@
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Creates a link.xml file in the output folder to use with Unity managed code stripping.
/// </summary>
public class GenerateLinkXml : IBuildTask
{
/// <inheritdoc />
public int Version { get { return 1; } }
#pragma warning disable 649
[InjectContext(ContextUsage.In)]
IBuildParameters m_Parameters;
[InjectContext(ContextUsage.In)]
IBuildResults m_Results;
#pragma warning restore 649
const string k_LinkXml = "link.xml";
/// <inheritdoc/>
public ReturnCode Run()
{
if (!m_Parameters.WriteLinkXML)
return ReturnCode.SuccessNotRun;
var linker = LinkXmlGenerator.CreateDefault();
foreach (var writeResult in m_Results.WriteResults)
{
linker.AddTypes(writeResult.Value.includedTypes);
#if UNITY_2021_1_OR_NEWER
linker.AddSerializedClass(writeResult.Value.includedSerializeReferenceFQN);
#else
if (writeResult.Value.GetType().GetProperty("includedSerializeReferenceFQN") != null)
linker.AddSerializedClass(writeResult.Value.GetType().GetProperty("includedSerializeReferenceFQN").GetValue(writeResult.Value) as System.Collections.Generic.IEnumerable<string>);
#endif
}
var linkPath = m_Parameters.GetOutputFilePathForIdentifier(k_LinkXml);
linker.Save(linkPath);
return ReturnCode.Success;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f39ee2ccb61c17549b2f6551af68ab88
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,67 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
using UnityEditor.Build.Pipeline.WriteTypes;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Creates sub asset load information.
/// </summary>
public class GenerateSubAssetPathMaps : IBuildTask
{
/// <inheritdoc />
public int Version { get { return 1; } }
#pragma warning disable 649
[InjectContext]
IBundleWriteData m_WriteData;
[InjectContext(ContextUsage.In, true)]
IBuildExtendedAssetData m_ExtendedAssetData;
#pragma warning restore 649
/// <inheritdoc />
public ReturnCode Run()
{
if (m_ExtendedAssetData == null || m_ExtendedAssetData.ExtendedData.IsNullOrEmpty())
return ReturnCode.SuccessNotRun;
Dictionary<string, IWriteOperation> fileToOperation = m_WriteData.WriteOperations.ToDictionary(x => x.Command.internalName, x => x);
foreach (var pair in m_ExtendedAssetData.ExtendedData)
{
GUID asset = pair.Key;
string mainFile = m_WriteData.AssetToFiles[asset][0];
var abOp = fileToOperation[mainFile] as AssetBundleWriteOperation;
int assetInfoIndex = abOp.Info.bundleAssets.FindIndex(x => x.asset == asset);
AssetLoadInfo assetInfo = abOp.Info.bundleAssets[assetInfoIndex];
int offset = 1;
foreach (var subAsset in pair.Value.Representations)
{
var secondaryAssetInfo = CreateSubAssetLoadInfo(assetInfo, subAsset);
abOp.Info.bundleAssets.Insert(assetInfoIndex + offset, secondaryAssetInfo);
offset++;
}
}
return ReturnCode.Success;
}
static AssetLoadInfo CreateSubAssetLoadInfo(AssetLoadInfo assetInfo, ObjectIdentifier subAsset)
{
var subAssetLoadInfo = new AssetLoadInfo();
subAssetLoadInfo.asset = assetInfo.asset;
subAssetLoadInfo.address = assetInfo.address;
subAssetLoadInfo.referencedObjects = new List<ObjectIdentifier>(assetInfo.referencedObjects);
subAssetLoadInfo.includedObjects = new List<ObjectIdentifier>(assetInfo.includedObjects);
var index = subAssetLoadInfo.includedObjects.IndexOf(subAsset);
subAssetLoadInfo.includedObjects.Swap(0, index);
return subAssetLoadInfo;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fa9edcd7497f45242bddd18f8e0582d4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,31 @@
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Processes all callbacks after the dependency calculation task.
/// </summary>
public class PostDependencyCallback : IBuildTask
{
/// <inheritdoc />
public int Version { get { return 1; } }
#pragma warning disable 649
[InjectContext]
IBuildParameters m_Parameters;
[InjectContext]
IDependencyData m_DependencyData;
[InjectContext(ContextUsage.In)]
IDependencyCallback m_Callback;
#pragma warning restore 649
/// <inheritdoc />
public ReturnCode Run()
{
return m_Callback.PostDependency(m_Parameters, m_DependencyData);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 225da2e71801378408ef9f717020c323
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,34 @@
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Processes all callbacks after the bundle packing task.
/// </summary>
public class PostPackingCallback : IBuildTask
{
/// <inheritdoc />
public int Version { get { return 1; } }
#pragma warning disable 649
[InjectContext]
IBuildParameters m_Parameters;
[InjectContext]
IDependencyData m_DependencyData;
[InjectContext]
IWriteData m_WriteData;
[InjectContext(ContextUsage.In)]
IPackingCallback m_Callback;
#pragma warning restore 649
/// <inheritdoc />
public ReturnCode Run()
{
return m_Callback.PostPacking(m_Parameters, m_DependencyData, m_WriteData);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1f0e09841d3888f46a1a3f84af21a639
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,31 @@
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Processes all callbacks after the script building task.
/// </summary>
public class PostScriptsCallback : IBuildTask
{
/// <inheritdoc />
public int Version { get { return 1; } }
#pragma warning disable 649
[InjectContext]
IBuildParameters m_Parameters;
[InjectContext]
IBuildResults m_Results;
[InjectContext(ContextUsage.In)]
IScriptsCallback m_Callback;
#pragma warning restore 649
/// <inheritdoc />
public ReturnCode Run()
{
return m_Callback.PostScripts(m_Parameters, m_Results);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3ed24626228f71246a5f32ebe0fb61e2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,37 @@
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Processes all callbacks after the writing task.
/// </summary>
public class PostWritingCallback : IBuildTask
{
/// <inheritdoc />
public int Version { get { return 1; } }
#pragma warning disable 649
[InjectContext]
IBuildParameters m_Parameters;
[InjectContext]
IDependencyData m_DependencyData;
[InjectContext]
IWriteData m_WriteData;
[InjectContext]
IBuildResults m_Results;
[InjectContext(ContextUsage.In)]
IWritingCallback m_Callback;
#pragma warning restore 649
/// <inheritdoc />
public ReturnCode Run()
{
return m_Callback.PostWriting(m_Parameters, m_DependencyData, m_WriteData, m_Results);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0f2109e178c64897a911d0c8471401d7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,126 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Validates scene dependency data and stores it in the cache.
/// </summary>
public class PreviewSceneDependencyData : IBuildTask
{
/// <inheritdoc />
public int Version { get { return 1; } }
#pragma warning disable 649
[InjectContext(ContextUsage.In)]
IBuildParameters m_Parameters;
[InjectContext(ContextUsage.In)]
IBuildContent m_Content;
[InjectContext]
IDependencyData m_DependencyData;
[InjectContext(ContextUsage.In, true)]
IProgressTracker m_Tracker;
[InjectContext(ContextUsage.In, true)]
IBuildCache m_Cache;
#pragma warning restore 649
CachedInfo GetCachedInfo(GUID scene, IEnumerable<ObjectIdentifier> references, SceneDependencyInfo sceneInfo, BuildUsageTagSet usageTags)
{
var info = new CachedInfo();
info.Asset = m_Cache.GetCacheEntry(scene, Version);
var dependencies = new HashSet<CacheEntry>();
foreach (ObjectIdentifier reference in references)
dependencies.Add(m_Cache.GetCacheEntry(reference));
info.Dependencies = dependencies.ToArray();
info.Data = new object[] { sceneInfo, usageTags };
return info;
}
/// <inheritdoc />
public ReturnCode Run()
{
IList<CachedInfo> cachedInfo = null;
IList<CachedInfo> uncachedInfo = null;
if (m_Parameters.UseCache && m_Cache != null)
{
IList<CacheEntry> entries = m_Content.Scenes.Select(x => m_Cache.GetCacheEntry(x, Version)).ToList();
m_Cache.LoadCachedData(entries, out cachedInfo);
uncachedInfo = new List<CachedInfo>();
}
for (int i = 0; i < m_Content.Scenes.Count; i++)
{
GUID scene = m_Content.Scenes[i];
string scenePath = AssetDatabase.GUIDToAssetPath(scene.ToString());
SceneDependencyInfo sceneInfo;
BuildUsageTagSet usageTags;
if (cachedInfo != null && cachedInfo[i] != null)
{
if (!m_Tracker.UpdateInfoUnchecked(string.Format("{0} (Cached)", scenePath)))
return ReturnCode.Canceled;
sceneInfo = (SceneDependencyInfo)cachedInfo[i].Data[0];
usageTags = cachedInfo[i].Data[1] as BuildUsageTagSet;
}
else
{
if (!m_Tracker.UpdateInfoUnchecked(scenePath))
return ReturnCode.Canceled;
var references = new HashSet<ObjectIdentifier>();
string[] dependencies = AssetDatabase.GetDependencies(scenePath);
foreach (var assetPath in dependencies)
{
var assetGuid = new GUID(AssetDatabase.AssetPathToGUID(assetPath));
if (ValidationMethods.ValidAsset(assetGuid) != ValidationMethods.Status.Asset)
continue;
// TODO: Use Cache to speed this up?
var assetIncludes = ContentBuildInterface.GetPlayerObjectIdentifiersInAsset(assetGuid, m_Parameters.Target);
var assetReferences = ContentBuildInterface.GetPlayerDependenciesForObjects(assetIncludes, m_Parameters.Target, m_Parameters.ScriptInfo);
references.UnionWith(assetIncludes);
references.UnionWith(assetReferences);
}
sceneInfo = new SceneDependencyInfo();
usageTags = new BuildUsageTagSet();
sceneInfo.SetScene(scenePath);
sceneInfo.SetProcessedScene(scenePath);
sceneInfo.SetReferencedObjects(references.ToArray());
if (uncachedInfo != null)
uncachedInfo.Add(GetCachedInfo(scene, sceneInfo.referencedObjects, sceneInfo, usageTags));
}
SetOutputInformation(scene, sceneInfo, usageTags);
}
if (m_Parameters.UseCache && m_Cache != null)
m_Cache.SaveCachedData(uncachedInfo);
return ReturnCode.Success;
}
void SetOutputInformation(GUID asset, SceneDependencyInfo sceneInfo, BuildUsageTagSet usageTags)
{
// Add generated scene information to BuildDependencyData
m_DependencyData.SceneInfo.Add(asset, sceneInfo);
m_DependencyData.SceneUsage.Add(asset, usageTags);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 46ff9bb9c11e4daa9d9424fa410fcc30
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,32 @@
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Sprites;
using UnityEditor.U2D;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Builds the cache data for all sprite atlases.
/// </summary>
public class RebuildSpriteAtlasCache : IBuildTask
{
/// <inheritdoc />
public int Version { get { return 1; } }
#pragma warning disable 649
[InjectContext(ContextUsage.In)]
IBuildParameters m_Parameters;
#pragma warning restore 649
/// <inheritdoc />
public ReturnCode Run()
{
// TODO: Need a return value if this ever can fail
#if !UNITY_2020_1_OR_NEWER
Packer.RebuildAtlasCacheIfNeeded(m_Parameters.Target, true, Packer.Execution.Normal);
#endif
SpriteAtlasUtility.PackAllAtlases(m_Parameters.Target);
return ReturnCode.Success;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 49d4a1540f2adfc418fd2e5dd536420b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,72 @@
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Removes all unusued sprite source data from asset references and scene references.
/// </summary>
public class StripUnusedSpriteSources : IBuildTask
{
/// <inheritdoc />
public int Version { get { return 2; } }
#pragma warning disable 649
[InjectContext]
IDependencyData m_DependencyData;
[InjectContext(ContextUsage.In, true)]
IBuildSpriteData m_SpriteData;
[InjectContext(ContextUsage.InOut, true)]
IBuildExtendedAssetData m_ExtendedAssetData;
#pragma warning restore 649
/// <inheritdoc />
public ReturnCode Run()
{
if (m_SpriteData == null || m_SpriteData.ImporterData.Count == 0)
return ReturnCode.SuccessNotRun;
if (EditorSettings.spritePackerMode == SpritePackerMode.Disabled)
return ReturnCode.SuccessNotRun;
var unusedSources = new HashSet<ObjectIdentifier>();
var textures = m_SpriteData.ImporterData.Values.Where(x => x.PackedSprite).Select(x => x.SourceTexture);
unusedSources.UnionWith(textures);
// Count refs from assets
var assetRefs = m_DependencyData.AssetInfo.SelectMany(x => x.Value.referencedObjects);
foreach (ObjectIdentifier reference in assetRefs)
unusedSources.Remove(reference);
// Count refs from scenes
var sceneRefs = m_DependencyData.SceneInfo.SelectMany(x => x.Value.referencedObjects);
foreach (ObjectIdentifier reference in sceneRefs)
unusedSources.Remove(reference);
SetOutputInformation(unusedSources);
return ReturnCode.Success;
}
void SetOutputInformation(HashSet<ObjectIdentifier> unusedSources)
{
foreach (var source in unusedSources)
{
var assetInfo = m_DependencyData.AssetInfo[source.guid];
assetInfo.includedObjects.RemoveAt(0);
ExtendedAssetData extendedData;
if (m_ExtendedAssetData != null && m_ExtendedAssetData.ExtendedData.TryGetValue(source.guid, out extendedData))
{
extendedData.Representations.Remove(source);
if (extendedData.Representations.Count == 1)
m_ExtendedAssetData.ExtendedData.Remove(source.guid);
}
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: db8e822ddcadee542b625284a3d0aa16
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,35 @@
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Sets the target build platform based on the build parameters.
/// </summary>
public class SwitchToBuildPlatform : IBuildTask
{
/// <inheritdoc />
public int Version { get { return 1; } }
#pragma warning disable 649
[InjectContext(ContextUsage.In)]
IBuildParameters m_Parameters;
[InjectContext(ContextUsage.In, true)]
IEditorBuildCallbacks m_InterfaceWrapper;
#pragma warning restore 649
/// <inheritdoc />
public ReturnCode Run()
{
if (EditorUserBuildSettings.SwitchActiveBuildTarget(m_Parameters.Group, m_Parameters.Target))
{
if (m_InterfaceWrapper != null)
m_InterfaceWrapper.InitializeCallbacks();
return ReturnCode.Success;
}
return ReturnCode.Error;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8195792273a50ed4982b1717d1203dbf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,143 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
using UnityEditor.Build.Utilities;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Updates the layout for bundle objects.
/// </summary>
public class UpdateBundleObjectLayout : IBuildTask
{
/// <inheritdoc />
public int Version { get { return 1; } }
#pragma warning disable 649
[InjectContext(ContextUsage.In, true)]
IBundleExplictObjectLayout m_Layout;
[InjectContext]
IBundleBuildContent m_Content;
[InjectContext(ContextUsage.In)]
IDependencyData m_DependencyData;
[InjectContext]
IBundleWriteData m_WriteData;
[InjectContext(ContextUsage.In)]
IDeterministicIdentifiers m_PackingMethod;
[InjectContext(ContextUsage.In, true)]
IBuildLogger m_Log;
#pragma warning restore 649
/// <inheritdoc />
public ReturnCode Run()
{
if (m_Layout == null || m_Layout.ExplicitObjectLocation.IsNullOrEmpty())
return ReturnCode.SuccessNotRun;
var ObjectToAssetReferences = new Dictionary<ObjectIdentifier, List<GUID>>();
var ObjectToFiles = new Dictionary<ObjectIdentifier, List<string>>();
using (m_Log.ScopedStep(LogLevel.Info, "PopulateReferencesMaps", true))
{
var task = Task.Run(() =>
{
using (m_Log.ScopedStep(LogLevel.Info, "Populate Assets Map", $"Count={m_DependencyData.AssetInfo.Count}"))
{
foreach (KeyValuePair<GUID, AssetLoadInfo> dependencyPair in m_DependencyData.AssetInfo)
{
PopulateReferencesMap(dependencyPair.Key, dependencyPair.Value.includedObjects, ObjectToAssetReferences);
PopulateReferencesMap(dependencyPair.Key, dependencyPair.Value.referencedObjects, ObjectToAssetReferences);
}
}
using (m_Log.ScopedStep(LogLevel.Info, "Populate Scenes Map", $"Count={m_DependencyData.SceneInfo.Count}"))
{
foreach (KeyValuePair<GUID, SceneDependencyInfo> dependencyPair in m_DependencyData.SceneInfo)
PopulateReferencesMap(dependencyPair.Key, dependencyPair.Value.referencedObjects, ObjectToAssetReferences);
}
});
using (m_Log.ScopedStep(LogLevel.Info, "Populate Files Map", $"Count={m_WriteData.FileToObjects.Count}"))
{
foreach (KeyValuePair<string, List<ObjectIdentifier>> filePair in m_WriteData.FileToObjects)
PopulateReferencesMap(filePair.Key, filePair.Value, ObjectToFiles);
}
task.Wait();
}
using (m_Log.ScopedStep(LogLevel.Info, "UpdateWriteData"))
{
foreach (var group in m_Layout.ExplicitObjectLocation.GroupBy(s => s.Value))
{
IEnumerable<ObjectIdentifier> objectIDs = group.Select(s => s.Key);
string bundleName = group.Key;
string internalName = string.Format(CommonStrings.AssetBundleNameFormat, m_PackingMethod.GenerateInternalFileName(bundleName));
foreach (var objectID in objectIDs)
{
UpdateAssetToFilesMap(internalName, ObjectToAssetReferences[objectID], m_WriteData.AssetToFiles);
RemoveObjectIDFromFiles(objectID, ObjectToFiles[objectID], m_WriteData.FileToObjects);
}
// Add new mapping for File to Bundle
UpdateFileToBundleMap(bundleName, internalName, m_WriteData.FileToBundle, m_Content.BundleLayout);
// Update File to Object map
UpdateFileToObjectMap(internalName, objectIDs, m_WriteData.FileToObjects);
}
}
return ReturnCode.Success;
}
internal static void PopulateReferencesMap<T>(T key, IList<ObjectIdentifier> objects, Dictionary<ObjectIdentifier, List<T>> map)
{
foreach (var obj in objects)
{
map.GetOrAdd(obj, out var set);
set.Add(key);
}
}
internal static void UpdateAssetToFilesMap(string file, List<GUID> assetsToUpdate, Dictionary<GUID, List<string>> AssetToFiles)
{
foreach (var asset in assetsToUpdate)
{
var assetFiles = AssetToFiles[asset];
if (!assetFiles.Contains(file))
assetFiles.Add(file);
}
}
internal static void RemoveObjectIDFromFiles(ObjectIdentifier objectID, List<string> files, Dictionary<string, List<ObjectIdentifier>> FileToObjects)
{
foreach (var file in files)
FileToObjects[file].Remove(objectID);
}
internal static void UpdateFileToBundleMap(string bundleName, string file, Dictionary<string, string> FileToBundle, Dictionary<string, List<GUID>> BundleLayout)
{
if (!FileToBundle.ContainsKey(file))
{
FileToBundle.Add(file, bundleName);
// NOTE: We want the output result to know about the new bundle, but since we are only
// assigning individual objects to this bundle and not full assets, the asset list will be empty
BundleLayout.Add(bundleName, new List<GUID>());
}
}
internal static void UpdateFileToObjectMap(string file, IEnumerable<ObjectIdentifier> newObjectIDs, Dictionary<string, List<ObjectIdentifier>> FileToObjects)
{
// This is called after remove, thus we can just AddRange as we already know these objects are not in any file
FileToObjects.GetOrAdd(file, out var objectIDs);
objectIDs.AddRange(newObjectIDs);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 69a3d9bdf64896044a9fdb61df07a1f8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,205 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
using UnityEngine;
using static UnityEditor.Build.Pipeline.Utilities.TaskCachingUtility;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Serializes all cache data.
/// </summary>
public class WriteSerializedFiles : IBuildTask, IRunCachedCallbacks<WriteSerializedFiles.Item>
{
/// <inheritdoc />
public int Version { get { return 4; } }
#pragma warning disable 649
[InjectContext(ContextUsage.In)]
IBuildParameters m_Parameters;
[InjectContext(ContextUsage.In)]
IDependencyData m_DependencyData;
[InjectContext(ContextUsage.In)]
IWriteData m_WriteData;
[InjectContext]
IBuildResults m_Results;
[InjectContext(ContextUsage.In, true)]
IProgressTracker m_Tracker;
[InjectContext(ContextUsage.In, true)]
IBuildCache m_Cache;
[InjectContext(ContextUsage.In, true)]
IBuildLogger m_Log;
#pragma warning restore 649
static Hash128 GetPlayerSettingsHash128(BuildTarget target)
{
return HashingMethods.Calculate(
PlayerSettings.stripUnusedMeshComponents,
PlayerSettings.bakeCollisionMeshes
#if UNITY_2020_1_OR_NEWER
, PlayerSettings.mipStripping ? PlayerSettingsApi.GetNumberOfMipsStripped() : 0
#endif
, PlayerSettings.GetGraphicsAPIs(target)
).ToHash128();
}
CacheEntry GetCacheEntry(IWriteOperation operation, BuildSettings settings, BuildUsageTagGlobal globalUsage, bool onlySaveFirstSerializedObject)
{
using (m_Log.ScopedStep(LogLevel.Verbose, "GetCacheEntry", operation.Command.fileName))
{
var entry = new CacheEntry();
entry.Type = CacheEntry.EntryType.Data;
entry.Guid = HashingMethods.Calculate("WriteSerializedFiles", operation.Command.fileName).ToGUID();
entry.Hash = HashingMethods.Calculate(Version, operation.GetHash128(m_Log), settings.GetHash128(), globalUsage, onlySaveFirstSerializedObject, GetPlayerSettingsHash128(settings.target)).ToHash128();
entry.Version = Version;
return entry;
}
}
static void SlimifySerializedObjects(ref WriteResult result)
{
var fileOffsets = new List<ObjectSerializedInfo>();
foreach (ResourceFile serializedFile in result.resourceFiles)
{
if (!serializedFile.serializedFile)
continue;
fileOffsets.Add(result.serializedObjects.First(x => x.header.fileName == serializedFile.fileAlias));
}
result.SetSerializedObjects(fileOffsets.ToArray());
}
CachedInfo GetCachedInfo(CacheEntry entry, WriteResult result, SerializedFileMetaData metaData)
{
var info = new CachedInfo();
info.Asset = entry;
info.Data = new object[] { result, metaData };
info.Dependencies = new CacheEntry[0];
return info;
}
class Item
{
public WriteResult Result;
public SerializedFileMetaData MetaData;
}
BuildSettings m_BuildSettings;
BuildUsageTagGlobal m_GlobalUsage;
IBuildCache m_UseCache;
internal void SetupTaskContext()
{
m_GlobalUsage = m_DependencyData.GlobalUsage;
foreach (var sceneInfo in m_DependencyData.SceneInfo)
m_GlobalUsage |= sceneInfo.Value.globalUsage;
m_BuildSettings = m_Parameters.GetContentBuildSettings();
m_UseCache = m_Parameters.UseCache ? m_Cache : null;
}
/// <inheritdoc />
public ReturnCode Run()
{
SetupTaskContext();
List<WorkItem<Item>> workItems = m_WriteData.WriteOperations.Select(
i => new WorkItem<Item>(new Item(), i.Command.internalName)
).ToList();
return TaskCachingUtility.RunCachedOperation<Item>(
m_UseCache,
m_Log,
m_Tracker,
workItems,
this);
}
internal static SerializedFileMetaData CalculateFileMetadata(ref WriteResult result)
{
List<RawHash> contentHashObjects = new List<RawHash>();
List<RawHash> fullHashObjects = new List<RawHash>();
foreach (ResourceFile file in result.resourceFiles)
{
RawHash fileHash = HashingMethods.CalculateFile(file.fileName);
RawHash contentHash = fileHash;
fullHashObjects.Add(fileHash);
if (file.serializedFile && result.serializedObjects.Count > 0)
{
ObjectSerializedInfo firstObj = result.serializedObjects.First(x => x.header.fileName == file.fileAlias);
using (var stream = new FileStream(file.fileName, FileMode.Open, FileAccess.Read))
{
stream.Position = (long)firstObj.header.offset;
contentHash = HashingMethods.CalculateStream(stream);
}
}
contentHashObjects.Add(contentHash);
}
SerializedFileMetaData data = new SerializedFileMetaData();
data.RawFileHash = HashingMethods.Calculate(fullHashObjects).ToHash128();
data.ContentHash = HashingMethods.Calculate(contentHashObjects).ToHash128();
return data;
}
/// <inheritdoc/>
CacheEntry IRunCachedCallbacks<Item>.CreateCacheEntry(WorkItem<Item> item)
{
return GetCacheEntry(m_WriteData.WriteOperations[item.Index], m_BuildSettings, m_GlobalUsage, ScriptableBuildPipeline.slimWriteResults);
}
/// <inheritdoc/>
void IRunCachedCallbacks<Item>.ProcessUncached(WorkItem<Item> item)
{
IWriteOperation op = m_WriteData.WriteOperations[item.Index];
string targetDir = m_UseCache != null ? m_UseCache.GetCachedArtifactsDirectory(item.entry) : m_Parameters.TempOutputFolder;
Directory.CreateDirectory(targetDir);
using (m_Log.ScopedStep(LogLevel.Info, $"Writing {op.GetType().Name}", op.Command.fileName))
{
#if UNITY_2020_2_OR_NEWER || ENABLE_DETAILED_PROFILE_CAPTURING
using (new ProfileCaptureScope(m_Log, ProfileCaptureOptions.None))
item.Context.Result = op.Write(targetDir, m_BuildSettings, m_GlobalUsage);
#else
item.Context.Result = op.Write(targetDir, m_BuildSettings, m_GlobalUsage);
#endif
}
item.Context.MetaData = CalculateFileMetadata(ref item.Context.Result);
if (ScriptableBuildPipeline.slimWriteResults)
SlimifySerializedObjects(ref item.Context.Result);
}
/// <inheritdoc/>
void IRunCachedCallbacks<Item>.ProcessCached(WorkItem<Item> item, CachedInfo info)
{
item.Context.Result = (WriteResult)info.Data[0];
item.Context.MetaData = (SerializedFileMetaData)info.Data[1];
}
/// <inheritdoc/>
void IRunCachedCallbacks<Item>.PostProcess(WorkItem<Item> item)
{
IWriteOperation op = m_WriteData.WriteOperations[item.Index];
m_Results.WriteResults.Add(op.Command.internalName, item.Context.Result);
m_Results.WriteResultsMetaData.Add(op.Command.internalName, item.Context.MetaData);
}
/// <inheritdoc/>
CachedInfo IRunCachedCallbacks<Item>.CreateCachedInfo(WorkItem<Item> item)
{
return GetCachedInfo(item.entry, item.Context.Result, item.Context.MetaData);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4d29a4991c6c8884aa7a81dc6d2e1cc5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: