@@ -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