#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 { /// /// Build Task that calculates teh included objects and references objects for custom assets not tracked by the AssetDatabase. /// /// public class CalculateCustomDependencyData : IBuildTask { /// 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 m_AssetInfo = new Dictionary(); Dictionary m_BuildUsage = new Dictionary(); /// 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(); var objectTypes = new List(); var dependencies = new HashSet(); 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 { entry }, out IList cachedInfos); var cachedInfo = cachedInfos[0]; if (cachedInfo != null) { assetInfo = (AssetLoadInfo)cachedInfo.Data[0]; buildUsage = (BuildUsageTagSet)cachedInfo.Data[1]; var objectTypes = (List)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 }); } 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(includedObjects); assetInfo.referencedObjects = new List(referencedObjects); ContentBuildInterface.CalculateBuildUsageTags(referencedObjects, includedObjects, globalUsage, buildUsage, m_DependencyData.DependencyUsageCache); } /// /// 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. /// /// Path to the Unity Serialized File /// Object Identifiers for all the objects in the serialized file /// Types for all the objects in the serialized file public void GetObjectIdentifiersAndTypesForSerializedFile(string path, out ObjectIdentifier[] objectIdentifiers, out Type[] types) { GetObjectIdentifiersAndTypesForSerializedFile(path, out objectIdentifiers, out types, default); } /// /// 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. /// /// Path to the Unity Serialized File /// Object Identifiers for all the objects in the serialized file /// Types for all the objects in the serialized file /// Additional global lighting usage information to include with this custom asset 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); } /// /// Adds mapping and bundle information for a custom asset that contains a set of unity objects. /// /// Object Identifiers that belong to this custom asset /// Path on disk for this custom asset /// Asset Bundle name where to add this custom asset /// Load address to used to load this asset from the Asset Bundle /// Type of the main object for this custom asset 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 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