WuhuIslandTesting/Library/PackageCache/com.unity.scriptablebuildpipeline@1.21.5/Editor/Tasks/CalculateAssetDependencyData.cs
2025-01-07 02:06:59 +01:00

646 lines
30 KiB
C#

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();
}
}
}