//#define DEBUG_LOG_NAME using UnityEngine; using System.Collections.Generic; namespace Cinemachine { /// /// Attempt to track on what clock transforms get updated /// [DocumentationSorting(DocumentationSortingAttribute.Level.Undoc)] internal class UpdateTracker { public enum UpdateClock { Fixed, Late } class UpdateStatus { const int kWindowSize = 30; int windowStart; int numWindowLateUpdateMoves; int numWindowFixedUpdateMoves; int numWindows; int lastFrameUpdated; Matrix4x4 lastPos; #if DEBUG_LOG_NAME string name; #endif public UpdateClock PreferredUpdate { get; private set; } #if DEBUG_LOG_NAME public UpdateStatus(string targetName, int currentFrame, Matrix4x4 pos) { name = targetName; #else public UpdateStatus(int currentFrame, Matrix4x4 pos) { #endif windowStart = currentFrame; lastFrameUpdated = Time.frameCount; PreferredUpdate = UpdateClock.Late; lastPos = pos; } public void OnUpdate(int currentFrame, UpdateClock currentClock, Matrix4x4 pos) { if (lastPos == pos) return; if (currentClock == UpdateClock.Late) ++numWindowLateUpdateMoves; else if (lastFrameUpdated != currentFrame) // only count 1 per rendered frame ++numWindowFixedUpdateMoves; lastPos = pos; UpdateClock choice; if (numWindowFixedUpdateMoves > 3 && numWindowLateUpdateMoves < numWindowFixedUpdateMoves / 3) choice = UpdateClock.Fixed; else choice = UpdateClock.Late; if (numWindows == 0) PreferredUpdate = choice; if (windowStart + kWindowSize <= currentFrame) { #if DEBUG_LOG_NAME Debug.Log(name + ": Window " + numWindows + ": Late=" + numWindowLateUpdateMoves + ", Fixed=" + numWindowFixedUpdateMoves); #endif PreferredUpdate = choice; ++numWindows; windowStart = currentFrame; numWindowLateUpdateMoves = (PreferredUpdate == UpdateClock.Late) ? 1 : 0; numWindowFixedUpdateMoves = (PreferredUpdate == UpdateClock.Fixed) ? 1 : 0; } } } static Dictionary mUpdateStatus = new Dictionary(); [RuntimeInitializeOnLoadMethod] static void InitializeModule() { mUpdateStatus.Clear(); } static List sToDelete = new List(); static void UpdateTargets(UpdateClock currentClock) { // Update the registry for all known targets int now = Time.frameCount; var iter = mUpdateStatus.GetEnumerator(); while (iter.MoveNext()) { var current = iter.Current; if (current.Key == null) sToDelete.Add(current.Key); // target was deleted else current.Value.OnUpdate(now, currentClock, current.Key.localToWorldMatrix); } for (int i = sToDelete.Count-1; i >= 0; --i) mUpdateStatus.Remove(sToDelete[i]); sToDelete.Clear(); } public static UpdateClock GetPreferredUpdate(Transform target) { if (Application.isPlaying && target != null) { UpdateStatus status; if (mUpdateStatus.TryGetValue(target, out status)) return status.PreferredUpdate; // Add the target to the registry #if DEBUG_LOG_NAME status = new UpdateStatus(target.name, Time.frameCount, target.localToWorldMatrix); #else status = new UpdateStatus(Time.frameCount, target.localToWorldMatrix); #endif mUpdateStatus.Add(target, status); } return UpdateClock.Late; } static float mLastUpdateTime; public static void OnUpdate(UpdateClock currentClock) { // Do something only if we are the first controller processing this frame float now = CinemachineCore.CurrentTime; if (now != mLastUpdateTime) { mLastUpdateTime = now; UpdateTargets(currentClock); } } } }