Skip to content

Commit 335be33

Browse files
CHANGED: Expand Optimized Controls performance testing (#1905)
* Update existing performance tests It will make the tests run for more cases to see the impact of each internal feature flag. * Add tests for more use cases Adds a gamepad and mouse tests. This showcases that the optimizations won't improve performance for all use cases. * Add test which only calls InputSystem.Update() Evaluates cases when there are no control changes or reads from the user to assess if there's any impact by the optimizations. * Add test that shows degraded performance. When using ReadCacheValue for devices that have state updates every frame, there's a performance cost. * Add missing comments to some tests
1 parent 07c502d commit 335be33

File tree

1 file changed

+206
-16
lines changed

1 file changed

+206
-16
lines changed

Assets/Tests/InputSystem/CorePerformanceTests.cs

Lines changed: 206 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -546,24 +546,41 @@ public void TODO_CanSaveAndRestoreSystemInLessThan10Milliseconds() // Currently
546546

547547
#endif
548548

549-
internal enum OptimizedControlsTest
549+
internal enum OptimizationTestType
550550
{
551+
NoOptimization,
551552
OptimizedControls,
552-
NormalControls
553+
ReadValueCaching,
554+
OptimizedControlsAndReadValueCaching
553555
}
554556

555-
[Test, Performance]
556-
[Category("Performance")]
557-
[TestCase(OptimizedControlsTest.OptimizedControls)]
558-
[TestCase(OptimizedControlsTest.NormalControls)]
559-
public void Performance_OptimizedControls_ReadingMousePosition100kTimes(OptimizedControlsTest testSetup)
557+
public void SetInternalFeatureFlagsFromTestType(OptimizationTestType testType)
560558
{
561-
var useOptimizedControls = testSetup == OptimizedControlsTest.OptimizedControls;
559+
var useOptimizedControls = testType == OptimizationTestType.OptimizedControls
560+
|| testType == OptimizationTestType.OptimizedControlsAndReadValueCaching;
561+
var useReadValueCaching = testType == OptimizationTestType.ReadValueCaching
562+
|| testType == OptimizationTestType.OptimizedControlsAndReadValueCaching;
563+
562564
InputSystem.settings.SetInternalFeatureFlag(InputFeatureNames.kUseOptimizedControls, useOptimizedControls);
563-
InputSystem.settings.SetInternalFeatureFlag(InputFeatureNames.kUseReadValueCaching, useOptimizedControls);
565+
InputSystem.settings.SetInternalFeatureFlag(InputFeatureNames.kUseReadValueCaching, useReadValueCaching);
564566
InputSystem.settings.SetInternalFeatureFlag(InputFeatureNames.kParanoidReadValueCachingChecks, false);
567+
}
568+
569+
[Test, Performance]
570+
[Category("Performance")]
571+
[TestCase(OptimizationTestType.NoOptimization)]
572+
[TestCase(OptimizationTestType.OptimizedControls)]
573+
[TestCase(OptimizationTestType.ReadValueCaching)]
574+
[TestCase(OptimizationTestType.OptimizedControlsAndReadValueCaching)]
575+
// Isolated tests for reading from Mouse device to evaluate the performance of the optimizations.
576+
// Does not take into account the performance of the InputSystem.Update() call.
577+
public void Performance_OptimizedControls_ReadingMousePosition100kTimes(OptimizationTestType testType)
578+
{
579+
SetInternalFeatureFlagsFromTestType(testType);
565580

566581
var mouse = InputSystem.AddDevice<Mouse>();
582+
var useOptimizedControls = testType == OptimizationTestType.OptimizedControls
583+
|| testType == OptimizationTestType.OptimizedControlsAndReadValueCaching;
567584
Assert.That(mouse.position.x.optimizedControlDataType, Is.EqualTo(useOptimizedControls ? InputStateBlock.FormatFloat : InputStateBlock.FormatInvalid));
568585
Assert.That(mouse.position.y.optimizedControlDataType, Is.EqualTo(useOptimizedControls ? InputStateBlock.FormatFloat : InputStateBlock.FormatInvalid));
569586
Assert.That(mouse.position.optimizedControlDataType, Is.EqualTo(useOptimizedControls ? InputStateBlock.FormatVector2 : InputStateBlock.FormatInvalid));
@@ -579,17 +596,188 @@ public void Performance_OptimizedControls_ReadingMousePosition100kTimes(Optimize
579596
.Run();
580597
}
581598

599+
[Test, Performance]
600+
[Category("Performance")]
601+
[TestCase(OptimizationTestType.NoOptimization)]
602+
[TestCase(OptimizationTestType.OptimizedControls)]
603+
[TestCase(OptimizationTestType.ReadValueCaching)]
604+
[TestCase(OptimizationTestType.OptimizedControlsAndReadValueCaching)]
605+
// Currently these tests shows that all the optimizations have a performance cost when reading from a Mouse device.
606+
// OptimizedControls option is slower because of an extra check that is only done in Editor and Development Builds.
607+
// ReadValueCaching option is slower because Mouse state (FastMouse) is changed every update, which means cached
608+
// values are always stale. And currently there is a cost when caching the value.
609+
public void Performance_OptimizedControls_ReadAndUpdateMousePosition1kTimes(OptimizationTestType testType)
610+
{
611+
SetInternalFeatureFlagsFromTestType(testType);
612+
613+
var mouse = InputSystem.AddDevice<Mouse>();
614+
InputSystem.Update();
615+
616+
var useOptimizedControls = testType == OptimizationTestType.OptimizedControls
617+
|| testType == OptimizationTestType.OptimizedControlsAndReadValueCaching;
618+
Assert.That(mouse.position.x.optimizedControlDataType, Is.EqualTo(useOptimizedControls ? InputStateBlock.FormatFloat : InputStateBlock.FormatInvalid));
619+
Assert.That(mouse.position.y.optimizedControlDataType, Is.EqualTo(useOptimizedControls ? InputStateBlock.FormatFloat : InputStateBlock.FormatInvalid));
620+
Assert.That(mouse.position.optimizedControlDataType, Is.EqualTo(useOptimizedControls ? InputStateBlock.FormatVector2 : InputStateBlock.FormatInvalid));
621+
622+
Measure.Method(() =>
623+
{
624+
var pos = new Vector2();
625+
for (var i = 0; i < 1000; ++i)
626+
{
627+
pos += mouse.position.ReadValue();
628+
InputSystem.Update();
629+
630+
if (i % 100 == 0)
631+
{
632+
// Make sure there's a new different value every 100 frames.
633+
InputSystem.QueueStateEvent(mouse, new MouseState { position = new Vector2(i + 1, i + 2) });
634+
}
635+
}
636+
})
637+
.MeasurementCount(100)
638+
.WarmupCount(5)
639+
.Run();
640+
}
641+
642+
[Test, Performance]
643+
[Category("Performance")]
644+
[TestCase(OptimizationTestType.NoOptimization)]
645+
[TestCase(OptimizationTestType.ReadValueCaching)]
646+
// These tests shows a use case where ReadValueCaching optimization will perform better than without any
647+
// optimization.
648+
// It shows that there's a performance improvement when the control values being read are not changing every frame.
649+
public void Performance_OptimizedControls_ReadAndUpdateGamepad1kTimes(OptimizationTestType testType)
650+
{
651+
SetInternalFeatureFlagsFromTestType(testType);
652+
653+
var gamepad = InputSystem.AddDevice<Gamepad>();
654+
655+
InputSystem.Update();
656+
657+
Measure.Method(() =>
658+
{
659+
var pos = new Vector2();
660+
InputSystem.QueueStateEvent(gamepad, new GamepadState { leftStick = new Vector2(0.3f, 0.1f) });
661+
InputSystem.Update();
662+
663+
pos = gamepad.leftStick.value;
664+
Assert.That(gamepad.leftStick.m_CachedValueIsStale, Is.False);
665+
666+
for (var i = 0; i < 1000; ++i)
667+
{
668+
InputSystem.Update();
669+
pos = gamepad.leftStick.value;
670+
Assert.That(gamepad.leftStick.m_CachedValueIsStale, Is.False);
671+
672+
if (i % 100 == 0)
673+
{
674+
// Make sure there's a new different value every 100 frames to mark the cached value as stale.
675+
InputSystem.QueueStateEvent(gamepad, new GamepadState { leftStick = new Vector2(i / 1000f, i / 1000f) });
676+
InputSystem.Update();
677+
}
678+
}
679+
})
680+
.MeasurementCount(100)
681+
.WarmupCount(10)
682+
.Run();
683+
}
684+
685+
[Test, Performance]
686+
[Category("Performance")]
687+
[TestCase(OptimizationTestType.NoOptimization)]
688+
[TestCase(OptimizationTestType.ReadValueCaching)]
689+
// This shows a use case where ReadValueCaching optimization will perform worse when controls have stale cached
690+
// values every frame. Meaning, when control values change in every frame.
691+
public void Performance_OptimizedControls_ReadAndUpdateGamepadNewValuesEveryFrame1kTimes(OptimizationTestType testType)
692+
{
693+
SetInternalFeatureFlagsFromTestType(testType);
694+
695+
var gamepad = InputSystem.AddDevice<Gamepad>();
696+
697+
InputSystem.Update();
698+
699+
Measure.Method(() =>
700+
{
701+
var pos = new Vector2();
702+
InputSystem.QueueStateEvent(gamepad, new GamepadState { leftStick = new Vector2(0.1f, 0.1f) });
703+
InputSystem.Update();
704+
705+
gamepad.leftStick.ReadValue();
706+
Assert.That(gamepad.leftStick.m_CachedValueIsStale, Is.False);
707+
708+
for (var i = 0; i < 1000; ++i)
709+
{
710+
InputSystem.Update();
711+
pos = gamepad.leftStick.value;
712+
Assert.That(gamepad.leftStick.m_CachedValueIsStale, Is.False);
713+
// Make sure there's a new different value every frames to mark the cached value as stale.
714+
InputSystem.QueueStateEvent(gamepad, new GamepadState { leftStick = new Vector2(i / 1000f, i / 1000f) });
715+
}
716+
})
717+
.MeasurementCount(100)
718+
.WarmupCount(10)
719+
.Run();
720+
}
721+
722+
[Test, Performance]
723+
[Category("Performance")]
724+
[TestCase(OptimizationTestType.NoOptimization)]
725+
[TestCase(OptimizationTestType.OptimizedControls)]
726+
[TestCase(OptimizationTestType.ReadValueCaching)]
727+
[TestCase(OptimizationTestType.OptimizedControlsAndReadValueCaching)]
728+
// These tests evaluate the performance when there's no read value performed and only InputSystem.Update() is called.
729+
// Emulates a scenario where the controls are not being changed to evaluate the impact of the optimizations.
730+
public void Performance_OptimizedControls_UpdateOnly1kTimes(OptimizationTestType testType)
731+
{
732+
SetInternalFeatureFlagsFromTestType(testType);
733+
734+
// This adds FastMouse, which updates state every frame and can lead to a performance cost
735+
// when using ReadValueCaching.
736+
var mouse = InputSystem.AddDevice<Mouse>();
737+
InputSystem.Update();
738+
739+
Measure.Method(() =>
740+
{
741+
CallUpdate();
742+
})
743+
.MeasurementCount(100)
744+
.SampleGroup("Mouse Only")
745+
.WarmupCount(10)
746+
.Run();
747+
748+
InputSystem.RemoveDevice(mouse);
749+
InputSystem.AddDevice<Gamepad>();
750+
InputSystem.Update();
751+
752+
Measure.Method(() =>
753+
{
754+
CallUpdate();
755+
})
756+
.MeasurementCount(100)
757+
.SampleGroup("Gamepad Only")
758+
.WarmupCount(10)
759+
.Run();
760+
761+
return;
762+
763+
void CallUpdate()
764+
{
765+
for (var i = 0; i < 1000; ++i) InputSystem.Update();
766+
}
767+
}
768+
582769
#if ENABLE_VR
583770
[Test, Performance]
584771
[Category("Performance")]
585-
[TestCase(OptimizedControlsTest.OptimizedControls)]
586-
[TestCase(OptimizedControlsTest.NormalControls)]
587-
public void Performance_OptimizedControls_ReadingPose4kTimes(OptimizedControlsTest testSetup)
772+
[TestCase(OptimizationTestType.NoOptimization)]
773+
[TestCase(OptimizationTestType.OptimizedControls)]
774+
[TestCase(OptimizationTestType.ReadValueCaching)]
775+
[TestCase(OptimizationTestType.OptimizedControlsAndReadValueCaching)]
776+
// Isolated tests for reading from XR Pose device to evaluate the performance of the optimizations.
777+
// Does not take into account the performance of the InputSystem.Update() call.
778+
public void Performance_OptimizedControls_ReadingPose4kTimes(OptimizationTestType testType)
588779
{
589-
var useOptimizedControls = testSetup == OptimizedControlsTest.OptimizedControls;
590-
InputSystem.settings.SetInternalFeatureFlag(InputFeatureNames.kUseOptimizedControls, useOptimizedControls);
591-
InputSystem.settings.SetInternalFeatureFlag(InputFeatureNames.kUseReadValueCaching, useOptimizedControls);
592-
InputSystem.settings.SetInternalFeatureFlag(InputFeatureNames.kParanoidReadValueCachingChecks, false);
780+
SetInternalFeatureFlagsFromTestType(testType);
593781

594782
runtime.ReportNewInputDevice(XRTests.PoseDeviceState.CreateDeviceDescription().ToJson());
595783

@@ -598,6 +786,8 @@ public void Performance_OptimizedControls_ReadingPose4kTimes(OptimizedControlsTe
598786
var device = InputSystem.devices[0];
599787

600788
var poseControl = device["posecontrol"] as UnityEngine.InputSystem.XR.PoseControl;
789+
var useOptimizedControls = testType == OptimizationTestType.OptimizedControls
790+
|| testType == OptimizationTestType.OptimizedControlsAndReadValueCaching;
601791
Assert.That(poseControl.optimizedControlDataType, Is.EqualTo(useOptimizedControls ? InputStateBlock.FormatPose : InputStateBlock.FormatInvalid));
602792

603793
Measure.Method(() =>

0 commit comments

Comments
 (0)