480 lines
23 KiB
C#
480 lines
23 KiB
C#
using System;
|
|
using Unity.Collections;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.SubsystemsImplementation;
|
|
|
|
namespace UnityEngine.XR.ARSubsystems
|
|
{
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public class XRSessionSubsystem
|
|
: SubsystemWithProvider<XRSessionSubsystem, XRSessionSubsystemDescriptor, XRSessionSubsystem.Provider>
|
|
{
|
|
static readonly ConfigurationChooser s_DefaultConfigurationChooser = new DefaultConfigurationChooser();
|
|
|
|
ConfigurationChooser m_ConfigurationChooser;
|
|
|
|
/// <summary>
|
|
/// Returns an implementation-defined pointer associated with the session.
|
|
/// </summary>
|
|
public IntPtr nativePtr => provider.nativePtr;
|
|
|
|
/// <summary>
|
|
/// Returns a unique session identifier for this session.
|
|
/// </summary>
|
|
public Guid sessionId => provider.sessionId;
|
|
|
|
/// <summary>
|
|
/// Asynchronously retrieves the <see cref="SessionAvailability"/>. Used to determine whether
|
|
/// the current device supports XR and if the necessary software is installed.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This platform-agnostic method is typically implemented by a platform-specific package.
|
|
/// </remarks>
|
|
/// <returns>A <see cref="Promise{SessionAvailability}"/> which can be used to determine when the
|
|
/// availability has been determined and to retrieve the result.</returns>
|
|
public Promise<SessionAvailability> GetAvailabilityAsync() => provider.GetAvailabilityAsync();
|
|
|
|
/// <summary>
|
|
/// Asynchronously attempts to install XR software on the current device.
|
|
/// Throws if <see cref="XRSessionSubsystemDescriptor.supportsInstall"/> is <c>false</c>.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This platform-agnostic method is typically implemented by a platform-specific package.
|
|
/// </remarks>
|
|
/// <returns>A <see cref="Promise{SessionInstallationStatus}"/> which can be used to determine when the
|
|
/// installation completes and to retrieve the result.</returns>
|
|
public Promise<SessionInstallationStatus> InstallAsync()
|
|
{
|
|
if (!subsystemDescriptor.supportsInstall)
|
|
throw new NotSupportedException("InstallAsync is not supported on this platform.");
|
|
|
|
return provider.InstallAsync();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Do not call this directly. Call create on a valid <see cref="XRSessionSubsystemDescriptor"/> instead.
|
|
/// </summary>
|
|
public XRSessionSubsystem()
|
|
{
|
|
m_ConfigurationChooser = s_DefaultConfigurationChooser;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Restarts a session. [Stop](xref:UnityEngine.Subsystem.Stop) and [Start](xref:UnityEngine.Subsystem.Start)
|
|
/// pause and resume a session, respectively. <c>Restart</c> resets the session state and clears and any
|
|
/// detected trackables.
|
|
/// </summary>
|
|
public void Reset() => provider.Reset();
|
|
|
|
/// <summary>
|
|
/// Determines the <see cref="Configuration"/> the session will use given the requested <paramref name="features"/>.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// This method uses the current <see cref="configurationChooser"/> to choose a configuration using the requested
|
|
/// <paramref name="features"/> and the capabilities of the available configurations. The configuration chooser
|
|
/// is customizable, see <see cref="ConfigurationChooser"/>. If you do not set a configuration chooser, the
|
|
/// <see cref="DefaultConfigurationChooser"/> is used.
|
|
/// </para><para>
|
|
/// 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 <see cref="Feature.WorldFacingCamera"/> to <see cref="Feature.UserFacingCamera"/>.
|
|
/// </para>
|
|
/// </remarks>
|
|
/// <param name="features">A set of requested <see cref="Feature"/>s.</param>
|
|
/// <returns>The <see cref="Configuration"/> the session would use given the requested <paramref name="features"/>,
|
|
/// or <c>null</c> if configuration introspection is not available.</returns>
|
|
/// <seealso cref="Configuration"/>
|
|
/// <seealso cref="ConfigurationChooser"/>
|
|
/// <seealso cref="ConfigurationDescriptor"/>
|
|
/// <seealso cref="Feature"/>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Trigger the session's update loop.
|
|
/// </summary>
|
|
/// <param name="updateParams">Data needed by the session to perform its update.</param>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The current <see cref="Configuration"/> in use by the session.
|
|
/// </summary>
|
|
/// <seealso cref="XRSessionSubsystem.DetermineConfiguration(Feature)"/>
|
|
/// <seealso cref="Feature"/>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Get the requested <see cref="Feature"/>s. These are used to determine the session's <see cref="Configuration"/>.
|
|
/// </summary>
|
|
/// <seealso cref="XRSessionSubsystem.DetermineConfiguration(Feature)"/>
|
|
public Feature requestedFeatures => provider.requestedFeatures;
|
|
|
|
/// <summary>
|
|
/// 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
|
|
/// <see cref="XRSessionSubsystem.DetermineConfiguration(Feature)"/> to determine the best configuration given a set
|
|
/// of requested features.
|
|
/// </summary>
|
|
/// <param name="allocator">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).</param>
|
|
/// <returns>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 <c>NativeArray</c>.</returns>
|
|
public NativeArray<ConfigurationDescriptor> GetConfigurationDescriptors(Allocator allocator) => provider.GetConfigurationDescriptors(allocator);
|
|
|
|
/// <summary>
|
|
/// Should be invoked when the application is paused.
|
|
/// </summary>
|
|
public void OnApplicationPause() => provider.OnApplicationPause();
|
|
|
|
/// <summary>
|
|
/// Should be invoked when the application is resumed.
|
|
/// </summary>
|
|
public void OnApplicationResume() => provider.OnApplicationResume();
|
|
|
|
/// <summary>
|
|
/// Gets the <see cref="TrackingState"/> for the session.
|
|
/// </summary>
|
|
public TrackingState trackingState => provider.trackingState;
|
|
|
|
/// <summary>
|
|
/// The requested tracking mode. Query for support with <c>SubsystemDescriptor.supportedTrackingModes</c>.
|
|
/// </summary>
|
|
public Feature requestedTrackingMode
|
|
{
|
|
get => provider.requestedTrackingMode.TrackingModes();
|
|
set => provider.requestedTrackingMode = value.TrackingModes();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the current tracking mode in use by the subsystem.
|
|
/// </summary>
|
|
public Feature currentTrackingMode => provider.currentTrackingMode.TrackingModes();
|
|
|
|
/// <summary>
|
|
/// Get or set the <see cref="ConfigurationChooser"/> used by <see cref="XRSessionSubsystem.DetermineConfiguration(Feature)"/>.
|
|
/// If set to <see langword="null"/>, the <see cref="DefaultConfigurationChooser"/> is used.
|
|
/// </summary>
|
|
public ConfigurationChooser configurationChooser
|
|
{
|
|
get => m_ConfigurationChooser;
|
|
set { m_ConfigurationChooser = value ?? s_DefaultConfigurationChooser; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the <see cref="NotTrackingReason"/> for the session.
|
|
/// </summary>
|
|
public NotTrackingReason notTrackingReason => provider.notTrackingReason;
|
|
|
|
/// <summary>
|
|
/// Whether the AR session update is synchronized with the Unity frame rate.
|
|
/// If <c>true</c>, <see cref="Update(XRSessionUpdateParams)"/> should block until the next AR frame is available.
|
|
/// </summary>
|
|
/// <exception cref="System.NotSupportedException">Thrown if <see cref="XRSessionSubsystemDescriptor.supportsMatchFrameRate"/> is <c>False</c>.</exception>
|
|
public bool matchFrameRateEnabled
|
|
{
|
|
get => provider.matchFrameRateEnabled;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <seealso cref="matchFrameRateEnabled"/>
|
|
public bool matchFrameRateRequested
|
|
{
|
|
get => provider.matchFrameRateRequested;
|
|
set => provider.matchFrameRateRequested = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The native update rate of the AR Session.
|
|
/// </summary>
|
|
/// <exception cref="System.NotSupportedException">Thrown if <see cref="XRSessionSubsystemDescriptor.supportsMatchFrameRate"/> is <c>False</c>.</exception>
|
|
public int frameRate => provider.frameRate;
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
|
|
/// <seealso cref="OnCommandBufferExecute"/>
|
|
/// <seealso cref="requiresCommandBuffer"/>
|
|
public void OnCommandBufferSupportEnabled() => provider.OnCommandBufferSupportEnabled();
|
|
|
|
/// <summary>
|
|
/// Invoked by the ARCommandBufferSupportRendererFeature.ExecuteRenderPass()
|
|
/// method to provide subsystem access to the render pipeline's render pass command buffers.
|
|
/// </summary>
|
|
/// <param name="commandBuffer">The command buffer about to be executed.</param>
|
|
/// <seealso cref="OnCommandBufferSupportEnabled"/>
|
|
/// <seealso cref="requiresCommandBuffer"/>
|
|
public void OnCommandBufferExecute(CommandBuffer commandBuffer) => provider.OnCommandBufferExecute(commandBuffer);
|
|
|
|
/// <summary>
|
|
/// If the underlying subsystem provider requires access to the rendering pipeline
|
|
/// then that provider will return <see langword="true"/> here.
|
|
/// </summary>
|
|
/// <value><see langword="true"/> if the session provider overrides and handles <see cref="OnCommandBufferSupportEnabled"/> and <see cref="OnCommandBufferExecute"/> calls. Otherwise, <see langword="false"/>.</value>
|
|
public bool requiresCommandBuffer => provider.requiresCommandBuffer;
|
|
|
|
/// <summary>
|
|
/// The API this subsystem uses to interop with
|
|
/// different provider implementations.
|
|
/// </summary>
|
|
public class Provider : SubsystemProvider<XRSessionSubsystem>
|
|
{
|
|
/// <summary>
|
|
/// Invoked to start or resume a session. This is different from <see cref="OnApplicationResume"/>.
|
|
/// </summary>
|
|
public override void Start() {}
|
|
|
|
/// <summary>
|
|
/// Invoked to pause a running session. This is different from <see cref="OnApplicationPause"/>.
|
|
/// </summary>
|
|
public override void Stop() {}
|
|
|
|
/// <summary>
|
|
/// Perform any per-frame update logic here.
|
|
/// </summary>
|
|
/// <param name="updateParams">Parameters about the current state that may be needed to inform the session.</param>
|
|
public virtual void Update(XRSessionUpdateParams updateParams) { }
|
|
|
|
/// <summary>
|
|
/// Perform any per-frame update logic here. The session should use the configuration indicated by
|
|
/// <paramref name="configuration.descriptor.identifier"/>, which should be one of the ones returned
|
|
/// by <see cref="GetConfigurationDescriptors(Unity.Collections.Allocator)"/>.
|
|
/// </summary>
|
|
/// <param name="updateParams">Parameters about the current state that might be needed to inform the session.</param>
|
|
/// <param name="configuration">The configuration the session should use.</param>
|
|
public virtual void Update(XRSessionUpdateParams updateParams, Configuration configuration) { }
|
|
|
|
/// <summary>
|
|
/// Should return the features requested by the enabling of other <c>Subsystem</c>s.
|
|
/// </summary>
|
|
public virtual Feature requestedFeatures => Feature.None;
|
|
|
|
/// <summary>
|
|
/// Get or set the requested tracking mode (for example, the <see cref="Feature.AnyTrackingMode"/> bits).
|
|
/// </summary>
|
|
public virtual Feature requestedTrackingMode
|
|
{
|
|
get => Feature.None;
|
|
set {}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the current tracking mode (for example, the <see cref="Feature.AnyTrackingMode"/> bits).
|
|
/// </summary>
|
|
public virtual Feature currentTrackingMode => Feature.None;
|
|
|
|
/// <summary>
|
|
/// This getter should allocate a new <c>NativeArray</c> using <paramref name="allocator"/>
|
|
/// and populate it with the supported <see cref="ConfigurationDescriptor"/>s.
|
|
/// </summary>
|
|
/// <param name="allocator">The <c>[Allocator](https://docs.unity3d.com/ScriptReference/Unity.Collections.Allocator.html)</c>
|
|
/// to use to create the returned <c>NativeArray</c>.</param>
|
|
/// <returns>A newly allocated <c>NativeArray</c> of <see cref="ConfigurationDescriptor"/>s that describes the capabilities
|
|
/// of all the supported configurations.</returns>
|
|
public virtual NativeArray<ConfigurationDescriptor> GetConfigurationDescriptors(Allocator allocator) => default;
|
|
|
|
/// <summary>
|
|
/// Stop the session and destroy all associated resources.
|
|
/// </summary>
|
|
public override void Destroy() { }
|
|
|
|
/// <summary>
|
|
/// Reset the session. The behavior should be equivalent to destroying and recreating the session.
|
|
/// </summary>
|
|
public virtual void Reset() { }
|
|
|
|
/// <summary>
|
|
/// Invoked when the application is paused.
|
|
/// </summary>
|
|
public virtual void OnApplicationPause() { }
|
|
|
|
/// <summary>
|
|
/// Invoked when the application is resumed.
|
|
/// </summary>
|
|
public virtual void OnApplicationResume() { }
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <seealso cref="OnCommandBufferExecute"/>
|
|
/// <seealso cref="requiresCommandBuffer"/>
|
|
public virtual void OnCommandBufferSupportEnabled() {}
|
|
|
|
/// <summary>
|
|
/// Invoked by the ARCommandBufferSupportRendererFeature.ExecuteRenderPass()
|
|
/// method to provide subsystem access to the render pipeline's render pass command buffers.
|
|
/// </summary>
|
|
/// <param name="commandBuffer">The command buffer about to be executed.</param>
|
|
/// <seealso cref="OnCommandBufferSupportEnabled"/>
|
|
/// <seealso cref="requiresCommandBuffer"/>
|
|
public virtual void OnCommandBufferExecute(CommandBuffer commandBuffer) {}
|
|
|
|
/// <summary>
|
|
/// If the underlying subsystem provider requires access to the rendering pipeline
|
|
/// then that provider will return <see langword="true"/> here.
|
|
/// </summary>
|
|
/// <value><see langword="true"/> if the session provider overrides and handles <see cref="OnCommandBufferSupportEnabled"/> and <see cref="OnCommandBufferExecute"/> calls. Otherwise, <see langword="false"/>.</value>
|
|
public virtual bool requiresCommandBuffer => false;
|
|
|
|
/// <summary>
|
|
/// Get a pointer to an object associated with the session.
|
|
/// Callers should be able to manipulate the session in their own code using this.
|
|
/// </summary>
|
|
public virtual IntPtr nativePtr => IntPtr.Zero;
|
|
|
|
/// <summary>
|
|
/// Get the session's availability, such as whether the platform supports XR.
|
|
/// </summary>
|
|
/// <returns>A <see cref="Promise{T}"/> that the caller can yield on until availability is determined.</returns>
|
|
public virtual Promise<SessionAvailability> GetAvailabilityAsync()
|
|
{
|
|
return Promise<SessionAvailability>.CreateResolvedPromise(SessionAvailability.None);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempt to update or install necessary XR software. Will only be called if
|
|
/// <see cref="XRSessionSubsystemDescriptor.supportsInstall"/> is true.
|
|
/// </summary>
|
|
/// <returns>A <see cref="Promise{SessionInstallationStatus}"/> which can be used to determine when the
|
|
/// installation completes and to retrieve the result.</returns>
|
|
public virtual Promise<SessionInstallationStatus> InstallAsync()
|
|
{
|
|
return Promise<SessionInstallationStatus>.CreateResolvedPromise(SessionInstallationStatus.ErrorInstallNotSupported);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the <see cref="TrackingState"/> for the session.
|
|
/// </summary>
|
|
public virtual TrackingState trackingState => TrackingState.None;
|
|
|
|
/// <summary>
|
|
/// Get the <see cref="NotTrackingReason"/> for the session.
|
|
/// </summary>
|
|
public virtual NotTrackingReason notTrackingReason => NotTrackingReason.Unsupported;
|
|
|
|
/// <summary>
|
|
/// Get a unique identifier for this session.
|
|
/// </summary>
|
|
public virtual Guid sessionId => Guid.Empty;
|
|
|
|
/// <summary>
|
|
/// Whether the AR session update is synchronized with the Unity frame rate.
|
|
/// If <c>true</c>, <see cref="Update(XRSessionUpdateParams)"/> will block until the next AR frame is available.
|
|
/// </summary>
|
|
public virtual bool matchFrameRateEnabled => false;
|
|
|
|
/// <summary>
|
|
/// Whether the AR session update should be synchronized with the Unity frame rate.
|
|
/// If <c>true</c>, <see cref="Update(XRSessionUpdateParams)"/> should block until the next AR frame is available.
|
|
/// Must be implemented if
|
|
/// <see cref="XRSessionSubsystemDescriptor.supportsMatchFrameRate"/>
|
|
/// is <c>True</c>.
|
|
/// </summary>
|
|
public virtual bool matchFrameRateRequested
|
|
{
|
|
get => false;
|
|
set
|
|
{
|
|
if (value)
|
|
{
|
|
throw new NotSupportedException("Matching frame rate is not supported.");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The native update rate of the AR Session. Must be implemented if
|
|
/// <see cref="XRSessionSubsystemDescriptor.supportsMatchFrameRate"/>
|
|
/// is <c>True</c>.
|
|
/// </summary>
|
|
public virtual int frameRate =>
|
|
throw new NotSupportedException("Querying the frame rate is not supported by this session subsystem.");
|
|
}
|
|
}
|
|
}
|