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 { /// /// Serializes all cache data. /// public class WriteSerializedFiles : IBuildTask, IRunCachedCallbacks { /// 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(); 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; } /// public ReturnCode Run() { SetupTaskContext(); List> workItems = m_WriteData.WriteOperations.Select( i => new WorkItem(new Item(), i.Command.internalName) ).ToList(); return TaskCachingUtility.RunCachedOperation( m_UseCache, m_Log, m_Tracker, workItems, this); } internal static SerializedFileMetaData CalculateFileMetadata(ref WriteResult result) { List contentHashObjects = new List(); List fullHashObjects = new List(); 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; } /// CacheEntry IRunCachedCallbacks.CreateCacheEntry(WorkItem item) { return GetCacheEntry(m_WriteData.WriteOperations[item.Index], m_BuildSettings, m_GlobalUsage, ScriptableBuildPipeline.slimWriteResults); } /// void IRunCachedCallbacks.ProcessUncached(WorkItem 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); } /// void IRunCachedCallbacks.ProcessCached(WorkItem item, CachedInfo info) { item.Context.Result = (WriteResult)info.Data[0]; item.Context.MetaData = (SerializedFileMetaData)info.Data[1]; } /// void IRunCachedCallbacks.PostProcess(WorkItem 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); } /// CachedInfo IRunCachedCallbacks.CreateCachedInfo(WorkItem item) { return GetCachedInfo(item.entry, item.Context.Result, item.Context.MetaData); } } }