using System; using Unity.Collections; using UnityEngine.Rendering; using UnityEngine.SubsystemsImplementation; namespace UnityEngine.XR.ARSubsystems { /// /// This subsystem controls the lifecycle of an XR session. Some platforms, /// particularly those that have non-XR modes, need to be able to turn the /// session on and off to enter and exit XR modes of operation. /// public class XRSessionSubsystem : SubsystemWithProvider { static readonly ConfigurationChooser s_DefaultConfigurationChooser = new DefaultConfigurationChooser(); ConfigurationChooser m_ConfigurationChooser; /// /// Returns an implementation-defined pointer associated with the session. /// public IntPtr nativePtr => provider.nativePtr; /// /// Returns a unique session identifier for this session. /// public Guid sessionId => provider.sessionId; /// /// Asynchronously retrieves the . Used to determine whether /// the current device supports XR and if the necessary software is installed. /// /// /// This platform-agnostic method is typically implemented by a platform-specific package. /// /// A which can be used to determine when the /// availability has been determined and to retrieve the result. public Promise GetAvailabilityAsync() => provider.GetAvailabilityAsync(); /// /// Asynchronously attempts to install XR software on the current device. /// Throws if is false. /// /// /// This platform-agnostic method is typically implemented by a platform-specific package. /// /// A which can be used to determine when the /// installation completes and to retrieve the result. public Promise InstallAsync() { if (!subsystemDescriptor.supportsInstall) throw new NotSupportedException("InstallAsync is not supported on this platform."); return provider.InstallAsync(); } /// /// Do not call this directly. Call create on a valid instead. /// public XRSessionSubsystem() { m_ConfigurationChooser = s_DefaultConfigurationChooser; } /// /// Restarts a session. [Stop](xref:UnityEngine.Subsystem.Stop) and [Start](xref:UnityEngine.Subsystem.Start) /// pause and resume a session, respectively. Restart resets the session state and clears and any /// detected trackables. /// public void Reset() => provider.Reset(); /// /// Determines the the session will use given the requested . /// /// /// /// This method uses the current to choose a configuration using the requested /// and the capabilities of the available configurations. The configuration chooser /// is customizable, see . If you do not set a configuration chooser, the /// is used. /// /// You can use this method to determine what would happen if you were to enable or disable a particular feature. /// For example, you can use this method to determine which features would be enabled if you were to change the /// camera mode from to . /// /// /// A set of requested s. /// The the session would use given the requested , /// or null if configuration introspection is not available. /// /// /// /// public Configuration? DetermineConfiguration(Feature features) { var descriptors = GetConfigurationDescriptors(Allocator.Temp); if (descriptors.IsCreated) { using (descriptors) { if (descriptors.Length > 0) { #if DEVELOPMENT_BUILD || UNITY_EDITOR if (m_FirstUpdate) { var sb = new System.Text.StringBuilder(); foreach (var descriptor in descriptors) { sb.Append($"Configuration Descriptor {HexString(descriptor.identifier)} (rank {descriptor.rank}): {descriptor.capabilities.ToStringList()}\n"); } Debug.Log(sb.ToString()); m_FirstUpdate = false; } #endif return m_ConfigurationChooser.ChooseConfiguration(descriptors, features); } } } return null; } /// /// Trigger the session's update loop. /// /// Data needed by the session to perform its update. public void Update(XRSessionUpdateParams updateParams) { var features = requestedFeatures; currentConfiguration = DetermineConfiguration(features); if (currentConfiguration.HasValue) { #if DEVELOPMENT_BUILD || UNITY_EDITOR DebugPrintConfigurationChange(currentConfiguration.Value, features); #endif provider.Update(updateParams, currentConfiguration.Value); } else { provider.Update(updateParams); } } /// /// The current in use by the session. /// /// /// public Configuration? currentConfiguration { get; private set; } #if DEVELOPMENT_BUILD || UNITY_EDITOR unsafe string HexString(IntPtr ptr) => sizeof(IntPtr) == 4 ? $"0x{ptr.ToInt32():x}" : $"0x{ptr.ToInt64():x}"; void DebugPrintConfigurationChange(Configuration configuration, Feature desiredFeatures) { if (configuration != m_PreviousConfiguration || desiredFeatures != m_PreviousDesiredFeatures) { var sb = new System.Text.StringBuilder(); sb.Append($"Using session configuration {HexString(configuration.descriptor.identifier)}"); if (configuration.descriptor.identifier == m_PreviousConfiguration.descriptor.identifier) { sb.Append(" (unchanged)"); } sb.Append("\n\tRequested Features: "); sb.Append(desiredFeatures.ToStringList()); sb.Append("\n\tSupported Features: "); sb.Append(configuration.features.ToStringList()); sb.Append("\n\tRequested features not satisfied: "); sb.Append(desiredFeatures.SetDifference(configuration.features).ToStringList()); Debug.Log(sb.ToString()); m_PreviousConfiguration = configuration; m_PreviousDesiredFeatures = desiredFeatures; } } Feature m_PreviousDesiredFeatures; Configuration m_PreviousConfiguration; bool m_FirstUpdate = true; #endif /// /// Get the requested s. These are used to determine the session's . /// /// public Feature requestedFeatures => provider.requestedFeatures; /// /// Get the list of supported configuration descriptors. The session can have multiple, discrete modes of operation. /// A configuration represents the capabilities of a mode of operation, which can be a subset of the session's overal /// capabilities. That is, the session might support many features, but not all at the same time. This is used by /// to determine the best configuration given a set /// of requested features. /// /// The [allocator](https://docs.unity3d.com/ScriptReference/Unity.Collections.Allocator.html) /// to use for the returned [NativeArray](https://docs.unity3d.com/ScriptReference/Unity.Collections.NativeArray_1.html). /// Allocates a new [NativeArray](https://docs.unity3d.com/ScriptReference/Unity.Collections.NativeArray_1.html) /// and populates it with descriptors describing the supported configurations. The caller /// owns the memory and is responsible for calling [Dispose](https://docs.unity3d.com/ScriptReference/Unity.Collections.NativeArray_1.Dispose.html) /// on the NativeArray. public NativeArray GetConfigurationDescriptors(Allocator allocator) => provider.GetConfigurationDescriptors(allocator); /// /// Should be invoked when the application is paused. /// public void OnApplicationPause() => provider.OnApplicationPause(); /// /// Should be invoked when the application is resumed. /// public void OnApplicationResume() => provider.OnApplicationResume(); /// /// Gets the for the session. /// public TrackingState trackingState => provider.trackingState; /// /// The requested tracking mode. Query for support with SubsystemDescriptor.supportedTrackingModes. /// public Feature requestedTrackingMode { get => provider.requestedTrackingMode.TrackingModes(); set => provider.requestedTrackingMode = value.TrackingModes(); } /// /// Get the current tracking mode in use by the subsystem. /// public Feature currentTrackingMode => provider.currentTrackingMode.TrackingModes(); /// /// Get or set the used by . /// If set to , the is used. /// public ConfigurationChooser configurationChooser { get => m_ConfigurationChooser; set { m_ConfigurationChooser = value ?? s_DefaultConfigurationChooser; } } /// /// Gets the for the session. /// public NotTrackingReason notTrackingReason => provider.notTrackingReason; /// /// Whether the AR session update is synchronized with the Unity frame rate. /// If true, should block until the next AR frame is available. /// /// Thrown if is False. public bool matchFrameRateEnabled { get => provider.matchFrameRateEnabled; } /// /// Get or set whether the match frame rate feature should be enabled. /// When enabled, the AR session update is synchronized with the Unity frame rate. /// /// public bool matchFrameRateRequested { get => provider.matchFrameRateRequested; set => provider.matchFrameRateRequested = value; } /// /// The native update rate of the AR Session. /// /// Thrown if is False. public int frameRate => provider.frameRate; /// /// Invoked when using the Universal Rendering Pipeline in conjunction with /// the ARCommandBufferSupportRendererFeature to provide subsystem awareness /// that the render pipeline's render pass is enabled. /// /// /// public void OnCommandBufferSupportEnabled() => provider.OnCommandBufferSupportEnabled(); /// /// Invoked by the ARCommandBufferSupportRendererFeature.ExecuteRenderPass() /// method to provide subsystem access to the render pipeline's render pass command buffers. /// /// The command buffer about to be executed. /// /// public void OnCommandBufferExecute(CommandBuffer commandBuffer) => provider.OnCommandBufferExecute(commandBuffer); /// /// If the underlying subsystem provider requires access to the rendering pipeline /// then that provider will return here. /// /// if the session provider overrides and handles and calls. Otherwise, . public bool requiresCommandBuffer => provider.requiresCommandBuffer; /// /// The API this subsystem uses to interop with /// different provider implementations. /// public class Provider : SubsystemProvider { /// /// Invoked to start or resume a session. This is different from . /// public override void Start() {} /// /// Invoked to pause a running session. This is different from . /// public override void Stop() {} /// /// Perform any per-frame update logic here. /// /// Parameters about the current state that may be needed to inform the session. public virtual void Update(XRSessionUpdateParams updateParams) { } /// /// Perform any per-frame update logic here. The session should use the configuration indicated by /// , which should be one of the ones returned /// by . /// /// Parameters about the current state that might be needed to inform the session. /// The configuration the session should use. public virtual void Update(XRSessionUpdateParams updateParams, Configuration configuration) { } /// /// Should return the features requested by the enabling of other Subsystems. /// public virtual Feature requestedFeatures => Feature.None; /// /// Get or set the requested tracking mode (for example, the bits). /// public virtual Feature requestedTrackingMode { get => Feature.None; set {} } /// /// Get the current tracking mode (for example, the bits). /// public virtual Feature currentTrackingMode => Feature.None; /// /// This getter should allocate a new NativeArray using /// and populate it with the supported s. /// /// The [Allocator](https://docs.unity3d.com/ScriptReference/Unity.Collections.Allocator.html) /// to use to create the returned NativeArray. /// A newly allocated NativeArray of s that describes the capabilities /// of all the supported configurations. public virtual NativeArray GetConfigurationDescriptors(Allocator allocator) => default; /// /// Stop the session and destroy all associated resources. /// public override void Destroy() { } /// /// Reset the session. The behavior should be equivalent to destroying and recreating the session. /// public virtual void Reset() { } /// /// Invoked when the application is paused. /// public virtual void OnApplicationPause() { } /// /// Invoked when the application is resumed. /// public virtual void OnApplicationResume() { } /// /// Invoked by the ARCommandBufferSupportRendererFeature render pass creation /// and render graph recording (if applicable) to provide subsystem awareness /// that the render pipeline's render pass is enabled. /// /// /// public virtual void OnCommandBufferSupportEnabled() {} /// /// Invoked by the ARCommandBufferSupportRendererFeature.ExecuteRenderPass() /// method to provide subsystem access to the render pipeline's render pass command buffers. /// /// The command buffer about to be executed. /// /// public virtual void OnCommandBufferExecute(CommandBuffer commandBuffer) {} /// /// If the underlying subsystem provider requires access to the rendering pipeline /// then that provider will return here. /// /// if the session provider overrides and handles and calls. Otherwise, . public virtual bool requiresCommandBuffer => false; /// /// Get a pointer to an object associated with the session. /// Callers should be able to manipulate the session in their own code using this. /// public virtual IntPtr nativePtr => IntPtr.Zero; /// /// Get the session's availability, such as whether the platform supports XR. /// /// A that the caller can yield on until availability is determined. public virtual Promise GetAvailabilityAsync() { return Promise.CreateResolvedPromise(SessionAvailability.None); } /// /// Attempt to update or install necessary XR software. Will only be called if /// is true. /// /// A which can be used to determine when the /// installation completes and to retrieve the result. public virtual Promise InstallAsync() { return Promise.CreateResolvedPromise(SessionInstallationStatus.ErrorInstallNotSupported); } /// /// Get the for the session. /// public virtual TrackingState trackingState => TrackingState.None; /// /// Get the for the session. /// public virtual NotTrackingReason notTrackingReason => NotTrackingReason.Unsupported; /// /// Get a unique identifier for this session. /// public virtual Guid sessionId => Guid.Empty; /// /// Whether the AR session update is synchronized with the Unity frame rate. /// If true, will block until the next AR frame is available. /// public virtual bool matchFrameRateEnabled => false; /// /// Whether the AR session update should be synchronized with the Unity frame rate. /// If true, should block until the next AR frame is available. /// Must be implemented if /// /// is True. /// public virtual bool matchFrameRateRequested { get => false; set { if (value) { throw new NotSupportedException("Matching frame rate is not supported."); } } } /// /// The native update rate of the AR Session. Must be implemented if /// /// is True. /// public virtual int frameRate => throw new NotSupportedException("Querying the frame rate is not supported by this session subsystem."); } } }