Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Plan of Record] Improve MRTK ease of use: system facades, registrar interface, config ux #3545

Closed
david-c-kline opened this issue Feb 28, 2019 · 113 comments
Assignees
Labels
BREAKING CHANGE Feature Request Feature request from the community

Comments

@david-c-kline
Copy link

david-c-kline commented Feb 28, 2019

Plan of Record: Support Service Specific Scene Presence

Microsoft Mixed Reality Toolkit v2

This document describes a design change for the Microsoft Mixed Reality Toolkit v2 concerning how developer customers can choose the MRTK and its services.

The Issue: Usability

The MRTK team has received feedback from multiple sources that the current, single object presence in the application scene is difficult to use with regards to feature discovery and modularization. Multiple customers, both internal and external, have requested an alternative to beta 2’s MixedRealityToolkit object in the form of system specific locator components (ex: MixedRealityInputLocator) that can be additively included.

One piece of feedback, heard from multiple sources, points out the promise of additive vs. subtractive consumption of MRTK. The comments largely center around two themes:

  1. It is not easy, at a glance, to see if a scene is using a specific MRTK service. Many developers from whom we heard desire an individual service presence in the scene hierarchy.
  2. The root Mixed Reality Toolkit configuration profile makes it appear that all services are present in the application, even when disabled. There is a strong desire for scoping the profile inspector to show only installed and active services.

Solution: Enable Support for Individual Services to have Scene Presence

The solution defines the following changes:

  1. Provide service registration interfaces to enable customers to create a custom service locator and/or fully stand-alone service components
  2. Provide support for exposing active services and their configuration settings in the scene hierarchy at edit time via service facades
  3. Enable customers to create hybrid application architectures, if desired, that use any number of service locators in conjunction with any number of stand-alone services
  4. Create a light-weight service registry that maps service interfaces with the concrete type that manages service registration
  5. An updated scene hierarchy to contain the service registry, service facades, etc
  6. Modified MRTK project configuration flow to provide enhanced flexibility for customers when setting up their projects

Please see the Solution Details section of this document for information the described changes.

Goals

  1. Respond to direct customer feedback
  2. Provide customers with a choice of how they consume MRTK services
  3. Minimize required client code changes
  4. Preserve as much of the MRTK service locator functionality as possible (anticipating 90% or greater equivalence)

Non-Goals

  1. Removal of the service locator pattern
  2. Require developers to change how they wish to work

Solution Details

Service Registration Interfaces

To enable developers to create custom service locators and/or extend an existing service locator to add support for MRTK services, a set of interfaces will be created to support the registration and retrieval of service instances.

It is anticipated that there will either be one or three interfaces that will be created. The following sections describe the pros and cons of each implementation.

Single Registrar Interface

In the single interface approach, the MRTK will define IMixedRealityServiceRegistrar. This interface would define explicit methods for supporting objects implementing the following service types (listed by interface):

  • IMixedRealityService
  • IMixedRealityExtensionService
  • IMixedRealityDataProvider

Each method (Register, Unregister, Get, CheckRegistration) will have a version that is type safe to the listed interfaces.

Pros Cons
Only one interface to implement Implementors may be required to implement stub methods
Can provide a single base class supporting all service types

Multiple Registrar Interfaces

In the multiple interface design, each type of service would have a corresponding registrar interface:

  • IMixedRealityServiceRegistrar
  • IMixedRealityExtensionServiceRegistrar
  • IMixedRealityDataProviderRegistrar

This approach would allow customers to support only the specific types of services necessary to build their component.

Pros Cons
Developers are free to implement only the required service type support Potentially mutltiple interfaces to implement
Develipers do not have to implement stub methods Cannot consume individual interface base class implementations (C# limitations)

Base Class Implementations

As with much of the MRTK, base classes will be provided to provide robust, reusable implementations of the registrar interface(s).

Providing the Registrar Instance to Services

When the registrar instantiates a service instance, it will provide itself as a constructor argument. The following example shows the parameter to be added to each service constructor.

public ServiceClass(IMixedRealityRegistrar registrar, ...);

A similar pattern will occur for data providers.

When a service requests registration of a data provider, it calls RegisterDataProvider, handing a reference to itself.

registrar.RegisterDataProvider<IMixedRealitySpatialObserver, IMixedRealitySpatialAwarenessSystem>(IMixedRealitySpatialAwarenessSystem system, Type observerType, ...);

The data provider's constructor is passed the provided reference in its constructor.

public SpatialObserver(IMixedRealitySpatialAwarenessSystem system, ...);

Service Facades

Service facades are an editor only monobehavior that exists in the application scene to give customers and easy, at-a-glance representation of the active services in the scene. When selected, the facade will display the service specific profile inspector and allow for easy configuration.

Backing these facades will be added to the scene and backed by the MRTK service locator. Developers not wishing these facades to be in their scene will be able to disable them in the MixedRealityToolkitConfiguration profile.

Hybrid Application Architecture

Hybrid applications architecture is a term intended to describe an application which utilizes any number of service locators and/or stand-alone service implementations.

To support hybrid architectures, MRTK will add a light-weight service registry that enables client code easy access to concrete service implementations.

Service Registry

The service registry in MRTK is an optional component that client code can use to acquire an instance of a desired service.

The registry can be thought of as a table that maps service interfaces to the object that manages the concrete implementation(s) .

The following table illustrates an example registry:

Registrar Interface
MixedRealityToolkitServiceLocator IBoundarySystem
CustomServiceLocator ISpatialAwarenessSystem
StandaloneInputSystem IInputSystem

The component managing the service registry can be attached to client script(s) or developers can choose to bypass the registry and hard-code references to the object managing service registration.

For example:

IServiceInterface service = MixedRealityToolkit.Instance.GetService<IServiceInterface>();

Scene Hierarchy

To help keep the scene hierarchy uncluttered, the default MRTK configuration process will create a Mixed Reality Toolkit parent object. This object will be the parent for the service locator and any enabled facades.

It is recommended that this object be the parent of objects created by registered services (ex: spatial awareness mesh objects).

The following image illustrates the default hierarchy when the MRTK service locator is used with the input and spatial awareness system facades enabled.

Hierarchy With Facades

Project Configuration UX

To make it easy and intuitive for customers to configure MRTK to suit their application's requirements, the Mixed Reality Toolkit menu's Configure item will display a selection dialog similar to the following illustration.

Configuration Dialog

@StephenHodgson
Copy link
Contributor

How do we know this docx doesn't contain a malware macro ;)

But seriously, could we get these in a better format that's readable from a browser window?

@david-c-kline
Copy link
Author

I can reformat to .md but it will take time

@david-c-kline
Copy link
Author

david-c-kline commented Mar 1, 2019

GitHub does not appear to support attaching .md files.... Sorry about the length of this comment.

Proposal: Support Individual Service Locators

Microsoft Mixed Reality Toolkit v2

This document describes a design change proposal for the Microsoft Mixed Reality Toolkit v2 concerning how developer customers consume the MRTK and its system services.

The Issue: Usability

The MRTK team has received feedback from multiple sources that the current, single object presence in the application scene is difficult to use with regards to feature discovery and modularization. Multiple customers, both internal and external, have requested an alternative to beta 2’s MixedRealityToolkit object in the form of system specific locator components (ex: MixedRealityInputLocator) that can be additively included.

One piece of feedback, heard from multiple sources, points out the promise of additive vs. subtractive consumption of MRTK. The comments largely center around two themes:

  1. It is not possible, at a glance, to see if a scene is using a specific MRTK system. Many developers from whom we heard desire an individual system presence in the scene hierarchy.
  2. The root Mixed Reality Toolkit configuration profile makes it appear that all systems are present in the application, even when disabled. There is a strong desire for scoping the profile inspector to show only installed and active systems.

Proposed Solution

The proposed solution will provide support for both a single, global service locator component (MixedRealityToolkit) and for individual service locator components out of the box in MRTK.

This proposal will also enable developer customers to provide their own service locator implementations that can support custom functionality and/or support for a customized collection of pre-existing services.

Core Scene Component Refactor and Creation of System Serivce Locator Components

The proposal is to create a set of interfaces and base classes that that the MixedRealityToolkit object and other service locator objects can be built upon.

The MRTK would then provide additional service locators for:

• Camera
• Boundary
• Diagnostics
• Input
• Locomotion (aka Teleportation)
• Spatial Awareness

Configuration Profile UI Changes

As part of this work, the configuration profile inspectors will be updated to reduce the complexity and to enable “one stop” modification of settings.

This proposal does not mandate merging related profiles, nor does it eliminate profile specific inspectors.

Data Provider Registration

As of beta 2, all data providers are registered in the Additional Service Providers profile.

With this proposal, all data providers will be registered in the profile of the relevant system and loaded / managed by the user's selected service locator, which is potentially the service itself. The additional providers profile will be reserved for extension systems and services.

For example, the Windows Mixed Reality Device Manager and Open VR Device Manager components will be registered as data providers with the Input System locator.

Details

The following sections describe architectural details for this proposal. As the proposal is implemented, some details may change.

IMixedRealityServiceLocator

The IMixedRealityServiceLocator interface will define the core requirements for all service locator implementations.

Note: It is under consideration as to whether or not this interface will be subdivided by service type to allow some locator implementations to be simpler and to reduce the number of required methods.

Each locator is responsible for loading supported services. The global locator will continue to support loading all currently defined as well as customer created systems and services (implementing IMixedRealityService). The following APIs are the minimum set that will be required.

/// <summary>
/// Registers a service with the service locator.
/// </summary>
/// <typeparam name="T">The interface type of the service to be registered (ex: IMixedRealityBoundarySystem.
/// <param name="serviceInstance">Instance of the service class.</param>
/// <returns>True if the service was successfully registered, false otherwise.</returns>
bool RegisterService<T>(
    IMixedRealityService serviceInstance) where T : IMixedRealityService;
/// <summary>
/// Registers a service with the service locator.
/// </summary>
/// <typeparam name="T">The interface type of the service to be registered (ex: IMixedRealityBoundarySystem).
/// <param name="concreteType">The concrete type to instantiate.</param>
/// <param name="supportedPlatforms">The platform(s) on which the service is supported.</param>
/// <param name="args">Optional arguments used when instantiating the concrete type.</param>
/// <returns>True if the service was successfully registered, false otherwise.</returns>
bool RegisterService<T>(
    Type concreteType, 
    SupportedPlatforms supportedPlatforms = (SupportedPlatforms)(-1),
    params object[] args = null) where T : IMixedRealityService;
/// <summary>
/// Unregisters a service from the service locator.
/// </summary>
/// <param name="name">The name of the service to unregister.</param>
/// <returns>True if the service was successfully unregistered, false otherwise.</returns>
bool UnregisterService(string name);
/// <summary>
/// Unregisters a service from the service locator.
/// </summary>
/// <typeparam name="T">The interface type of the service to be unregistered (ex: IMixedRealityBoundarySystem).
/// <param name="name">The name of the service to unregister.</param>
/// <returns>True if the service was successfully unregistered, false otherwise.</returns>
/// <remarks>If the name argument is not specified, the first instance will be unregistered</remarks>
bool UnregisterService<T>(
    string name = null) where T : IMixedRealityService;
/// <summary>
/// Unregisters a service from the service locator.
/// </summary>
/// <param name="service">The specific service instance to unregister.</param>
/// <returns>True if the service was successfully unregistered, false otherwise.</returns>
bool UnregisterService(IMixedRealityService serviceInstance);
/// <summary>
/// Unregisters all services from the service locator.
/// </summary>
void UnregisterServices();
/// <summary>
/// Unregisters all services from the service locator.
/// </summary>
/// <typeparam name="T">The interface type of the services to be unregistered (ex: IMixedRealityBoundarySystem).
void UnregisterServices<T>() where T : IMixedRealityService;
/// <summary>
/// Checks to see if a service has been registered with the service locator.
/// </summary>
/// <param name="name">The name of the service.</param>
/// <returns>True if the service is registered, false otherwise.</returns>
bool IsRegistered(string name);
/// <summary>
/// Checks to see if a service has been registered with the service locator.
/// </summary>
/// <typeparam name="T">The interface type of the service (ex: IMixedRealityBoundarySystem).
/// <param name="name">The name of the service.</param>
/// <returns>True if the service is registered, false otherwise.</returns>
bool IsRegistered<T>(string name == null) where T : IMixedRealityService;
/// <summary>
/// Gets the instance of the registered service.
/// </summary>
/// <param name="name">The name of the service.</param>
/// <returns>The registered service instance, as IMixedRealityService.</returns>
IMixedRealityService GetService(string name);
/// <summary>
/// Gets the instance of the registered service.
/// </summary>
/// <typeparam name="T">The interface type of the service (ex: IMixedRealityBoundarySystem).
/// <param name="name">The name of the service.</param>
/// <returns>The registered service instance as the requested type.</returns>
T GetService<T>(string name) where T : IMixedRealityService;
/// <summary>
/// Gets the collection of the registered service instances matching the requested type.
/// </summary>
/// <returns>Read-only collection of the service instances, as IMixedRealityService.</returns>
IReadOnlyList<IMixedRealityService> GetServices();
/// <summary>
/// Gets the collection of the registered service instances matching the requested type.
/// </summary>
/// <typeparam name="T">The interface type of the service (ex: IMixedRealityBoundarySystem).
/// <returns>Read-only collection of the service instances, as type requested type.</returns>
IReadOnlyList<T> GetServices<T>() where T : IMixedRealityService;

IMixedRealityExtensionServicelocator

The IMixedRealityServicelocator can manage any service that implements IMixedRealityService and the IMixedRealityExtensionService is defined as extending this interface.

The IMixedRealityExtensionServicelocator interface is an optional interface that provides methods that can be implemented to limit management to implementations of IMixedRealityExtensionService.

/// <summary>
/// Registers an extension service with the service locator.
/// </summary>
/// <typeparam name="T">The interface type of the service to be registered.
/// <param name="serviceInstance">Instance of the service class.</param>
/// <returns>True if the service was successfully registered, false otherwise.</returns>
bool RegisterExtensionService<T>(
    IMixedRealityExtensionService serviceInstance) where T : IMixedRealityExtensionService;
/// <summary>
/// Registers an extension service with the service locator.
/// </summary>
/// <typeparam name="T">The interface type of the service to be registered.
/// <returns>True if the service was successfully registered, false otherwise.</returns>
bool RegisterExtensionService<T>(
    Type concreteType, 
    SupportedPlatforms supportedPlatforms = (SupportedPlatforms)(-1),     
    params object[] args = null) where T : IMixedRealityExtensionService;
/// <summary>
/// Unregisters an extension service from the service locator.
/// </summary>
/// <param name="name">The name of the service to unregister.</param>
/// <returns>True if the service was successfully unregistered, false otherwise.</returns>
bool UnregisterExtensionService(string name);
/// <summary>
/// Unregisters a service from the service locator.
/// </summary>
/// <typeparam name="T">The interface type of the service to be unregistered.
/// <param name="name">The name of the service to unregister.</param>
/// <returns>True if the service was successfully unregistered, false otherwise.</returns>
/// <remarks>If the name argument is not specified, the first instance will be unregistered</remarks>
bool UnregisterExtensionService<T>(string name == null) where T : IMixedRealityExtensionService;
/// <summary>
/// Unregisters an extension service from the service locator.
/// </summary>
/// <param name="service">The specific service instance to unregister.</param>
/// <returns>True if the service was successfully unregistered, false otherwise.</returns>
bool UnregisterExtensionService(IMixedRealityExtensionService serviceInstance);        
/// <summary>
/// Unregisters all extension services from the service locator.
/// </summary>
bool UnregisterExtensionServices();
/// <summary>
/// Unregisters all extension services from the service locator.
/// </summary>
/// <typeparam name="T">The interface type of the services to be unregistered.
bool UnregisterExtensionServices<T>() where T: IMixedRealityExtensionService;
/// <summary>
/// Checks to see if an extension service has been registered with the service locator.
/// </summary>
/// <param name="name">The name of the service.</param>
/// <returns>True if the service is registered, false otherwise.</returns>
bool IsExtensionServiceRegistered(string name);
/// <summary>
/// Checks to see if an extension service has been registered with the service locator.
/// </summary>
/// <typeparam name="T">The interface type of the service.
/// <param name="name">The name of the service.</param>
/// <returns>True if the service is registered, false otherwise.</returns>
bool IsExtensionServiceRegistered<T>(string name == null) where T : IMixedRealityExtensionService;
/// <summary>
/// Gets the instance of the registered extension service.
/// </summary>
/// <param name="name">The name of the service.</param>
/// <returns>The registered service instance, as IMixedRealityExtensionService.</returns>
IMixedRealityExtensionService GetExtensionService(string name);
/// <summary>
/// Gets the instance of the registered extension service.
/// </summary>
/// <typeparam name="T">The interface type of the service (ex: IMixedRealityBoundarySystem).
/// <param name="name">The name of the service.</param>
/// <returns>The registered service instance as the requested type.</returns>
T GetExtensionService<T>(string name) where T : IMixedRealityExtensionService;
/// <summary>
/// Gets the collection of the registered extension service instances matching the requested type.
/// </summary>
/// <returns>Read-only collection of the service instances, as IMixedRealityService.</returns>
IReadOnlyList<IMixedRealityExtensionService> GetExtensionServices();
/// <summary>
/// Gets the collection of the registered extension service instances matching the requested type.
/// </summary>
/// <typeparam name="T">The interface type of the service (ex: IMixedRealityBoundarySystem).
/// <returns>Read-only collection of the service instances, as tye requested type.</returns>
IReadOnlyList<T> GetExtensionServices<T>() where T: IMixedRealityExtensionService;

IMixedRealityDataProviderlocator

As with extension services, the IMixedRealityServicelocator can also manage data providers that implement the IMixedRealityDataProvider interface. Similarly, the IMixedRealityExtensionServicelocator supports management of IMixedRealityDataProvider implementations.

The IMixedRealityDataProviderlocator interface is an optional interface that provides methods that can be implemented to limit management to implementations of IMixedRealityDataProvider.

/// <summary>
/// Registers a data provider with the data provider locator.
/// </summary>
/// <typeparam name="T">The interface type of the data provider to be registered.
/// <param name="dataProviderInstance">Instance of the data provider class.</param>
/// <returns>True if the data provider was successfully registered, false otherwise.</returns>
bool RegisterDataProvider<T>(
    IMixedRealityDataProvider dataProviderInstance) where T : IMixedRealityDataProvider;
/// <summary>
/// Registers a data provider with the data provider locator.
/// </summary>
/// <typeparam name="T">The interface type of the data provider to be registered.
/// <returns>True if the data provider was successfully registered, false otherwise.</returns>
bool RegisterDataProvider<T>(
    Type concreteType, 
    SupportedPlatforms supportedPlatforms = (SupportedPlatforms)(-1),
    params object[] args = null) where T : IMixedRealityDataProvider;
/// <summary>
/// Unregisters a data provider from the data provider locator.
/// </summary>
/// <param name="name">The name of the data provider to unregister.</param>
/// <returns>True if the data provider was successfully unregistered, false otherwise.</returns>
bool UnregisterDataProvider(string name);
/// <summary>
/// Unregisters a data provider from the data provider locator.
/// </summary>
/// <typeparam name="T">The interface type of the data provider to be unregistered.
/// <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>
bool UnregisterDataProvider<T>(string name == null) where T : IMixedRealityDataProvider;       
/// <summary>
/// Unregisters a data provider from the service locator.
/// </summary>
/// <param name="service">The specific data provider instance to unregister.</param>
/// <returns>True if the data provider was successfully unregistered, false otherwise.</returns>
bool UnregisterDataProviderService(IMixedRealityDataProvider dataProviderInstance);
/// <summary>
/// Unregisters all data providers from the data provider locator.
/// </summary>
bool UnregisterDataProviders();
/// <summary>
/// Unregisters all data providers from the data provider locator.
/// </summary>
/// <typeparam name="T">The interface type of the data providers to be unregistered.
bool UnregisterDataProviders<T>() where T: IMixedRealityDataProvider;
/// <summary>
/// Checks to see if a data provider has been registered with the service locator.
/// </summary>
/// <param name="name">The name of the data provider.</param>
/// <returns>True if the data provider is registered, false otherwise.</returns>
bool IsDataProviderRegistered(string name);
/// <summary>
/// Checks to see if a data provider has been registered with the service locator.
/// </summary>
/// <typeparam name="T">The interface type of the data provider.
/// <param name="name">The name of the data provider.</param>
/// <returns>True if the data provider is registered, false otherwise.</returns>
bool IsDataProviderRegistered<T>(string name == null) where T : IMixedRealityDataProvider;
/// <summary>
/// Gets the instance of the registered data provider.
/// </summary>
/// <param name="name">The name of the data provider.</param>
/// <returns>The registered data provider instance, as IMixedRealityDataProvider.</returns>
IMixedRealityDataProvider GetDataProvider(string name);
/// <summary>
/// Gets the instance of the registered data provider.
/// </summary>
/// <typeparam name="T">The interface type of the data provider.
/// <param name="name">The name of the data provider.</param>
/// <returns>The registered data provider instance as the requested type.</returns>
T GetDataProvider<T>(string name) where T : IMixedRealityDataProvider;
/// <summary>
/// Gets the collection of the registered data provider instances matching the requested type.
/// </summary
/// <returns>Read-only collection of the data provider instances, as IMixedRealitydata provider.</returns>
IReadOnlyList<IMixedRealityDataProvider> GetDataProviders();
/// <summary>
/// Gets the collection of the registered data provider instances matching the requested type.
/// </summary>
/// <typeparam name="T">The interface type of the data provider.
/// <returns>Read-only collection of the data provider instances, as tye requested type.</returns>
IReadOnlyList<T> GetDataProviders<T>() where T: IMixedRealityDataProvider;

MixedRealityServicelocatorBase

The MixedRealityServicelocatorBase class will provide a default implementation of the IMixedRealityServicelocator interface and will be leveraged by MixedRealityExtensionServicelocatorBase and MixedRealityDataProviderlocatorBase.

Custom service locator implementations are encouraged to inherit from MixedRealityServicelocatorBase and customize behaviors as needed to avoid the need to implement duplicate code.

MixedRealityExtensionServicelocatorBase

The MixedRealityExtensionServicelocatorBase class will use MixedRealityServicelocatorBase to handle core service management functionality and add a layer of type checking to ensure that all managed services implement IMixedRealityExtensionService.

MixedRealityDataProviderlocatorBase

The MixedRealityDataProviderlocatorBase class will use MixedRealityServiceExtensionlocatorBase to handle core service management functionality and add a layer of type checking to ensure that all managed services implement IMixedRealityDataProvider.

MixedRealityToolkit Class Refactor

As part of the interface and base class definition and implementation process, the MixedRealityToolkit class will be refactored with common and reusable code moving into the base class.

System locators

locators for each of the systems listed previously will be created leveraging the base classes and interfaces. Not all system locators will require all features (ex: the Spatial Awareness system does not use a profile) and each will implement the minimum set of interfaces necessary.

System Implementation Changes

As the systems will no longer be able to expect the concrete MixedRealityToolkit class to be managing them, the current system implementations will need to be updated. These changes will involve requesting data from the locator via interface contracts.

Data Providers

With this proposal, each system will bear the responsibility for Unloading and managing registered data providers. The system implementations will call into the system locator to Unload, Unregister and request data about the registered data providers.

Scene Presence

To better support a wide variety of developers and applications, while maintaining the goal if MRTK being minimally intrusive in the scene hierarchy, the scene presence will be modified to add a Mixed Reality Toolkit top level GameObject which will contain the MRTK’s system locator(s).

The following images illustrate the single, global locator and individual component locator options.

For customers wishing to use the current, global system locator, the hierarchy now nests the MixedRealityToolkit object in a parent of the same name (exact name TBD).

Single, Global locator

Customers that prefer to explicitly add individual system locators will see each added under the MixedRealityToolkit parent object.

Individual locators

Each of the previous illustrations show the hierarchy that will appear when using the Mixed Reality Toolkit > Configure menu. Customers are free to manually modify / build the MRTK presence in their scene(s) manually if desired.

For reference, the scene hierarchy in beta 2 is shown in the following illustration.

MRTK v2 Beta 2 locator

While the above is arguably somewhat cleaner, collapsing the new MixedRealityToolkit parent object presents a similar appearance. Additionally, adding a parent object allows for better containment of scene components added and managed by MRTK, for example spatial mesh objects.

Configuration Menu

The Mixed Reality Toolkit menu’s Configure item will be expanded to support configuring for the global system locator or individual component locators. The following image illustrates early thinking of the configuration UI.

Configuration Dialog Concept

Configuration Profiles

Existing system configuration profiles will continue to be used by the systems regardless of the chosen locator. It is expected that some minor modifications to the user interface / navigation may be required to best provide a delightful customer experience.

Examples

The following sections describe examples of how locators and services will be discovered and referenced in code.

• locator Access in Services
• Service Discovery in Application Code
• Data Provider Access in Application Code

locator Access in Services

When a locator loads a service, the constructor will be passed an instance of IMixedRealityServicelocator to provide access to an equivalent of beta 2’s MixedRealityToolkit.Instance property.

Data Provider Management

Through the IMixedRealityServicelocator instance, services will be able to request data provider management (register, unregister, etc.).
Service Discovery in Application Code

There are two proposals for providing service access to application code to replace beta 2’s direct MixedRealityToolkit calls. For example (ex: MixedRealityToolkit.Instance.InputSystem).

Proposal 1: GetServicelocator()

One possible solution is to provide a script that is attached to the MixedRealityToolkit parent object that iterates through the scene and builds a table of locator objects and which services the manage. This table would be created by locators registering themselves at startup. Application code would then request the appropriate locator for their service(s) of interest.

Proposal 2: Script Registration of Service locator

Another proposed solution involves customers dragging the instance of the locator to a script in the inspector.

The MixedRealityToolkit would provide a generic solution that can be used or extended by customer projects. This solution would provide a mapping of service interface to locator instance. For example:

Service Interface Service locator
IMixedRealityBoundarySystem MixedRealityBoundarySystemlocator
IMixedRealityInputSystem ContosoInputSystemlocator
IMixedRealitySpatialAwarenessSystem MixedRealityToolkit
IMixedRealityTeleportSystem MixedRealityToolkit

Data Provider Access in Application Code

Access to data provider instances should be provided by the service which consumes the provider. This allows most application code to avoid needing knowledge of the locator and allows services to control when data providers are registered and unregistered.

The current (beta 2) Spatial Awareness system implementation demonstrates this pattern:

/// <summary>
/// Gets the collection of registered <see cref="IMixedRealitySpatialAwarenessObserver"/> data providers.
/// </summary>
/// <returns>
/// Read only copy of the list of registered observers.
/// </returns>
IReadOnlyList<IMixedRealitySpatialAwarenessObserver> GetObservers();
/// <summary>
/// Get the collection of registered observers of the specified type.
/// </summary>
/// <typeparam name="T">The desired spatial awareness observer type (ex: <see cref="IMixedRealitySpatialAwarenessMeshObserver"/>)</typeparam>
/// <returns>
/// Readonly copy of the list of registered observers that implement the specified type.
/// </returns>
IReadOnlyList<T> GetObservers<T>() where T : IMixedRealitySpatialAwarenessObserver;
/// <summary>
/// Get the <see cref="IMixedRealitySpatialAwarenessObserver"/> that is registered under the specified name.
/// </summary>
/// <param name="name">The friendly name of the observer.</param>
/// <returns>
/// The requested observer, or null if one cannot be found.
/// </returns>
/// <remarks>
/// If more than one observer is registered under the specified name, the first will be returned.
/// </remarks>
IMixedRealitySpatialAwarenessObserver GetObserver(string name);
/// <summary>
/// Get the observer that is registered under the specified name matching the specified type.
/// </summary>
/// <typeparam name="T">The desired spatial awareness observer type (ex: <see cref="IMixedRealitySpatialAwarenessMeshObserver"/>)</typeparam>
/// <param name="name">The friendly name of the observer.</param>
/// <returns>
/// The requested observer, or null if one cannot be found.
/// </returns>
/// <remarks>
/// If more than one observer is registered under the specified name, the first will be returned.
/// </remarks>
T GetObserver<T>(string name) where T : IMixedRealitySpatialAwarenessObserver;

After calling one of the GetObserver methods, applications are able to modify properties and control the activity of the spatial awareness observers.

@hridpath
Copy link

hridpath commented Mar 1, 2019

@davidkline-ms I am always causious when un-known text is used as identifiers for object recognition. Using the Friendly Name as an index into a list of objects can cause errors due to name collisions.

I would like to see if the usage of Namespaces can be impemented in the when registering and un-regestering services by name or type. This would allow for multiple implementations of a service to be alive but unique. For example having multiple controllers available to be used at the same time.

Scenario: When a student is running an application in WHMD or HoloLens an instructor may want to show the Student where to click or point them in a direction to assit in thier learning. The students could be using the Motion Controllers and the Instructor could be using an XBox One controller. Different Cursors and interaction can be defined for visuals.

i.e
bool RegisterService(string "name", string "nameSpace)
bool Un-RegisterService(string "name", string "nameSpace)

bool RegisterService<T>(string "name", string "nameSpace) where<T> = baseClass or interface
bool Un-RegisterService<T>(string "name", string "nameSpace)

bool RegisterService<T>(Type typeWithNamspace)
bool Un-RegisterService<T>(Type typeWithNamespace)

@david-c-kline
Copy link
Author

@hridpath, thanks for the feedback!

@StephenHodgson
Copy link
Contributor

I think everyone is overthinking a lot of this stuff, and there's a focus on "Scene" objects, when the fact of the matter is, none of this stuff exists in the scene.

We're working with POCO objects. Classes and structures that should (in theory) be independent from Unity as much as possible. (Let's not even talk about how adding additional GameObjects that don't really do much of anything, could impact performance).

I think taking a bigger focus on educating people how to understand and use the system would help.

Yes, this new way of doing things is very different to traditional unity developers, but I think it's counterproductive to give into the pressure of such feedback without attempting to educate people.

A bit nitpicky here with the terminology, but didn't we agree (months and months ago) that the term manager implied a single instance of something (and we ended up with system). Adding manager to the end of anything with System seems redundant, less descriptive, and more confusing.

@radicalad
Copy link
Member

Just want to give a little extra context. This feedback is coming directly from partners building production HoloLens applications with MRTK.

In fact many Microsoft employees close to MRTK worked directly with them to educate and guide them. The general feedback has been services and profiles are confusing, the system feels heavy and the MRTK Singleton is very aggressive. The feedback isn’t around POCO and decoupling services from MonoBehaviors.

We’re in beta, we’ve received legitimate feedback, we shouldn’t brush it off, we should figure out how to make MRTK work better. Education/documentation is only part of the solution.

@Ecnassianer
Copy link
Contributor

I'm a huge fan of the increased scene presence. This will help me teach the folks I work with about how MRTK works.

@Railboy
Copy link
Contributor

Railboy commented Mar 1, 2019

Big fan of this proposal. Directly addresses some of the loudest criticism / chatter I've been hearing from folks using the toolkit. I've been an advocate of the existing approach for a while but there's no shame in accepting that it didn't get the traction we'd hoped for. Like @radicalad says, we're in beta - now's the time.

@StephenHodgson agreed that educating people should be our goal - and the best way to teach someone about a new system is to present in familiar terms. Increased scene presence for services is a win regardless of (frankly minimal) perf costs. Nobody likes to be told their ignorance is the reason something is hard to use, and who can blame them?

@davidkline-ms This is just nitpicking but I do find the names hard to look at. I feel like there's got to be a better suffix than Manager - ServiceExecutor? ServiceLocator? ServiceHandler?

@david-c-kline
Copy link
Author

@davidkline-ms This is just nitpicking but I do find the names hard to look at. I feel like there's got to be a better suffix than Manager - ServiceExecutor? ServiceLocator? ServiceHandler?

While I can see that, the intent of these interfaces is to manage (load, unload, enumerate, etc) services. I'll give the interface names / service controllers (would that work?) some thought and get consensus from others before submitting any code. Hopefully the proposal's use of "manager" doesn't distract from the goal.

Also, please note that the proposal does not include removing the current approach, just augmenting it with the ability to add a system specific scene presence as well as the ability for others to easily add support for MRTK systems in their own components.

@Railboy
Copy link
Contributor

Railboy commented Mar 1, 2019

@davidkline-ms Agreed, the names are not a dealbreaker.

@StephenHodgson
Copy link
Contributor

StephenHodgson commented Mar 1, 2019

The general feedback has been services and profiles are confusing, the system feels heavy and the MRTK Singleton is very aggressive.

Could we elaborate on this? There isn't much substantial objective criteria here to formulate an appropriate response.

Nobody likes to be told their ignorance is the reason something is hard to use, and who can blame them?

💯% agree, and that wasn't exactly what I was trying to say, just that I think we should give people time to get familiar with it before deciding to fundamentally change the way it all works.

It'll be interesting to see how much workflows will continue change once Unity finishes up with ECS and DOTS. Traditional Unity development pipelines are going to be fundamentally changing a lot here in the near future.

Overall I think this is a step backwards, not forwards.

@david-c-kline
Copy link
Author

before deciding to fundamentally change the way it all works.

The existing MixedRealityToolkit object will remain an option. This is about providing options based on customer feedback.

@paseb
Copy link

paseb commented Mar 1, 2019

The general feedback has been services and profiles are confusing, the system feels heavy and the MRTK Singleton is very aggressive.

Could we elaborate on this? There isn't much substantial objective criteria here to formulate an appropriate response.

I can very much elaborate on this one. We have an unwieldy and over-engineered set of nested profiles and a frustratingly restrictive architecture. Which basically leads to the inability for anyone to incorporate MRTK in an existing project. If I used it in a new project I would quickly run into an inflexible set of requirements that kill iteration times. In general quality in products is a function of evolutionary or iteration time steps. Ideally everything should be easily extensible and able to function as a standalone service in whatever structure a developer wants to incorporate it. Case study, use the toolkit with others at a game jam or hackathon and observe.

💯% agree, and that wasn't exactly what I was trying to say, just that I think we should give people time to get familiar with it before deciding to fundamentally change the way it all works.
It'll be interesting to see how much workflows will continue change once Unity finishes up with ECS and DOTS. Traditional Unity development pipelines are going to be fundamentally changing a lot here in the near future.
Overall I think this is a step backwards, not forwards.

I couldn't disagree more. The phrase "give people time to get familiar" is an indicator that it's not intuitive. It's like a level designer saying that someone is "playing it wrong" only because they designed it for their golden path. Regardless of Unity's structural changes and features, if you rely on what their "new" infrastructures are you will always be on some shaky ground until they are proven and solidified. The step to having more namespaces than the library of congress and one ring to rule them all was a larger step backwards IMO.

thanks,
-pat

@Railboy
Copy link
Contributor

Railboy commented Mar 1, 2019

@StephenHodgson If your predictions about Unity's workflow are true then maybe this is just a case of a system being slightly ahead of its time. In which case it would be best to wait for Unity to boil the frog a bit longer and revisit the issue when devs are more receptive. You can't surf a wave that's still underwater.

@StephenHodgson
Copy link
Contributor

Option 2 adds a layer of complexity to satisfy design views of developers but doesn't degrade the overall functioning of the project.

Agreed, that's my biggest fear with this proposal.

The phrase "give people time to get familiar" is an indicator that it's not intuitive. It's like a level designer saying that someone is "playing it wrong" only because they designed it for their golden path.

Ouch, yup. You're absolutely right 🤣

@StephenHodgson
Copy link
Contributor

StephenHodgson commented Mar 1, 2019

One thing that I'd like to note with the service locator object, it that it's really just a manifest for all of the services that are running. It doesn't really do much of anything else, besides doing some scene setup with the event system, and camera.

I'm curious to know how this impacts everyone's thoughts on why it feels heavy handed and unweildy. The biggest problem it was trying to solve was ensuring that the object lifetimes were handled appropriately and MonoBehaviour events forwarded.

Ideally everything should be easily extensible and able to function as a standalone service in whatever structure a developer wants to incorporate it

The first part I believe to already be true, but if you could elaborate on the other structures?

Case study, use the toolkit with others at a game jam or hackathon and observe.

I wish I could have attended some, it would have been very interesting to see and learn from.

The mrtk was supposed to handle 90% of the needs, while letting the developer get that last 10%

For example:
You only have 48 hours, and you'd like to create an app that does x feature.

  1. Import the mrtk.
  2. Press configure

By this point everything should just work out of the box. No additional setup needed.

  1. Devs register their own custom service in the additional service provider profile, and iterate on that.
  2. Devs setup their scene and UI (can be done in parallel with 3)

Is there something above I missed?

@StephenHodgson
Copy link
Contributor

I would like to see if the usage of Namespaces can be impemented in the when registering and un-regestering services by name or type. This would allow for multiple implementations of a service to be alive but unique.

Love this idea @hridpath

@Cameron-Micka
Copy link
Member

The TL;DR I'm for this proposal and anything that makes our "mangers" system more Unity-like and scene accessible.

I 💯% agree for the need of system presence in the scene hierarchy. One of the reasons I believe Unity has become successful, especially among people new to game/3D development, is the accessibility of inspecting your app's state at runtime via the scene view. As it stands currently I can't really tell what state my systems are in and/or which ones are enabled without stepping though a debugger or plunging though profiles.

I also agree with @Railboy about

present in familiar terms.

A value I take into consideration when building new features in Unity is "does something akin to this featue already exist, and how can I make my feature behave as similar as possible to the way it's currently done?" When doing this you provided your users with familiarity and reduce friction when stepping into your new feature. The real win is when users can be productive right out of the gate because they have used a similar feature before.

@Ecnassianer
Copy link
Contributor

Unity has become successful [because of] the accessibility of inspecting your app's state at runtime via the scene view. As it stands currently I can't really tell what state my systems are in and/or which ones are enabled without stepping though a debugger or plunging though profiles.

^ This

@StephenHodgson
Copy link
Contributor

StephenHodgson commented Mar 1, 2019

Having a visual representation of the currently running systems is good feedback, but I'm not entirely convinced that we should use the scene as a representation of said information. The scene is only for UX/UI. It's a single layer of the application that should be reactive to the business level app logic.

What about if we had another way to represent them without putting them in the scene? These services/managers don't exist in the scene in any way, nor should they (they're purely c#). I'm open to making a window that looks very similar to the scene window, with a hierarchy that shows the relationships between services, and their data models.

Let's call it the service graph

@StephenHodgson
Copy link
Contributor

TL;DR the whole point of the re-architecture was to get away from the dependency of the MonoBehavour and the scene

These “Components” are intended to be attached to GameObjects which live in the project’s various scenes. However, this has the unfortunate side effect of making traditional patterns like MVC or MVVM impossible to use in the Unity development environment, out of the box. Instead, many developers closely couple application logic with the UI / UX logic — a practice frowned upon by many professional software engineers.

@Ecnassianer
Copy link
Contributor

I'm open to making a window that looks very similar to the scene window, with a hierarchy that shows the relationships between services, and their data models.

That's missing the point. I'm a Unity dev, and I have an incredibly powerful debugging tool that's constantly maintained and improved by one of the most accomplished game engine development teams in the world. Let me use that tool. Don't make me go without. Don't make me use Visual Studio. Don't make me use a home brewed version of that tool that only exists in MRTK.

david-c-kline pushed a commit that referenced this issue Mar 21, 2019
Service Registration (#3545) - Part 2 [Breaking Change for Service Implementors]
MenelvagorMilsom added a commit that referenced this issue Mar 21, 2019
david-c-kline pushed a commit that referenced this issue Apr 1, 2019
Service registration (#3545) part 3 - systems load data providers
@Yoyozilla Yoyozilla added Feature Request Feature request from the community and removed Plan of Record labels Apr 16, 2019
@david-c-kline
Copy link
Author

Closing as the infrastructure work is complete. Prefabs for standalone service usage are to be added in an experimental namespace in the SDK.

Configuration UX will be addressed via a separate set of work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
BREAKING CHANGE Feature Request Feature request from the community
Projects
None yet
Development

No branches or pull requests