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

Add Component Cache #11686

Merged
merged 10 commits into from
Jul 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions com.microsoft.mrtk.core/Utilities/ComponentCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using UnityEngine;

namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// A static helper class which caches instances of a component of type T.
/// This is called in place of calling FindFirstObjectByType and FindObjectsByType to improve performance.
/// </summary>
public static class ComponentCache<T> where T : Component
{
private static T cacheFirstInstance = null;

/// <summary>
/// Finds the first instance of an object of the given type.
/// </summary>
/// <returns>
/// The first instance of an object of the given type.
/// </returns>
public static T FindFirstActiveInstance()
{
_ = TryFindFirstActiveInstance(out T result);
return result;
}

/// <summary>
/// Finds the first instance of an object of the given type.
/// </summary>
/// <returns>
/// True if an instance was found, and false otherwise.
/// </returns>
public static bool TryFindFirstActiveInstance(out T result)
{
if (cacheFirstInstance == null || !cacheFirstInstance.gameObject.activeInHierarchy)
{
cacheFirstInstance = Object.FindFirstObjectByType<T>();
}

result = cacheFirstInstance;
return result != null;
}
}
}
11 changes: 11 additions & 0 deletions com.microsoft.mrtk.core/Utilities/ComponentCache.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions com.microsoft.mrtk.core/Utilities/ControllerLookup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,12 @@ public XRBaseController RightHandController
set => rightHandController = value;
}

private void OnValidate()
{
if (FindObjectsByType<ControllerLookup>(FindObjectsSortMode.None).Length > 1)
{
Debug.LogWarning("Found more than one instance of the ControllerLookup class in the hierarchy. There should only be one");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,7 @@ internal class FlatScreenModeDetector : MonoBehaviour, IInteractionModeDetector

public void Awake()
{
ControllerLookup[] lookups = GameObject.FindObjectsOfType(typeof(ControllerLookup)) as ControllerLookup[];
if (lookups.Length == 0)
{
Debug.LogError("Could not locate an instance of the ControllerLookup class in the hierarchy. It is recommended to add ControllerLookup to your camera rig.");
}
else if (lookups.Length > 1)
{
Debug.LogWarning("Found more than one instance of the ControllerLookup class in the hierarchy. Defaulting to the first instance.");
controllerLookup = lookups[0];
}
else
{
controllerLookup = lookups[0];
}
controllerLookup = ComponentCache<ControllerLookup>.FindFirstActiveInstance();
}

/// <inheritdoc />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ internal protected XRInteractionManager InteractionManager
{
if (interactionManager == null)
{
interactionManager = FindObjectOfType<XRInteractionManager>();
interactionManager = ComponentCache<XRInteractionManager>.FindFirstActiveInstance();
}

return interactionManager;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ private void Awake()
{
if (interactionManager == null)
{
interactionManager = FindObjectOfType<XRInteractionManager>();
interactionManager = ComponentCache<XRInteractionManager>.FindFirstActiveInstance();
}

if (interactionManager == null)
Expand Down
18 changes: 1 addition & 17 deletions com.microsoft.mrtk.spatialmanipulation/Solvers/Solver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -244,25 +244,9 @@ protected virtual void Start()
{
// Find the controller lookup class in the hierarchy. Solvers that require access to the
// left, right or gaze controllers will use the references stored in this class.
//
// While FindObjectsOfType can be an expensive method, the controllerLookup variable is
// static. The hierarchy traversal will occur a maximum of once per scene.
if (controllerLookup == null)
{
ControllerLookup[] lookups = GameObject.FindObjectsOfType(typeof(ControllerLookup)) as ControllerLookup[];
if (lookups.Length == 0)
{
Debug.LogError("Could not locate an instance of the ControllerLookup class in the hierarchy. It is recommended to add ControllerLookup to your camera rig.");
}
else if (lookups.Length > 1)
{
Debug.LogWarning("Found more than one instance of the ControllerLookup class in the hierarchy. Defaulting to the first instance.");
controllerLookup = lookups[0];
}
else
{
controllerLookup = lookups[0];
}
controllerLookup = ComponentCache<ControllerLookup>.FindFirstActiveInstance();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ private void RegisterPlacementAction()

if (interactionManager == null)
{
interactionManager = FindObjectOfType<XRInteractionManager>();
interactionManager = ComponentCache<XRInteractionManager>.FindFirstActiveInstance();
if (interactionManager == null)
{
Debug.LogError("No interaction manager found in scene. Please add an interaction manager to the scene.");
Expand Down
2 changes: 1 addition & 1 deletion com.microsoft.mrtk.uxcore/Interop/UGUIInputAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ internal protected XRInteractionManager InteractionManager
else
{
// Otherwise, go find one.
interactionManager = FindObjectOfType<XRInteractionManager>();
interactionManager = ComponentCache<XRInteractionManager>.FindFirstActiveInstance();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

using TMPro;
using UnityEngine;
#if MRTK_INPUT_PRESENT && MRTK_SPEECH_PRESENT
using Microsoft.MixedReality.Toolkit.Input;
#endif

namespace Microsoft.MixedReality.Toolkit.UX
{
Expand Down Expand Up @@ -47,6 +50,12 @@ private void Start()
{
// Check if input and speech packages are present
#if MRTK_INPUT_PRESENT && MRTK_SPEECH_PRESENT
// If we can't find any active speech interactors, then do not enable the labels.
if (!ComponentCache<SpeechInteractor>.FindFirstActiveInstance())
{
return;
}

SeeItSayItLabel.SetActive(true);

// Children must be disabled so that they are not initially visible
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
// Licensed under the MIT License.

using System.Collections;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.MixedReality.Toolkit.Core.Tests;
using Microsoft.MixedReality.Toolkit.Input;
using Microsoft.MixedReality.Toolkit.Input.Tests;
using NUnit.Framework;
using TMPro;
Expand All @@ -21,11 +23,19 @@ public class SeeItSayItLabelEnablerTests : BaseRuntimeInputTests
[UnityTest]
public IEnumerator TestEnableAndSetLabel()
{
#if MRTK_INPUT_PRESENT && MRTK_SPEECH_PRESENT
SpeechInteractor interactor = Object.FindAnyObjectByType<SpeechInteractor>(FindObjectsInactive.Include);
interactor.gameObject.SetActive(true);

GameObject testButton = SetUpButton(true, Control.None);
yield return null;
if (Application.isBatchMode)
{
LogAssert.Expect(LogType.Exception, new Regex("Speech recognition is not supported on this machine"));
}

Transform label = testButton.transform.GetChild(0);
#if MRTK_INPUT_PRESENT && MRTK_SPEECH_PRESENT

Transform sublabel = label.transform.GetChild(0);
Assert.IsTrue(label.gameObject.activeSelf, "Label is enabled");
Assert.IsTrue(!sublabel.gameObject.activeSelf, "Child objects are disabled");
Expand Down Expand Up @@ -57,11 +67,18 @@ public IEnumerator TestVoiceCommandsUnavailable()
[UnityTest]
public IEnumerator TestPositionCanvasLabel()
{
#if MRTK_INPUT_PRESENT && MRTK_SPEECH_PRESENT
SpeechInteractor interactor = Object.FindAnyObjectByType<SpeechInteractor>(FindObjectsInactive.Include);
interactor.gameObject.SetActive(true);

GameObject testButton = SetUpButton(true, Control.Canvas);
yield return null;
if (Application.isBatchMode)
{
LogAssert.Expect(LogType.Exception, new Regex("Speech recognition is not supported on this machine"));
}

Transform label = testButton.transform.GetChild(0);
#if MRTK_INPUT_PRESENT && MRTK_SPEECH_PRESENT
RectTransform sublabel = label.transform.GetChild(0) as RectTransform;
Assert.AreEqual(sublabel.anchoredPosition3D, new Vector3(10, -30, -10), "Label is positioned correctly");
#else
Expand All @@ -76,11 +93,18 @@ public IEnumerator TestPositionCanvasLabel()
[UnityTest]
public IEnumerator TestPositionNonCanvasLabel()
{
#if MRTK_INPUT_PRESENT && MRTK_SPEECH_PRESENT
SpeechInteractor interactor = Object.FindAnyObjectByType<SpeechInteractor>(FindObjectsInactive.Include);
interactor.gameObject.SetActive(true);

GameObject testButton = SetUpButton(true, Control.NonCanvas);
yield return null;
if (Application.isBatchMode)
{
LogAssert.Expect(LogType.Exception, new Regex("Speech recognition is not supported on this machine"));
}

Transform label = testButton.transform.GetChild(0);
#if MRTK_INPUT_PRESENT && MRTK_SPEECH_PRESENT
Assert.AreEqual(label.transform.localPosition, new Vector3(10f, -.504f, -.004f), "Label is positioned correctly");
#else
Assert.IsTrue(!label.gameObject.activeSelf, "Did not enable label because voice commands unavailable.");
Expand Down