mixedreality/com.microsoft.mixedreality..../Runtime/FeaturePlugins/OpenXRFeaturePlugin.cs

262 lines
10 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features;
using UnityEngine.XR.OpenXR.NativeTypes;
namespace Microsoft.MixedReality.OpenXR
{
internal abstract class OpenXRFeaturePlugin<TPlugin>
: OpenXRFeaturePlugin where TPlugin : OpenXRFeaturePlugin<TPlugin>
{
private static OpenXRFeature m_feature;
internal static TPlugin Feature => (TPlugin)m_feature;
protected override void OnEnable()
{
// Important notes about this Feature reference initialization:
// - Awake and OnEnable are called sequentially when this ScriptableObject is created, far earlier than when values such as the Feature singleton should be used.
// - ScriptableObject::OnEnable always runs when the feature is supported, independent of whether or not the enabled value defined in OpenXRFeature is true.
// - The reference must be refreshed in OnEnable, as Awake is not called on domain refresh (e.g. editing scripts while the editor is open).
// - References to OpenXR Project Settings are avoided, as they may not be available (e.g. while regenerating the project settings asset).
var featureInCurrentPlatform = OpenXRSettings.Instance.GetFeature<TPlugin>();
if (featureInCurrentPlatform == this)
{
// C# scriptable object might be enabled for project settings in editor
// that's meant for other platforms, and won't be used when running the app.
// Only initialize those scriptable objects in the current platform.
// This field will be refreshed upon app domain refresh.
m_feature = this;
}
base.OnEnable();
}
}
internal abstract class OpenXRFeaturePlugin
: OpenXRFeature, IOpenXRContext, ISubsystemPlugin
{
private List<SubsystemController> m_subsystemControllers = new List<SubsystemController>();
public ulong Instance { get; private set; } = 0;
public ulong SystemId { get; private set; } = 0;
public ulong Session { get; private set; } = 0;
public bool IsSessionRunning { get; private set; } = false;
public XrSessionState SessionState { get; private set; } = XrSessionState.Unknown;
public ulong SceneOriginSpace { get; private set; } = 0;
public event OpenXRContextEvent InstanceCreated; // after instance is created
public event OpenXRContextEvent InstanceDestroying; // before instance is destroyed
public event OpenXRContextEvent SessionCreated; // after session is created
public event OpenXRContextEvent SessionDestroying; // before session is destroyed
public event OpenXRContextEvent SessionBegun; // after session is begun
public event OpenXRContextEvent SessionEnding; // before session is ended
// Convert protected OpenXRFeature.xrGetInstanceProcAddr to internal visibility
internal static IntPtr PFN_xrGetInstanceProcAddr => OpenXRFeature.xrGetInstanceProcAddr;
protected override void OnEnable()
{
base.OnEnable();
OpenXRFeaturePluginManager.OnFeaturePluginInitializing(this);
if (OpenXRFeaturePluginManager.NativeLibAvailable)
{
PluginEnvironmentSubsystem.InitializePlugin();
}
}
protected override IntPtr HookGetInstanceProcAddr(IntPtr func)
{
OpenXRFeaturePluginManager.InitializeOpenXRFeatureList();
if (OpenXRFeaturePluginManager.IsResponsibleForNativeLib(this))
{
func = NativeLib.HookGetInstanceProcAddr(func);
}
return func;
}
protected void AddSubsystemController(SubsystemController subsystemController)
{
m_subsystemControllers.Add(subsystemController);
}
protected override void OnSubsystemCreate()
{
m_subsystemControllers.ForEach(controller => controller.OnSubsystemCreate(this));
}
protected override void OnSubsystemStart()
{
if (OpenXRFeaturePluginManager.IsResponsibleForNativeLib(this))
{
NativeLib.OnSubsystemsStarting();
}
m_subsystemControllers.ForEach(controller => controller.OnSubsystemStart(this));
}
protected override void OnSubsystemStop()
{
m_subsystemControllers.ForEach(controller => controller.OnSubsystemStop(this));
if (OpenXRFeaturePluginManager.IsResponsibleForNativeLib(this))
{
NativeLib.OnSubsystemsStopped();
}
}
protected override void OnSubsystemDestroy()
{
m_subsystemControllers.ForEach(controller => controller.OnSubsystemDestroy(this));
}
protected override bool OnInstanceCreate(ulong instance)
{
if (Instance != 0)
{
Debug.LogWarning("New instance was created without properly destroying the previous one.");
}
Instance = instance;
string[] enabledExtensionNames = OpenXRRuntime.GetEnabledExtensions();
if (OpenXRFeaturePluginManager.IsResponsibleForNativeLib(this))
{
NativeLib.OnInstanceCreated(instance, PFN_xrGetInstanceProcAddr, enabledExtensionNames, enabledExtensionNames.Length);
}
InstanceCreated?.Invoke(this, EventArgs.Empty);
return true;
}
protected override void OnInstanceDestroy(ulong instance)
{
if (Instance == 0)
{
// Unity might call destroy when instance handle was not successfully created
// Ignore such cases since there's no resources associated with instance of 0.
return;
}
if (SystemId != 0)
{
// Unity's OnSystemChange event won't trigger when destroying instance.
// Reset resources associated with system before destroying the instance.
SystemId = 0;
if (OpenXRFeaturePluginManager.IsResponsibleForNativeLib(this))
{
NativeLib.SetXrSystemId(0);
}
}
InstanceDestroying?.Invoke(this, EventArgs.Empty);
Instance = 0;
if (OpenXRFeaturePluginManager.IsResponsibleForNativeLib(this))
{
NativeLib.OnInstanceDestroyed();
}
}
protected override void OnSystemChange(ulong systemId)
{
SystemId = systemId;
if (OpenXRFeaturePluginManager.IsResponsibleForNativeLib(this))
{
NativeLib.SetXrSystemId(systemId);
}
}
protected override void OnSessionCreate(ulong session)
{
Session = session;
PluginEnvironmentSubsystem.OnSessionCreated();
if (OpenXRFeaturePluginManager.IsResponsibleForNativeLib(this))
{
NativeLib.SetXrSession(session);
}
SessionCreated?.Invoke(this, EventArgs.Empty);
}
protected override void OnSessionBegin(ulong session)
{
// This virtual function is called right after xrSessionBegin returns,
// All C# scripts should observe that the session is running.
IsSessionRunning = true;
if (OpenXRFeaturePluginManager.IsResponsibleForNativeLib(this))
{
NativeLib.SetXrSessionRunning(true);
}
SessionBegun?.Invoke(this, EventArgs.Empty);
}
protected override void OnSessionStateChange(int oldState, int newState)
{
SessionState = (XrSessionState)newState;
if (OpenXRFeaturePluginManager.IsResponsibleForNativeLib(this))
{
NativeLib.SetSessionState((uint)newState);
}
}
protected override void OnSessionEnd(ulong session)
{
SessionEnding?.Invoke(this, EventArgs.Empty);
if (OpenXRFeaturePluginManager.IsResponsibleForNativeLib(this))
{
NativeLib.SetXrSessionRunning(false);
}
// This virtual function is called right before xrSessionEnd is called.
// All C# scripts should still observe that the session is running until this point.
IsSessionRunning = false;
}
protected override void OnSessionDestroy(ulong session)
{
SessionDestroying?.Invoke(this, EventArgs.Empty);
Session = 0;
if (OpenXRFeaturePluginManager.IsResponsibleForNativeLib(this))
{
NativeLib.SetXrSession(0);
}
}
protected override void OnAppSpaceChange(ulong sceneOriginSpace)
{
SceneOriginSpace = sceneOriginSpace;
if (OpenXRFeaturePluginManager.IsResponsibleForNativeLib(this))
{
NativeLib.SetSceneOriginSpace(sceneOriginSpace);
}
}
// Convert protected function to internal
internal static new void SetEnvironmentBlendMode(XrEnvironmentBlendMode environmentBlendMode)
{
OpenXRFeature.SetEnvironmentBlendMode(environmentBlendMode);
}
// Convert protected function to internal
internal static new XrEnvironmentBlendMode GetEnvironmentBlendMode()
{
return OpenXRFeature.GetEnvironmentBlendMode();
}
void ISubsystemPlugin.CreateSubsystem<TDescriptor, TSubsystem>(List<TDescriptor> descriptors, string id) =>
base.CreateSubsystem<TDescriptor, TSubsystem>(descriptors, id);
void ISubsystemPlugin.StartSubsystem<T>() => base.StartSubsystem<T>();
void ISubsystemPlugin.StopSubsystem<T>() => base.StopSubsystem<T>();
void ISubsystemPlugin.DestroySubsystem<T>() => base.DestroySubsystem<T>();
}
}