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.");
}
}
}