using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using UnityEditor; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.UIElements; using UnityEngine.Serialization; [assembly: InternalsVisibleTo("Unity.AdaptivePerformance.Tests")] namespace UnityEngine.AdaptivePerformance { /// /// Class to handle active loader and subsystem management for Adaptive Performance. This class is to be added as a /// ScriptableObject asset in your project and should only be referenced by an /// instance for its use. /// /// Given a list of loaders, it will attempt to load each loader in the given order. Unity will use the first /// loader that is successful and ignore all remaining loaders. The successful loader /// is accessible through the property on the manager. /// /// Depending on configuration, the instance will automatically manage the active loader /// at the correct points in the application lifecycle. You can override certain points in the active loader lifecycle /// and manually manage them by toggling the and /// properties. Disabling implies that you are responsible for the full lifecycle /// of the Adaptive Performance session normally handled by the instance. Setting this to false also sets /// to false. /// /// Disabling only implies that you are responsible for starting and stopping /// the through the and APIs. /// /// Unity executes atomatic lifecycle management as follows: /// /// * OnEnable calls internally. The loader list will be iterated over and the first successful loader will be set as the active loader. /// * Start calls internally. Ask the active loader to start all subsystems. /// * OnDisable calls internally. Ask the active loader to stop all subsystems. /// * OnDestroy calls internally. Deinitialize and remove the active loader. /// public sealed class AdaptivePerformanceManagerSettings : ScriptableObject { [HideInInspector] bool m_InitializationComplete = false; [SerializeField] [Tooltip("Determines if the Adaptive Performance Manager instance is responsible for creating and destroying the appropriate loader instance.")] bool m_AutomaticLoading = false; /// /// Get and set Automatic Loading state for this manager. When this is true, the manager will automatically call /// and for you. When false, /// is also set to false and remains that way. This means that disabling automatic loading disables all automatic behavior /// for the manager. /// public bool automaticLoading { get { return m_AutomaticLoading; } set { m_AutomaticLoading = value; } } [SerializeField] [Tooltip("Determines if the Adaptive Performance Manager instance is responsible for starting and stopping subsystems for the active loader instance.")] bool m_AutomaticRunning = false; /// /// Get and set the automatic running state for this manager. When this is true, the manager will call /// and APIs at appropriate times. When false, or when is false, /// it is up to the user of the manager to handle that same functionality. /// public bool automaticRunning { get { return m_AutomaticRunning; } set { m_AutomaticRunning = value; } } [SerializeField] [Tooltip("List of Adaptive Performance Loader instances arranged in desired load order.")] List m_Loaders = new List(); /// /// List of loaders currently managed by this Adaptive Performance Manager instance. /// public List loaders { get { return m_Loaders; } #if UNITY_EDITOR set { m_Loaders = value; } #endif } /// /// Read-only boolean that is true if initialization is completed and false otherwise. Because initialization is /// handled as a Coroutine, applications that use the auto-lifecycle management of AdaptivePerformanceManager /// will need to wait for init to complete before checking for an ActiveLoader and calling StartSubsystems. /// public bool isInitializationComplete { get { return m_InitializationComplete; } } [HideInInspector] static AdaptivePerformanceLoader s_ActiveLoader = null; /// /// Returns the current singleton active loader instance. /// [HideInInspector] public AdaptivePerformanceLoader activeLoader { get { return s_ActiveLoader; } private set { s_ActiveLoader = value; } } /// /// Returns the current active loader, cast to the requested type. Useful shortcut when you need /// to get the active loader as something less generic than AdaptivePerformanceLoader. /// /// Requested type of the loader. /// The active loader as requested type, or null if no active loader currently exists. public T ActiveLoaderAs() where T : AdaptivePerformanceLoader { return activeLoader as T; } /// /// Iterate over the configured list of loaders and attempt to initialize each one. The first one /// that succeeds is set as the active loader and initialization immediately terminates. /// /// When this completes, will be set to true. This will mark that it is safe to /// call other parts of the API, but does not guarantee that init successfully created a loader. To check that init successfully created a loader, /// you need to check that ActiveLoader is not null. /// /// **Note**: There can only be one active loader. Any attempt to initialize a new active loader with one /// already set will cause a warning to be logged and immediate exit of this function. /// /// This method is synchronous and on return all state should be immediately checkable. /// public void InitializeLoaderSync() { if (isInitializationComplete && activeLoader != null) { Debug.LogWarning( "Adaptive Performance Management has already initialized an active loader in this scene." + "Please make sure to stop all subsystems and deinitialize the active loader before initializing a new one."); return; } foreach (var loader in loaders) { if (loader != null) { if (loader.Initialize()) { activeLoader = loader; m_InitializationComplete = true; return; } } } activeLoader = null; } /// /// Iterate over the configured list of loaders and attempt to initialize each one. The first one /// that succeeds is set as the active loader and initialization immediately terminates. /// /// When complete, will be set to true. This will mark that it is safe to /// call other parts of the API, but does not guarantee that init successfully created a loader. To check that init successfully created a loader, /// you need to check that ActiveLoader is not null. /// /// **Note:** There can only be one active loader. Any attempt to initialize a new active loader with one /// already set will cause a warning to be logged and this function wil immeditely exit. /// /// Iteration is done asynchronously. You must call this method within the context of a Coroutine. /// /// /// Enumerator marking the next spot to continue execution at. public IEnumerator InitializeLoader() { if (isInitializationComplete && activeLoader != null) { Debug.LogWarning( "Adaptive Performance Management has already initialized an active loader in this scene." + "Please make sure to stop all subsystems and deinitialize the active loader before initializing a new one."); yield break; } foreach (var loader in loaders) { if (loader != null) { if (loader.Initialize()) { activeLoader = loader; m_InitializationComplete = true; yield break; } } yield return null; } activeLoader = null; } /// /// If there is an active loader, this will request the loader to start all the subsystems that it /// is managing. /// /// You must wait for to be set to true before calling this API. /// public void StartSubsystems() { if (!m_InitializationComplete) { Debug.LogWarning( "Call to StartSubsystems without an initialized manager." + "Please make sure to wait for initialization to complete before calling this API."); return; } if (activeLoader != null) { activeLoader.Start(); } } /// /// If there is an active loader, this will request the loader to stop all the subsystems that it /// is managing. /// /// You must wait for to be set to true before calling this API. /// public void StopSubsystems() { if (!m_InitializationComplete) { Debug.LogWarning( "Call to StopSubsystems without an initialized manager." + "Please make sure to wait for initialization to complete before calling this API."); return; } if (activeLoader != null) { activeLoader.Stop(); } } /// /// If there is an active loader, this function will deinitialize it and remove the active loader instance from /// management. Unity will automatically call before deinitialization to make sure /// that things are cleaned up appropriately. /// /// You must wait for to be set to true before calling this API. /// /// On return, will be set to false. /// public void DeinitializeLoader() { if (!m_InitializationComplete) { Debug.LogWarning( "Call to DeinitializeLoader without an initialized manager." + "Please make sure to wait for initialization to complete before calling this API."); return; } StopSubsystems(); if (activeLoader != null) { activeLoader.Deinitialize(); activeLoader = null; } m_InitializationComplete = false; } // Use this for initialization void Start() { if (automaticLoading && automaticRunning) { StartSubsystems(); } } void OnDisable() { if (automaticLoading && automaticRunning) { StopSubsystems(); } } void OnDestroy() { if (automaticLoading) { DeinitializeLoader(); } } } }