mixedreality/com.microsoft.mixedreality..../Core/Services/BaseDataProviderAccessCoreS...

342 lines
13 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System;
using System.Collections.Generic;
using Unity.Profiling;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Abstract class for core MRTK system with functionality defined for managing and accessing IMixedRealityDataProviders
/// </summary>
public abstract class BaseDataProviderAccessCoreSystem : BaseCoreSystem, IMixedRealityDataProviderAccess
{
private readonly List<IMixedRealityDataProvider> dataProviders = new List<IMixedRealityDataProvider>();
public override void Reset()
{
base.Reset();
foreach (var provider in dataProviders)
{
provider.Reset();
}
}
/// <inheritdoc />
public override void Enable()
{
base.Enable();
foreach (var provider in dataProviders)
{
provider.Enable();
}
}
private static readonly ProfilerMarker UpdatePerfMarker = new ProfilerMarker("[MRTK] BaseDataProviderAccessCoreSystem.Update");
/// <inheritdoc />
public override void Update()
{
using (UpdatePerfMarker.Auto())
{
base.Update();
foreach (var provider in dataProviders)
{
provider.Update();
}
}
}
private static readonly ProfilerMarker LateUpdatePerfMarker = new ProfilerMarker("[MRTK] BaseDataProviderAccessCoreSystem.LateUpdate");
/// <inheritdoc />
public override void LateUpdate()
{
using (LateUpdatePerfMarker.Auto())
{
base.LateUpdate();
foreach (var provider in dataProviders)
{
provider.LateUpdate();
}
}
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="registrar">The <see cref="IMixedRealityServiceRegistrar"/> instance that loaded the service.</param>
/// <param name="profile">The configuration profile for the service.</param>
[Obsolete("This constructor is obsolete (registrar parameter is no longer required) and will be removed in a future version of the Microsoft Mixed Reality Toolkit.")]
protected BaseDataProviderAccessCoreSystem(
IMixedRealityServiceRegistrar registrar,
BaseMixedRealityProfile profile = null) : this(profile)
{
Registrar = registrar;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="profile">The configuration profile for the service.</param>
protected BaseDataProviderAccessCoreSystem(
BaseMixedRealityProfile profile = null) : base(profile)
{ }
#region IMixedRealityDataProviderAccess Implementation
/// <inheritdoc />
public virtual IReadOnlyList<IMixedRealityDataProvider> GetDataProviders()
{
return dataProviders.AsReadOnly();
}
/// <inheritdoc />
public virtual IReadOnlyList<T> GetDataProviders<T>() where T : IMixedRealityDataProvider
{
List<T> selected = new List<T>();
foreach (var provider in dataProviders)
{
if (provider is T providerT)
{
selected.Add(providerT);
}
}
return selected;
}
/// <inheritdoc />
public virtual IMixedRealityDataProvider GetDataProvider(string name)
{
foreach (var provider in dataProviders)
{
if (provider.Name == name)
{
return provider;
}
}
return null;
}
/// <inheritdoc />
public virtual T GetDataProvider<T>(string name = null) where T : IMixedRealityDataProvider
{
foreach (var provider in dataProviders)
{
if (provider is T providerT)
{
if (name == null || provider.Name == name)
{
return providerT;
}
}
}
return default(T);
}
#endregion IMixedRealityDataProviderAccess Implementation
/// <summary>
/// Registers a data provider of the specified type.
/// </summary>
protected bool RegisterDataProvider<T>(
Type concreteType,
string providerName,
SupportedPlatforms supportedPlatforms = (SupportedPlatforms)(-1),
params object[] args) where T : IMixedRealityDataProvider
{
return RegisterDataProviderInternal<T>(
true, // Retry with an added IMixedRealityService parameter
concreteType,
providerName,
supportedPlatforms,
args);
}
/// <summary>
/// Registers a data provider of the specified type.
/// </summary>
[Obsolete("RegisterDataProvider<T>(Type, SupportedPlatforms, param object[]) is obsolete and will be removed from a future version of MRTK\n" +
"Please use RegisterDataProvider<T>(Type, string, SupportedPlatforms, params object[])")]
protected bool RegisterDataProvider<T>(
Type concreteType,
SupportedPlatforms supportedPlatforms = (SupportedPlatforms)(-1),
params object[] args) where T : IMixedRealityDataProvider
{
return RegisterDataProvider<T>(
concreteType,
string.Empty,
supportedPlatforms,
args);
}
/// <summary>
/// Internal method that creates an instance of the specified concrete type and registers the provider.
/// </summary>
private bool RegisterDataProviderInternal<T>(
bool retryWithRegistrar,
Type concreteType,
string providerName,
SupportedPlatforms supportedPlatforms = (SupportedPlatforms)(-1),
params object[] args) where T : IMixedRealityDataProvider
{
if (!PlatformUtility.IsPlatformSupported(supportedPlatforms))
{
DebugUtilities.LogVerboseFormat(
"Not registering data provider of type {0} with name {1} because the current platform is not in supported platforms {2}",
concreteType,
providerName,
supportedPlatforms);
return false;
}
if (concreteType == null)
{
if (!Application.isEditor)
{
Debug.LogWarning($"Unable to register {typeof(T).Name} data provider ({(!string.IsNullOrWhiteSpace(providerName) ? providerName : "unknown")}) because the value of concreteType is null.\n" +
"This may be caused by code being stripped during linking. The link.xml file in the MixedRealityToolkit.Generated folder is used to control code preservation.\n" +
"More information can be found at https://docs.unity3d.com/Manual/ManagedCodeStripping.html.");
}
return false;
}
SupportedUnityXRPipelines selectedPipeline =
#if UNITY_2020_1_OR_NEWER
SupportedUnityXRPipelines.XRSDK;
#elif UNITY_2019
XRSettingsUtilities.XRSDKEnabled ? SupportedUnityXRPipelines.XRSDK : SupportedUnityXRPipelines.LegacyXR;
#else
SupportedUnityXRPipelines.LegacyXR;
#endif
if (MixedRealityExtensionServiceAttribute.Find(concreteType) is MixedRealityDataProviderAttribute providerAttribute
&& !providerAttribute.SupportedUnityXRPipelines.IsMaskSet(selectedPipeline))
{
DebugUtilities.LogVerboseFormat("{0} not suitable for the current XR pipeline ({1})", concreteType.Name, selectedPipeline);
return false;
}
if (!typeof(IMixedRealityDataProvider).IsAssignableFrom(concreteType))
{
Debug.LogError($"Unable to register the {concreteType.Name} data provider. It does not implement {typeof(IMixedRealityDataProvider)}.");
return false;
}
T dataProviderInstance;
try
{
dataProviderInstance = (T)Activator.CreateInstance(concreteType, args);
}
catch (Exception e)
{
if (retryWithRegistrar && (e is MissingMethodException))
{
Debug.LogWarning($"Failed to find an appropriate constructor for the {concreteType.Name} data provider. Adding the Registrar instance and re-attempting registration.");
#pragma warning disable 0618
List<object> updatedArgs = new List<object>();
updatedArgs.Add(Registrar);
if (args != null)
{
updatedArgs.AddRange(args);
}
return RegisterDataProviderInternal<T>(
false, // Do NOT retry, we have already added the configured IMIxedRealityServiceRegistrar
concreteType,
providerName,
supportedPlatforms,
updatedArgs.ToArray());
#pragma warning restore 0618
}
Debug.LogError($"Failed to register the {concreteType.Name} data provider: {e.GetType()} - {e.Message}");
// Failures to create the concrete type generally surface as nested exceptions - just logging
// the top level exception itself may not be helpful. If there is a nested exception (for example,
// null reference in the constructor of the object itself), it's helpful to also surface those here.
if (e.InnerException != null)
{
Debug.LogError("Underlying exception information: " + e.InnerException);
}
return false;
}
return RegisterDataProvider(dataProviderInstance);
}
/// <summary>
/// Registers a service of the specified type.
/// </summary>
/// <typeparam name="T">The interface type of the data provider to be registered.</typeparam>
/// <param name="dataProviderInstance">An instance of the data provider to be registered.</param>
protected bool RegisterDataProvider<T>(T dataProviderInstance) where T : IMixedRealityDataProvider
{
if (dataProviderInstance == null)
{
Debug.LogWarning($"Unable to add a {dataProviderInstance.Name} data provider with a null instance.");
return false;
}
dataProviders.Add(dataProviderInstance);
dataProviderInstance.Initialize();
return true;
}
/// <summary>
/// Unregisters a data provider of the specified type.
/// </summary>
/// <typeparam name="T">The interface type of the data provider to be unregistered.</typeparam>
/// <param name="name">The name of the data provider to unregister.</param>
/// <returns>True if the data provider was successfully unregistered, false otherwise.</returns>
/// <remarks>If the name argument is not specified, the first instance will be unregistered</remarks>
protected bool UnregisterDataProvider<T>(string name = null) where T : IMixedRealityDataProvider
{
T dataProviderInstance = GetDataProvider<T>(name);
if (dataProviderInstance == null) { return false; }
return UnregisterDataProvider(dataProviderInstance);
}
/// <summary>
/// Unregisters a data provider.
/// </summary>
/// <typeparam name="T">The interface type of the data provider to be unregistered.</typeparam>
/// <param name="service">The specific data provider instance to unregister.</param>
/// <returns>True if the data provider was successfully unregistered, false otherwise.</returns>
protected bool UnregisterDataProvider<T>(T dataProviderInstance) where T : IMixedRealityDataProvider
{
if (dataProviderInstance == null)
{
return false;
}
if (dataProviders.Contains(dataProviderInstance))
{
dataProviders.Remove(dataProviderInstance);
dataProviderInstance.Disable();
dataProviderInstance.Destroy();
return true;
}
return false;
}
}
}