Skip to content

Commit 102b004

Browse files
authored
NEW: ProfilerMarkers for measuring enabling/disabling of InputActions and for binding resolution. (#2056)
1 parent 788d2cc commit 102b004

File tree

5 files changed

+119
-90
lines changed

5 files changed

+119
-90
lines changed

Assets/Tests/InputSystem/CorePerformanceTests.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1113,7 +1113,10 @@ public void Performance_OptimizedControls_ReadingPose4kTimes(OptimizationTestTyp
11131113
"InputSystem.onAfterUpdate",
11141114
"PreUpdate.NewInputUpdate",
11151115
"PreUpdate.InputForUIUpdate",
1116-
"FixedUpdate.NewInputFixedUpdate"
1116+
"FixedUpdate.NewInputFixedUpdate",
1117+
"InputAction.Disable",
1118+
"InputAction.Enable",
1119+
"InputActionMap.ResolveBindings"
11171120
};
11181121

11191122
[PrebuildSetup(typeof(ProjectWideActionsBuildSetup))]

Assets/Tests/InputSystem/Plugins/UITests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1793,13 +1793,15 @@ public IEnumerator UI_CanReleaseAndPressTouchesOnSameFrame()
17931793
.Matches((UICallbackReceiver.Event e) => e.pointerData.pointerType == UIPointerType.Touch).And
17941794
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == secondPosition));
17951795

1796+
#if UNITY_2021_2_OR_NEWER
17961797
Assert.That(scene.rightChildReceiver.events,
17971798
Has.Exactly(1).With.Property("type").EqualTo(EventType.PointerMove).And
17981799
.Matches((UICallbackReceiver.Event e) => e.pointerData.device == touchScreen).And
17991800
.Matches((UICallbackReceiver.Event e) => e.pointerData.touchId == 2).And
18001801
.Matches((UICallbackReceiver.Event e) => e.pointerData.pointerId == pointerIdTouch2).And
18011802
.Matches((UICallbackReceiver.Event e) => e.pointerData.pointerType == UIPointerType.Touch).And
18021803
.Matches((UICallbackReceiver.Event e) => e.pointerData.position == secondPosition));
1804+
#endif
18031805

18041806
// Pointer 3
18051807
Assert.That(scene.rightChildReceiver.events,

Packages/com.unity.inputsystem/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ however, it has to be formatted properly to pass verification tests.
4242

4343
### Added
4444
- Added new API `InputSystem.settings.useIMGUIEditorForAssets` that should be used in custom `InputParameterEditor` that use both IMGUI and UI Toolkit.
45+
- Added ProfilerMakers to `InputAction.Enable()` and `InputActionMap.ResolveBindings()` to enable gathering of profiling data.
4546

4647
## [1.11.2] - 2024-10-16
4748

Packages/com.unity.inputsystem/InputSystem/Actions/InputAction.cs

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using Unity.Collections.LowLevel.Unsafe;
3+
using Unity.Profiling;
34
using UnityEngine.InputSystem.Controls;
45
using UnityEngine.InputSystem.LowLevel;
56
using UnityEngine.InputSystem.Utilities;
@@ -681,6 +682,12 @@ public bool wantsInitialStateCheck
681682
}
682683
}
683684

685+
/// <summary>
686+
/// ProfilerMarker for measuring the enabling/disabling of InputActions.
687+
/// </summary>
688+
static readonly ProfilerMarker k_InputActionEnableProfilerMarker = new ProfilerMarker("InputAction.Enable");
689+
static readonly ProfilerMarker k_InputActionDisableProfilerMarker = new ProfilerMarker("InputAction.Disable");
690+
684691
/// <summary>
685692
/// Construct an unnamed, free-standing action that is not part of any map or asset
686693
/// and has no bindings. Bindings can be added with <see
@@ -899,18 +906,21 @@ public override string ToString()
899906
/// <seealso cref="enabled"/>
900907
public void Enable()
901908
{
902-
if (enabled)
903-
return;
909+
using (k_InputActionEnableProfilerMarker.Auto())
910+
{
911+
if (enabled)
912+
return;
904913

905-
// For singleton actions, we create an internal-only InputActionMap
906-
// private to the action.
907-
var map = GetOrCreateActionMap();
914+
// For singleton actions, we create an internal-only InputActionMap
915+
// private to the action.
916+
var map = GetOrCreateActionMap();
908917

909-
// First time we're enabled, find all controls.
910-
map.ResolveBindingsIfNecessary();
918+
// First time we're enabled, find all controls.
919+
map.ResolveBindingsIfNecessary();
911920

912-
// Go live.
913-
map.m_State.EnableSingleAction(this);
921+
// Go live.
922+
map.m_State.EnableSingleAction(this);
923+
}
914924
}
915925

916926
/// <summary>
@@ -928,10 +938,13 @@ public void Enable()
928938
/// <seealso cref="Enable"/>
929939
public void Disable()
930940
{
931-
if (!enabled)
932-
return;
941+
using (k_InputActionDisableProfilerMarker.Auto())
942+
{
943+
if (!enabled)
944+
return;
933945

934-
m_ActionMap.m_State.DisableSingleAction(this);
946+
m_ActionMap.m_State.DisableSingleAction(this);
947+
}
935948
}
936949

937950
////REVIEW: is *not* cloning IDs here really the right thing to do?

Packages/com.unity.inputsystem/InputSystem/Actions/InputActionMap.cs

Lines changed: 87 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Collections.Generic;
44
using System.Linq;
55
using Unity.Collections;
6+
using Unity.Profiling;
67
using UnityEngine.InputSystem.Utilities;
78

89
////REVIEW: given we have the global ActionPerformed callback, do we really need the per-map callback?
@@ -313,6 +314,11 @@ public event Action<InputAction.CallbackContext> actionTriggered
313314
remove => m_ActionCallbacks.RemoveCallback(value);
314315
}
315316

317+
/// <summary>
318+
/// ProfilerMarker to measure how long it takes to resolve bindings.
319+
/// </summary>
320+
static readonly ProfilerMarker k_ResolveBindingsProfilerMarker = new ProfilerMarker("InputActionMap.ResolveBindings");
321+
316322
/// <summary>
317323
/// Construct an action map with default values.
318324
/// </summary>
@@ -1299,100 +1305,104 @@ internal bool ResolveBindingsIfNecessary()
12991305
/// </remarks>
13001306
internal void ResolveBindings()
13011307
{
1302-
// Make sure that if we trigger callbacks as part of disabling and re-enabling actions,
1303-
// we don't trigger a re-resolve while we're already resolving bindings.
1304-
using (InputActionRebindingExtensions.DeferBindingResolution())
1308+
using (k_ResolveBindingsProfilerMarker.Auto())
13051309
{
1306-
// In case we have actions that are currently enabled, we temporarily retain the
1307-
// UnmanagedMemory of our InputActionState so that we can sync action states after
1308-
// we have re-resolved bindings.
1309-
var oldMemory = new InputActionState.UnmanagedMemory();
1310-
try
1310+
// Make sure that if we trigger callbacks as part of disabling and re-enabling actions,
1311+
// we don't trigger a re-resolve while we're already resolving bindings.
1312+
using (InputActionRebindingExtensions.DeferBindingResolution())
13111313
{
1312-
OneOrMore<InputActionMap, ReadOnlyArray<InputActionMap>> actionMaps;
1314+
// In case we have actions that are currently enabled, we temporarily retain the
1315+
// UnmanagedMemory of our InputActionState so that we can sync action states after
1316+
// we have re-resolved bindings.
1317+
var oldMemory = new InputActionState.UnmanagedMemory();
1318+
try
1319+
{
1320+
OneOrMore<InputActionMap, ReadOnlyArray<InputActionMap>> actionMaps;
13131321

1314-
// Start resolving.
1315-
var resolver = new InputBindingResolver();
1322+
// Start resolving.
1323+
var resolver = new InputBindingResolver();
13161324

1317-
// If we're part of an asset, we share state and thus binding resolution with
1318-
// all maps in the asset.
1319-
var needFullResolve = m_State == null;
1320-
if (m_Asset != null)
1321-
{
1322-
actionMaps = m_Asset.actionMaps;
1323-
Debug.Assert(actionMaps.Count > 0, "Asset referred to by action map does not have action maps");
1325+
// If we're part of an asset, we share state and thus binding resolution with
1326+
// all maps in the asset.
1327+
var needFullResolve = m_State == null;
1328+
if (m_Asset != null)
1329+
{
1330+
actionMaps = m_Asset.actionMaps;
1331+
Debug.Assert(actionMaps.Count > 0, "Asset referred to by action map does not have action maps");
13241332

1325-
// If there's a binding mask set on the asset, apply it.
1326-
resolver.bindingMask = m_Asset.m_BindingMask;
1333+
// If there's a binding mask set on the asset, apply it.
1334+
resolver.bindingMask = m_Asset.m_BindingMask;
13271335

1328-
foreach (var map in actionMaps)
1336+
foreach (var map in actionMaps)
1337+
{
1338+
needFullResolve |= map.bindingResolutionNeedsFullReResolve;
1339+
map.needToResolveBindings = false;
1340+
map.bindingResolutionNeedsFullReResolve = false;
1341+
map.controlsForEachActionInitialized = false;
1342+
}
1343+
}
1344+
else
13291345
{
1330-
needFullResolve |= map.bindingResolutionNeedsFullReResolve;
1331-
map.needToResolveBindings = false;
1332-
map.bindingResolutionNeedsFullReResolve = false;
1333-
map.controlsForEachActionInitialized = false;
1346+
// Standalone action map (possibly a hidden one created for a singleton action).
1347+
// Gets its own private state.
1348+
1349+
actionMaps = this;
1350+
needFullResolve |= bindingResolutionNeedsFullReResolve;
1351+
needToResolveBindings = false;
1352+
bindingResolutionNeedsFullReResolve = false;
1353+
controlsForEachActionInitialized = false;
13341354
}
1335-
}
1336-
else
1337-
{
1338-
// Standalone action map (possibly a hidden one created for a singleton action).
1339-
// Gets its own private state.
1340-
1341-
actionMaps = this;
1342-
needFullResolve |= bindingResolutionNeedsFullReResolve;
1343-
needToResolveBindings = false;
1344-
bindingResolutionNeedsFullReResolve = false;
1345-
controlsForEachActionInitialized = false;
1346-
}
13471355

1348-
// If we already have a state, re-use the arrays we have already allocated.
1349-
// NOTE: We will install the arrays on the very same InputActionState instance below. In the
1350-
// case where we didn't have to grow the arrays, we should end up with zero GC allocations
1351-
// here.
1352-
var hasEnabledActions = false;
1353-
InputControlList<InputControl> activeControls = default;
1354-
if (m_State != null)
1355-
{
1356-
// Grab a clone of the current memory. We clone because disabling all the actions
1357-
// in the map will alter the memory state and we want the state before we start
1358-
// touching it.
1359-
oldMemory = m_State.memory.Clone();
1356+
// If we already have a state, re-use the arrays we have already allocated.
1357+
// NOTE: We will install the arrays on the very same InputActionState instance below. In the
1358+
// case where we didn't have to grow the arrays, we should end up with zero GC allocations
1359+
// here.
1360+
var hasEnabledActions = false;
1361+
InputControlList<InputControl> activeControls = default;
1362+
if (m_State != null)
1363+
{
1364+
// Grab a clone of the current memory. We clone because disabling all the actions
1365+
// in the map will alter the memory state and we want the state before we start
1366+
// touching it.
1367+
oldMemory = m_State.memory.Clone();
13601368

1361-
m_State.PrepareForBindingReResolution(needFullResolve, ref activeControls, ref hasEnabledActions);
1369+
m_State.PrepareForBindingReResolution(needFullResolve, ref activeControls, ref hasEnabledActions);
13621370

1363-
// Reuse the arrays we have so that we can avoid managed memory allocations, if possible.
1364-
resolver.StartWithPreviousResolve(m_State, isFullResolve: needFullResolve);
1371+
// Reuse the arrays we have so that we can avoid managed memory allocations, if possible.
1372+
resolver.StartWithPreviousResolve(m_State, isFullResolve: needFullResolve);
13651373

1366-
// Throw away old memory.
1367-
m_State.memory.Dispose();
1368-
}
1374+
// Throw away old memory.
1375+
m_State.memory.Dispose();
1376+
}
13691377

1370-
// Resolve all maps in the asset.
1371-
foreach (var map in actionMaps)
1372-
resolver.AddActionMap(map);
1378+
// Resolve all maps in the asset.
1379+
foreach (var map in actionMaps)
1380+
resolver.AddActionMap(map);
13731381

1374-
// Install state.
1375-
if (m_State == null)
1376-
{
1377-
m_State = new InputActionState();
1378-
m_State.Initialize(resolver);
1379-
}
1380-
else
1381-
{
1382-
m_State.ClaimDataFrom(resolver);
1382+
// Install state.
1383+
if (m_State == null)
1384+
{
1385+
m_State = new InputActionState();
1386+
m_State.Initialize(resolver);
1387+
}
1388+
else
1389+
{
1390+
m_State.ClaimDataFrom(resolver);
1391+
}
1392+
1393+
if (m_Asset != null)
1394+
{
1395+
foreach (var map in actionMaps)
1396+
map.m_State = m_State;
1397+
m_Asset.m_SharedStateForAllMaps = m_State;
1398+
}
1399+
1400+
m_State.FinishBindingResolution(hasEnabledActions, oldMemory, activeControls, isFullResolve: needFullResolve);
13831401
}
1384-
if (m_Asset != null)
1402+
finally
13851403
{
1386-
foreach (var map in actionMaps)
1387-
map.m_State = m_State;
1388-
m_Asset.m_SharedStateForAllMaps = m_State;
1404+
oldMemory.Dispose();
13891405
}
1390-
1391-
m_State.FinishBindingResolution(hasEnabledActions, oldMemory, activeControls, isFullResolve: needFullResolve);
1392-
}
1393-
finally
1394-
{
1395-
oldMemory.Dispose();
13961406
}
13971407
}
13981408
}

0 commit comments

Comments
 (0)