XRManagerSettings.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Runtime.CompilerServices;
  6. using UnityEditor;
  7. using UnityEngine;
  8. using UnityEngine.Rendering;
  9. using UnityEngine.UIElements;
  10. using UnityEngine.Serialization;
  11. using UnityEngine.XR.Management;
  12. [assembly: InternalsVisibleTo("Unity.XR.Management.Tests")]
  13. namespace UnityEngine.XR.Management
  14. {
  15. /// <summary>
  16. /// Class to handle active loader and subsystem management for XR. This class is to be added as a
  17. /// ScriptableObject asset in your project and should only be referenced by the an <see cref="XRGeneralSettings"/>
  18. /// instance for its use.
  19. ///
  20. /// Given a list of loaders, it will attempt to load each loader in the given order. The first
  21. /// loader that is successful wins and all remaining loaders are ignored. The loader
  22. /// that succeeds is accessible through the <see cref="activeLoader"/> property on the manager.
  23. ///
  24. /// Depending on configuration the <see cref="XRGeneralSettings"/> instance will automatically manage the active loader
  25. /// at correct points in the application lifecycle. The user can override certain points in the active loader lifecycle
  26. /// and manually manage them by toggling the <see cref="XRGeneralSettings.automaticLoading"/> and <see cref="XRGeneralSettings.automaticRunning"/>
  27. /// properties. Disabling <see cref="XRGeneralSettings.automaticLoading"/> implies the the user is responsible for the full lifecycle
  28. /// of the XR session normally handled by the <see cref="XRGeneralSettings"/> instance. Toggling this to false also toggles
  29. /// <see cref="XRGeneralSettings.automaticRunning"/> false.
  30. ///
  31. /// Disabling <see cref="XRGeneralSettings.automaticRunning"/> only implies that the user is responsible for starting and stopping
  32. /// the <see cref="activeLoader"/> through the <see cref="StartSubsystems"/> and <see cref="StopSubsystems"/> APIs.
  33. ///
  34. /// Automatic lifecycle management is executed as follows
  35. ///
  36. /// * OnEnable -> <see cref="InitializeLoader"/>. The loader list will be iterated over and the first successful loader will be set as the active loader.
  37. /// * Start -> <see cref="StartSubsystems"/>. Ask the active loader to start all subsystems.
  38. /// * OnDisable -> <see cref="StopSubsystems"/>. Ask the active loader to stop all subsystems.
  39. /// * OnDestroy -> <see cref="DeinitializeLoader"/>. Deinitialize and remove the active loader.
  40. /// </summary>
  41. public sealed class XRManagerSettings : ScriptableObject
  42. {
  43. [HideInInspector]
  44. bool m_InitializationComplete = false;
  45. #pragma warning disable 414
  46. // This property is only used by the scriptable object editing part of the system and as such no one
  47. // directly references it. Have to manually disable the console warning here so that we can
  48. // get a clean console report.
  49. [HideInInspector]
  50. [SerializeField]
  51. bool m_RequiresSettingsUpdate = false;
  52. #pragma warning restore 414
  53. [SerializeField]
  54. [Tooltip("Determines if the XR Manager instance is responsible for creating and destroying the appropriate loader instance.")]
  55. [FormerlySerializedAs("AutomaticLoading")]
  56. bool m_AutomaticLoading = false;
  57. /// <summary>
  58. /// Get and set Automatic Loading state for this manager. When this is true, the manager will automatically call
  59. /// <see cref="InitializeLoader"/> and <see cref="DeiitializeLoader"/> for you. When false <see cref="automaticRunning"/>
  60. /// is also set to false and remains that way. This means that disabling automatic loading disables all automatic behavior
  61. /// for the manager.
  62. /// </summary>
  63. public bool automaticLoading
  64. {
  65. get { return m_AutomaticLoading; }
  66. set { m_AutomaticLoading = value; }
  67. }
  68. [SerializeField]
  69. [Tooltip("Determines if the XR Manager instance is responsible for starting and stopping subsystems for the active loader instance.")]
  70. [FormerlySerializedAs("AutomaticRunning")]
  71. bool m_AutomaticRunning = false;
  72. /// <summary>
  73. /// Get and set automatic running state for this manager. When set to true the manager will call <see cref="StartSubsystems"/>
  74. /// and <see cref="StopSubsystems"/> APIs at appropriate times. When set to false, or when <see cref="automaticLoading"/> is false
  75. /// then it is up to the user of the manager to handle that same functionality.
  76. /// </summary>
  77. public bool automaticRunning
  78. {
  79. get { return m_AutomaticRunning; }
  80. set { m_AutomaticRunning = value; }
  81. }
  82. [SerializeField]
  83. [Tooltip("List of XR Loader instances arranged in desired load order.")]
  84. [FormerlySerializedAs("Loaders")]
  85. List<XRLoader> m_Loaders = new List<XRLoader>();
  86. /// <summary>
  87. /// List of loaders currently managed by this XR Manager instance.
  88. /// </summary>
  89. public List<XRLoader> loaders
  90. {
  91. get { return m_Loaders; }
  92. #if UNITY_EDITOR
  93. set { m_Loaders = value; }
  94. #endif
  95. }
  96. /// <summary>
  97. /// Read only boolean letting us know if initialization is completed. Because initialization is
  98. /// handled as a Coroutine, people taking advantage of the auto-lifecycle management of XRManager
  99. /// will need to wait for init to complete before checking for an ActiveLoader and calling StartSubsystems.
  100. /// </summary>
  101. public bool isInitializationComplete
  102. {
  103. get { return m_InitializationComplete; }
  104. }
  105. [HideInInspector]
  106. static XRLoader s_ActiveLoader = null;
  107. ///<summary>
  108. /// Return the current singleton active loader instance.
  109. ///
  110. ///</summary>
  111. [HideInInspector]
  112. public XRLoader activeLoader { get { return s_ActiveLoader; } private set { s_ActiveLoader = value; } }
  113. /// <summary>
  114. /// Return the current active loader, cast to the requested type. Useful shortcut when you need
  115. /// to get the active loader as something less generic than XRLoader.
  116. /// </summary>
  117. ///
  118. /// <typeparam name="T">Requested type of the loader</typeparam>
  119. ///
  120. /// <returns>The active loader as requested type, or null.</returns>
  121. public T ActiveLoaderAs<T>() where T : XRLoader
  122. {
  123. return activeLoader as T;
  124. }
  125. /// <summary>
  126. /// Iterate over the configured list of loaders and attempt to initialize each one. The first one
  127. /// that succeeds is set as the active loader and initialization immediately terminates.
  128. ///
  129. /// When complete <see cref="isInitializationComplete"/> will be set to true. This will mark that it is safe to
  130. /// call other parts of the API. This does not guarantee that init successfully created a loader. For that
  131. /// you need to check that ActiveLoader is not null.
  132. ///
  133. /// Note that there can only be one active loader. Any attempt to initialize a new active loader with one
  134. /// already set will cause a warning to be logged and immediate exit of this function.
  135. ///
  136. /// This method is synchronous and on return all state should be immediately checkable.
  137. /// </summary>
  138. public void InitializeLoaderSync()
  139. {
  140. if (activeLoader != null)
  141. {
  142. Debug.LogWarning(
  143. "XR Management has already initialized an active loader in this scene." +
  144. "Please make sure to stop all subsystems and deinitialize the active loader before initializing a new one.");
  145. return;
  146. }
  147. foreach (var loader in loaders)
  148. {
  149. if (loader != null)
  150. {
  151. if (CheckGraphicsAPICompatibility(loader) && loader.Initialize())
  152. {
  153. activeLoader = loader;
  154. m_InitializationComplete = true;
  155. return;
  156. }
  157. }
  158. }
  159. activeLoader = null;
  160. }
  161. /// <summary>
  162. /// Iterate over the configured list of loaders and attempt to initialize each one. The first one
  163. /// that succeeds is set as the active loader and initialization immediately terminates.
  164. ///
  165. /// When complete <see cref="isInitializationComplete"/> will be set to true. This will mark that it is safe to
  166. /// call other parts of the API. This does not guarantee that init successfully created a loader. For that
  167. /// you need to check that ActiveLoader is not null.
  168. ///
  169. /// Note that there can only be one active loader. Any attempt to initialize a new active loader with one
  170. /// already set will cause a warning to be logged and immediate exit of this function.
  171. ///
  172. /// Iteration is done asynchronously and this method must be called within the context of a Coroutine.
  173. /// </summary>
  174. ///
  175. /// <returns>Enumerator marking the next spot to continue execution at.</returns>
  176. public IEnumerator InitializeLoader()
  177. {
  178. if (activeLoader != null)
  179. {
  180. Debug.LogWarning(
  181. "XR Management has already initialized an active loader in this scene." +
  182. "Please make sure to stop all subsystems and deinitialize the active loader before initializing a new one.");
  183. yield break;
  184. }
  185. foreach (var loader in loaders)
  186. {
  187. if (loader != null)
  188. {
  189. if (CheckGraphicsAPICompatibility(loader) && loader.Initialize())
  190. {
  191. activeLoader = loader;
  192. m_InitializationComplete = true;
  193. yield break;
  194. }
  195. }
  196. yield return null;
  197. }
  198. activeLoader = null;
  199. }
  200. private bool CheckGraphicsAPICompatibility(XRLoader loader)
  201. {
  202. GraphicsDeviceType deviceType = SystemInfo.graphicsDeviceType;
  203. List<GraphicsDeviceType> supportedDeviceTypes = loader.GetSupportedGraphicsDeviceTypes(false);
  204. // To help with backward compatibility, if the compatibility list is empty we assume that it does not implement the GetSupportedGraphicsDeviceTypes method
  205. // Therefore we revert to the previous behavior of building or starting the loader regardless of gfx api settings.
  206. if (supportedDeviceTypes.Count > 0 && !supportedDeviceTypes.Contains(deviceType))
  207. {
  208. Debug.LogWarning(String.Format("The {0} does not support the initialized graphics device, {1}. Please change the preffered Graphics API in PlayerSettings. Attempting to start the next XR loader.", loader.name, deviceType.ToString()));
  209. return false;
  210. }
  211. return true;
  212. }
  213. /// <summary>
  214. /// If there is an active loader, this will request the loader to start all the subsystems that it
  215. /// is managing.
  216. ///
  217. /// You must wait for <see cref="isInitializationComplete"/> to be set to true prior to calling this API.
  218. /// </summary>
  219. public void StartSubsystems()
  220. {
  221. if (!m_InitializationComplete)
  222. {
  223. Debug.LogWarning(
  224. "Call to StartSubsystems without an initialized manager." +
  225. "Please make sure wait for initialization to complete before calling this API.");
  226. return;
  227. }
  228. if (activeLoader != null)
  229. {
  230. activeLoader.Start();
  231. }
  232. }
  233. /// <summary>
  234. /// If there is an active loader, this will request the loader to stop all the subsystems that it
  235. /// is managing.
  236. ///
  237. /// You must wait for <see cref="isInitializationComplete"/> to be set to tru prior to calling this API.
  238. /// </summary>
  239. public void StopSubsystems()
  240. {
  241. if (!m_InitializationComplete)
  242. {
  243. Debug.LogWarning(
  244. "Call to StopSubsystems without an initialized manager." +
  245. "Please make sure wait for initialization to complete before calling this API.");
  246. return;
  247. }
  248. if (activeLoader != null)
  249. {
  250. activeLoader.Stop();
  251. }
  252. }
  253. /// <summary>
  254. /// If there is an active loader, this function will deinitialize it and remove the active loader instance from
  255. /// management. We will automatically call <see cref="StopSubsystems"/> prior to deinitialization to make sure
  256. /// that things are cleaned up appropriately.
  257. ///
  258. /// You must wait for <see cref="isInitializationComplete"/> to be set to tru prior to calling this API.
  259. ///
  260. /// Upon return <see cref="isInitializationComplete"/> will be rest to false;
  261. /// </summary>
  262. public void DeinitializeLoader()
  263. {
  264. if (!m_InitializationComplete)
  265. {
  266. Debug.LogWarning(
  267. "Call to DeinitializeLoader without an initialized manager." +
  268. "Please make sure wait for initialization to complete before calling this API.");
  269. return;
  270. }
  271. StopSubsystems();
  272. if (activeLoader != null)
  273. {
  274. activeLoader.Deinitialize();
  275. activeLoader = null;
  276. }
  277. m_InitializationComplete = false;
  278. }
  279. // Use this for initialization
  280. void Start()
  281. {
  282. if (automaticLoading && automaticRunning)
  283. {
  284. StartSubsystems();
  285. }
  286. }
  287. void OnDisable()
  288. {
  289. if (automaticLoading && automaticRunning)
  290. {
  291. StopSubsystems();
  292. }
  293. }
  294. void OnDestroy()
  295. {
  296. if (automaticLoading)
  297. {
  298. DeinitializeLoader();
  299. }
  300. }
  301. }
  302. }