From d7fdac47415373919253b297b5b82a3ce5c662aa Mon Sep 17 00:00:00 2001 From: Tim Keosababian <36088732+timkeo@users.noreply.github.com> Date: Mon, 22 Jan 2024 16:16:07 -0800 Subject: [PATCH 01/10] Implement basic support for FEPM (ISX-1842) - Rework InputSystem's Init functions to handle optional domain reloads - Bypass InitializeInPlayer() "Reset" and "Restore" functions if DRs disabled Only the first step to getting this working, but enables FEPM support for golden path scenarios. --- .../InputSystem/InputSystem.cs | 80 +++++++++++++------ .../Tests/TestFixture/InputTestFixture.cs | 3 +- 2 files changed, 56 insertions(+), 27 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs b/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs index c7f78b18bd..be416ecb64 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs @@ -80,9 +80,19 @@ namespace UnityEngine.InputSystem #if UNITY_EDITOR [InitializeOnLoad] #endif - public static partial class InputSystem { + static InputSystem() + { + GlobalInitialize(true); + } + + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + private static void RuntimeInitialize() + { + GlobalInitialize(false); + } + #region Layouts /// @@ -3102,7 +3112,7 @@ public static InputActionAsset actions return; var valueIsNotNull = value != null; -#if UNITY_EDITOR + #if UNITY_EDITOR // Do not allow assigning non-persistent assets (pure in-memory objects) if (valueIsNotNull && !EditorUtility.IsPersistent(value)) throw new ArgumentException($"Assigning a non-persistent {nameof(InputActionAsset)} to this property is not allowed. The assigned asset need to be persisted on disc inside the /Assets folder."); @@ -3117,7 +3127,7 @@ public static InputActionAsset actions // Note that we do not enable/disable any actions until play-mode } - } + } /// /// Event that is triggered if the instance assigned to property changes. @@ -3129,7 +3139,7 @@ public static InputActionAsset actions /// /// public static event Action onActionsChange - { + { add => s_Manager.onActionsChange += value; remove => s_Manager.onActionsChange -= value; } @@ -3487,30 +3497,42 @@ private static bool ShouldEnableRemoting() // The rest here is internal stuff to manage singletons, survive domain reloads, // and to support the reset ability for tests. - static InputSystem() + + private static bool IsDomainReloadDisabledForPlayMode() { - #if UNITY_EDITOR - InitializeInEditor(); - #else - InitializeInPlayer(); +#if UNITY_EDITOR && !ENABLE_CORECLR + if (!EditorSettings.enterPlayModeOptionsEnabled || (EditorSettings.enterPlayModeOptions & EnterPlayModeOptions.DisableDomainReload) == 0) + return false; #endif + return true; } - ////FIXME: Unity is not calling this method if it's inside an #if block that is not - //// visible to the editor; that shouldn't be the case - [RuntimeInitializeOnLoadMethod(loadType: RuntimeInitializeLoadType.SubsystemRegistration)] - private static void RunInitializeInPlayer() + private static void GlobalInitialize(bool calledFromCtor) + { + // This method is called twice: once from the static ctor and again from RuntimeInitialize(). + // We handle the calls differently for the Editor and Player. + +#if UNITY_EDITOR + // If Domain Reloads are enabled, InputSystem is initialized via the ctor and we can ignore + // the second call from "Runtime", otherwise (DRs are disabled) the ctor isn't fired, so we + // must initialize via the Runtime call. + + if (calledFromCtor || IsDomainReloadDisabledForPlayMode()) { - // We're using this method just to make sure the class constructor is called - // so we don't need any code in here. When the engine calls this method, the - // class constructor will be run if it hasn't been run already. + InitializeInEditor(calledFromCtor); + } +#else + // In the Player, simply initialize InputSystem from the ctor and then execute the initial update + // from the second call. This saves us from needing another RuntimeInitializeOnLoad attribute. - // IL2CPP has a bug that causes the class constructor to not be run when - // the RuntimeInitializeOnLoadMethod is invoked. So we need an explicit check - // here until that is fixed (case 1014293). - #if !UNITY_EDITOR - if (s_Manager == null) + if (calledFromCtor) + { InitializeInPlayer(); + } + else + { + RunInitialUpdate(); + } #endif } @@ -3524,18 +3546,24 @@ internal static void EnsureInitialized() #if UNITY_EDITOR internal static InputSystemObject s_SystemObject; - internal static void InitializeInEditor(IInputRuntime runtime = null) + internal static void InitializeInEditor(bool calledFromCtor, IInputRuntime runtime = null) { Profiler.BeginSample("InputSystem.InitializeInEditor"); + // This is only necessary after a Domain Reload but otherwise can be skipped. + bool globalReset = calledFromCtor || !IsDomainReloadDisabledForPlayMode(); + + if (globalReset) Reset(runtime: runtime); var existingSystemObjects = Resources.FindObjectsOfTypeAll(); if (existingSystemObjects != null && existingSystemObjects.Length > 0) { + if (globalReset) + { ////FIXME: does not preserve action map state - // We're coming back out of a domain reload. We're restoring part of the + // If we're coming back out of a domain reload. We're restoring part of the // InputManager state here but we're still waiting from layout registrations // that happen during domain initialization. @@ -3559,6 +3587,7 @@ internal static void InitializeInEditor(IInputRuntime runtime = null) // Get rid of saved state. s_SystemObject.systemState = new State(); } + } else { s_SystemObject = ScriptableObject.CreateInstance(); @@ -3761,7 +3790,6 @@ private static void InitializeInPlayer(IInputRuntime runtime = null, InputSettin #endif // UNITY_EDITOR - [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] private static void RunInitialUpdate() { // Request an initial Update so that user methods such as Start and Awake @@ -3898,9 +3926,9 @@ private static void Reset(bool enableRemoting = false, IInputRuntime runtime = n // This is the point where we initialise project-wide actions for the Editor, Editor Tests and Player Tests. // Note this is too early for editor ! actions is not setup yet. - #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS EnableActions(); - #endif +#endif Profiler.EndSample(); } diff --git a/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestFixture.cs b/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestFixture.cs index 14c3f6398f..22e75e6f8b 100644 --- a/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestFixture.cs +++ b/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestFixture.cs @@ -905,10 +905,11 @@ internal void SimulateDomainReload() // This quite invasively goes into InputSystem internals. Unfortunately, we // have no proper way of simulating domain reloads ATM. So we directly call various // internal methods here in a sequence similar to what we'd get during a domain reload. + // Since we're faking it, pass 'true' for calledFromCtor param. InputSystem.s_SystemObject.OnBeforeSerialize(); InputSystem.s_SystemObject = null; - InputSystem.InitializeInEditor(runtime); + InputSystem.InitializeInEditor(true, runtime); } #endif From 7371ea8f2f09bcbdf069fa2789e29805c660a7ab Mon Sep 17 00:00:00 2001 From: Tim Keosababian <36088732+timkeo@users.noreply.github.com> Date: Thu, 25 Jan 2024 16:06:00 -0800 Subject: [PATCH 02/10] Refactor InputManager and tests for cleaner init flows (ISX-1842) - Move Reset/Restore state functionality out of InputSystem to the Test assembly (InputTestStateManager.cs) - Refactor InputManager Init/Dispose to be cleaner and better abstracted: * Adds CreateAndInitialize static method * Replaces Destroy() with IDisposable implementation * InputManager creates "default" InputSettings object if none provided * Runtime, Settings, and Metrics fields now private - Update InitializeInEditor() to incorporate changes - Update and fix tests For the most part, the logic should be mostly preserved. InitializeInEditor() has the biggest (logical) change because Reset() (moved to Tests) contained some actual init calls that needed to be pulled out. However, we *should* be making the same calls in the same order. However, this change does seem to "break" some of the OnScreenTests(); they're now unstable. This will need to be fixed in a later commit. --- Assets/Tests/InputSystem/CoreTests_Editor.cs | 31 +- .../Tests/InputSystem/CoreTests_Remoting.cs | 10 +- Assets/Tests/InputSystem/Plugins/HIDTests.cs | 4 +- .../InputSystem/InputAnalytics.cs | 10 +- .../InputSystem/InputManager.cs | 143 ++++++--- .../InputSystem/InputSystem.cs | 277 +++++------------- .../InputSystem/Plugins/iOS/iOSSupport.cs | 2 +- .../Tests/TestFixture/InputTestFixture.cs | 24 +- .../TestFixture/InputTestStateManager.cs | 196 +++++++++++++ .../TestFixture/InputTestStateManager.cs.meta | 2 + 10 files changed, 402 insertions(+), 297 deletions(-) create mode 100644 Packages/com.unity.inputsystem/Tests/TestFixture/InputTestStateManager.cs create mode 100644 Packages/com.unity.inputsystem/Tests/TestFixture/InputTestStateManager.cs.meta diff --git a/Assets/Tests/InputSystem/CoreTests_Editor.cs b/Assets/Tests/InputSystem/CoreTests_Editor.cs index 0a74266c6b..8afe88742c 100644 --- a/Assets/Tests/InputSystem/CoreTests_Editor.cs +++ b/Assets/Tests/InputSystem/CoreTests_Editor.cs @@ -71,6 +71,19 @@ public static Version ReadVersion() } } + private void SimulateDomainReload() + { + // This quite invasively goes into InputSystem internals. Unfortunately, we + // have no proper way of simulating domain reloads ATM. So we directly call various + // internal methods here in a sequence similar to what we'd get during a domain reload. + // Since we're faking it, pass 'true' for calledFromCtor param. + + InputSystem.s_SystemObject.OnBeforeSerialize(); + InputSystem.s_SystemObject = null; + InputSystem.s_Manager = null; // Do NOT Dispose()! The native memory cannot be freed as it's reference by saved state + InputSystem.InitializeInEditor(true, runtime); + } + [Test] [Category("Editor")] public void Editor_PackageVersionAndAssemblyVersionAreTheSame() @@ -147,11 +160,11 @@ public void Editor_CanSaveAndRestoreState() }.ToJson()); InputSystem.Update(); - InputSystem.SaveAndReset(); + m_StateManager.SaveAndReset(false, null); Assert.That(InputSystem.devices, Has.Count.EqualTo(0)); - InputSystem.Restore(); + m_StateManager.Restore(); Assert.That(InputSystem.devices, Has.Exactly(1).With.Property("layout").EqualTo("MyDevice").And.TypeOf()); @@ -195,11 +208,11 @@ public void Editor_DomainReload_CanRestoreDevicesBuiltWithDynamicallyGeneratedLa Assert.That(InputSystem.devices, Has.Exactly(1).TypeOf()); - InputSystem.SaveAndReset(); + m_StateManager.SaveAndReset(false, null); Assert.That(InputSystem.devices, Is.Empty); - var state = InputSystem.GetSavedState(); + var state = m_StateManager.GetSavedState(); var manager = InputSystem.s_Manager; manager.m_SavedAvailableDevices = state.managerState.availableDevices; @@ -209,7 +222,7 @@ public void Editor_DomainReload_CanRestoreDevicesBuiltWithDynamicallyGeneratedLa Assert.That(InputSystem.devices, Has.Exactly(1).TypeOf()); - InputSystem.Restore(); + m_StateManager.Restore(); } [Test] @@ -350,7 +363,7 @@ public void Editor_DomainReload_CanRemoveDevicesDuringDomainReload() [Category("Editor")] public void Editor_RestoringStateWillCleanUpEventHooks() { - InputSystem.SaveAndReset(); + m_StateManager.SaveAndReset(false, null); var receivedOnEvent = 0; var receivedOnDeviceChange = 0; @@ -358,7 +371,7 @@ public void Editor_RestoringStateWillCleanUpEventHooks() InputSystem.onEvent += (e, d) => ++ receivedOnEvent; InputSystem.onDeviceChange += (c, d) => ++ receivedOnDeviceChange; - InputSystem.Restore(); + m_StateManager.Restore(); var device = InputSystem.AddDevice("Gamepad"); InputSystem.QueueStateEvent(device, new GamepadState()); @@ -375,8 +388,8 @@ public void Editor_RestoringStateWillRestoreObjectsOfLayoutBuilder() var builder = new TestLayoutBuilder {layoutToLoad = "Gamepad"}; InputSystem.RegisterLayoutBuilder(() => builder.DoIt(), "TestLayout"); - InputSystem.SaveAndReset(); - InputSystem.Restore(); + m_StateManager.SaveAndReset(false, null); + m_StateManager.Restore(); var device = InputSystem.AddDevice("TestLayout"); diff --git a/Assets/Tests/InputSystem/CoreTests_Remoting.cs b/Assets/Tests/InputSystem/CoreTests_Remoting.cs index e8eb988e0a..17590b339a 100644 --- a/Assets/Tests/InputSystem/CoreTests_Remoting.cs +++ b/Assets/Tests/InputSystem/CoreTests_Remoting.cs @@ -477,11 +477,7 @@ private class FakeRemote : IDisposable public FakeRemote() { runtime = new InputTestRuntime(); - var manager = new InputManager(); - manager.m_Settings = ScriptableObject.CreateInstance(); - manager.InitializeData(); - manager.InstallRuntime(runtime); - manager.ApplySettings(); + var manager = InputManager.CreateAndInitialize(runtime, null, true); local = new InputRemoting(InputSystem.s_Manager); remote = new InputRemoting(manager); @@ -524,8 +520,8 @@ public void Dispose() } if (remoteManager != null) { - Object.Destroy(remoteManager.m_Settings); - remoteManager.Destroy(); + Object.Destroy(remoteManager.settings); + remoteManager.Dispose(); } } } diff --git a/Assets/Tests/InputSystem/Plugins/HIDTests.cs b/Assets/Tests/InputSystem/Plugins/HIDTests.cs index a36018f22b..4b9559160c 100644 --- a/Assets/Tests/InputSystem/Plugins/HIDTests.cs +++ b/Assets/Tests/InputSystem/Plugins/HIDTests.cs @@ -708,8 +708,8 @@ public void Devices_HIDDescriptorSurvivesReload() }.ToJson()); InputSystem.Update(); - InputSystem.SaveAndReset(); - InputSystem.Restore(); + m_StateManager.SaveAndReset(false, null); + m_StateManager.Restore(); var hid = (HID)InputSystem.devices.First(x => x is HID); diff --git a/Packages/com.unity.inputsystem/InputSystem/InputAnalytics.cs b/Packages/com.unity.inputsystem/InputSystem/InputAnalytics.cs index d154ec0c1b..f77005d49b 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputAnalytics.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputAnalytics.cs @@ -17,7 +17,7 @@ internal static class InputAnalytics public static void Initialize(InputManager manager) { - Debug.Assert(manager.m_Runtime != null); + Debug.Assert(manager.runtime != null); } public static void OnStartup(InputManager manager) @@ -60,8 +60,8 @@ public static void OnStartup(InputManager manager) data.old_enabled = EditorPlayerSettingHelpers.oldSystemBackendsEnabled; #endif - manager.m_Runtime.RegisterAnalyticsEvent(kEventStartup, 10, 100); - manager.m_Runtime.SendAnalyticsEvent(kEventStartup, data); + manager.runtime.RegisterAnalyticsEvent(kEventStartup, 10, 100); + manager.runtime.SendAnalyticsEvent(kEventStartup, data); } public static void OnShutdown(InputManager manager) @@ -77,8 +77,8 @@ public static void OnShutdown(InputManager manager) total_event_processing_time = (float)metrics.totalEventProcessingTime, }; - manager.m_Runtime.RegisterAnalyticsEvent(kEventShutdown, 10, 100); - manager.m_Runtime.SendAnalyticsEvent(kEventShutdown, data); + manager.runtime.RegisterAnalyticsEvent(kEventShutdown, 10, 100); + manager.runtime.SendAnalyticsEvent(kEventShutdown, data); } /// diff --git a/Packages/com.unity.inputsystem/InputSystem/InputManager.cs b/Packages/com.unity.inputsystem/InputSystem/InputManager.cs index 5bee4ac31a..cff96c98d6 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputManager.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputManager.cs @@ -16,6 +16,8 @@ #if UNITY_EDITOR using UnityEngine.InputSystem.Editor; +using UnityEngine.Tilemaps; + #endif #if UNITY_EDITOR @@ -55,13 +57,85 @@ namespace UnityEngine.InputSystem /// /// Manages devices, layouts, and event processing. /// - internal partial class InputManager + internal partial class InputManager : IDisposable { + private InputManager() { } + + public static InputManager CreateAndInitialize(IInputRuntime runtime, InputSettings settings, bool fakeRemove = false) + { + var newInst = new InputManager(); + + // If settings object wasn't provided, create a temporary settings object for now + if (settings == null) + { + settings = ScriptableObject.CreateInstance(); + settings.hideFlags = HideFlags.HideAndDontSave; + } + newInst.m_Settings = settings; + +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + newInst.InitializeActions(); +#endif // UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + + newInst.InitializeData(); + newInst.InstallRuntime(runtime); + + // Skip if initializing for "Fake Remove" manager (in tests) + if (!fakeRemove) + newInst.InstallGlobals(); + + newInst.ApplySettings(); + +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + newInst.ApplyActions(); +#endif + return newInst; + } + +#region Dispose implementation + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // Notify devices are being removed but don't actually removed them; no point when disposing + for (var i = 0; i < m_DevicesCount; ++i) + m_Devices[i].NotifyRemoved(); + + m_StateBuffers.FreeAll(); + UninstallGlobals(); + + // If we're still holding the "temporary" settings object make sure to delete it + if (m_Settings != null && m_Settings.hideFlags == HideFlags.HideAndDontSave) + Object.DestroyImmediate(m_Settings); + + // Project-wide Actions are never temporary so we do not destroy them. + } + + disposedValue = true; + } + } + + ~InputManager() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + private bool disposedValue; +#endregion + public ReadOnlyArray devices => new ReadOnlyArray(m_Devices, 0, m_DevicesCount); public TypeTable processors => m_Processors; public TypeTable interactions => m_Interactions; public TypeTable composites => m_Composites; + internal IInputRuntime runtime => m_Runtime; public InputMetrics metrics { @@ -91,7 +165,6 @@ public InputSettings settings { get { - Debug.Assert(m_Settings != null); return m_Settings; } set @@ -99,6 +172,10 @@ public InputSettings settings if (value == null) throw new ArgumentNullException(nameof(value)); + // Delete the "temporary" settings if necessary + if (m_Settings != null && m_Settings.hideFlags == HideFlags.HideAndDontSave) + ScriptableObject.DestroyImmediate(m_Settings); + if (m_Settings == value) return; @@ -1769,45 +1846,6 @@ public void Update(InputUpdateType updateType) m_Runtime.Update(updateType); } - internal void Initialize(IInputRuntime runtime, InputSettings settings) - { - Debug.Assert(settings != null); - - m_Settings = settings; - -#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS - InitializeActions(); -#endif // UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS - InitializeData(); - InstallRuntime(runtime); - InstallGlobals(); - - ApplySettings(); - #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS - ApplyActions(); - #endif - } - - internal void Destroy() - { - // There isn't really much of a point in removing devices but we still - // want to clear out any global state they may be keeping. So just tell - // the devices that they got removed without actually removing them. - for (var i = 0; i < m_DevicesCount; ++i) - m_Devices[i].NotifyRemoved(); - - // Free all state memory. - m_StateBuffers.FreeAll(); - - // Uninstall globals. - UninstallGlobals(); - - // Destroy settings if they are temporary. - if (m_Settings != null && m_Settings.hideFlags == HideFlags.HideAndDontSave) - Object.DestroyImmediate(m_Settings); - - // Project-wide Actions are never temporary so we do not destroy them. - } #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS // Initialize project-wide actions: @@ -2113,12 +2151,12 @@ internal struct AvailableDevice private bool m_HaveSentStartupAnalytics; #endif - internal IInputRuntime m_Runtime; - internal InputMetrics m_Metrics; - internal InputSettings m_Settings; - #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + private IInputRuntime m_Runtime; + private InputMetrics m_Metrics; + private InputSettings m_Settings; +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS private InputActionAsset m_Actions; - #endif +#endif #if UNITY_EDITOR internal IInputDiagnostics m_Diagnostics; @@ -2564,6 +2602,8 @@ private void OnBeforeUpdate(InputUpdateType updateType) /// internal void ApplySettings() { + Debug.Assert(m_Settings != null); + // Sync update mask. var newUpdateMask = InputUpdateType.Editor; if ((m_UpdateMask & InputUpdateType.BeforeRender) != 0) @@ -3917,8 +3957,16 @@ internal void RestoreStateWithoutDevices(SerializedState state) m_Metrics = state.metrics; m_PollingFrequency = state.pollingFrequency; - if (m_Settings != null) + // Cached settings might be null if the ScriptableObject was destroyed; create new default instance in this case. + if (state.settings == null) + { + state.settings = ScriptableObject.CreateInstance(); + state.settings.hideFlags = HideFlags.HideAndDontSave; + } + + if (m_Settings != null && m_Settings != state.settings) Object.DestroyImmediate(m_Settings); + m_Settings = state.settings; #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS @@ -4076,7 +4124,6 @@ private bool RestoreDeviceFromSavedState(ref DeviceState deviceState, InternedSt return true; } - #endif // UNITY_EDITOR || DEVELOPMENT_BUILD } } diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs b/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs index be416ecb64..0fca27280f 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs @@ -2905,7 +2905,7 @@ public static InputSettings settings if (value == null) throw new ArgumentNullException(nameof(value)); - if (s_Manager.m_Settings == value) + if (s_Manager.settings == value) return; // In the editor, we keep track of the settings asset through EditorBuildSettings. @@ -3429,8 +3429,8 @@ public static int ListEnabledActions(List actions) /// The boolean value to set to public static bool runInBackground { - get => s_Manager.m_Runtime.runInBackground; - set => s_Manager.m_Runtime.runInBackground = value; + get => s_Manager.runtime.runInBackground; + set => s_Manager.runtime.runInBackground = value; } ////REVIEW: restrict metrics to editor and development builds? @@ -3446,7 +3446,7 @@ public static bool runInBackground #if DEVELOPMENT_BUILD || UNITY_EDITOR internal static RemoteInputPlayerConnection s_RemoteConnection; - private static void SetUpRemoting() + internal static void SetUpRemoting() { Debug.Assert(s_Manager != null); @@ -3527,7 +3527,7 @@ private static void GlobalInitialize(bool calledFromCtor) if (calledFromCtor) { - InitializeInPlayer(); + InitializeInPlayer(null, true); } else { @@ -3550,11 +3550,26 @@ internal static void InitializeInEditor(bool calledFromCtor, IInputRuntime runti { Profiler.BeginSample("InputSystem.InitializeInEditor"); - // This is only necessary after a Domain Reload but otherwise can be skipped. bool globalReset = calledFromCtor || !IsDomainReloadDisabledForPlayMode(); + // We must initialize a new InputManager object first thing since other parts + // of the init flow depend on it. if (globalReset) - Reset(runtime: runtime); + { + if (s_Manager != null) + s_Manager.Dispose(); + + // Settings object should get set by an actual InputSettings asset. + s_Manager = InputManager.CreateAndInitialize(runtime ?? NativeInputRuntime.instance, null); + s_Manager.runtime.onPlayModeChanged = OnPlayModeChange; + s_Manager.runtime.onProjectChange = OnProjectChange; + + InputEditorUserSettings.s_Settings = new InputEditorUserSettings.SerializedState(); + +#if !UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION + InputSystem.PerformDefaultPluginInitialization(); +#endif + } var existingSystemObjects = Resources.FindObjectsOfTypeAll(); if (existingSystemObjects != null && existingSystemObjects.Length > 0) @@ -3594,13 +3609,9 @@ internal static void InitializeInEditor(bool calledFromCtor, IInputRuntime runti s_SystemObject.hideFlags = HideFlags.HideAndDontSave; // See if we have a remembered settings object. - if (EditorBuildSettings.TryGetConfigObject(InputSettingsProvider.kEditorBuildSettingsConfigKey, - out InputSettings settingsAsset)) + if (EditorBuildSettings.TryGetConfigObject(InputSettingsProvider.kEditorBuildSettingsConfigKey, out InputSettings settingsAsset)) { - if (s_Manager.m_Settings.hideFlags == HideFlags.HideAndDontSave) - ScriptableObject.DestroyImmediate(s_Manager.m_Settings); - s_Manager.m_Settings = settingsAsset; - s_Manager.ApplySettings(); + s_Manager.settings = settingsAsset; } #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS @@ -3626,7 +3637,7 @@ internal static void InitializeInEditor(bool calledFromCtor, IInputRuntime runti // running in batch mode. if (!s_SystemObject.newInputBackendsCheckedAsEnabled && !EditorPlayerSettingHelpers.newSystemBackendsEnabled && - !s_Manager.m_Runtime.isInBatchMode) + !s_Manager.runtime.isInBatchMode) { const string dialogText = "This project is using the new input system package but the native platform backends for the new input system are not enabled in the player settings. " + "This means that no input from native devices will come through." + @@ -3719,7 +3730,7 @@ internal static void OnPlayModeChange(PlayModeStateChange change) } } - private static void OnProjectChange() + internal static void OnProjectChange() { ////TODO: use dirty count to find whether settings have actually changed // May have added, removed, moved, or renamed settings asset. Force a refresh @@ -3730,7 +3741,7 @@ private static void OnProjectChange() // temporary settings object. // NOTE: We access m_Settings directly here to make sure we're not running into asserts // from the settings getter checking it has a valid object. - if (EditorUtility.InstanceIDToObject(s_Manager.m_Settings.GetInstanceID()) == null) + if (EditorUtility.InstanceIDToObject(s_Manager.settings.GetInstanceID()) == null) { var newSettings = ScriptableObject.CreateInstance(); newSettings.hideFlags = HideFlags.HideAndDontSave; @@ -3762,15 +3773,16 @@ internal static void TrackDirtyInputActionAsset(InputActionAsset asset) } #else - private static void InitializeInPlayer(IInputRuntime runtime = null, InputSettings settings = null) + internal static void InitializeInPlayer(IInputRuntime runtime, bool loadSettingsAsset) { - if (settings == null) - settings = Resources.FindObjectsOfTypeAll().FirstOrDefault() ?? ScriptableObject.CreateInstance(); + InputSettings settings = null; + + if (loadSettingsAsset) + settings = Resources.FindObjectsOfTypeAll().FirstOrDefault(); // No domain reloads in the player so we don't need to look for existing // instances. - s_Manager = new InputManager(); - s_Manager.Initialize(runtime ?? NativeInputRuntime.instance, settings); + s_Manager = InputManager.CreateAndInitialize(runtime ?? NativeInputRuntime.instance, settings); #if !UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION PerformDefaultPluginInitialization(); @@ -3790,6 +3802,37 @@ private static void InitializeInPlayer(IInputRuntime runtime = null, InputSettin #endif // UNITY_EDITOR +#if UNITY_INCLUDE_TESTS + // + // A (hopefully) temporary work-around to being unable to define UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // within the Test-Framework assembly; called from within InputTestStateManager.Reset(). + // + // This code should be removed when Actions init/reset flows are refactored, but we need it for now + // so ProjectWideActionstests will pass + // + internal static void DisableActionsForTests() + { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // Note that in a test setup we might enter reset with project-wide actions already enabled but the + // reset itself has pushed the action system state on the state stack. To avoid action state memory + // problems we disable actions here and also request asset to be marked dirty and reimported. + DisableActions(triggerSetupChanged: true); + if (s_Manager != null) + s_Manager.actions = null; +#endif // UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + } + + internal static void EnableActionsForTests() + { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // Touching the `actions` property will initialise it here (if it wasn't already). + // This is the point where we initialise project-wide actions for the Editor, Editor Tests and Player Tests. + actions?.Enable(); +#endif + + } +#endif // UNITY_INCLUDE_TESTS + private static void RunInitialUpdate() { // Request an initial Update so that user methods such as Start and Awake @@ -3803,7 +3846,7 @@ private static void RunInitialUpdate() } #if !UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION - private static void PerformDefaultPluginInitialization() + internal static void PerformDefaultPluginInitialization() { UISupport.Initialize(); @@ -3862,102 +3905,10 @@ private static void PerformDefaultPluginInitialization() #endif // UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION - // For testing, we want the ability to push/pop system state even in the player. - // However, we don't want it in release players. -#if DEVELOPMENT_BUILD || UNITY_EDITOR - /// - /// Return the input system to its default state. - /// - private static void Reset(bool enableRemoting = false, IInputRuntime runtime = null) - { - Profiler.BeginSample("InputSystem.Reset"); - -#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS - // Note that in a test setup we might enter reset with project-wide actions already enabled but the - // reset itself has pushed the action system state on the state stack. To avoid action state memory - // problems we disable actions here and also request asset to be marked dirty and reimported. - DisableActions(triggerSetupChanged: true); - if (s_Manager != null) - s_Manager.actions = null; -#endif // UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS - - // Some devices keep globals. Get rid of them by pretending the devices - // are removed. - if (s_Manager != null) - { - foreach (var device in s_Manager.devices) - device.NotifyRemoved(); - s_Manager.UninstallGlobals(); - } - // Create temporary settings. In the tests, this is all we need. But outside of tests,d - // this should get replaced with an actual InputSettings asset. - var settings = ScriptableObject.CreateInstance(); - settings.hideFlags = HideFlags.HideAndDontSave; - - #if UNITY_EDITOR - s_Manager = new InputManager(); - s_Manager.Initialize( - runtime: runtime ?? NativeInputRuntime.instance, - settings: settings); - - s_Manager.m_Runtime.onPlayModeChanged = OnPlayModeChange; - s_Manager.m_Runtime.onProjectChange = OnProjectChange; - - InputEditorUserSettings.s_Settings = new InputEditorUserSettings.SerializedState(); - - if (enableRemoting) - SetUpRemoting(); - - #if !UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION - PerformDefaultPluginInitialization(); - #endif - - #else - InitializeInPlayer(runtime, settings); - #endif - - Mouse.s_PlatformMouseDevice = null; - - InputEventListener.s_ObserverState = default; - InputUser.ResetGlobals(); - EnhancedTouchSupport.Reset(); - - // This is the point where we initialise project-wide actions for the Editor, Editor Tests and Player Tests. - // Note this is too early for editor ! actions is not setup yet. -#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS - EnableActions(); -#endif - - Profiler.EndSample(); - } - - /// - /// Destroy the current setup of the input system. - /// - /// - /// NOTE: This also de-allocates data we're keeping in unmanaged memory! - /// - private static void Destroy() - { - // NOTE: Does not destroy InputSystemObject. We want to destroy input system - // state repeatedly during tests but we want to not create InputSystemObject - // over and over. - s_Manager.Destroy(); - if (s_RemoteConnection != null) - Object.DestroyImmediate(s_RemoteConnection); - #if UNITY_EDITOR - EditorInputControlLayoutCache.Clear(); - InputDeviceDebuggerWindow.s_OnToolbarGUIActions.Clear(); - InputEditorUserSettings.s_Settings = new InputEditorUserSettings.SerializedState(); - #endif - - s_Manager = null; - s_RemoteConnection = null; - s_Remote = null; - } +#if DEVELOPMENT_BUILD || UNITY_EDITOR /// /// Snapshot of the state used by the input system. /// @@ -3972,105 +3923,15 @@ internal struct State [SerializeField] public RemoteInputPlayerConnection remoteConnection; [SerializeField] public InputManager.SerializedState managerState; [SerializeField] public InputRemoting.SerializedState remotingState; - #if UNITY_EDITOR +#if UNITY_EDITOR [SerializeField] public InputEditorUserSettings.SerializedState userSettings; [SerializeField] public string systemObject; - #endif +#endif ////TODO: make these saved states capable of surviving domain reloads [NonSerialized] public ISavedState inputActionState; [NonSerialized] public ISavedState touchState; [NonSerialized] public ISavedState inputUserState; } - - private static Stack s_SavedStateStack; - - internal static State GetSavedState() - { - return s_SavedStateStack.Peek(); - } - - /// - /// Push the current state of the input system onto a stack and - /// reset the system to its default state. - /// - /// - /// The save stack is not able to survive domain reloads. It is intended solely - /// for use in tests. - /// - internal static void SaveAndReset(bool enableRemoting = false, IInputRuntime runtime = null) - { - if (s_SavedStateStack == null) - s_SavedStateStack = new Stack(); - - ////FIXME: does not preserve global state in InputActionState - ////TODO: preserve InputUser state - ////TODO: preserve EnhancedTouchSupport state - - s_SavedStateStack.Push(new State - { - manager = s_Manager, - remote = s_Remote, - remoteConnection = s_RemoteConnection, - managerState = s_Manager.SaveState(), - remotingState = s_Remote?.SaveState() ?? new InputRemoting.SerializedState(), - #if UNITY_EDITOR - userSettings = InputEditorUserSettings.s_Settings, - systemObject = JsonUtility.ToJson(s_SystemObject), - #endif - inputActionState = InputActionState.SaveAndResetState(), - touchState = EnhancedTouch.Touch.SaveAndResetState(), - inputUserState = InputUser.SaveAndResetState() - }); - - Reset(enableRemoting, runtime ?? InputRuntime.s_Instance); // Keep current runtime. - } - - ////FIXME: this method doesn't restore things like InputDeviceDebuggerWindow.onToolbarGUI - /// - /// Restore the state of the system from the last state pushed with . - /// - internal static void Restore() - { - Debug.Assert(s_SavedStateStack != null && s_SavedStateStack.Count > 0); - - // Load back previous state. - var state = s_SavedStateStack.Pop(); - - state.inputUserState.StaticDisposeCurrentState(); - state.touchState.StaticDisposeCurrentState(); - state.inputActionState.StaticDisposeCurrentState(); - - // Nuke what we have. - Destroy(); - - state.inputUserState.RestoreSavedState(); - state.touchState.RestoreSavedState(); - state.inputActionState.RestoreSavedState(); - - s_Manager = state.manager; - s_Remote = state.remote; - s_RemoteConnection = state.remoteConnection; - - InputUpdate.Restore(state.managerState.updateState); - - s_Manager.InstallRuntime(s_Manager.m_Runtime); - s_Manager.InstallGlobals(); - s_Manager.ApplySettings(); - - #if UNITY_EDITOR - InputEditorUserSettings.s_Settings = state.userSettings; - JsonUtility.FromJsonOverwrite(state.systemObject, s_SystemObject); - #endif - - // Get devices that keep global lists (like Gamepad) to re-initialize them - // by pretending the devices have been added. - foreach (var device in devices) - { - device.NotifyAdded(); - device.MakeCurrent(); - } - } - -#endif +#endif // DEVELOPMENT_BUILD || UNITY_EDITOR } } diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/iOS/iOSSupport.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/iOS/iOSSupport.cs index 981bac4fad..0652e877ca 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/iOS/iOSSupport.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/iOS/iOSSupport.cs @@ -53,7 +53,7 @@ public static void Initialize() InputSystem.RegisterLayout(); // Don't add devices for InputTestRuntime // TODO: Maybe there should be a better place for adding device from C# - if (InputSystem.s_Manager.m_Runtime is NativeInputRuntime) + if (InputSystem.s_Manager.runtime is NativeInputRuntime) { if (iOSStepCounter.IsAvailable()) InputSystem.AddDevice(); diff --git a/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestFixture.cs b/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestFixture.cs index 22e75e6f8b..1f4253b241 100644 --- a/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestFixture.cs +++ b/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestFixture.cs @@ -11,6 +11,8 @@ using UnityEngine.InputSystem.Utilities; using UnityEngine.TestTools; using UnityEngine.TestTools.Utils; +using UnityEngine.InputSystem.Users; + #if UNITY_EDITOR using UnityEditor; using UnityEngine.InputSystem.Editor; @@ -77,6 +79,8 @@ public virtual void Setup() { try { + m_StateManager = new InputTestStateManager(); + // Apparently, NUnit is reusing instances :( m_KeyInfos = default; m_IsUnityTest = default; @@ -92,7 +96,7 @@ public virtual void Setup() // Push current input system state on stack. #if DEVELOPMENT_BUILD || UNITY_EDITOR - InputSystem.SaveAndReset(enableRemoting: false, runtime: runtime); + m_StateManager.SaveAndReset(false, runtime); #endif // Override the editor messing with logic like canRunInBackground and focus and // make it behave like in the player. @@ -175,7 +179,7 @@ public virtual void TearDown() try { #if DEVELOPMENT_BUILD || UNITY_EDITOR - InputSystem.Restore(); + m_StateManager.Restore(); #endif runtime.Dispose(); @@ -297,6 +301,7 @@ public static void AssertStickValues(StickControl stick, Vector2 stickValue, flo Assert.That(stick.right.ReadUnprocessedValue(), Is.EqualTo(right).Within(0.0001), "Incorrect 'right' value"); } + internal InputTestStateManager m_StateManager; private Dictionary> m_KeyInfos; private bool m_Initialized; @@ -898,20 +903,5 @@ public ActionConstraint AndThen(ActionConstraint constraint) return this; } } - - #if UNITY_EDITOR - internal void SimulateDomainReload() - { - // This quite invasively goes into InputSystem internals. Unfortunately, we - // have no proper way of simulating domain reloads ATM. So we directly call various - // internal methods here in a sequence similar to what we'd get during a domain reload. - // Since we're faking it, pass 'true' for calledFromCtor param. - - InputSystem.s_SystemObject.OnBeforeSerialize(); - InputSystem.s_SystemObject = null; - InputSystem.InitializeInEditor(true, runtime); - } - - #endif } } diff --git a/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestStateManager.cs b/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestStateManager.cs new file mode 100644 index 0000000000..cfb788d90c --- /dev/null +++ b/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestStateManager.cs @@ -0,0 +1,196 @@ +using System; +using System.Collections.Generic; +using Unity.Collections; + +using UnityEngine.InputSystem; +using UnityEngine.InputSystem.LowLevel; +using UnityEngine.InputSystem.Users; +using UnityEngine.Profiling; +using UnityEngine.InputSystem.EnhancedTouch; + +#if UNITY_EDITOR +using UnityEditor; +using UnityEngine.InputSystem.Editor; +#endif + +namespace UnityEngine.InputSystem +{ + /// + /// Provides functions for saving and restore the InputSystem state across tests and domain reloads. + /// + internal class InputTestStateManager + { + public InputSystem.State GetSavedState() + { + return m_SavedStateStack.Peek(); + } + + /// + /// Push the current state of the input system onto a stack and + /// reset the system to its default state. + /// + /// + /// The save stack is not able to survive domain reloads. It is intended solely + /// for use in tests. + /// + public void SaveAndReset(bool enableRemoting, IInputRuntime runtime) + { + ////FIXME: does not preserve global state in InputActionState + ////TODO: preserve InputUser state + ////TODO: preserve EnhancedTouchSupport state + + m_SavedStateStack.Push(new InputSystem.State + { + manager = InputSystem.s_Manager, + remote = InputSystem.s_Remote, + remoteConnection = InputSystem.s_RemoteConnection, + managerState = InputSystem.s_Manager.SaveState(), + remotingState = InputSystem.s_Remote?.SaveState() ?? new InputRemoting.SerializedState(), +#if UNITY_EDITOR + userSettings = InputEditorUserSettings.s_Settings, + systemObject = JsonUtility.ToJson(InputSystem.s_SystemObject), +#endif + inputActionState = InputActionState.SaveAndResetState(), + touchState = EnhancedTouch.Touch.SaveAndResetState(), + inputUserState = InputUser.SaveAndResetState() + }); + + Reset(enableRemoting, runtime ?? InputRuntime.s_Instance); // Keep current runtime. + } + + /// + /// Return the input system to its default state. + /// + public void Reset(bool enableRemoting, IInputRuntime runtime) + { + Profiler.BeginSample("InputSystem.Reset"); + + InputSystem.DisableActionsForTests(); + + // Some devices keep globals. Get rid of them by pretending the devices + // are removed. + if (InputSystem.s_Manager != null) + { + foreach (var device in InputSystem.s_Manager.devices) + device.NotifyRemoved(); + + InputSystem.s_Manager.UninstallGlobals(); + } + +#if UNITY_EDITOR + + InputSystem.s_Manager = InputManager.CreateAndInitialize(runtime, null); + + InputSystem.s_Manager.runtime.onPlayModeChanged = InputSystem.OnPlayModeChange; + InputSystem.s_Manager.runtime.onProjectChange = InputSystem.OnProjectChange; + + InputEditorUserSettings.s_Settings = new InputEditorUserSettings.SerializedState(); + + if (enableRemoting) + InputSystem.SetUpRemoting(); + +#if !UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION + InputSystem.PerformDefaultPluginInitialization(); +#endif + +#else + // For tests need to use default InputSettings + InputSystem.InitializeInPlayer(runtime, false); +#endif // UNITY_EDITOR + + Mouse.s_PlatformMouseDevice = null; + + InputEventListener.s_ObserverState = default; + InputUser.ResetGlobals(); + EnhancedTouchSupport.Reset(); + + InputSystem.EnableActionsForTests(); + + Profiler.EndSample(); + } + + /// + /// Destroy the current setup of the input system. + /// + /// + /// NOTE: This also de-allocates data we're keeping in unmanaged memory! + /// + private static void Destroy() + { + // NOTE: Does not destroy InputSystemObject. We want to destroy input system + // state repeatedly during tests but we want to not create InputSystemObject + // over and over. + InputSystem.s_Manager.Dispose(); + if (InputSystem.s_RemoteConnection != null) + Object.DestroyImmediate(InputSystem.s_RemoteConnection); +#if UNITY_EDITOR + EditorInputControlLayoutCache.Clear(); + InputDeviceDebuggerWindow.s_OnToolbarGUIActions.Clear(); + InputEditorUserSettings.s_Settings = new InputEditorUserSettings.SerializedState(); +#endif + + InputSystem.s_Manager = null; + InputSystem.s_RemoteConnection = null; + InputSystem.s_Remote = null; + } + + ////FIXME: this method doesn't restore things like InputDeviceDebuggerWindow.onToolbarGUI + /// + /// Restore the state of the system from the last state pushed with . + /// + public void Restore() + { + Debug.Assert(m_SavedStateStack.Count > 0); + + // Load back previous state. + var state = m_SavedStateStack.Pop(); + + state.inputUserState.StaticDisposeCurrentState(); + state.touchState.StaticDisposeCurrentState(); + state.inputActionState.StaticDisposeCurrentState(); + + // Nuke what we have. + Destroy(); + + state.inputUserState.RestoreSavedState(); + state.touchState.RestoreSavedState(); + state.inputActionState.RestoreSavedState(); + + InputSystem.s_Manager = state.manager; + InputSystem.s_Remote = state.remote; + InputSystem.s_RemoteConnection = state.remoteConnection; + + InputUpdate.Restore(state.managerState.updateState); + + InputSystem.s_Manager.InstallRuntime(InputSystem.s_Manager.runtime); + InputSystem.s_Manager.InstallGlobals(); + + // IMPORTANT + // If InputManager was using the "temporary" settings object, then it'll have been deleted during Reset() + // and the saved Manager settings state will also be null, since it's a ScriptableObject. + // In this case we manually create and set new temp settings object. + if (InputSystem.s_Manager.settings == null) + { + var tmpSettings = ScriptableObject.CreateInstance(); + tmpSettings.hideFlags = HideFlags.HideAndDontSave; + InputSystem.s_Manager.settings = tmpSettings; + } + else InputSystem.s_Manager.ApplySettings(); + +#if UNITY_EDITOR + InputEditorUserSettings.s_Settings = state.userSettings; + JsonUtility.FromJsonOverwrite(state.systemObject, InputSystem.s_SystemObject); +#endif + + // Get devices that keep global lists (like Gamepad) to re-initialize them + // by pretending the devices have been added. + foreach (var device in InputSystem.devices) + { + device.NotifyAdded(); + device.MakeCurrent(); + } + } + + private Stack m_SavedStateStack = new Stack(); + } +} diff --git a/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestStateManager.cs.meta b/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestStateManager.cs.meta new file mode 100644 index 0000000000..3242066427 --- /dev/null +++ b/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestStateManager.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 90487f30114ceb14093b1cc699c8b303 \ No newline at end of file From b1bf0b8d167178fc31edfee4392d40fe3aa3b13f Mon Sep 17 00:00:00 2001 From: Tim Keosababian <36088732+timkeo@users.noreply.github.com> Date: Wed, 31 Jan 2024 15:07:59 -0800 Subject: [PATCH 03/10] Refactor ProjectWideActions to fix initialization with FEPM (ISX-1840) - Rework the class so it operates as a Singleton (basically how it's being used) - Move actions get/set implementations from InputSystem to ProjectWideActions class * Combine functionality for tighter cohesion despite being an Editor * Update #ifefs to allow Player to access some of these members - Call EnsureInitialized() from Editor init flows to fix the actual bug Tested using ProjectWideActions sample and verified could move cube when domain reloads disabled. Ran Package tests (Edit/Play/Player) to verify no (new) failures. --- .../InputSystem/CoreTests_ProjectWideActions.cs | 3 --- .../ProjectWideActions/ProjectWideActionsAsset.cs | 13 ++++--------- .../InputSystem/InputSystem.cs | 15 +++++++-------- .../Plugins/PlayerInput/PlayerInput.cs | 5 ++--- .../Tests/TestFixture/InputTestStateManager.cs | 4 ++-- 5 files changed, 15 insertions(+), 25 deletions(-) diff --git a/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs b/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs index 93fea59859..7d6d7fa27c 100644 --- a/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs +++ b/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs @@ -38,9 +38,6 @@ private static void CreateAndAssignProjectWideTestAsset() else EditorBuildSettings.RemoveConfigObject(name: kSavedActionsObject); - // Create temporary asset and assign as setting - var asset = ProjectWideActionsAsset.CreateDefaultAssetAtPath(kAssetPath); - ProjectWideActionsBuildProvider.actionsToIncludeInPlayerBuild = asset; #endif } diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs index 784b52506d..b194a4d820 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using System.Collections.Generic; @@ -28,7 +28,6 @@ class ProjectSettingsPostprocessor : AssetPostprocessor private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths, bool didDomainReload) #else private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) -#endif { if (!migratedInputActionAssets) { @@ -46,19 +45,14 @@ private static void OnPostprocessAllAssets(string[] importedAssets, string[] del } } } + } + } - private static void MoveInputManagerAssetActionsToProjectWideInputActionAsset() { var objects = AssetDatabase.LoadAllAssetsAtPath(EditorHelpers.GetPhysicalPath(kAssetPathInputManager)); if (objects == null) return; - var inputActionsAsset = objects.FirstOrDefault(o => o != null && o.name == kAssetNameProjectWideInputActions) as InputActionAsset; - if (inputActionsAsset != default) - { - // Found some actions in the InputManager.asset file - // - string path = ProjectWideActionsAsset.kDefaultAssetPath; if (File.Exists(EditorHelpers.GetPhysicalPath(path))) { @@ -311,6 +305,7 @@ private static InputActionAsset CreateAssetAtPathFromJson(string assetPath, stri InputActionAssetManager.SaveAsset(assetPath, inputActionAsset.ToJson()); return AssetDatabase.LoadAssetAtPath(assetPath); } +#endif // UNITY_EDITOR } } #endif // UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs b/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs index 0fca27280f..57ad5b4543 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs @@ -3099,6 +3099,9 @@ private static void DisableActions(bool triggerSetupChanged = false) public static InputActionAsset actions { get => s_Manager?.actions; + { + return UnityEngine.InputSystem.Editor.ProjectWideActionsAsset.instance; + } set { // Prevent this property from being assigned in play-mode. @@ -3110,7 +3113,6 @@ public static InputActionAsset actions var current = s_Manager.actions; if (ReferenceEquals(current, value)) return; - var valueIsNotNull = value != null; #if UNITY_EDITOR // Do not allow assigning non-persistent assets (pure in-memory objects) @@ -3127,8 +3129,6 @@ public static InputActionAsset actions // Note that we do not enable/disable any actions until play-mode } - } - /// /// Event that is triggered if the instance assigned to property changes. /// @@ -3143,7 +3143,6 @@ public static event Action onActionsChange add => s_Manager.onActionsChange += value; remove => s_Manager.onActionsChange -= value; } - #endif // UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS /// @@ -3571,6 +3570,10 @@ internal static void InitializeInEditor(bool calledFromCtor, IInputRuntime runti #endif } +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + ProjectWideActionsAsset.EnsureInitialized(); +#endif + var existingSystemObjects = Resources.FindObjectsOfTypeAll(); if (existingSystemObjects != null && existingSystemObjects.Length > 0) { @@ -3627,10 +3630,8 @@ internal static void InitializeInEditor(bool calledFromCtor, IInputRuntime runti } Debug.Assert(settings != null); - #if UNITY_EDITOR Debug.Assert(EditorUtility.InstanceIDToObject(settings.GetInstanceID()) != null, "InputSettings has lost its native object"); - #endif // If native backends for new input system aren't enabled, ask user whether we should // enable them (requires restart). We only ask once per session and don't ask when @@ -3906,8 +3907,6 @@ internal static void PerformDefaultPluginInitialization() #endif // UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION - - #if DEVELOPMENT_BUILD || UNITY_EDITOR /// /// Snapshot of the state used by the input system. diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/PlayerInput.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/PlayerInput.cs index 0dbfc82e16..da59650ed3 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/PlayerInput.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/PlayerInput.cs @@ -1611,15 +1611,14 @@ private void AssignPlayerIndex() } } - #if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS void Reset() { // Set default actions to project wide actions. m_Actions = InputSystem.actions; // TODO Need to monitor changes? } - - #endif +#endif private void OnEnable() { diff --git a/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestStateManager.cs b/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestStateManager.cs index cfb788d90c..6c1e7d37ae 100644 --- a/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestStateManager.cs +++ b/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestStateManager.cs @@ -65,7 +65,7 @@ public void Reset(bool enableRemoting, IInputRuntime runtime) { Profiler.BeginSample("InputSystem.Reset"); - InputSystem.DisableActionsForTests(); + UnityEngine.InputSystem.Editor.ProjectWideActionsAsset.TestHook_Disable(); // Some devices keep globals. Get rid of them by pretending the devices // are removed. @@ -104,7 +104,7 @@ public void Reset(bool enableRemoting, IInputRuntime runtime) InputUser.ResetGlobals(); EnhancedTouchSupport.Reset(); - InputSystem.EnableActionsForTests(); + UnityEngine.InputSystem.Editor.ProjectWideActionsAsset.TestHook_Enable(); Profiler.EndSample(); } From c8efa0085cb2208d705c17b956a3f56ba8f5b743 Mon Sep 17 00:00:00 2001 From: Tim Keosababian <36088732+timkeo@users.noreply.github.com> Date: Thu, 1 Feb 2024 08:56:20 -0800 Subject: [PATCH 04/10] Refactor various InputSystem members to improved separation of concerns (ISX-1842) - Make InputSystem static fields private with read-only accessors - Add InputSystemTestHooks.cs for "Test Hook" operations that need direct access to fields - Factor out InputSystemObject (and related State) and rename to InputSystemStateManager - Factor out "dirty asset tracking" functionality to a separate Utility class Despite touching a bunch of files, this is a low-risk set of changes; functions are moved to new files and types renamed, but no real changes to functionality. Validated changes with Edit/Play/Player tests. --- Assets/Tests/InputSystem/CoreTests_Actions.cs | 18 +-- Assets/Tests/InputSystem/CoreTests_Devices.cs | 10 +- Assets/Tests/InputSystem/CoreTests_Editor.cs | 33 ++--- Assets/Tests/InputSystem/CoreTests_Events.cs | 12 +- .../Tests/InputSystem/CoreTests_Remoting.cs | 12 +- Assets/Tests/InputSystem/CoreTests_State.cs | 2 +- .../InputSystem/Actions/InputActionAsset.cs | 2 +- .../InputSystem/Actions/InputActionState.cs | 14 +- .../InputSystem/Devices/InputDevice.cs | 2 +- .../AssetEditor/InputActionPropertiesView.cs | 2 +- .../InputControlPickerDropdown.cs | 4 +- .../Editor/Debugger/InputDebuggerWindow.cs | 12 +- .../Debugger/InputDeviceDebuggerWindow.cs | 4 +- .../Editor/EditorInputControlLayoutCache.cs | 2 +- .../Editor/Internal/InputStateWindow.cs | 2 +- .../Settings/InputEditorUserSettings.cs | 2 +- .../Editor/UITKAssetEditor/Views/Selectors.cs | 2 +- .../InputSystem/Events/InputEventListener.cs | 12 +- .../InputSystem/InputManager.cs | 8 +- .../InputSystem/InputSettings.cs | 2 +- .../InputSystem/InputSystem.cs | 133 +++++------------- .../InputSystem/InputSystemObject.cs | 37 ----- .../InputSystem/InputSystemObject.cs.meta | 3 - .../InputSystem/InputSystemStateManager.cs | 69 +++++++++ .../InputSystemStateManager.cs.meta | 2 + .../InputSystem/InputSystemTestHooks.cs | 89 ++++++++++++ .../InputSystem/InputSystemTestHooks.cs.meta | 2 + .../Plugins/DualShock/DualShockGamepadHID.cs | 4 +- .../InputSystem/Plugins/HID/HIDSupport.cs | 2 +- .../Plugins/Switch/SwitchProControllerHID.cs | 2 +- .../InputSystem/Plugins/iOS/iOSSupport.cs | 2 +- .../InputSystem/State/InputState.cs | 22 +-- .../InputSystem/State/InputStateHistory.cs | 2 +- .../Utilities/DirtyAssetTracker.cs | 44 ++++++ .../Utilities/DirtyAssetTracker.cs.meta | 2 + .../Tests/TestFixture/InputTestFixture.cs | 4 +- .../TestFixture/InputTestStateManager.cs | 88 +++--------- 37 files changed, 357 insertions(+), 307 deletions(-) delete mode 100644 Packages/com.unity.inputsystem/InputSystem/InputSystemObject.cs delete mode 100644 Packages/com.unity.inputsystem/InputSystem/InputSystemObject.cs.meta create mode 100644 Packages/com.unity.inputsystem/InputSystem/InputSystemStateManager.cs create mode 100644 Packages/com.unity.inputsystem/InputSystem/InputSystemStateManager.cs.meta create mode 100644 Packages/com.unity.inputsystem/InputSystem/InputSystemTestHooks.cs create mode 100644 Packages/com.unity.inputsystem/InputSystem/InputSystemTestHooks.cs.meta create mode 100644 Packages/com.unity.inputsystem/InputSystem/Utilities/DirtyAssetTracker.cs create mode 100644 Packages/com.unity.inputsystem/InputSystem/Utilities/DirtyAssetTracker.cs.meta diff --git a/Assets/Tests/InputSystem/CoreTests_Actions.cs b/Assets/Tests/InputSystem/CoreTests_Actions.cs index c4ae3b7c5d..02e2b33c13 100644 --- a/Assets/Tests/InputSystem/CoreTests_Actions.cs +++ b/Assets/Tests/InputSystem/CoreTests_Actions.cs @@ -6080,12 +6080,12 @@ public void Actions_AddingSameProcessorTwice_DoesntImpactUIHideState() InputSystem.RegisterProcessor(); Assert.That(InputSystem.TryGetProcessor("ConstantFloat1Test"), Is.Not.EqualTo(null)); - bool hide = InputSystem.s_Manager.processors.ShouldHideInUI("ConstantFloat1Test"); + bool hide = InputSystem.manager.processors.ShouldHideInUI("ConstantFloat1Test"); Assert.That(hide, Is.EqualTo(false)); InputSystem.RegisterProcessor(); // Check we haven't caused this to alias with itself and cause it to be hidden in the UI - hide = InputSystem.s_Manager.processors.ShouldHideInUI("ConstantFloat1Test"); + hide = InputSystem.manager.processors.ShouldHideInUI("ConstantFloat1Test"); Assert.That(hide, Is.EqualTo(false)); } @@ -6731,7 +6731,7 @@ public void Actions_RegisteringExistingInteractionUnderNewName_CreatesAlias() { InputSystem.RegisterInteraction("TestTest"); - Assert.That(InputSystem.s_Manager.interactions.aliases.Contains(new InternedString("TestTest"))); + Assert.That(InputSystem.manager.interactions.aliases.Contains(new InternedString("TestTest"))); } #endif // UNITY_EDITOR @@ -9023,7 +9023,7 @@ public void Actions_RegisteringExistingCompositeUnderNewName_CreatesAlias() { InputSystem.RegisterBindingComposite("TestTest"); - Assert.That(InputSystem.s_Manager.composites.aliases.Contains(new InternedString("TestTest"))); + Assert.That(InputSystem.manager.composites.aliases.Contains(new InternedString("TestTest"))); } #endif // UNITY_EDITOR @@ -11103,7 +11103,7 @@ public void Actions_DisablingAllActions_RemovesAllTheirStateMonitors() // Not the most elegant test as we reach into internals here but with the // current API, it's not possible to enumerate monitors from outside. - Assert.That(InputSystem.s_Manager.m_StateChangeMonitors, + Assert.That(InputSystem.manager.m_StateChangeMonitors, Has.All.Matches( (InputManager.StateChangeMonitorsForDevice x) => x.memoryRegions.All(r => r.sizeInBits == 0))); } @@ -12123,7 +12123,7 @@ public void Actions_CompositeBindingResetWhenResetDeviceCalledWhileExecutingActi // Disable the Keyboard while action is being performed. // This simulates an "OnFocusLost" event occurring while processing the Action, e.g. when switching primary displays or moving the main window actionPerformed = true; - InputSystem.s_Manager.EnableOrDisableDevice(keyboard.device, false, InputManager.DeviceDisableScope.TemporaryWhilePlayerIsInBackground); + InputSystem.manager.EnableOrDisableDevice(keyboard.device, false, InputManager.DeviceDisableScope.TemporaryWhilePlayerIsInBackground); }; map.Enable(); @@ -12136,7 +12136,7 @@ public void Actions_CompositeBindingResetWhenResetDeviceCalledWhileExecutingActi Assert.IsTrue(actionPerformed); // Re enable the Keyboard (before keys are released) and execute Action again - InputSystem.s_Manager.EnableOrDisableDevice(keyboard.device, true, InputManager.DeviceDisableScope.TemporaryWhilePlayerIsInBackground); + InputSystem.manager.EnableOrDisableDevice(keyboard.device, true, InputManager.DeviceDisableScope.TemporaryWhilePlayerIsInBackground); actionPerformed = false; Release(keyboard.leftShiftKey); @@ -12155,7 +12155,7 @@ public void Actions_CompositeBindingResetWhenResetDeviceCalledWhileExecutingActi Release(keyboard.f1Key); // Re enable the Keyboard (after keys are released) and execute Action one more time - InputSystem.s_Manager.EnableOrDisableDevice(keyboard.device, true, InputManager.DeviceDisableScope.TemporaryWhilePlayerIsInBackground); + InputSystem.manager.EnableOrDisableDevice(keyboard.device, true, InputManager.DeviceDisableScope.TemporaryWhilePlayerIsInBackground); Press(keyboard.leftCtrlKey); Press(keyboard.leftShiftKey); @@ -12169,7 +12169,7 @@ public void Actions_CompositeBindingResetWhenResetDeviceCalledWhileExecutingActi Press(keyboard.f1Key); // Re enable the Keyboard (before keys are released) and verify Action isn't triggered when Key pressed first - InputSystem.s_Manager.EnableOrDisableDevice(keyboard.device, true, InputManager.DeviceDisableScope.TemporaryWhilePlayerIsInBackground); + InputSystem.manager.EnableOrDisableDevice(keyboard.device, true, InputManager.DeviceDisableScope.TemporaryWhilePlayerIsInBackground); Press(keyboard.f1Key); Press(keyboard.leftCtrlKey); diff --git a/Assets/Tests/InputSystem/CoreTests_Devices.cs b/Assets/Tests/InputSystem/CoreTests_Devices.cs index 0408664175..579e1f0389 100644 --- a/Assets/Tests/InputSystem/CoreTests_Devices.cs +++ b/Assets/Tests/InputSystem/CoreTests_Devices.cs @@ -572,11 +572,11 @@ public void Devices_AddingDeviceThatUsesBeforeRenderUpdates_CausesBeforeRenderUp InputSystem.RegisterLayout(deviceJson); - Assert.That(InputSystem.s_Manager.updateMask & InputUpdateType.BeforeRender, Is.EqualTo((InputUpdateType)0)); + Assert.That(InputSystem.manager.updateMask & InputUpdateType.BeforeRender, Is.EqualTo((InputUpdateType)0)); InputSystem.AddDevice("CustomGamepad"); - Assert.That(InputSystem.s_Manager.updateMask & InputUpdateType.BeforeRender, Is.EqualTo(InputUpdateType.BeforeRender)); + Assert.That(InputSystem.manager.updateMask & InputUpdateType.BeforeRender, Is.EqualTo(InputUpdateType.BeforeRender)); } [Test] @@ -596,15 +596,15 @@ public void Devices_RemovingLastDeviceThatUsesBeforeRenderUpdates_CausesBeforeRe var device1 = InputSystem.AddDevice("CustomGamepad"); var device2 = InputSystem.AddDevice("CustomGamepad"); - Assert.That(InputSystem.s_Manager.updateMask & InputUpdateType.BeforeRender, Is.EqualTo(InputUpdateType.BeforeRender)); + Assert.That(InputSystem.manager.updateMask & InputUpdateType.BeforeRender, Is.EqualTo(InputUpdateType.BeforeRender)); InputSystem.RemoveDevice(device1); - Assert.That(InputSystem.s_Manager.updateMask & InputUpdateType.BeforeRender, Is.EqualTo(InputUpdateType.BeforeRender)); + Assert.That(InputSystem.manager.updateMask & InputUpdateType.BeforeRender, Is.EqualTo(InputUpdateType.BeforeRender)); InputSystem.RemoveDevice(device2); - Assert.That(InputSystem.s_Manager.updateMask & InputUpdateType.BeforeRender, Is.EqualTo((InputUpdateType)0)); + Assert.That(InputSystem.manager.updateMask & InputUpdateType.BeforeRender, Is.EqualTo((InputUpdateType)0)); } private class TestDeviceReceivingAddAndRemoveNotification : Mouse diff --git a/Assets/Tests/InputSystem/CoreTests_Editor.cs b/Assets/Tests/InputSystem/CoreTests_Editor.cs index 8afe88742c..88ba90641c 100644 --- a/Assets/Tests/InputSystem/CoreTests_Editor.cs +++ b/Assets/Tests/InputSystem/CoreTests_Editor.cs @@ -71,19 +71,6 @@ public static Version ReadVersion() } } - private void SimulateDomainReload() - { - // This quite invasively goes into InputSystem internals. Unfortunately, we - // have no proper way of simulating domain reloads ATM. So we directly call various - // internal methods here in a sequence similar to what we'd get during a domain reload. - // Since we're faking it, pass 'true' for calledFromCtor param. - - InputSystem.s_SystemObject.OnBeforeSerialize(); - InputSystem.s_SystemObject = null; - InputSystem.s_Manager = null; // Do NOT Dispose()! The native memory cannot be freed as it's reference by saved state - InputSystem.InitializeInEditor(true, runtime); - } - [Test] [Category("Editor")] public void Editor_PackageVersionAndAssemblyVersionAreTheSame() @@ -213,7 +200,7 @@ public void Editor_DomainReload_CanRestoreDevicesBuiltWithDynamicallyGeneratedLa Assert.That(InputSystem.devices, Is.Empty); var state = m_StateManager.GetSavedState(); - var manager = InputSystem.s_Manager; + var manager = InputSystem.manager; manager.m_SavedAvailableDevices = state.managerState.availableDevices; manager.m_SavedDeviceStates = state.managerState.devices; @@ -232,7 +219,7 @@ public void Editor_DomainReload_PreservesUsagesOnDevices() var device = InputSystem.AddDevice(); InputSystem.SetDeviceUsage(device, CommonUsages.LeftHand); - SimulateDomainReload(); + InputSystem.TestHook_SimulateDomainReload(runtime); var newDevice = InputSystem.devices[0]; @@ -252,7 +239,7 @@ public void Editor_DomainReload_PreservesEnabledState() Assert.That(device.enabled, Is.False); - SimulateDomainReload(); + InputSystem.TestHook_SimulateDomainReload(runtime); var newDevice = InputSystem.devices[0]; @@ -265,7 +252,7 @@ public void Editor_DomainReload_InputSystemInitializationCausesDevicesToBeRecrea { InputSystem.AddDevice(); - SimulateDomainReload(); + InputSystem.TestHook_SimulateDomainReload(runtime); Assert.That(InputSystem.devices, Has.Count.EqualTo(1)); Assert.That(InputSystem.devices[0], Is.TypeOf()); @@ -302,7 +289,7 @@ public void Editor_DomainReload_CustomDevicesAreRestoredAsLayoutsBecomeAvailable InputSystem.RegisterLayout(kLayout); InputSystem.AddDevice("CustomDevice"); - SimulateDomainReload(); + InputSystem.TestHook_SimulateDomainReload(runtime); Assert.That(InputSystem.devices, Is.Empty); @@ -323,7 +310,7 @@ public void Editor_DomainReload_RetainsUnsupportedDevices() }); InputSystem.Update(); - SimulateDomainReload(); + InputSystem.TestHook_SimulateDomainReload(runtime); Assert.That(InputSystem.GetUnsupportedDevices(), Has.Count.EqualTo(1)); Assert.That(InputSystem.GetUnsupportedDevices()[0].interfaceName, Is.EqualTo("SomethingUnknown")); @@ -2517,7 +2504,7 @@ public void TODO_Editor_SettingsModifiedInPlayMode_AreRestoredWhenReEnteringEdit [Category("Editor")] public void Editor_AlwaysKeepsEditorUpdatesEnabled() { - Assert.That(InputSystem.s_Manager.updateMask & InputUpdateType.Editor, Is.EqualTo(InputUpdateType.Editor)); + Assert.That(InputSystem.manager.updateMask & InputUpdateType.Editor, Is.EqualTo(InputUpdateType.Editor)); } [Test] @@ -2939,15 +2926,15 @@ public void Editor_LeavingPlayMode_DestroysAllActionStates() action.Enable(); Assert.That(InputActionState.s_GlobalState.globalList.length, Is.EqualTo(1)); - Assert.That(InputSystem.s_Manager.m_StateChangeMonitors.Length, Is.GreaterThan(0)); - Assert.That(InputSystem.s_Manager.m_StateChangeMonitors[0].count, Is.EqualTo(1)); + Assert.That(InputSystem.manager.m_StateChangeMonitors.Length, Is.GreaterThan(0)); + Assert.That(InputSystem.manager.m_StateChangeMonitors[0].count, Is.EqualTo(1)); // Exit play mode. InputSystem.OnPlayModeChange(PlayModeStateChange.ExitingPlayMode); InputSystem.OnPlayModeChange(PlayModeStateChange.EnteredEditMode); Assert.That(InputActionState.s_GlobalState.globalList.length, Is.Zero); - Assert.That(InputSystem.s_Manager.m_StateChangeMonitors[0].listeners[0].control, Is.Null); // Won't get removed, just cleared. + Assert.That(InputSystem.manager.m_StateChangeMonitors[0].listeners[0].control, Is.Null); // Won't get removed, just cleared. } [Test] diff --git a/Assets/Tests/InputSystem/CoreTests_Events.cs b/Assets/Tests/InputSystem/CoreTests_Events.cs index 6addd65598..df3367f98e 100644 --- a/Assets/Tests/InputSystem/CoreTests_Events.cs +++ b/Assets/Tests/InputSystem/CoreTests_Events.cs @@ -431,7 +431,7 @@ public void Events_CanSwitchToFullyManualUpdates() #if UNITY_EDITOR // Edit mode updates shouldn't have been disabled in editor. - Assert.That(InputSystem.s_Manager.updateMask & InputUpdateType.Editor, Is.Not.Zero); + Assert.That(InputSystem.manager.updateMask & InputUpdateType.Editor, Is.Not.Zero); #endif InputSystem.QueueStateEvent(mouse, new MouseState().WithButton(MouseButton.Left)); @@ -458,8 +458,8 @@ public void Events_CanSwitchToProcessingInFixedUpdates() Assert.That(InputSystem.settings.updateMode, Is.EqualTo(InputSettings.UpdateMode.ProcessEventsInFixedUpdate)); Assert.That(receivedOnChange, Is.True); - Assert.That(InputSystem.s_Manager.updateMask & InputUpdateType.Fixed, Is.EqualTo(InputUpdateType.Fixed)); - Assert.That(InputSystem.s_Manager.updateMask & InputUpdateType.Dynamic, Is.EqualTo(InputUpdateType.None)); + Assert.That(InputSystem.manager.updateMask & InputUpdateType.Fixed, Is.EqualTo(InputUpdateType.Fixed)); + Assert.That(InputSystem.manager.updateMask & InputUpdateType.Dynamic, Is.EqualTo(InputUpdateType.None)); InputSystem.QueueStateEvent(mouse, new MouseState().WithButton(MouseButton.Left)); runtime.currentTimeForFixedUpdate += Time.fixedDeltaTime; @@ -475,19 +475,19 @@ public void Events_CanSwitchToProcessingInFixedUpdates() [Category("Events")] public void Events_ShouldRunUpdate_AppliesUpdateMask() { - InputSystem.s_Manager.updateMask = InputUpdateType.Dynamic; + InputSystem.manager.updateMask = InputUpdateType.Dynamic; Assert.That(runtime.onShouldRunUpdate.Invoke(InputUpdateType.Dynamic)); Assert.That(!runtime.onShouldRunUpdate.Invoke(InputUpdateType.Fixed)); Assert.That(!runtime.onShouldRunUpdate.Invoke(InputUpdateType.Manual)); - InputSystem.s_Manager.updateMask = InputUpdateType.Manual; + InputSystem.manager.updateMask = InputUpdateType.Manual; Assert.That(!runtime.onShouldRunUpdate.Invoke(InputUpdateType.Dynamic)); Assert.That(!runtime.onShouldRunUpdate.Invoke(InputUpdateType.Fixed)); Assert.That(runtime.onShouldRunUpdate.Invoke(InputUpdateType.Manual)); - InputSystem.s_Manager.updateMask = InputUpdateType.Default; + InputSystem.manager.updateMask = InputUpdateType.Default; Assert.That(runtime.onShouldRunUpdate.Invoke(InputUpdateType.Dynamic)); Assert.That(runtime.onShouldRunUpdate.Invoke(InputUpdateType.Fixed)); diff --git a/Assets/Tests/InputSystem/CoreTests_Remoting.cs b/Assets/Tests/InputSystem/CoreTests_Remoting.cs index 17590b339a..e44f3db236 100644 --- a/Assets/Tests/InputSystem/CoreTests_Remoting.cs +++ b/Assets/Tests/InputSystem/CoreTests_Remoting.cs @@ -297,7 +297,7 @@ public void Remote_CanConnectInputSystemsOverEditorPlayerConnection() connectionToPlayer.Bind(fakeEditorConnection, true); // Bind a local remote on the player side. - var local = new InputRemoting(InputSystem.s_Manager); + var local = new InputRemoting(InputSystem.manager); local.Subscribe(connectionToEditor); connectionToEditor.Subscribe(local); @@ -479,11 +479,11 @@ public FakeRemote() runtime = new InputTestRuntime(); var manager = InputManager.CreateAndInitialize(runtime, null, true); - local = new InputRemoting(InputSystem.s_Manager); + local = new InputRemoting(InputSystem.manager); remote = new InputRemoting(manager); var remoteInstaller = new GlobalsInstallerObserver(manager); - var localInstaller = new GlobalsInstallerObserver(InputSystem.s_Manager); + var localInstaller = new GlobalsInstallerObserver(InputSystem.manager); // The installers will ensure the globals environment is prepared right before // the receiver processes the message. There are some static fields, such as @@ -501,14 +501,12 @@ public FakeRemote() public void SwitchToRemoteState() { - InputSystem.s_Manager = remoteManager; - InputStateBuffers.SwitchTo(remoteManager.m_StateBuffers, remoteManager.defaultUpdateType); + InputSystem.TestHook_SwitchToDifferentInputManager(remoteManager); } public void SwitchToLocalState() { - InputSystem.s_Manager = localManager; - InputStateBuffers.SwitchTo(localManager.m_StateBuffers, localManager.defaultUpdateType); + InputSystem.TestHook_SwitchToDifferentInputManager(localManager); } public void Dispose() diff --git a/Assets/Tests/InputSystem/CoreTests_State.cs b/Assets/Tests/InputSystem/CoreTests_State.cs index 7fee57d947..dff74db724 100644 --- a/Assets/Tests/InputSystem/CoreTests_State.cs +++ b/Assets/Tests/InputSystem/CoreTests_State.cs @@ -1196,7 +1196,7 @@ public void State_FixedUpdatesAreDisabledByDefault() { Assert.That(InputSystem.settings.updateMode, Is.EqualTo(InputSettings.UpdateMode.ProcessEventsInDynamicUpdate)); Assert.That(runtime.onShouldRunUpdate(InputUpdateType.Fixed), Is.False); - Assert.That(InputSystem.s_Manager.updateMask & InputUpdateType.Fixed, Is.EqualTo(InputUpdateType.None)); + Assert.That(InputSystem.manager.updateMask & InputUpdateType.Fixed, Is.EqualTo(InputUpdateType.None)); } [Test] diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs index 598ce1cd2b..23b7aafd57 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs @@ -869,7 +869,7 @@ IEnumerator IEnumerable.GetEnumerator() internal void MarkAsDirty() { #if UNITY_EDITOR - InputSystem.TrackDirtyInputActionAsset(this); + DirtyAssetTracker.TrackDirtyInputActionAsset(this); #endif } diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs index c931232931..2ae8abf0d6 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs @@ -1120,7 +1120,7 @@ private void EnableControls(int mapIndex, int controlStartIndex, int numControls "Control start index out of range"); Debug.Assert(controlStartIndex + numControls <= totalControlCount, "Control range out of bounds"); - var manager = InputSystem.s_Manager; + var manager = InputSystem.manager; for (var i = 0; i < numControls; ++i) { var controlIndex = controlStartIndex + i; @@ -1149,7 +1149,7 @@ private void DisableControls(int mapIndex, int controlStartIndex, int numControl "Control start index out of range"); Debug.Assert(controlStartIndex + numControls <= totalControlCount, "Control range out of bounds"); - var manager = InputSystem.s_Manager; + var manager = InputSystem.manager; for (var i = 0; i < numControls; ++i) { var controlIndex = controlStartIndex + i; @@ -1225,7 +1225,7 @@ private void HookOnBeforeUpdate() if (m_OnBeforeUpdateDelegate == null) m_OnBeforeUpdateDelegate = OnBeforeInitialUpdate; - InputSystem.s_Manager.onBeforeUpdate += m_OnBeforeUpdateDelegate; + InputSystem.manager.onBeforeUpdate += m_OnBeforeUpdateDelegate; m_OnBeforeUpdateHooked = true; } @@ -1234,7 +1234,7 @@ private void UnhookOnBeforeUpdate() if (!m_OnBeforeUpdateHooked) return; - InputSystem.s_Manager.onBeforeUpdate -= m_OnBeforeUpdateDelegate; + InputSystem.manager.onBeforeUpdate -= m_OnBeforeUpdateDelegate; m_OnBeforeUpdateHooked = false; } @@ -1267,7 +1267,7 @@ private void OnBeforeInitialUpdate() // Go through all binding states and for every binding that needs an initial state check, // go through all bound controls and for each one that isn't in its default state, pretend // that the control just got actuated. - var manager = InputSystem.s_Manager; + var manager = InputSystem.manager; for (var bindingIndex = 0; bindingIndex < totalBindingCount; ++bindingIndex) { ref var bindingState = ref bindingStates[bindingIndex]; @@ -2102,7 +2102,7 @@ internal void StartTimeout(float seconds, ref TriggerState trigger) Debug.Assert(trigger.controlIndex >= 0 && trigger.controlIndex < totalControlCount, "Control index out of range"); Debug.Assert(trigger.interactionIndex >= 0 && trigger.interactionIndex < totalInteractionCount, "Interaction index out of range"); - var manager = InputSystem.s_Manager; + var manager = InputSystem.manager; var currentTime = trigger.time; var control = controls[trigger.controlIndex]; var interactionIndex = trigger.interactionIndex; @@ -2131,7 +2131,7 @@ private void StopTimeout(int interactionIndex) ref var interactionState = ref interactionStates[interactionIndex]; - var manager = InputSystem.s_Manager; + var manager = InputSystem.manager; manager.RemoveStateChangeMonitorTimeout(this, interactionState.timerMonitorIndex, interactionIndex); // Update state. diff --git a/Packages/com.unity.inputsystem/InputSystem/Devices/InputDevice.cs b/Packages/com.unity.inputsystem/InputSystem/Devices/InputDevice.cs index cafc7392d4..4dca95d093 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Devices/InputDevice.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Devices/InputDevice.cs @@ -557,7 +557,7 @@ public unsafe long ExecuteCommand(ref TCommand command) var commandPtr = (InputDeviceCommand*)UnsafeUtility.AddressOf(ref command); // Give callbacks first shot. - var manager = InputSystem.s_Manager; + var manager = InputSystem.manager; manager.m_DeviceCommandCallbacks.LockForChanges(); for (var i = 0; i < manager.m_DeviceCommandCallbacks.length; ++i) { diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputActionPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputActionPropertiesView.cs index 093283868f..d6bf5e4759 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputActionPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputActionPropertiesView.cs @@ -81,7 +81,7 @@ protected override void DrawGeneralProperties() private void BuildControlTypeList() { var types = new List(); - var allLayouts = InputSystem.s_Manager.m_Layouts; + var allLayouts = InputSystem.manager.m_Layouts; foreach (var layoutName in allLayouts.layoutTypes.Keys) { if (EditorInputControlLayoutCache.TryGetLayout(layoutName).hideInUI) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlPickerDropdown.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlPickerDropdown.cs index 6609f622f5..4468acd77a 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlPickerDropdown.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/ControlPicker/InputControlPickerDropdown.cs @@ -47,7 +47,7 @@ public void SetExpectedControlLayout(string expectedControlLayout) m_ExpectedControlType = typeof(InputDevice); else m_ExpectedControlType = !string.IsNullOrEmpty(expectedControlLayout) - ? InputSystem.s_Manager.m_Layouts.GetControlTypeForLayout(new InternedString(expectedControlLayout)) + ? InputSystem.manager.m_Layouts.GetControlTypeForLayout(new InternedString(expectedControlLayout)) : null; // If the layout is for a device, automatically switch to device @@ -421,7 +421,7 @@ private bool LayoutMatchesExpectedControlLayoutFilter(string layout) if (m_ExpectedControlType == null) return true; - var layoutType = InputSystem.s_Manager.m_Layouts.GetControlTypeForLayout(new InternedString(layout)); + var layoutType = InputSystem.manager.m_Layouts.GetControlTypeForLayout(new InternedString(layout)); return m_ExpectedControlType.IsAssignableFrom(layoutType); } diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/Debugger/InputDebuggerWindow.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/Debugger/InputDebuggerWindow.cs index 6086f7d605..a98a3dd7a6 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/Debugger/InputDebuggerWindow.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/Debugger/InputDebuggerWindow.cs @@ -212,9 +212,9 @@ private static void ResetDevice(InputDevice device, bool hard) { var playerUpdateType = InputDeviceDebuggerWindow.DetermineUpdateTypeToShow(device); var currentUpdateType = InputState.currentUpdateType; - InputStateBuffers.SwitchTo(InputSystem.s_Manager.m_StateBuffers, playerUpdateType); + InputStateBuffers.SwitchTo(InputSystem.manager.m_StateBuffers, playerUpdateType); InputSystem.ResetDevice(device, alsoResetDontResetControls: hard); - InputStateBuffers.SwitchTo(InputSystem.s_Manager.m_StateBuffers, currentUpdateType); + InputStateBuffers.SwitchTo(InputSystem.manager.m_StateBuffers, currentUpdateType); } private static void ToggleAddDevicesNotSupportedByProject() @@ -225,15 +225,15 @@ private static void ToggleAddDevicesNotSupportedByProject() private void ToggleDiagnosticMode() { - if (InputSystem.s_Manager.m_Diagnostics != null) + if (InputSystem.manager.m_Diagnostics != null) { - InputSystem.s_Manager.m_Diagnostics = null; + InputSystem.manager.m_Diagnostics = null; } else { if (m_Diagnostics == null) m_Diagnostics = new InputDiagnostics(); - InputSystem.s_Manager.m_Diagnostics = m_Diagnostics; + InputSystem.manager.m_Diagnostics = m_Diagnostics; } } @@ -311,7 +311,7 @@ private void DrawToolbarGUI() menu.AddItem(Contents.addDevicesNotSupportedByProjectContent, InputEditorUserSettings.addDevicesNotSupportedByProject, ToggleAddDevicesNotSupportedByProject); - menu.AddItem(Contents.diagnosticsModeContent, InputSystem.s_Manager.m_Diagnostics != null, + menu.AddItem(Contents.diagnosticsModeContent, InputSystem.manager.m_Diagnostics != null, ToggleDiagnosticMode); menu.AddItem(Contents.touchSimulationContent, InputEditorUserSettings.simulateTouch, ToggleTouchSimulation); diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/Debugger/InputDeviceDebuggerWindow.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/Debugger/InputDeviceDebuggerWindow.cs index d38943abc8..0689b0fd88 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/Debugger/InputDeviceDebuggerWindow.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/Debugger/InputDeviceDebuggerWindow.cs @@ -356,9 +356,9 @@ private void RefreshControlTreeValues() m_InputUpdateTypeShownInControlTree = DetermineUpdateTypeToShow(m_Device); var currentUpdateType = InputState.currentUpdateType; - InputStateBuffers.SwitchTo(InputSystem.s_Manager.m_StateBuffers, m_InputUpdateTypeShownInControlTree); + InputStateBuffers.SwitchTo(InputSystem.manager.m_StateBuffers, m_InputUpdateTypeShownInControlTree); m_ControlTree.RefreshControlValues(); - InputStateBuffers.SwitchTo(InputSystem.s_Manager.m_StateBuffers, currentUpdateType); + InputStateBuffers.SwitchTo(InputSystem.manager.m_StateBuffers, currentUpdateType); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "device", Justification = "Keep this for future implementation")] diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/EditorInputControlLayoutCache.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/EditorInputControlLayoutCache.cs index 81dac4b311..7800d5ea06 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/EditorInputControlLayoutCache.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/EditorInputControlLayoutCache.cs @@ -246,7 +246,7 @@ internal static void Clear() // If our layout data is outdated, rescan all the layouts in the system. private static void Refresh() { - var manager = InputSystem.s_Manager; + var manager = InputSystem.manager; if (manager.m_LayoutRegistrationVersion == s_LayoutRegistrationVersion) return; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/InputStateWindow.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/InputStateWindow.cs index 8376792d48..5f996a3610 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/InputStateWindow.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/InputStateWindow.cs @@ -152,7 +152,7 @@ private unsafe void PollBuffersFromControl(InputControl control, bool selectBuff private static unsafe void* TryGetDeviceState(InputDevice device, BufferSelector selector) { - var manager = InputSystem.s_Manager; + var manager = InputSystem.manager; var deviceIndex = device.m_DeviceIndex; switch (selector) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/Settings/InputEditorUserSettings.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/Settings/InputEditorUserSettings.cs index d2d3527cdb..108f3a36d1 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/Settings/InputEditorUserSettings.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/Settings/InputEditorUserSettings.cs @@ -87,7 +87,7 @@ internal struct SerializedState private static void OnChange() { Save(); - InputSystem.s_Manager.ApplySettings(); + InputSystem.manager.ApplySettings(); } internal static void Load() diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/Selectors.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/Selectors.cs index 90ecf92f5c..3559418da1 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/Selectors.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/Selectors.cs @@ -277,7 +277,7 @@ public static IEnumerable GetCompositePartOptions(string bindingName, st public static IEnumerable BuildControlTypeList(InputActionType selectedActionType) { - var allLayouts = InputSystem.s_Manager.m_Layouts; + var allLayouts = InputSystem.manager.m_Layouts; // "Any" is always in first position (index 0) yield return "Any"; diff --git a/Packages/com.unity.inputsystem/InputSystem/Events/InputEventListener.cs b/Packages/com.unity.inputsystem/InputSystem/Events/InputEventListener.cs index 6ffb0bbfdd..d9f1354738 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Events/InputEventListener.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Events/InputEventListener.cs @@ -57,8 +57,8 @@ public struct InputEventListener : IObservable { if (callback == null) throw new ArgumentNullException(nameof(callback)); - lock (InputSystem.s_Manager) - InputSystem.s_Manager.onEvent += callback; + lock (InputSystem.manager) + InputSystem.manager.onEvent += callback; return default; } @@ -82,8 +82,8 @@ public struct InputEventListener : IObservable { if (callback == null) throw new ArgumentNullException(nameof(callback)); - lock (InputSystem.s_Manager) - InputSystem.s_Manager.onEvent -= callback; + lock (InputSystem.manager) + InputSystem.manager.onEvent -= callback; return default; } @@ -110,7 +110,7 @@ public IDisposable Subscribe(IObserver observer) s_ObserverState = new ObserverState(); if (s_ObserverState.observers.length == 0) - InputSystem.s_Manager.onEvent += s_ObserverState.onEventDelegate; + InputSystem.manager.onEvent += s_ObserverState.onEventDelegate; s_ObserverState.observers.AppendWithCapacity(observer); return new DisposableObserver { observer = observer }; @@ -142,7 +142,7 @@ public void Dispose() if (index >= 0) s_ObserverState.observers.RemoveAtWithCapacity(index); if (s_ObserverState.observers.length == 0) - InputSystem.s_Manager.onEvent -= s_ObserverState.onEventDelegate; + InputSystem.manager.onEvent -= s_ObserverState.onEventDelegate; } } } diff --git a/Packages/com.unity.inputsystem/InputSystem/InputManager.cs b/Packages/com.unity.inputsystem/InputSystem/InputManager.cs index cff96c98d6..af5b33094f 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputManager.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputManager.cs @@ -3159,10 +3159,10 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev if ((currentEventType == StateEvent.Type || currentEventType == DeltaStateEvent.Type) && (updateType & InputUpdateType.Editor) == 0 && - InputSystem.s_SystemObject.exitEditModeTime > 0 && - currentEventTimeInternal >= InputSystem.s_SystemObject.exitEditModeTime && - (currentEventTimeInternal < InputSystem.s_SystemObject.enterPlayModeTime || - InputSystem.s_SystemObject.enterPlayModeTime == 0)) + InputSystem.domainStateManager.exitEditModeTime > 0 && + currentEventTimeInternal >= InputSystem.domainStateManager.exitEditModeTime && + (currentEventTimeInternal < InputSystem.domainStateManager.enterPlayModeTime || + InputSystem.domainStateManager.enterPlayModeTime == 0)) { m_InputEventStream.Advance(false); continue; diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSettings.cs b/Packages/com.unity.inputsystem/InputSystem/InputSettings.cs index c9cdafa369..245b374164 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputSettings.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputSettings.cs @@ -786,7 +786,7 @@ internal bool IsFeatureEnabled(string featureName) internal void OnChange() { if (InputSystem.settings == this) - InputSystem.s_Manager.ApplySettings(); + InputSystem.manager.ApplySettings(); } internal const int s_OldUnsupportedFixedAndDynamicUpdateSetting = 0; diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs b/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs index 57ad5b4543..1da213844b 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs @@ -3439,11 +3439,13 @@ public static bool runInBackground /// Up-to-date metrics on input system activity. public static InputMetrics metrics => s_Manager.metrics; - internal static InputManager s_Manager; - internal static InputRemoting s_Remote; + internal static InputManager manager => s_Manager; + private static InputManager s_Manager; + private static InputRemoting s_Remote; #if DEVELOPMENT_BUILD || UNITY_EDITOR - internal static RemoteInputPlayerConnection s_RemoteConnection; + private static RemoteInputPlayerConnection s_RemoteConnection; + internal static RemoteInputPlayerConnection remoteConnection => s_RemoteConnection; internal static void SetUpRemoting() { @@ -3543,7 +3545,10 @@ internal static void EnsureInitialized() } #if UNITY_EDITOR - internal static InputSystemObject s_SystemObject; + + // TODO: ISX-1860 + private static InputSystemStateManager s_DomainStateManager; + internal static InputSystemStateManager domainStateManager => s_DomainStateManager; internal static void InitializeInEditor(bool calledFromCtor, IInputRuntime runtime = null) { @@ -3574,7 +3579,7 @@ internal static void InitializeInEditor(bool calledFromCtor, IInputRuntime runti ProjectWideActionsAsset.EnsureInitialized(); #endif - var existingSystemObjects = Resources.FindObjectsOfTypeAll(); + var existingSystemObjects = Resources.FindObjectsOfTypeAll(); if (existingSystemObjects != null && existingSystemObjects.Length > 0) { if (globalReset) @@ -3585,31 +3590,28 @@ internal static void InitializeInEditor(bool calledFromCtor, IInputRuntime runti // InputManager state here but we're still waiting from layout registrations // that happen during domain initialization. - s_SystemObject = existingSystemObjects[0]; - s_Manager.RestoreStateWithoutDevices(s_SystemObject.systemState.managerState); - InputDebuggerWindow.ReviveAfterDomainReload(); - // Restore remoting state. - s_RemoteConnection = s_SystemObject.systemState.remoteConnection; - SetUpRemoting(); - s_Remote.RestoreState(s_SystemObject.systemState.remotingState, s_Manager); + // Restore remoting state. + s_RemoteConnection = s_DomainStateManager.systemState.remoteConnection; + SetUpRemoting(); + s_Remote.RestoreState(s_DomainStateManager.systemState.remotingState, s_Manager); - // Get manager to restore devices on first input update. By that time we - // should have all (possibly updated) layout information in place. - s_Manager.m_SavedDeviceStates = s_SystemObject.systemState.managerState.devices; - s_Manager.m_SavedAvailableDevices = s_SystemObject.systemState.managerState.availableDevices; + // Get s_Manager to restore devices on first input update. By that time we + // should have all (possibly updated) layout information in place. + s_Manager.m_SavedDeviceStates = s_DomainStateManager.systemState.managerState.devices; + s_Manager.m_SavedAvailableDevices = s_DomainStateManager.systemState.managerState.availableDevices; - // Restore editor settings. - InputEditorUserSettings.s_Settings = s_SystemObject.systemState.userSettings; + // Restore editor settings. + InputEditorUserSettings.s_Settings = s_DomainStateManager.systemState.userSettings; - // Get rid of saved state. - s_SystemObject.systemState = new State(); - } + // Get rid of saved state. + s_DomainStateManager.systemState = new InputSystemState(); + } } else { - s_SystemObject = ScriptableObject.CreateInstance(); - s_SystemObject.hideFlags = HideFlags.HideAndDontSave; + s_DomainStateManager = ScriptableObject.CreateInstance(); + s_DomainStateManager.hideFlags = HideFlags.HideAndDontSave; // See if we have a remembered settings object. if (EditorBuildSettings.TryGetConfigObject(InputSettingsProvider.kEditorBuildSettingsConfigKey, out InputSettings settingsAsset)) @@ -3636,7 +3638,7 @@ internal static void InitializeInEditor(bool calledFromCtor, IInputRuntime runti // If native backends for new input system aren't enabled, ask user whether we should // enable them (requires restart). We only ask once per session and don't ask when // running in batch mode. - if (!s_SystemObject.newInputBackendsCheckedAsEnabled && + if (!s_DomainStateManager.newInputBackendsCheckedAsEnabled && !EditorPlayerSettingHelpers.newSystemBackendsEnabled && !s_Manager.runtime.isInBatchMode) { @@ -3650,7 +3652,7 @@ internal static void InitializeInEditor(bool calledFromCtor, IInputRuntime runti EditorHelpers.RestartEditorAndRecompileScripts(); } } - s_SystemObject.newInputBackendsCheckedAsEnabled = true; + s_DomainStateManager.newInputBackendsCheckedAsEnabled = true; #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS // Make sure project wide input actions are enabled. @@ -3670,15 +3672,15 @@ internal static void OnPlayModeChange(PlayModeStateChange change) switch (change) { case PlayModeStateChange.ExitingEditMode: - s_SystemObject.settings = JsonUtility.ToJson(settings); - s_SystemObject.exitEditModeTime = InputRuntime.s_Instance.currentTime; - s_SystemObject.enterPlayModeTime = 0; + s_DomainStateManager.settings = JsonUtility.ToJson(settings); + s_DomainStateManager.exitEditModeTime = InputRuntime.s_Instance.currentTime; + s_DomainStateManager.enterPlayModeTime = 0; // InputSystem.actions is not setup yet break; case PlayModeStateChange.EnteredPlayMode: - s_SystemObject.enterPlayModeTime = InputRuntime.s_Instance.currentTime; + s_DomainStateManager.enterPlayModeTime = InputRuntime.s_Instance.currentTime; s_Manager.SyncAllDevicesAfterEnteringPlayMode(); #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS EnableActions(); @@ -3704,29 +3706,15 @@ internal static void OnPlayModeChange(PlayModeStateChange change) InputActionState.DestroyAllActionMapStates(); // Restore settings. - if (!string.IsNullOrEmpty(s_SystemObject.settings)) + if (!string.IsNullOrEmpty(s_DomainStateManager.settings)) { - JsonUtility.FromJsonOverwrite(s_SystemObject.settings, settings); - s_SystemObject.settings = null; + JsonUtility.FromJsonOverwrite(s_DomainStateManager.settings, settings); + s_DomainStateManager.settings = null; settings.OnChange(); } - // reload input action assets marked as dirty from disk - if (s_TrackedDirtyAssets == null) - return; - - foreach (var assetGuid in s_TrackedDirtyAssets) - { - var assetPath = AssetDatabase.GUIDToAssetPath(assetGuid); - - if (string.IsNullOrEmpty(assetPath)) - continue; - - AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate); - } - - s_TrackedDirtyAssets.Clear(); - + // reload input assets marked as dirty from disk + DirtyAssetTracker.ReloadDirtyAssets(); break; } } @@ -3750,29 +3738,6 @@ internal static void OnProjectChange() } } - private static HashSet s_TrackedDirtyAssets; - - /// - /// Keep track of InputActionAsset assets that you want to re-load on exiting Play mode. This is useful because - /// some user actions, such as adding a new input binding at runtime, change the in-memory representation of the - /// input action asset and those changes survive when exiting Play mode. If you re-open an Input - /// Action Asset in the Editor that has been changed this way, you see the new bindings that have been added - /// during Play mode which you might not typically want to happen. - /// - /// You can avoid this by force re-loading from disk any asset that has been marked as dirty. - /// - /// - internal static void TrackDirtyInputActionAsset(InputActionAsset asset) - { - if (s_TrackedDirtyAssets == null) - s_TrackedDirtyAssets = new HashSet(); - - if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(asset, out string assetGuid, out long _) == false) - return; - - s_TrackedDirtyAssets.Add(assetGuid); - } - #else internal static void InitializeInPlayer(IInputRuntime runtime, bool loadSettingsAsset) { @@ -3906,31 +3871,5 @@ internal static void PerformDefaultPluginInitialization() #endif // UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION - -#if DEVELOPMENT_BUILD || UNITY_EDITOR - /// - /// Snapshot of the state used by the input system. - /// - /// - /// Can be taken across domain reloads. - /// - [Serializable] - internal struct State - { - [NonSerialized] public InputManager manager; - [NonSerialized] public InputRemoting remote; - [SerializeField] public RemoteInputPlayerConnection remoteConnection; - [SerializeField] public InputManager.SerializedState managerState; - [SerializeField] public InputRemoting.SerializedState remotingState; -#if UNITY_EDITOR - [SerializeField] public InputEditorUserSettings.SerializedState userSettings; - [SerializeField] public string systemObject; -#endif - ////TODO: make these saved states capable of surviving domain reloads - [NonSerialized] public ISavedState inputActionState; - [NonSerialized] public ISavedState touchState; - [NonSerialized] public ISavedState inputUserState; - } -#endif // DEVELOPMENT_BUILD || UNITY_EDITOR } } diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSystemObject.cs b/Packages/com.unity.inputsystem/InputSystem/InputSystemObject.cs deleted file mode 100644 index 4bb0ddc027..0000000000 --- a/Packages/com.unity.inputsystem/InputSystem/InputSystemObject.cs +++ /dev/null @@ -1,37 +0,0 @@ -#if UNITY_EDITOR -using UnityEngine.InputSystem.Editor; - -namespace UnityEngine.InputSystem -{ - /// - /// A hidden, internal object we put in the editor to bundle input system state - /// and help us survive domain reloads. - /// - /// - /// Player doesn't need this stuff because there's no domain reloads to survive. - /// - internal class InputSystemObject : ScriptableObject, ISerializationCallbackReceiver - { - [SerializeField] public InputSystem.State systemState; - [SerializeField] public bool newInputBackendsCheckedAsEnabled; - [SerializeField] public string settings; - [SerializeField] public double exitEditModeTime; - [SerializeField] public double enterPlayModeTime; - - public void OnBeforeSerialize() - { - // Save current system state. - systemState.manager = InputSystem.s_Manager; - systemState.remote = InputSystem.s_Remote; - systemState.remoteConnection = InputSystem.s_RemoteConnection; - systemState.managerState = InputSystem.s_Manager.SaveState(); - systemState.remotingState = InputSystem.s_Remote.SaveState(); - systemState.userSettings = InputEditorUserSettings.s_Settings; - } - - public void OnAfterDeserialize() - { - } - } -} -#endif // UNITY_EDITOR diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSystemObject.cs.meta b/Packages/com.unity.inputsystem/InputSystem/InputSystemObject.cs.meta deleted file mode 100644 index 0a9818e38c..0000000000 --- a/Packages/com.unity.inputsystem/InputSystem/InputSystemObject.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 5cdce2bffd1e49bda08b3db54a031207 -timeCreated: 1506825901 \ No newline at end of file diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSystemStateManager.cs b/Packages/com.unity.inputsystem/InputSystem/InputSystemStateManager.cs new file mode 100644 index 0000000000..fedf66999b --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/InputSystemStateManager.cs @@ -0,0 +1,69 @@ +using System; +using UnityEngine.InputSystem; +using UnityEngine; +using UnityEngine.InputSystem.Editor; +using UnityEngine.InputSystem.Utilities; + +namespace UnityEngine.InputSystem +{ +#if UNITY_EDITOR || DEVELOPMENT_BUILD + /// + /// Snapshot of the state used by the input system. + /// + /// + /// Can be taken across domain reloads. + /// + [Serializable] + internal struct InputSystemState + { + [NonSerialized] public InputManager manager; + [NonSerialized] public InputRemoting remote; + [SerializeField] public RemoteInputPlayerConnection remoteConnection; + [SerializeField] public InputManager.SerializedState managerState; + [SerializeField] public InputRemoting.SerializedState remotingState; +#if UNITY_EDITOR + [SerializeField] public InputEditorUserSettings.SerializedState userSettings; + [SerializeField] public string systemObject; +#endif + ////TODO: make these saved states capable of surviving domain reloads + [NonSerialized] public ISavedState inputActionState; + [NonSerialized] public ISavedState touchState; + [NonSerialized] public ISavedState inputUserState; + } + + // TODO: ISX-1860 +#if UNITY_EDITOR + /// + /// A hidden, internal object we put in the editor to bundle input system state + /// and help us survive domain reloads. + /// + /// + /// Player doesn't need this stuff because there's no domain reloads to survive, and + /// also doesn't have domain reloads. + /// + internal class InputSystemStateManager : ScriptableObject, ISerializationCallbackReceiver + { + [SerializeField] public InputSystemState systemState; + [SerializeField] public bool newInputBackendsCheckedAsEnabled; + [SerializeField] public string settings; + [SerializeField] public double exitEditModeTime; + [SerializeField] public double enterPlayModeTime; + + public void OnBeforeSerialize() + { + // Save current system state. + systemState.manager = InputSystem.manager; + systemState.remote = InputSystem.remoting; + systemState.remoteConnection = InputSystem.remoteConnection; + systemState.managerState = InputSystem.manager.SaveState(); + systemState.remotingState = InputSystem.remoting.SaveState(); + systemState.userSettings = InputEditorUserSettings.s_Settings; + } + + public void OnAfterDeserialize() + { + } + } +#endif // UNITY_EDITOR +#endif // UNITY_EDITOR || DEVELOPMENT_BUILD +} diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSystemStateManager.cs.meta b/Packages/com.unity.inputsystem/InputSystem/InputSystemStateManager.cs.meta new file mode 100644 index 0000000000..532aad0114 --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/InputSystemStateManager.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 3a1038634f66b98469a174a3e1a85190 \ No newline at end of file diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSystemTestHooks.cs b/Packages/com.unity.inputsystem/InputSystem/InputSystemTestHooks.cs new file mode 100644 index 0000000000..70974551b9 --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/InputSystemTestHooks.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; + +using UnityEngine.InputSystem.Editor; +using UnityEngine.InputSystem.LowLevel; + +#if UNITY_EDITOR || UNITY_INCLUDE_TESTS + +namespace UnityEngine.InputSystem +{ + /// + /// Extension of class to provide test-specific functionality + /// + public static partial class InputSystem + { +#if UNITY_EDITOR + internal static void TestHook_InitializeForPlayModeTests(bool enableRemoting, IInputRuntime runtime) + { + s_Manager = InputManager.CreateAndInitialize(runtime, null); + + s_Manager.runtime.onPlayModeChanged = InputSystem.OnPlayModeChange; + s_Manager.runtime.onProjectChange = InputSystem.OnProjectChange; + + InputEditorUserSettings.s_Settings = new InputEditorUserSettings.SerializedState(); + + if (enableRemoting) + InputSystem.SetUpRemoting(); + +#if !UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION + InputSystem.PerformDefaultPluginInitialization(); +#endif + } + + internal static void TestHook_SimulateDomainReload(IInputRuntime runtime) + { + // This quite invasive goes into InputSystem internals. Unfortunately, we + // have no proper way of simulating domain reloads ATM. So we directly call various + // internal methods here in a sequence similar to what we'd get during a domain reload. + // Since we're faking it, pass 'true' for calledFromCtor param. + + InputSystem.s_DomainStateManager.OnBeforeSerialize(); + InputSystem.s_DomainStateManager = null; + InputSystem.s_Manager = null; // Do NOT Dispose()! The native memory cannot be freed as it's reference by saved state + InputSystem.InitializeInEditor(true, runtime); + } +#endif // UNITY_EDITOR + + /// + /// Destroy the current setup of the input system. + /// + /// + /// NOTE: This also de-allocates data we're keeping in unmanaged memory! + /// + internal static void TestHook_DestroyAndReset() + { + // NOTE: Does not destroy InputSystemObject. We want to destroy input system + // state repeatedly during tests but we want to not create InputSystemObject + // over and over. + InputSystem.manager.Dispose(); + if (InputSystem.s_RemoteConnection != null) + Object.DestroyImmediate(InputSystem.s_RemoteConnection); + +#if UNITY_EDITOR + EditorInputControlLayoutCache.Clear(); + InputDeviceDebuggerWindow.s_OnToolbarGUIActions.Clear(); + InputEditorUserSettings.s_Settings = new InputEditorUserSettings.SerializedState(); +#endif + + InputSystem.s_Manager = null; + InputSystem.s_RemoteConnection = null; + InputSystem.s_Remote = null; + } + + internal static void TestHook_RestoreFromSavedState(InputSystemState savedState) + { + s_Manager = savedState.manager; + s_Remote = savedState.remote; + s_RemoteConnection = savedState.remoteConnection; + } + + internal static void TestHook_SwitchToDifferentInputManager(InputManager otherManager) + { + s_Manager = otherManager; + InputStateBuffers.SwitchTo(otherManager.m_StateBuffers, otherManager.defaultUpdateType); + } + } +} + +#endif // UNITY_EDITOR || UNITY_INCLUDE_TESTS diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSystemTestHooks.cs.meta b/Packages/com.unity.inputsystem/InputSystem/InputSystemTestHooks.cs.meta new file mode 100644 index 0000000000..83ac0bf722 --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/InputSystemTestHooks.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 1f95d379362f6c74ca1eb6368642199f \ No newline at end of file diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/DualShock/DualShockGamepadHID.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/DualShock/DualShockGamepadHID.cs index ca4ff95ea4..5d25b5ceb2 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/DualShock/DualShockGamepadHID.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/DualShock/DualShockGamepadHID.cs @@ -585,7 +585,7 @@ public unsafe void OnStateEvent(InputEventPtr eventPtr) || newState->buttons2 != currentState->buttons2; if (!actuated) - InputSystem.s_Manager.DontMakeCurrentlyUpdatingDeviceCurrent(); + InputSystem.manager.DontMakeCurrentlyUpdatingDeviceCurrent(); } InputState.Change(this, eventPtr); @@ -920,7 +920,7 @@ public unsafe void OnStateEvent(InputEventPtr eventPtr) || newState->buttons3 != currentState->buttons3; if (!actuatedOrChanged) - InputSystem.s_Manager.DontMakeCurrentlyUpdatingDeviceCurrent(); + InputSystem.manager.DontMakeCurrentlyUpdatingDeviceCurrent(); } InputState.Change(this, eventPtr); diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/HID/HIDSupport.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/HID/HIDSupport.cs index 29341e26d8..842d96bb42 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/HID/HIDSupport.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/HID/HIDSupport.cs @@ -94,7 +94,7 @@ public static ReadOnlyArray supportedHIDUsages s_SupportedHIDUsages = value.ToArray(); // Add HIDs we now support. - InputSystem.s_Manager.AddAvailableDevicesThatAreNowRecognized(); + InputSystem.manager.AddAvailableDevicesThatAreNowRecognized(); // Remove HIDs we no longer support. for (var i = 0; i < InputSystem.devices.Count; ++i) diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/Switch/SwitchProControllerHID.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/Switch/SwitchProControllerHID.cs index 6a619301da..97704c9347 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/Switch/SwitchProControllerHID.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/Switch/SwitchProControllerHID.cs @@ -253,7 +253,7 @@ public unsafe void OnStateEvent(InputEventPtr eventPtr) || newState->buttons2 != currentState->buttons2; if (!actuated) - InputSystem.s_Manager.DontMakeCurrentlyUpdatingDeviceCurrent(); + InputSystem.manager.DontMakeCurrentlyUpdatingDeviceCurrent(); } InputState.Change(this, eventPtr); diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/iOS/iOSSupport.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/iOS/iOSSupport.cs index 0652e877ca..d2975cf469 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/iOS/iOSSupport.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/iOS/iOSSupport.cs @@ -53,7 +53,7 @@ public static void Initialize() InputSystem.RegisterLayout(); // Don't add devices for InputTestRuntime // TODO: Maybe there should be a better place for adding device from C# - if (InputSystem.s_Manager.runtime is NativeInputRuntime) + if (InputSystem.manager.runtime is NativeInputRuntime) { if (iOSStepCounter.IsAvailable()) InputSystem.AddDevice(); diff --git a/Packages/com.unity.inputsystem/InputSystem/State/InputState.cs b/Packages/com.unity.inputsystem/InputSystem/State/InputState.cs index 6647417093..29b30adc08 100644 --- a/Packages/com.unity.inputsystem/InputSystem/State/InputState.cs +++ b/Packages/com.unity.inputsystem/InputSystem/State/InputState.cs @@ -44,8 +44,8 @@ public static class InputState /// public static event Action onChange { - add => InputSystem.s_Manager.onDeviceStateChange += value; - remove => InputSystem.s_Manager.onDeviceStateChange -= value; + add => InputSystem.manager.onDeviceStateChange += value; + remove => InputSystem.manager.onDeviceStateChange -= value; } public static unsafe void Change(InputDevice device, InputEventPtr eventPtr, InputUpdateType updateType = default) @@ -65,7 +65,7 @@ public static unsafe void Change(InputDevice device, InputEventPtr eventPtr, Inp else { #if UNITY_EDITOR - InputSystem.s_Manager.m_Diagnostics?.OnEventFormatMismatch(eventPtr, device); + InputSystem.manager.m_Diagnostics?.OnEventFormatMismatch(eventPtr, device); #endif return; } @@ -75,8 +75,8 @@ public static unsafe void Change(InputDevice device, InputEventPtr eventPtr, Inp $"State format {stateFormat} from event does not match state format {device.stateBlock.format} of device {device}", nameof(eventPtr)); - InputSystem.s_Manager.UpdateState(device, eventPtr, - updateType != default ? updateType : InputSystem.s_Manager.defaultUpdateType); + InputSystem.manager.UpdateState(device, eventPtr, + updateType != default ? updateType : InputSystem.manager.defaultUpdateType); } /// @@ -122,8 +122,8 @@ public static unsafe void Change(InputControl control, ref TState state, var statePtr = UnsafeUtility.AddressOf(ref state); var stateOffset = control.stateBlock.byteOffset - device.stateBlock.byteOffset; - InputSystem.s_Manager.UpdateState(device, - updateType != default ? updateType : InputSystem.s_Manager.defaultUpdateType, statePtr, stateOffset, + InputSystem.manager.UpdateState(device, + updateType != default ? updateType : InputSystem.manager.defaultUpdateType, statePtr, stateOffset, (uint)stateSize, eventPtr.valid ? eventPtr.internalTime @@ -209,7 +209,7 @@ public static void AddChangeMonitor(InputControl control, IInputStateChangeMonit if (!control.device.added) throw new ArgumentException($"Device for control '{control}' has not been added to system"); - InputSystem.s_Manager.AddStateChangeMonitor(control, monitor, monitorIndex, groupIndex); + InputSystem.manager.AddStateChangeMonitor(control, monitor, monitorIndex, groupIndex); } public static IInputStateChangeMonitor AddChangeMonitor(InputControl control, @@ -234,7 +234,7 @@ public static void RemoveChangeMonitor(InputControl control, IInputStateChangeMo if (monitor == null) throw new ArgumentNullException(nameof(monitor)); - InputSystem.s_Manager.RemoveStateChangeMonitor(control, monitor, monitorIndex); + InputSystem.manager.RemoveStateChangeMonitor(control, monitor, monitorIndex); } /// @@ -256,7 +256,7 @@ public static void AddChangeMonitorTimeout(InputControl control, IInputStateChan if (monitor == null) throw new ArgumentNullException(nameof(monitor)); - InputSystem.s_Manager.AddStateChangeMonitorTimeout(control, monitor, time, monitorIndex, timerIndex); + InputSystem.manager.AddStateChangeMonitorTimeout(control, monitor, time, monitorIndex, timerIndex); } public static void RemoveChangeMonitorTimeout(IInputStateChangeMonitor monitor, long monitorIndex = -1, int timerIndex = -1) @@ -264,7 +264,7 @@ public static void RemoveChangeMonitorTimeout(IInputStateChangeMonitor monitor, if (monitor == null) throw new ArgumentNullException(nameof(monitor)); - InputSystem.s_Manager.RemoveStateChangeMonitorTimeout(monitor, monitorIndex, timerIndex); + InputSystem.manager.RemoveStateChangeMonitorTimeout(monitor, monitorIndex, timerIndex); } private class StateChangeMonitorDelegate : IInputStateChangeMonitor diff --git a/Packages/com.unity.inputsystem/InputSystem/State/InputStateHistory.cs b/Packages/com.unity.inputsystem/InputSystem/State/InputStateHistory.cs index e98443475f..49c02bc30f 100644 --- a/Packages/com.unity.inputsystem/InputSystem/State/InputStateHistory.cs +++ b/Packages/com.unity.inputsystem/InputSystem/State/InputStateHistory.cs @@ -115,7 +115,7 @@ public int extraMemoryPerRecord public InputUpdateType updateMask { - get => m_UpdateMask ?? InputSystem.s_Manager.updateMask & ~InputUpdateType.Editor; + get => m_UpdateMask ?? InputSystem.manager.updateMask & ~InputUpdateType.Editor; set { if (value == InputUpdateType.None) diff --git a/Packages/com.unity.inputsystem/InputSystem/Utilities/DirtyAssetTracker.cs b/Packages/com.unity.inputsystem/InputSystem/Utilities/DirtyAssetTracker.cs new file mode 100644 index 0000000000..ccccc05afd --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/Utilities/DirtyAssetTracker.cs @@ -0,0 +1,44 @@ +#if UNITY_EDITOR +using System.Collections.Generic; +using UnityEditor; + +namespace UnityEngine.InputSystem.Utilities +{ + internal static class DirtyAssetTracker + { + /// + /// Keep track of InputActionAsset assets that you want to re-load. This is useful because some user actions, + /// such as adding a new input binding at runtime, change the in-memory representation of the input action asset and + /// those changes survive when exiting Play mode. If you re-open an Input Action Asset in the Editor that has been changed + /// this way, you see the new bindings that have been added during Play mode which you might not typically want to happen. + /// + /// You can avoid this by force re-loading from disk any asset that has been marked as dirty. + /// + /// + public static void TrackDirtyInputActionAsset(InputActionAsset asset) + { + if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(asset, out string assetGuid, out long _) == false) + return; + + s_TrackedDirtyAssets.Add(assetGuid); + } + + public static void ReloadDirtyAssets() + { + foreach (var assetGuid in s_TrackedDirtyAssets) + { + var assetPath = AssetDatabase.GUIDToAssetPath(assetGuid); + + if (string.IsNullOrEmpty(assetPath)) + continue; + + AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate); + } + + s_TrackedDirtyAssets.Clear(); + } + + private static HashSet s_TrackedDirtyAssets = new HashSet(); + } +} +#endif \ No newline at end of file diff --git a/Packages/com.unity.inputsystem/InputSystem/Utilities/DirtyAssetTracker.cs.meta b/Packages/com.unity.inputsystem/InputSystem/Utilities/DirtyAssetTracker.cs.meta new file mode 100644 index 0000000000..38c81f9be8 --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/Utilities/DirtyAssetTracker.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: fa1cedc2b36c5fc49a203b0e02a22d64 \ No newline at end of file diff --git a/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestFixture.cs b/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestFixture.cs index 1f4253b241..e4f468946f 100644 --- a/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestFixture.cs +++ b/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestFixture.cs @@ -108,7 +108,7 @@ public virtual void Setup() // so turn them off. #if UNITY_EDITOR if (Application.isPlaying && IsUnityTest()) - InputSystem.s_Manager.m_UpdateMask &= ~InputUpdateType.Editor; + InputSystem.manager.m_UpdateMask &= ~InputUpdateType.Editor; #endif // We use native collections in a couple places. We when leak them, we want to know where exactly @@ -124,7 +124,7 @@ public virtual void Setup() NativeInputRuntime.instance.onUpdate = (InputUpdateType updateType, ref InputEventBuffer buffer) => { - if (InputSystem.s_Manager.ShouldRunUpdate(updateType)) + if (InputSystem.manager.ShouldRunUpdate(updateType)) InputSystem.Update(updateType); // We ignore any input coming from native. buffer.Reset(); diff --git a/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestStateManager.cs b/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestStateManager.cs index 6c1e7d37ae..892b90f64f 100644 --- a/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestStateManager.cs +++ b/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestStateManager.cs @@ -20,7 +20,7 @@ namespace UnityEngine.InputSystem /// internal class InputTestStateManager { - public InputSystem.State GetSavedState() + public InputSystemState GetSavedState() { return m_SavedStateStack.Peek(); } @@ -39,16 +39,16 @@ public void SaveAndReset(bool enableRemoting, IInputRuntime runtime) ////TODO: preserve InputUser state ////TODO: preserve EnhancedTouchSupport state - m_SavedStateStack.Push(new InputSystem.State + m_SavedStateStack.Push(new InputSystemState { - manager = InputSystem.s_Manager, - remote = InputSystem.s_Remote, - remoteConnection = InputSystem.s_RemoteConnection, - managerState = InputSystem.s_Manager.SaveState(), - remotingState = InputSystem.s_Remote?.SaveState() ?? new InputRemoting.SerializedState(), + manager = InputSystem.manager, + remote = InputSystem.remoting, + remoteConnection = InputSystem.remoteConnection, + managerState = InputSystem.manager.SaveState(), + remotingState = InputSystem.remoting?.SaveState() ?? new InputRemoting.SerializedState(), #if UNITY_EDITOR userSettings = InputEditorUserSettings.s_Settings, - systemObject = JsonUtility.ToJson(InputSystem.s_SystemObject), + systemObject = JsonUtility.ToJson(InputSystem.domainStateManager), #endif inputActionState = InputActionState.SaveAndResetState(), touchState = EnhancedTouch.Touch.SaveAndResetState(), @@ -69,32 +69,19 @@ public void Reset(bool enableRemoting, IInputRuntime runtime) // Some devices keep globals. Get rid of them by pretending the devices // are removed. - if (InputSystem.s_Manager != null) + if (InputSystem.manager != null) { - foreach (var device in InputSystem.s_Manager.devices) + foreach (var device in InputSystem.manager.devices) device.NotifyRemoved(); - InputSystem.s_Manager.UninstallGlobals(); + InputSystem.manager.UninstallGlobals(); } #if UNITY_EDITOR - - InputSystem.s_Manager = InputManager.CreateAndInitialize(runtime, null); - - InputSystem.s_Manager.runtime.onPlayModeChanged = InputSystem.OnPlayModeChange; - InputSystem.s_Manager.runtime.onProjectChange = InputSystem.OnProjectChange; - - InputEditorUserSettings.s_Settings = new InputEditorUserSettings.SerializedState(); - - if (enableRemoting) - InputSystem.SetUpRemoting(); - -#if !UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION - InputSystem.PerformDefaultPluginInitialization(); -#endif - + // Perform special initialization for running Editor tests + InputSystem.TestHook_InitializeForPlayModeTests(enableRemoting, runtime); #else - // For tests need to use default InputSettings + // For Player tests we can use the normal initialization InputSystem.InitializeInPlayer(runtime, false); #endif // UNITY_EDITOR @@ -109,31 +96,6 @@ public void Reset(bool enableRemoting, IInputRuntime runtime) Profiler.EndSample(); } - /// - /// Destroy the current setup of the input system. - /// - /// - /// NOTE: This also de-allocates data we're keeping in unmanaged memory! - /// - private static void Destroy() - { - // NOTE: Does not destroy InputSystemObject. We want to destroy input system - // state repeatedly during tests but we want to not create InputSystemObject - // over and over. - InputSystem.s_Manager.Dispose(); - if (InputSystem.s_RemoteConnection != null) - Object.DestroyImmediate(InputSystem.s_RemoteConnection); -#if UNITY_EDITOR - EditorInputControlLayoutCache.Clear(); - InputDeviceDebuggerWindow.s_OnToolbarGUIActions.Clear(); - InputEditorUserSettings.s_Settings = new InputEditorUserSettings.SerializedState(); -#endif - - InputSystem.s_Manager = null; - InputSystem.s_RemoteConnection = null; - InputSystem.s_Remote = null; - } - ////FIXME: this method doesn't restore things like InputDeviceDebuggerWindow.onToolbarGUI /// /// Restore the state of the system from the last state pushed with . @@ -149,37 +111,33 @@ public void Restore() state.touchState.StaticDisposeCurrentState(); state.inputActionState.StaticDisposeCurrentState(); - // Nuke what we have. - Destroy(); + InputSystem.TestHook_DestroyAndReset(); state.inputUserState.RestoreSavedState(); state.touchState.RestoreSavedState(); state.inputActionState.RestoreSavedState(); - InputSystem.s_Manager = state.manager; - InputSystem.s_Remote = state.remote; - InputSystem.s_RemoteConnection = state.remoteConnection; - + InputSystem.TestHook_RestoreFromSavedState(state); InputUpdate.Restore(state.managerState.updateState); - InputSystem.s_Manager.InstallRuntime(InputSystem.s_Manager.runtime); - InputSystem.s_Manager.InstallGlobals(); + InputSystem.manager.InstallRuntime(InputSystem.manager.runtime); + InputSystem.manager.InstallGlobals(); // IMPORTANT // If InputManager was using the "temporary" settings object, then it'll have been deleted during Reset() // and the saved Manager settings state will also be null, since it's a ScriptableObject. // In this case we manually create and set new temp settings object. - if (InputSystem.s_Manager.settings == null) + if (InputSystem.manager.settings == null) { var tmpSettings = ScriptableObject.CreateInstance(); tmpSettings.hideFlags = HideFlags.HideAndDontSave; - InputSystem.s_Manager.settings = tmpSettings; + InputSystem.manager.settings = tmpSettings; } - else InputSystem.s_Manager.ApplySettings(); + else InputSystem.manager.ApplySettings(); #if UNITY_EDITOR InputEditorUserSettings.s_Settings = state.userSettings; - JsonUtility.FromJsonOverwrite(state.systemObject, InputSystem.s_SystemObject); + JsonUtility.FromJsonOverwrite(state.systemObject, InputSystem.domainStateManager); #endif // Get devices that keep global lists (like Gamepad) to re-initialize them @@ -191,6 +149,6 @@ public void Restore() } } - private Stack m_SavedStateStack = new Stack(); + private Stack m_SavedStateStack = new Stack(); } } From a58a65a033466002a481de07cd4e2c96c2fa2071 Mon Sep 17 00:00:00 2001 From: Tim Keosababian <36088732+timkeo@users.noreply.github.com> Date: Thu, 21 Mar 2024 15:29:14 -0700 Subject: [PATCH 05/10] Fix branch after rebasing with develop (ISX-1840) - Most ProjectWideAction changes discarded since feature was re-implemented (no longer relevant) - Recreate InputSystem "Test Hook" for Enabling/Disabling Actions from tests - Small fixes from unresolved conflicts or bad merges during rebase - Various formatting changes (to be more consistant) --- .../CoreTests_ProjectWideActions.cs | 3 + .../InputSystem/Plugins/InputForUITests.cs | 26 ++--- .../ProjectWideActionsAsset.cs | 13 ++- .../InputSystem/InputManager.cs | 110 +++++++++--------- .../InputSystem/InputSystem.cs | 88 +++++++------- .../InputSystem/InputSystemStateManager.cs | 2 + .../InputSystem/InputSystemTestHooks.cs | 3 +- .../TestFixture/InputTestStateManager.cs | 4 +- 8 files changed, 123 insertions(+), 126 deletions(-) diff --git a/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs b/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs index 7d6d7fa27c..93fea59859 100644 --- a/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs +++ b/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs @@ -38,6 +38,9 @@ private static void CreateAndAssignProjectWideTestAsset() else EditorBuildSettings.RemoveConfigObject(name: kSavedActionsObject); + // Create temporary asset and assign as setting + var asset = ProjectWideActionsAsset.CreateDefaultAssetAtPath(kAssetPath); + ProjectWideActionsBuildProvider.actionsToIncludeInPlayerBuild = asset; #endif } diff --git a/Assets/Tests/InputSystem/Plugins/InputForUITests.cs b/Assets/Tests/InputSystem/Plugins/InputForUITests.cs index e1cb63bf55..543b73639f 100644 --- a/Assets/Tests/InputSystem/Plugins/InputForUITests.cs +++ b/Assets/Tests/InputSystem/Plugins/InputForUITests.cs @@ -60,7 +60,7 @@ public override void TearDown() EventProvider.ClearMockProvider(); m_InputForUIEvents.Clear(); - InputSystem.s_Manager.actions = storedActions; + InputSystem.manager.actions = storedActions; #if UNITY_EDITOR if (File.Exists(kAssetPath)) @@ -193,7 +193,7 @@ public void UIActionNavigation_FiresUINavigationEvents_FromInputsGamepadJoystick // Remove the project-wide actions asset in play mode and player. // It will call InputSystem.onActionChange and re-set InputSystemProvider.actionAsset // This the case where no project-wide actions asset is available in the project. - InputSystem.s_Manager.actions = null; + InputSystem.manager.actions = null; } Update(); @@ -267,7 +267,7 @@ public void UIActionSubmit_FiresUISubmitEvents_FromInputsGamepadJoystickAndKeybo Update(); if (!useProjectWideActionsAsset) { - InputSystem.s_Manager.actions = null; + InputSystem.manager.actions = null; } Update(); @@ -304,7 +304,7 @@ public void UIActionCancel_FiresUICancelEvents_FromInputsGamepadAndKeyboard(bool Update(); if (!useProjectWideActionsAsset) { - InputSystem.s_Manager.actions = null; + InputSystem.manager.actions = null; } Update(); @@ -334,7 +334,7 @@ public void UIActionPoint_FiresUIPointEvents_FromInputsMousePenAndTouch(bool use Update(); if (!useProjectWideActionsAsset) { - InputSystem.s_Manager.actions = null; + InputSystem.manager.actions = null; } Update(); @@ -386,7 +386,7 @@ public void UIActionClick_FiresUIClickEvents_FromInputsMousePenAndTouch(bool use Update(); if (!useProjectWideActionsAsset) { - InputSystem.s_Manager.actions = null; + InputSystem.manager.actions = null; } Update(); @@ -471,7 +471,7 @@ public void UIActionScroll_FiresUIScrollEvents_FromInputMouse(bool useProjectWid Update(); if (!useProjectWideActionsAsset) { - InputSystem.s_Manager.actions = null; + InputSystem.manager.actions = null; } Update(); @@ -502,7 +502,7 @@ public void UIActionScroll_FiresUIScrollEvents_FromInputMouse(bool useProjectWid public void DefaultActions_ShouldNotGenerateAnyVerificationWarnings(bool useProjectWideActions) { if (!useProjectWideActions) - InputSystem.s_Manager.actions = null; + InputSystem.manager.actions = null; Update(); LogAssert.NoUnexpectedReceived(); } @@ -515,7 +515,7 @@ public void ActionsWithoutUIMap_ShouldGenerateWarnings() var asset = ProjectWideActionsAsset.CreateDefaultAssetAtPath(kAssetPath); asset.RemoveActionMap(asset.FindActionMap("UI", throwIfNotFound: true)); - InputSystem.s_Manager.actions = asset; + InputSystem.manager.actions = asset; Update(); var link = EditorHelpers.GetHyperlink(kAssetPath); @@ -551,7 +551,7 @@ public void ActionMapWithNonExistentRequiredAction_ShouldGenerateWarning(string var action = asset.FindAction(actionPath); action.Rename("Other"); - InputSystem.s_Manager.actions = asset; + InputSystem.manager.actions = asset; Update(); //var link = AssetDatabase.GetAssetPath()//EditorHelpers.GetHyperlink(kAssetPath); @@ -594,7 +594,7 @@ public void ActionMapWithUnboundRequiredAction_ShouldGenerateWarning(string acti asset.AddActionMap(newMap); - InputSystem.s_Manager.actions = asset; + InputSystem.manager.actions = asset; Update(); LogAssert.Expect(LogType.Warning, new Regex($"^InputAction with path '{actionPath}' in asset \"{kAssetPath}\" do not have any configured bindings.")); @@ -619,7 +619,7 @@ public void ActionWithUnexpectedActionType_ShouldGenerateWarning(string actionPa var expectedType = action.type; action.m_Type = unexpectedType; // change directly via internals for now - InputSystem.s_Manager.actions = asset; + InputSystem.manager.actions = asset; Update(); LogAssert.Expect(LogType.Warning, @@ -645,7 +645,7 @@ public void ActionWithDifferentExpectedControlType_ShouldGenerateWarning(string var expectedControlType = action.expectedControlType; action.expectedControlType = unexpectedControlType; - InputSystem.s_Manager.actions = asset; + InputSystem.manager.actions = asset; Update(); LogAssert.Expect(LogType.Warning, diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs index b194a4d820..784b52506d 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs @@ -1,4 +1,4 @@ -#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using System.Collections.Generic; @@ -28,6 +28,7 @@ class ProjectSettingsPostprocessor : AssetPostprocessor private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths, bool didDomainReload) #else private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) +#endif { if (!migratedInputActionAssets) { @@ -45,14 +46,19 @@ private static void OnPostprocessAllAssets(string[] importedAssets, string[] del } } } - } - } + private static void MoveInputManagerAssetActionsToProjectWideInputActionAsset() { var objects = AssetDatabase.LoadAllAssetsAtPath(EditorHelpers.GetPhysicalPath(kAssetPathInputManager)); if (objects == null) return; + var inputActionsAsset = objects.FirstOrDefault(o => o != null && o.name == kAssetNameProjectWideInputActions) as InputActionAsset; + if (inputActionsAsset != default) + { + // Found some actions in the InputManager.asset file + // + string path = ProjectWideActionsAsset.kDefaultAssetPath; if (File.Exists(EditorHelpers.GetPhysicalPath(path))) { @@ -305,7 +311,6 @@ private static InputActionAsset CreateAssetAtPathFromJson(string assetPath, stri InputActionAssetManager.SaveAsset(assetPath, inputActionAsset.ToJson()); return AssetDatabase.LoadAssetAtPath(assetPath); } -#endif // UNITY_EDITOR } } #endif // UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS diff --git a/Packages/com.unity.inputsystem/InputSystem/InputManager.cs b/Packages/com.unity.inputsystem/InputSystem/InputManager.cs index af5b33094f..62d4a0bb16 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputManager.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputManager.cs @@ -73,9 +73,9 @@ public static InputManager CreateAndInitialize(IInputRuntime runtime, InputSetti } newInst.m_Settings = settings; -#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS newInst.InitializeActions(); -#endif // UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + #endif // UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS newInst.InitializeData(); newInst.InstallRuntime(runtime); @@ -86,9 +86,9 @@ public static InputManager CreateAndInitialize(IInputRuntime runtime, InputSetti newInst.ApplySettings(); -#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS newInst.ApplyActions(); -#endif + #endif return newInst; } @@ -383,9 +383,6 @@ internal bool ShouldDrawWarningIconForBinding(string bindingPath) "InputSystem.ShouldDrawWarningIconForBinding"); } -#endif // UNITY_EDITOR - -#if UNITY_EDITOR private bool m_RunPlayerUpdatesInEditMode; /// @@ -400,7 +397,8 @@ public bool runPlayerUpdatesInEditMode get => m_RunPlayerUpdatesInEditMode; set => m_RunPlayerUpdatesInEditMode = value; } -#endif + +#endif // UNITY_EDITOR private bool gameIsPlaying => #if UNITY_EDITOR @@ -411,7 +409,7 @@ public bool runPlayerUpdatesInEditMode private bool gameHasFocus => #if UNITY_EDITOR - m_RunPlayerUpdatesInEditMode || m_HasFocus || gameShouldGetInputRegardlessOfFocus; + m_RunPlayerUpdatesInEditMode || m_HasFocus || gameShouldGetInputRegardlessOfFocus; #else m_HasFocus || gameShouldGetInputRegardlessOfFocus; #endif @@ -1847,15 +1845,15 @@ public void Update(InputUpdateType updateType) } -#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS // Initialize project-wide actions: // - In editor (edit mode or play-mode) we always use the editor build preferences persisted setting. // - In player build we always attempt to find a preloaded asset. private void InitializeActions() { -#if UNITY_EDITOR + #if UNITY_EDITOR m_Actions = ProjectWideActionsBuildProvider.actionsToIncludeInPlayerBuild; -#else + #else m_Actions = null; var candidates = Resources.FindObjectsOfTypeAll(); foreach (var candidate in candidates) @@ -1866,10 +1864,9 @@ private void InitializeActions() break; } } -#endif // UNITY_EDITOR + #endif // UNITY_EDITOR } - -#endif // UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + #endif // UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS internal void InitializeData() { @@ -1885,10 +1882,10 @@ internal void InitializeData() // can manually turn off one of them to optimize operation. m_UpdateMask = InputUpdateType.Dynamic | InputUpdateType.Fixed; m_HasFocus = Application.isFocused; -#if UNITY_EDITOR + #if UNITY_EDITOR m_EditorIsActive = true; m_UpdateMask |= InputUpdateType.Editor; -#endif + #endif // Default polling frequency is 60 Hz. m_PollingFrequency = 60; @@ -2154,9 +2151,9 @@ internal struct AvailableDevice private IInputRuntime m_Runtime; private InputMetrics m_Metrics; private InputSettings m_Settings; -#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS private InputActionAsset m_Actions; -#endif + #endif #if UNITY_EDITOR internal IInputDiagnostics m_Diagnostics; @@ -2505,7 +2502,7 @@ private void RestoreDevicesAfterDomainReloadIfNecessary() #endif } -#if UNITY_EDITOR + #if UNITY_EDITOR private void SyncAllDevicesWhenEditorIsActivated() { var isActive = m_Runtime.isEditorActive; @@ -2538,7 +2535,7 @@ internal void SyncAllDevicesAfterEnteringPlayMode() SyncAllDevices(); } -#endif + #endif // UNITY_EDITOR private void WarnAboutDevicesFailingToRecreateAfterDomainReload() { @@ -2558,7 +2555,7 @@ private void WarnAboutDevicesFailingToRecreateAfterDomainReload() // At this point, we throw the device states away and forget about // what we had before the domain reload. m_SavedDeviceStates = null; - #endif + #endif // UNITY_EDITOR } private void OnBeforeUpdate(InputUpdateType updateType) @@ -2720,9 +2717,8 @@ internal void ApplyActions() // Let listeners know. DelegateHelpers.InvokeCallbacksSafe(ref m_ActionsChangedListeners, "InputSystem.onActionsChange"); } - #endif - + internal unsafe long ExecuteGlobalCommand(ref TCommand command) where TCommand : struct, IInputDeviceCommandInfo { @@ -2884,7 +2880,7 @@ internal void OnFocusChanged(bool focus) m_HasFocus = focus; } -#if UNITY_EDITOR + #if UNITY_EDITOR internal void LeavePlayMode() { // Reenable all devices and reset their play mode state. @@ -2912,7 +2908,7 @@ private void OnPlayerLoopInitialization() InputStateBuffers.SwitchTo(m_StateBuffers, InputUpdate.s_LatestUpdateType); } -#endif + #endif // UNITY_EDITOR internal bool ShouldRunUpdate(InputUpdateType updateType) { @@ -2923,7 +2919,7 @@ internal bool ShouldRunUpdate(InputUpdateType updateType) var mask = m_UpdateMask; -#if UNITY_EDITOR + #if UNITY_EDITOR // If the player isn't running, the only thing we run is editor updates, except if // explicitly overriden via `runUpdatesInEditMode`. // NOTE: This means that in edit mode (outside of play mode) we *never* switch to player @@ -2932,7 +2928,7 @@ internal bool ShouldRunUpdate(InputUpdateType updateType) // it will see gamepad inputs going to the editor and respond to them. if (!gameIsPlaying && updateType != InputUpdateType.Editor && !runPlayerUpdatesInEditMode) return false; -#endif + #endif // UNITY_EDITOR return (updateType & mask) != 0; } @@ -3031,21 +3027,21 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev // Figure out if we can just flush the buffer and early out. var canFlushBuffer = false -#if UNITY_EDITOR + #if UNITY_EDITOR // If out of focus and runInBackground is off and ExactlyAsInPlayer is on, discard input. || (!gameHasFocus && m_Settings.editorInputBehaviorInPlayMode == InputSettings.EditorInputBehaviorInPlayMode.AllDeviceInputAlwaysGoesToGameView && (!m_Runtime.runInBackground || m_Settings.backgroundBehavior == InputSettings.BackgroundBehavior.ResetAndDisableAllDevices)) -#else + #else || (!gameHasFocus && !m_Runtime.runInBackground) -#endif + #endif ; var canEarlyOut = // Early out if there's no events to process. eventBuffer.eventCount == 0 || canFlushBuffer -#if UNITY_EDITOR + #if UNITY_EDITOR // If we're in the background and not supposed to process events in this update (but somehow // still ended up here), we're done. || ((!gameHasFocus || gameShouldGetInputRegardlessOfFocus) && @@ -3056,11 +3052,11 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev // When the game is playing and has focus, we never process input in editor updates. All we // do is just switch to editor state buffers and then exit. || (gameIsPlaying && gameHasFocus && updateType == InputUpdateType.Editor)) -#endif + #endif ; -#if UNITY_EDITOR + #if UNITY_EDITOR var dropStatusEvents = false; if (!gameIsPlaying && gameShouldGetInputRegardlessOfFocus && (eventBuffer.sizeInBytes > (100 * 1024))) { @@ -3069,7 +3065,7 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev canEarlyOut = false; dropStatusEvents = true; } -#endif + #endif if (canEarlyOut) { @@ -3133,7 +3129,7 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev var currentEventTimeInternal = currentEventReadPtr->internalTime; var currentEventType = currentEventReadPtr->type; -#if UNITY_EDITOR + #if UNITY_EDITOR if (dropStatusEvents) { // If the type here is a status event, ask advance not to leave the event in the buffer. Otherwise, leave it there. @@ -3144,7 +3140,7 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev continue; } -#endif + #endif // In the editor, we discard all input events that occur in-between exiting edit mode and having // entered play mode as otherwise we'll spill a bunch of UI events that have occurred while the @@ -3155,7 +3151,7 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev // here such as throwing partial touches away and then letting the rest of a touch go through. // Could be that ultimately we need to issue a full reset of all devices at the beginning of // play mode in the editor. -#if UNITY_EDITOR + #if UNITY_EDITOR if ((currentEventType == StateEvent.Type || currentEventType == DeltaStateEvent.Type) && (updateType & InputUpdateType.Editor) == 0 && @@ -3167,7 +3163,7 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev m_InputEventStream.Advance(false); continue; } -#endif + #endif // If we're timeslicing, check if the event time is within limits. if (timesliceEvents && currentEventTimeInternal >= currentTime) @@ -3181,10 +3177,10 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev device = TryGetDeviceById(currentEventReadPtr->deviceId); if (device == null) { -#if UNITY_EDITOR + #if UNITY_EDITOR ////TODO: see if this is a device we haven't created and if so, just ignore m_Diagnostics?.OnCannotFindDeviceForEvent(new InputEventPtr(currentEventReadPtr)); -#endif + #endif m_InputEventStream.Advance(false); continue; @@ -3192,7 +3188,7 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev // In the editor, we may need to bump events from editor updates into player updates // and vice versa. -#if UNITY_EDITOR + #if UNITY_EDITOR if (isPlaying && !gameHasFocus) { if (m_Settings.editorInputBehaviorInPlayMode == InputSettings.EditorInputBehaviorInPlayMode @@ -3223,7 +3219,7 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev } } } -#endif + #endif // UNITY_EDITOR // If device is disabled, we let the event through only in certain cases. // Removal and configuration change events should always be processed. @@ -3233,12 +3229,12 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev (device.m_DeviceFlags & (InputDevice.DeviceFlags.DisabledInRuntime | InputDevice.DeviceFlags.DisabledWhileInBackground)) != 0) { -#if UNITY_EDITOR + #if UNITY_EDITOR // If the device is disabled in the backend, getting events for them // is something that indicates a problem in the backend so diagnose. if ((device.m_DeviceFlags & InputDevice.DeviceFlags.DisabledInRuntime) != 0) m_Diagnostics?.OnEventForDisabledDevice(currentEventReadPtr, device); -#endif + #endif m_InputEventStream.Advance(false); continue; @@ -3310,17 +3306,17 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev // Give the device a chance to do something with data before we propagate it to event listeners. if (device.hasEventPreProcessor) { -#if UNITY_EDITOR + #if UNITY_EDITOR var eventSizeBeforePreProcessor = currentEventReadPtr->sizeInBytes; -#endif + #endif var shouldProcess = ((IEventPreProcessor)device).PreProcessEvent(currentEventReadPtr); -#if UNITY_EDITOR + #if UNITY_EDITOR if (currentEventReadPtr->sizeInBytes > eventSizeBeforePreProcessor) { Profiler.EndSample(); throw new AccessViolationException($"'{device}'.PreProcessEvent tries to grow an event from {eventSizeBeforePreProcessor} bytes to {currentEventReadPtr->sizeInBytes} bytes, this will potentially corrupt events after the current event and/or cause out-of-bounds memory access."); } -#endif + #endif if (!shouldProcess) { // Skip event if PreProcessEvent considers it to be irrelevant. @@ -3372,7 +3368,7 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev if (currentEventTimeInternal < device.m_LastUpdateTimeInternal && !(deviceIsStateCallbackReceiver && device.stateBlock.format != eventPtr.stateFormat)) { -#if UNITY_EDITOR + #if UNITY_EDITOR m_Diagnostics?.OnEventTimestampOutdated(new InputEventPtr(currentEventReadPtr), device); #elif UNITY_ANDROID // Android keyboards can send events out of order: Holding down a key will send multiple @@ -3403,9 +3399,9 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev // If the state format doesn't match, ignore the event. if (device.stateBlock.format != eventPtr.stateFormat) { -#if UNITY_EDITOR + #if UNITY_EDITOR m_Diagnostics?.OnEventFormatMismatch(currentEventReadPtr, device); -#endif + #endif break; } @@ -3421,9 +3417,9 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev // Only events should. If running play mode updates in editor, we want to defer to the play mode // callbacks to set the last update time to avoid dropping events only processed by the editor state. if (device.m_LastUpdateTimeInternal <= eventPtr.internalTime -#if UNITY_EDITOR + #if UNITY_EDITOR && !(updateType == InputUpdateType.Editor && runPlayerUpdatesInEditMode) -#endif + #endif ) device.m_LastUpdateTimeInternal = eventPtr.internalTime; @@ -3809,7 +3805,7 @@ private bool FlipBuffersForDeviceIfNecessary(InputDevice device, InputUpdateType return false; } -#if UNITY_EDITOR + #if UNITY_EDITOR ////REVIEW: should this use the editor update ticks as quasi-frame-boundaries? // Updates go to the editor only if the game isn't playing or does not have focus. // Otherwise we fall through to the logic that flips for the *next* dynamic and @@ -3823,7 +3819,7 @@ private bool FlipBuffersForDeviceIfNecessary(InputDevice device, InputUpdateType m_StateBuffers.m_EditorStateBuffers.SwapBuffers(device.m_DeviceIndex); return true; } -#endif + #endif // Flip buffers if we haven't already for this frame. if (device.m_CurrentUpdateStepCount != InputUpdate.s_UpdateStepCount) @@ -3841,7 +3837,7 @@ private bool FlipBuffersForDeviceIfNecessary(InputDevice device, InputUpdateType // Stuff everything that we want to survive a domain reload into // a m_SerializedState. -#if UNITY_EDITOR || DEVELOPMENT_BUILD + #if UNITY_EDITOR || DEVELOPMENT_BUILD [Serializable] internal struct DeviceState { diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs b/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs index 1da213844b..73adf69103 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs @@ -3099,9 +3099,6 @@ private static void DisableActions(bool triggerSetupChanged = false) public static InputActionAsset actions { get => s_Manager?.actions; - { - return UnityEngine.InputSystem.Editor.ProjectWideActionsAsset.instance; - } set { // Prevent this property from being assigned in play-mode. @@ -3122,13 +3119,15 @@ public static InputActionAsset actions // Track reference to enable including it in built Players, note that it will discard any non-persisted // object reference ProjectWideActionsBuildProvider.actionsToIncludeInPlayerBuild = value; -#endif // UNITY_EDITOR + #endif // UNITY_EDITOR // Update underlying value s_Manager.actions = value; // Note that we do not enable/disable any actions until play-mode } + } + /// /// Event that is triggered if the instance assigned to property changes. /// @@ -3139,7 +3138,7 @@ public static InputActionAsset actions /// /// public static event Action onActionsChange - { + { add => s_Manager.onActionsChange += value; remove => s_Manager.onActionsChange -= value; } @@ -3483,17 +3482,16 @@ private static void SetUpRemotingInternal() #if !UNITY_EDITOR private static bool ShouldEnableRemoting() { -#if UNITY_INCLUDE_TESTS + #if UNITY_INCLUDE_TESTS var isRunningTests = true; -#else + #else var isRunningTests = false; -#endif + #endif if (isRunningTests) return false; // Don't remote while running tests. return true; } - - #endif + #endif //!UNITY_EDITOR #endif // DEVELOPMENT_BUILD || UNITY_EDITOR // The rest here is internal stuff to manage singletons, survive domain reloads, @@ -3501,7 +3499,7 @@ private static bool ShouldEnableRemoting() private static bool IsDomainReloadDisabledForPlayMode() { -#if UNITY_EDITOR && !ENABLE_CORECLR + #if UNITY_EDITOR && !ENABLE_CORECLR if (!EditorSettings.enterPlayModeOptionsEnabled || (EditorSettings.enterPlayModeOptions & EnterPlayModeOptions.DisableDomainReload) == 0) return false; #endif @@ -3534,7 +3532,7 @@ private static void GlobalInitialize(bool calledFromCtor) { RunInitialUpdate(); } - #endif +#endif // UNITY_EDITOR } // Initialization is triggered by accessing InputSystem. Some parts (like InputActions) @@ -3570,26 +3568,25 @@ internal static void InitializeInEditor(bool calledFromCtor, IInputRuntime runti InputEditorUserSettings.s_Settings = new InputEditorUserSettings.SerializedState(); -#if !UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION + #if !UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION InputSystem.PerformDefaultPluginInitialization(); -#endif + #endif } -#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS - ProjectWideActionsAsset.EnsureInitialized(); -#endif - var existingSystemObjects = Resources.FindObjectsOfTypeAll(); if (existingSystemObjects != null && existingSystemObjects.Length > 0) { if (globalReset) { - ////FIXME: does not preserve action map state + ////FIXME: does not preserve action map state // If we're coming back out of a domain reload. We're restoring part of the - // InputManager state here but we're still waiting from layout registrations - // that happen during domain initialization. + // InputManager state here but we're still waiting from layout registrations + // that happen during domain initialization. + s_DomainStateManager = existingSystemObjects[0]; + s_Manager.RestoreStateWithoutDevices(s_DomainStateManager.systemState.managerState); + InputDebuggerWindow.ReviveAfterDomainReload(); // Restore remoting state. s_RemoteConnection = s_DomainStateManager.systemState.remoteConnection; @@ -3654,11 +3651,11 @@ internal static void InitializeInEditor(bool calledFromCtor, IInputRuntime runti } s_DomainStateManager.newInputBackendsCheckedAsEnabled = true; -#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS // Make sure project wide input actions are enabled. // Note that this will always fail if entering play-mode within editor since not yet in play-mode. EnableActions(); -#endif + #endif RunInitialUpdate(); @@ -3695,9 +3692,9 @@ internal static void OnPlayModeChange(PlayModeStateChange change) ////REVIEW: is there any other cleanup work we want to before? should we automatically nuke //// InputDevices that have been created with AddDevice<> during play mode? case PlayModeStateChange.EnteredEditMode: -#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS DisableActions(false); -#endif + #endif // Nuke all InputUsers. InputUser.ResetGlobals(); @@ -3728,8 +3725,6 @@ internal static void OnProjectChange() // Also, if the asset holding our current settings got deleted, switch back to a // temporary settings object. - // NOTE: We access m_Settings directly here to make sure we're not running into asserts - // from the settings getter checking it has a valid object. if (EditorUtility.InstanceIDToObject(s_Manager.settings.GetInstanceID()) == null) { var newSettings = ScriptableObject.CreateInstance(); @@ -3738,7 +3733,7 @@ internal static void OnProjectChange() } } -#else +#else // UNITY_EDITOR internal static void InitializeInPlayer(IInputRuntime runtime, bool loadSettingsAsset) { InputSettings settings = null; @@ -3750,52 +3745,47 @@ internal static void InitializeInPlayer(IInputRuntime runtime, bool loadSettings // instances. s_Manager = InputManager.CreateAndInitialize(runtime ?? NativeInputRuntime.instance, settings); -#if !UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION + #if !UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION PerformDefaultPluginInitialization(); -#endif + #endif // Automatically enable remoting in development players. -#if DEVELOPMENT_BUILD + #if DEVELOPMENT_BUILD if (ShouldEnableRemoting()) SetUpRemoting(); -#endif + #endif -#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS // && !UNITY_INCLUDE_TESTS + #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS // This is the point where we initialise project-wide actions for the Player EnableActions(); -#endif + #endif } #endif // UNITY_EDITOR #if UNITY_INCLUDE_TESTS // - // A (hopefully) temporary work-around to being unable to define UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS - // within the Test-Framework assembly; called from within InputTestStateManager.Reset(). - // - // This code should be removed when Actions init/reset flows are refactored, but we need it for now - // so ProjectWideActionstests will pass + // We cannot define UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONSw with the Test-Framework assembly, and + // so this hook is needed; it's called from InputTestStateManager.Reset(). // - internal static void DisableActionsForTests() + internal static void TestHook_DisableActions() { -#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS // Note that in a test setup we might enter reset with project-wide actions already enabled but the // reset itself has pushed the action system state on the state stack. To avoid action state memory // problems we disable actions here and also request asset to be marked dirty and reimported. DisableActions(triggerSetupChanged: true); if (s_Manager != null) s_Manager.actions = null; -#endif // UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + #endif // UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS } - internal static void EnableActionsForTests() + internal static void TestHook_EnableActions() { -#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS - // Touching the `actions` property will initialise it here (if it wasn't already). - // This is the point where we initialise project-wide actions for the Editor, Editor Tests and Player Tests. - actions?.Enable(); -#endif - + // Note this is too early for editor ! actions is not setup yet. + #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + EnableActions(); + #endif } #endif // UNITY_INCLUDE_TESTS diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSystemStateManager.cs b/Packages/com.unity.inputsystem/InputSystem/InputSystemStateManager.cs index fedf66999b..cab2b50071 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputSystemStateManager.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputSystemStateManager.cs @@ -1,7 +1,9 @@ using System; using UnityEngine.InputSystem; using UnityEngine; +#if UNITY_EDITOR using UnityEngine.InputSystem.Editor; +#endif using UnityEngine.InputSystem.Utilities; namespace UnityEngine.InputSystem diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSystemTestHooks.cs b/Packages/com.unity.inputsystem/InputSystem/InputSystemTestHooks.cs index 70974551b9..01e6355ab0 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputSystemTestHooks.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputSystemTestHooks.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; - +#if UNITY_EDITOR using UnityEngine.InputSystem.Editor; +#endif using UnityEngine.InputSystem.LowLevel; #if UNITY_EDITOR || UNITY_INCLUDE_TESTS diff --git a/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestStateManager.cs b/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestStateManager.cs index 892b90f64f..3126546de9 100644 --- a/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestStateManager.cs +++ b/Packages/com.unity.inputsystem/Tests/TestFixture/InputTestStateManager.cs @@ -65,7 +65,7 @@ public void Reset(bool enableRemoting, IInputRuntime runtime) { Profiler.BeginSample("InputSystem.Reset"); - UnityEngine.InputSystem.Editor.ProjectWideActionsAsset.TestHook_Disable(); + InputSystem.TestHook_DisableActions(); // Some devices keep globals. Get rid of them by pretending the devices // are removed. @@ -91,7 +91,7 @@ public void Reset(bool enableRemoting, IInputRuntime runtime) InputUser.ResetGlobals(); EnhancedTouchSupport.Reset(); - UnityEngine.InputSystem.Editor.ProjectWideActionsAsset.TestHook_Enable(); + InputSystem.TestHook_EnableActions(); Profiler.EndSample(); } From c534e1851fbb6648b578d7764b0bd772fa4af257 Mon Sep 17 00:00:00 2001 From: Tim Keosababian <36088732+timkeo@users.noreply.github.com> Date: Fri, 5 Apr 2024 16:40:56 -0700 Subject: [PATCH 06/10] Refactor DeferBindingResolution to remove statics (ISX-1840) - Consolidate functionality into DeferBindingResolutionContext class - Instantiate Context object as a non-static InputManager field exposed via InputManager methods --- .../InputSystem/Actions/InputActionMap.cs | 6 +- .../Actions/InputActionRebindingExtensions.cs | 77 +++++++++++++------ .../InputSystem/Actions/InputActionState.cs | 30 -------- .../InputSystem/InputManager.cs | 24 ++++++ 4 files changed, 80 insertions(+), 57 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionMap.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionMap.cs index d26eb8f251..d2ddfeddc4 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionMap.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionMap.cs @@ -809,8 +809,6 @@ private enum Flags BindingsForEachActionInitialized = 1 << 3, } - internal static int s_DeferBindingResolution; - internal struct DeviceArray { private bool m_HaveValue; @@ -1206,7 +1204,7 @@ internal bool LazyResolveBindings(bool fullResolve) needToResolveBindings = true; bindingResolutionNeedsFullReResolve |= fullResolve; - if (s_DeferBindingResolution > 0) + if (InputSystem.manager.areDeferredBindingsToResolve) return false; // Have to do it straight away. @@ -1225,7 +1223,7 @@ internal bool ResolveBindingsIfNecessary() { if (m_State != null && m_State.isProcessingControlStateChange) { - Debug.Assert(s_DeferBindingResolution > 0, "While processing control state changes, binding resolution should be suppressed"); + Debug.Assert(InputSystem.manager.areDeferredBindingsToResolve, "While processing control state changes, binding resolution should be suppressed"); return false; } diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionRebindingExtensions.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionRebindingExtensions.cs index bdf7888ceb..6a9af20747 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionRebindingExtensions.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionRebindingExtensions.cs @@ -2776,39 +2776,70 @@ public static RebindingOperation PerformInteractiveRebinding(this InputAction ac return rebind; } + internal static DeferBindingResolutionContext DeferBindingResolution() + { + return InputSystem.manager.DeferBindingResolution(); + } + } + + internal class DeferBindingResolutionContext : IDisposable + { + public int deferredCount => m_DeferredCount; + + public void Acquire() + { + ++m_DeferredCount; + } + + public void Release() + { + if (m_DeferredCount > 0) + --m_DeferredCount; + if (m_DeferredCount == 0) + ExecuteDeferredResolutionOfBindings(); + } + /// - /// Temporarily suspend immediate re-resolution of bindings. + /// Allows usage within using() blocks. /// - /// - /// When changing control setups, it may take multiple steps to get to the final setup but each individual - /// step may trigger bindings to be resolved again in order to update controls on actions (see ). - /// Using this struct, this can be avoided and binding resolution can be deferred to after the whole operation - /// is complete and the final binding setup is in place. - /// - internal static DeferBindingResolutionWrapper DeferBindingResolution() + public void Dispose() { - if (s_DeferBindingResolutionWrapper == null) - s_DeferBindingResolutionWrapper = new DeferBindingResolutionWrapper(); - s_DeferBindingResolutionWrapper.Acquire(); - return s_DeferBindingResolutionWrapper; + Release(); } - private static DeferBindingResolutionWrapper s_DeferBindingResolutionWrapper; - - internal class DeferBindingResolutionWrapper : IDisposable + private void ExecuteDeferredResolutionOfBindings() { - public void Acquire() + ++m_DeferredCount; + try { - ++InputActionMap.s_DeferBindingResolution; - } + ref var globalList = ref InputActionState.s_GlobalState.globalList; - public void Dispose() + for (var i = 0; i < globalList.length; ++i) + { + var handle = globalList[i]; + + var state = handle.IsAllocated ? (InputActionState)handle.Target : null; + if (state == null) + { + // Stale entry in the list. State has already been reclaimed by GC. Remove it. + if (handle.IsAllocated) + globalList[i].Free(); + globalList.RemoveAtWithCapacity(i); + --i; + continue; + } + + for (var n = 0; n < state.totalMapCount; ++n) + state.maps[n].ResolveBindingsIfNecessary(); + } + } + finally { - if (InputActionMap.s_DeferBindingResolution > 0) - --InputActionMap.s_DeferBindingResolution; - if (InputActionMap.s_DeferBindingResolution == 0) - InputActionState.DeferredResolutionOfBindings(); + --m_DeferredCount; } } + + + private int m_DeferredCount; } } diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs index 2ae8abf0d6..04ade667eb 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs @@ -4486,36 +4486,6 @@ internal static void OnDeviceChange(InputDevice device, InputDeviceChange change } } - internal static void DeferredResolutionOfBindings() - { - ++InputActionMap.s_DeferBindingResolution; - try - { - for (var i = 0; i < s_GlobalState.globalList.length; ++i) - { - var handle = s_GlobalState.globalList[i]; - - var state = handle.IsAllocated ? (InputActionState)handle.Target : null; - if (state == null) - { - // Stale entry in the list. State has already been reclaimed by GC. Remove it. - if (handle.IsAllocated) - s_GlobalState.globalList[i].Free(); - s_GlobalState.globalList.RemoveAtWithCapacity(i); - --i; - continue; - } - - for (var n = 0; n < state.totalMapCount; ++n) - state.maps[n].ResolveBindingsIfNecessary(); - } - } - finally - { - --InputActionMap.s_DeferBindingResolution; - } - } - internal static void DisableAllActions() { for (var i = 0; i < s_GlobalState.globalList.length; ++i) diff --git a/Packages/com.unity.inputsystem/InputSystem/InputManager.cs b/Packages/com.unity.inputsystem/InputSystem/InputManager.cs index 62d4a0bb16..8391d32a89 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputManager.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputManager.cs @@ -65,6 +65,9 @@ public static InputManager CreateAndInitialize(IInputRuntime runtime, InputSetti { var newInst = new InputManager(); + // Not directly used by InputManager, but we need a single instance that's used in a variety of places without a static field + newInst.m_DeferBindingResolutionContext = new DeferBindingResolutionContext(); + // If settings object wasn't provided, create a temporary settings object for now if (settings == null) { @@ -2070,6 +2073,25 @@ internal void UninstallGlobals() } } + /// + /// Acquires a temporary "lock" to suspend immediate re-resolution of bindings. + /// + /// + /// When changing control setups, it may take multiple steps to get to the final setup but each individual + /// step may trigger bindings to be resolved again in order to update controls on actions (see ). + /// Using Acquire/Release semantics via the returned context object, binding resolution can be deferred until the entire operation + /// is complete and the final binding setup is in place. + /// + /// NOTE: Returned DeferBindingResolutionContext object is used globally for all ActionMaps. + /// + internal DeferBindingResolutionContext DeferBindingResolution() + { + m_DeferBindingResolutionContext.Acquire(); + return m_DeferBindingResolutionContext; + } + + internal bool areDeferredBindingsToResolve => m_DeferBindingResolutionContext.deferredCount > 0; + [Serializable] internal struct AvailableDevice { @@ -2159,6 +2181,8 @@ internal struct AvailableDevice internal IInputDiagnostics m_Diagnostics; #endif + private DeferBindingResolutionContext m_DeferBindingResolutionContext; + ////REVIEW: Make it so that device names *always* have a number appended? (i.e. Gamepad1, Gamepad2, etc. instead of Gamepad, Gamepad1, etc) private void MakeDeviceNameUnique(InputDevice device) From e042a1fdc0dc8b7620983b8b71abb767ef9eb982 Mon Sep 17 00:00:00 2001 From: Tim Keosababian <36088732+timkeo@users.noreply.github.com> Date: Wed, 10 Apr 2024 14:02:26 -0700 Subject: [PATCH 07/10] Refactor GlobalState usages to re-initialize statics (ISX-1840) - Static field (in each class) initialized via RuntimeInitializeOnLoadMethod() hook - This functionality is a bit clunky and should be refactored but that's out-of-scope for this work --- .../InputSystem/Actions/InputActionState.cs | 7 +++++++ .../InputSystem/Plugins/EnhancedTouch/Touch.cs | 14 ++++++++------ .../InputSystem/Plugins/Users/InputUser.cs | 7 +++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs index 04ade667eb..7330ffcf4b 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs @@ -4237,6 +4237,13 @@ internal struct GlobalState internal static GlobalState s_GlobalState; + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + private static void InitializeGlobalActionState() + { + ResetGlobals(); + s_GlobalState = default; + } + internal static ISavedState SaveAndResetState() { // Save current state diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/EnhancedTouch/Touch.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/EnhancedTouch/Touch.cs index 096f8c9659..52b88b85f8 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/EnhancedTouch/Touch.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/EnhancedTouch/Touch.cs @@ -593,12 +593,14 @@ internal struct GlobalState #endif } - private static GlobalState CreateGlobalState() - { // Convenient method since parameterized construction is default - return new GlobalState { historyLengthPerFinger = 64 }; - } + internal static GlobalState s_GlobalState; - internal static GlobalState s_GlobalState = CreateGlobalState(); + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + private static void InitializeGlobalTouchState() + { + // Touch GlobalState doesn't require Dispose operations + s_GlobalState = new GlobalState { historyLengthPerFinger = 64 }; + } internal static ISavedState SaveAndResetState() { @@ -609,7 +611,7 @@ internal static ISavedState SaveAndResetState() () => { /* currently nothing to dispose */ }); // Reset global state - s_GlobalState = CreateGlobalState(); + InitializeGlobalTouchState(); return savedState; } diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/Users/InputUser.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/Users/InputUser.cs index 362e475669..f11035a7c0 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/Users/InputUser.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/Users/InputUser.cs @@ -1873,6 +1873,13 @@ private struct GlobalState private static GlobalState s_GlobalState; + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + private static void InitializeGlobalUserState() + { + ResetGlobals(); + s_GlobalState = default; + } + internal static ISavedState SaveAndResetState() { // Save current state and provide an opaque interface to restore it From d1aba5cdcb8f160f5e31f53b0d3f452fca0c072b Mon Sep 17 00:00:00 2001 From: Tim Keosababian <36088732+timkeo@users.noreply.github.com> Date: Thu, 11 Apr 2024 14:51:11 -0700 Subject: [PATCH 08/10] Update Plugin classes to fix static usages (ISX-1840) - Add a flag to guard against re-initializing Plugins within PerformDefaultPluginInitialization() - Refactor SteamSupport to fix Init/Shutdown flows and improve state management - Refactor PlayerInput's static fields into a single GlobalState struct (matching other classes) - Move EnhancedTouch static fields into Touch's GlobalState struct - Minor refactoring of SteamSupport to improve init/cleanp-up flows (especially with tests) --- .../Tests/InputSystem/Plugins/SteamTests.cs | 8 +- .../InputSystem/InputSystem.cs | 16 ++ .../InputSystem/InputSystemTestHooks.cs | 3 + .../Plugins/DualShock/DualShockGamepadHID.cs | 4 +- .../EnhancedTouch/EnhancedTouchSupport.cs | 19 +- .../Plugins/EnhancedTouch/Touch.cs | 4 + .../Plugins/PlayerInput/PlayerInput.cs | 171 ++++++++++-------- .../Plugins/PlayerInput/PlayerInputManager.cs | 18 +- .../InputSystem/Plugins/Steam/SteamSupport.cs | 49 +++-- .../UI/InputSystemUIInputModuleEditor.cs | 1 + 10 files changed, 175 insertions(+), 118 deletions(-) diff --git a/Assets/Tests/InputSystem/Plugins/SteamTests.cs b/Assets/Tests/InputSystem/Plugins/SteamTests.cs index 09a1039bc1..2298dfc71a 100644 --- a/Assets/Tests/InputSystem/Plugins/SteamTests.cs +++ b/Assets/Tests/InputSystem/Plugins/SteamTests.cs @@ -36,13 +36,7 @@ public override void Setup() public override void TearDown() { base.TearDown(); - m_SteamAPI = null; - - SteamSupport.s_API = null; - SteamSupport.s_InputDevices = null; - SteamSupport.s_ConnectedControllers = null; - SteamSupport.s_InputDeviceCount = 0; - SteamSupport.s_HooksInstalled = false; + SteamSupport.Shutdown(); } [Test] diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs b/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs index 73adf69103..634a3516a1 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs @@ -3802,8 +3802,24 @@ private static void RunInitialUpdate() } #if !UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION + + #if UNITY_EDITOR + // Plug-ins must only be initialized once, since many of them use static fields. + // When Domain Reloads are disabled, we must guard against this method being called a second time. + private static bool s_PluginsInitialized = false; + #endif + internal static void PerformDefaultPluginInitialization() { + #if UNITY_EDITOR + if (s_PluginsInitialized) + { + Debug.Assert(false, "Attempted to re-initialize InputSystem Plugins!"); + return; + } + s_PluginsInitialized = true; + #endif + UISupport.Initialize(); #if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WSA || UNITY_ANDROID || UNITY_IOS || UNITY_TVOS || UNITY_VISIONOS diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSystemTestHooks.cs b/Packages/com.unity.inputsystem/InputSystem/InputSystemTestHooks.cs index 01e6355ab0..1ac0be8b62 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputSystemTestHooks.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputSystemTestHooks.cs @@ -28,6 +28,8 @@ internal static void TestHook_InitializeForPlayModeTests(bool enableRemoting, II InputSystem.SetUpRemoting(); #if !UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION + // Reset the flag so can re-initialize Plugins between tests. + InputSystem.s_PluginsInitialized = false; InputSystem.PerformDefaultPluginInitialization(); #endif } @@ -42,6 +44,7 @@ internal static void TestHook_SimulateDomainReload(IInputRuntime runtime) InputSystem.s_DomainStateManager.OnBeforeSerialize(); InputSystem.s_DomainStateManager = null; InputSystem.s_Manager = null; // Do NOT Dispose()! The native memory cannot be freed as it's reference by saved state + InputSystem.s_PluginsInitialized = false; InputSystem.InitializeInEditor(true, runtime); } #endif // UNITY_EDITOR diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/DualShock/DualShockGamepadHID.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/DualShock/DualShockGamepadHID.cs index 5d25b5ceb2..d1b289178d 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/DualShock/DualShockGamepadHID.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/DualShock/DualShockGamepadHID.cs @@ -675,8 +675,8 @@ public DualSenseHIDInputReport ToHIDInputReport() [StructLayout(LayoutKind.Explicit)] internal struct DualSenseHIDMinimalInputReport { - public static int ExpectedSize1 = 10; - public static int ExpectedSize2 = 78; + public const int ExpectedSize1 = 10; + public const int ExpectedSize2 = 78; [FieldOffset(0)] public byte reportId; [FieldOffset(1)] public byte leftStickX; diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/EnhancedTouch/EnhancedTouchSupport.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/EnhancedTouch/EnhancedTouchSupport.cs index 92437b307f..b3bdc45b9d 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/EnhancedTouch/EnhancedTouchSupport.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/EnhancedTouch/EnhancedTouchSupport.cs @@ -63,10 +63,7 @@ public static class EnhancedTouchSupport /// Whether enhanced touch support is currently enabled. /// /// True if EnhancedTouch support has been enabled. - public static bool enabled => s_Enabled > 0; - - private static int s_Enabled; - private static InputSettings.UpdateMode s_UpdateMode; + public static bool enabled => Touch.s_GlobalState.enhancedTouchEnabled > 0; /// /// Enable enhanced touch support. @@ -82,8 +79,8 @@ public static class EnhancedTouchSupport /// public static void Enable() { - ++s_Enabled; - if (s_Enabled > 1) + ++Touch.s_GlobalState.enhancedTouchEnabled; + if (Touch.s_GlobalState.enhancedTouchEnabled > 1) return; InputSystem.onDeviceChange += OnDeviceChange; @@ -107,8 +104,8 @@ public static void Disable() { if (!enabled) return; - --s_Enabled; - if (s_Enabled > 0) + --Touch.s_GlobalState.enhancedTouchEnabled; + if (Touch.s_GlobalState.enhancedTouchEnabled > 0) return; InputSystem.onDeviceChange -= OnDeviceChange; @@ -131,7 +128,7 @@ internal static void Reset() Touch.s_GlobalState.editorState.Destroy(); Touch.s_GlobalState.editorState = default; #endif - s_Enabled = 0; + Touch.s_GlobalState.enhancedTouchEnabled = 0; } private static void SetUpState() @@ -141,7 +138,7 @@ private static void SetUpState() Touch.s_GlobalState.editorState.updateMask = InputUpdateType.Editor; #endif - s_UpdateMode = InputSystem.settings.updateMode; + Touch.s_GlobalState.enhancedTouchUpdateMode = InputSystem.settings.updateMode; foreach (var device in InputSystem.devices) OnDeviceChange(device, InputDeviceChange.Added); @@ -186,7 +183,7 @@ private static void OnDeviceChange(InputDevice device, InputDeviceChange change) private static void OnSettingsChange() { var currentUpdateMode = InputSystem.settings.updateMode; - if (s_UpdateMode == currentUpdateMode) + if (Touch.s_GlobalState.enhancedTouchUpdateMode == currentUpdateMode) return; TearDownState(); SetUpState(); diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/EnhancedTouch/Touch.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/EnhancedTouch/Touch.cs index 52b88b85f8..169f3d34ed 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/EnhancedTouch/Touch.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/EnhancedTouch/Touch.cs @@ -587,6 +587,10 @@ internal struct GlobalState internal CallbackArray> onFingerMove; internal CallbackArray> onFingerUp; + // Used by EnhancedTouchSupport but placed here to consolidate static fields + internal int enhancedTouchEnabled; + internal InputSettings.UpdateMode enhancedTouchUpdateMode; + internal FingerAndTouchState playerState; #if UNITY_EDITOR internal FingerAndTouchState editorState; diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/PlayerInput.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/PlayerInput.cs index da59650ed3..8a511591bb 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/PlayerInput.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/PlayerInput.cs @@ -800,7 +800,7 @@ public ReadOnlyArray devices /// /// /// - public static ReadOnlyArray all => new ReadOnlyArray(s_AllActivePlayers, 0, s_AllActivePlayersCount); + public static ReadOnlyArray all => new ReadOnlyArray(s_GlobalState.allActivePlayers, 0, s_GlobalState.allActivePlayersCount); /// /// Whether PlayerInput operates in single-player mode. @@ -814,7 +814,7 @@ public ReadOnlyArray devices /// /// public static bool isSinglePlayer => - s_AllActivePlayersCount <= 1 && + s_GlobalState.allActivePlayersCount <= 1 && (PlayerInputManager.instance == null || !PlayerInputManager.instance.joiningEnabled); /// @@ -996,9 +996,9 @@ public void SwitchCurrentActionMap(string mapNameOrId) /// public static PlayerInput GetPlayerByIndex(int playerIndex) { - for (var i = 0; i < s_AllActivePlayersCount; ++i) - if (s_AllActivePlayers[i].playerIndex == playerIndex) - return s_AllActivePlayers[i]; + for (var i = 0; i < s_GlobalState.allActivePlayersCount; ++i) + if (s_GlobalState.allActivePlayers[i].playerIndex == playerIndex) + return s_GlobalState.allActivePlayers[i]; return null; } @@ -1022,10 +1022,10 @@ public static PlayerInput FindFirstPairedToDevice(InputDevice device) if (device == null) throw new ArgumentNullException(nameof(device)); - for (var i = 0; i < s_AllActivePlayersCount; ++i) + for (var i = 0; i < s_GlobalState.allActivePlayersCount; ++i) { - if (ReadOnlyArrayExtensions.ContainsReference(s_AllActivePlayers[i].devices, device)) - return s_AllActivePlayers[i]; + if (ReadOnlyArrayExtensions.ContainsReference(s_GlobalState.allActivePlayers[i].devices, device)) + return s_GlobalState.allActivePlayers[i]; } return null; @@ -1051,11 +1051,11 @@ public static PlayerInput Instantiate(GameObject prefab, int playerIndex = -1, s throw new ArgumentNullException(nameof(prefab)); // Set initialization data. - s_InitPlayerIndex = playerIndex; - s_InitSplitScreenIndex = splitScreenIndex; - s_InitControlScheme = controlScheme; + s_GlobalState.initPlayerIndex = playerIndex; + s_GlobalState.initSplitScreenIndex = splitScreenIndex; + s_GlobalState.initControlScheme = controlScheme; if (pairWithDevice != null) - ArrayHelpers.AppendWithCapacity(ref s_InitPairWithDevices, ref s_InitPairWithDevicesCount, pairWithDevice); + ArrayHelpers.AppendWithCapacity(ref s_GlobalState.initPairWithDevices, ref s_GlobalState.initPairWithDevicesCount, pairWithDevice); return DoInstantiate(prefab); } @@ -1083,13 +1083,13 @@ public static PlayerInput Instantiate(GameObject prefab, int playerIndex = -1, s throw new ArgumentNullException(nameof(prefab)); // Set initialization data. - s_InitPlayerIndex = playerIndex; - s_InitSplitScreenIndex = splitScreenIndex; - s_InitControlScheme = controlScheme; + s_GlobalState.initPlayerIndex = playerIndex; + s_GlobalState.initSplitScreenIndex = splitScreenIndex; + s_GlobalState.initControlScheme = controlScheme; if (pairWithDevices != null) { for (var i = 0; i < pairWithDevices.Length; ++i) - ArrayHelpers.AppendWithCapacity(ref s_InitPairWithDevices, ref s_InitPairWithDevicesCount, pairWithDevices[i]); + ArrayHelpers.AppendWithCapacity(ref s_GlobalState.initPairWithDevices, ref s_GlobalState.initPairWithDevicesCount, pairWithDevices[i]); } return DoInstantiate(prefab); @@ -1097,7 +1097,7 @@ public static PlayerInput Instantiate(GameObject prefab, int playerIndex = -1, s private static PlayerInput DoInstantiate(GameObject prefab) { - var destroyIfDeviceSetupUnsuccessful = s_DestroyIfDeviceSetupUnsuccessful; + var destroyIfDeviceSetupUnsuccessful = s_GlobalState.destroyIfDeviceSetupUnsuccessful; GameObject instance; try @@ -1108,13 +1108,13 @@ private static PlayerInput DoInstantiate(GameObject prefab) finally { // Reset init data. - s_InitPairWithDevicesCount = 0; - if (s_InitPairWithDevices != null) - Array.Clear(s_InitPairWithDevices, 0, s_InitPairWithDevicesCount); - s_InitControlScheme = null; - s_InitPlayerIndex = -1; - s_InitSplitScreenIndex = -1; - s_DestroyIfDeviceSetupUnsuccessful = false; + s_GlobalState.initPairWithDevicesCount = 0; + if (s_GlobalState.initPairWithDevices != null) + Array.Clear(s_GlobalState.initPairWithDevices, 0, s_GlobalState.initPairWithDevicesCount); + s_GlobalState.initControlScheme = null; + s_GlobalState.initPlayerIndex = -1; + s_GlobalState.initSplitScreenIndex = -1; + s_GlobalState.destroyIfDeviceSetupUnsuccessful = false; } var playerInput = instance.GetComponentInChildren(); @@ -1180,18 +1180,41 @@ private static PlayerInput DoInstantiate(GameObject prefab) [NonSerialized] private Action m_DeviceChangeDelegate; [NonSerialized] private bool m_OnDeviceChangeHooked; - internal static int s_AllActivePlayersCount; - internal static PlayerInput[] s_AllActivePlayers; - private static Action s_UserChangeDelegate; + /// + /// Holds global (static) Player data + /// + internal struct GlobalState + { + public int allActivePlayersCount; + public PlayerInput[] allActivePlayers; + public Action userChangeDelegate; + + // The following information is used when the next PlayerInput component is enabled. + + public int initPairWithDevicesCount; + public InputDevice[] initPairWithDevices; + public int initPlayerIndex; + public int initSplitScreenIndex; + public string initControlScheme; + public bool destroyIfDeviceSetupUnsuccessful; + } + private static GlobalState s_GlobalState; - // The following information is used when the next PlayerInput component is enabled. + // For sanity purposes, GlobalState is private with properties accessing specific fields + internal static int allActivePlayersCount => s_GlobalState.allActivePlayersCount; + internal static PlayerInput[] allActivePlayers => s_GlobalState.allActivePlayers; + internal static bool destroyIfDeviceSetupUnsuccessful { get; set; } - private static int s_InitPairWithDevicesCount; - private static InputDevice[] s_InitPairWithDevices; - private static int s_InitPlayerIndex = -1; - private static int s_InitSplitScreenIndex = -1; - private static string s_InitControlScheme; - internal static bool s_DestroyIfDeviceSetupUnsuccessful; + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + private static void InitializeGlobalPlayerState() + { + // Touch GlobalState doesn't require Dispose operations + s_GlobalState = new GlobalState + { + initPlayerIndex = -1, + initSplitScreenIndex = -1 + }; + } private void InitializeActions() { @@ -1202,8 +1225,8 @@ private void InitializeActions() // Check if we need to duplicate our actions by looking at all other players. If any // has the same actions, duplicate. - for (var i = 0; i < s_AllActivePlayersCount; ++i) - if (s_AllActivePlayers[i].m_Actions == m_Actions && s_AllActivePlayers[i] != this) + for (var i = 0; i < s_GlobalState.allActivePlayersCount; ++i) + if (s_GlobalState.allActivePlayers[i].m_Actions == m_Actions && s_GlobalState.allActivePlayers[i] != this) { var oldActions = m_Actions; m_Actions = Instantiate(m_Actions); @@ -1394,10 +1417,10 @@ private void AssignUserAndDevices() { // If we have devices we are meant to pair with, do so. Otherwise, don't // do anything as we don't know what kind of input to look for. - if (s_InitPairWithDevicesCount > 0) + if (s_GlobalState.initPairWithDevicesCount > 0) { - for (var i = 0; i < s_InitPairWithDevicesCount; ++i) - m_InputUser = InputUser.PerformPairingWithDevice(s_InitPairWithDevices[i], m_InputUser); + for (var i = 0; i < s_GlobalState.initPairWithDevicesCount; ++i) + m_InputUser = InputUser.PerformPairingWithDevice(s_GlobalState.initPairWithDevices[i], m_InputUser); } else { @@ -1411,15 +1434,15 @@ private void AssignUserAndDevices() // If we have control schemes, try to find the one we should use. if (m_Actions.controlSchemes.Count > 0) { - if (!string.IsNullOrEmpty(s_InitControlScheme)) + if (!string.IsNullOrEmpty(s_GlobalState.initControlScheme)) { // We've been given a control scheme to initialize this. Try that one and // that one only. Might mean we end up with missing devices. - var controlScheme = m_Actions.FindControlScheme(s_InitControlScheme); + var controlScheme = m_Actions.FindControlScheme(s_GlobalState.initControlScheme); if (controlScheme == null) { - Debug.LogError($"No control scheme '{s_InitControlScheme}' in '{m_Actions}'", this); + Debug.LogError($"No control scheme '{s_GlobalState.initControlScheme}' in '{m_Actions}'", this); } else { @@ -1443,13 +1466,13 @@ private void AssignUserAndDevices() // If we did not end up with a usable scheme by now but we've been given devices to pair with, // search for a control scheme matching the given devices. - if (s_InitPairWithDevicesCount > 0 && (!m_InputUser.valid || m_InputUser.controlScheme == null)) + if (s_GlobalState.initPairWithDevicesCount > 0 && (!m_InputUser.valid || m_InputUser.controlScheme == null)) { // The devices we've been given may not be all the devices required to satisfy a given control scheme so we // want to pick any one control scheme that is the best match for the devices we have regardless of whether // we'll need additional devices. TryToActivateControlScheme will take care of that. var controlScheme = InputControlScheme.FindControlSchemeForDevices( - new ReadOnlyArray(s_InitPairWithDevices, 0, s_InitPairWithDevicesCount), m_Actions.controlSchemes, + new ReadOnlyArray(s_GlobalState.initPairWithDevices, 0, s_GlobalState.initPairWithDevicesCount), m_Actions.controlSchemes, allowUnsuccesfulMatch: true); if (controlScheme != null) TryToActivateControlScheme(controlScheme.Value); @@ -1457,7 +1480,7 @@ private void AssignUserAndDevices() // If we don't have a working control scheme by now and we haven't been instructed to use // one specific control scheme, try each one in the asset one after the other until we // either find one we can use or run out of options. - else if ((!m_InputUser.valid || m_InputUser.controlScheme == null) && string.IsNullOrEmpty(s_InitControlScheme)) + else if ((!m_InputUser.valid || m_InputUser.controlScheme == null) && string.IsNullOrEmpty(s_GlobalState.initControlScheme)) { using (var availableDevices = InputUser.GetUnpairedInputDevices()) { @@ -1475,10 +1498,10 @@ private void AssignUserAndDevices() // device is present that matches the binding and that isn't used by any other player, we'll // pair to the player. - if (s_InitPairWithDevicesCount > 0) + if (s_GlobalState.initPairWithDevicesCount > 0) { - for (var i = 0; i < s_InitPairWithDevicesCount; ++i) - m_InputUser = InputUser.PerformPairingWithDevice(s_InitPairWithDevices[i], m_InputUser); + for (var i = 0; i < s_GlobalState.initPairWithDevicesCount; ++i) + m_InputUser = InputUser.PerformPairingWithDevice(s_GlobalState.initPairWithDevices[i], m_InputUser); } else { @@ -1531,7 +1554,7 @@ private bool TryToActivateControlScheme(InputControlScheme controlScheme) ////FIXME: this will fall apart if account management is involved and a user needs to log in on device first // Pair any devices we may have been given. - if (s_InitPairWithDevicesCount > 0) + if (s_GlobalState.initPairWithDevicesCount > 0) { ////REVIEW: should AndPairRemainingDevices() require that there is at least one existing //// device paired to the user that is usable with the given control scheme? @@ -1541,17 +1564,17 @@ private bool TryToActivateControlScheme(InputControlScheme controlScheme) // we have the player grab all the devices in s_InitPairWithDevices along with a control // scheme that fits none of them and then AndPairRemainingDevices() supplying the devices // actually needed by the control scheme. - for (var i = 0; i < s_InitPairWithDevicesCount; ++i) + for (var i = 0; i < s_GlobalState.initPairWithDevicesCount; ++i) { - var device = s_InitPairWithDevices[i]; + var device = s_GlobalState.initPairWithDevices[i]; if (!controlScheme.SupportsDevice(device)) return false; } // We're good. Give the devices to the user. - for (var i = 0; i < s_InitPairWithDevicesCount; ++i) + for (var i = 0; i < s_GlobalState.initPairWithDevicesCount; ++i) { - var device = s_InitPairWithDevices[i]; + var device = s_GlobalState.initPairWithDevices[i]; m_InputUser = InputUser.PerformPairingWithDevice(device, m_InputUser); } } @@ -1572,16 +1595,16 @@ private bool TryToActivateControlScheme(InputControlScheme controlScheme) private void AssignPlayerIndex() { - if (s_InitPlayerIndex != -1) - m_PlayerIndex = s_InitPlayerIndex; + if (s_GlobalState.initPlayerIndex != -1) + m_PlayerIndex = s_GlobalState.initPlayerIndex; else { var minPlayerIndex = int.MaxValue; var maxPlayerIndex = int.MinValue; - for (var i = 0; i < s_AllActivePlayersCount; ++i) + for (var i = 0; i < s_GlobalState.allActivePlayersCount; ++i) { - var playerIndex = s_AllActivePlayers[i].playerIndex; + var playerIndex = s_GlobalState.allActivePlayers[i].playerIndex; minPlayerIndex = Math.Min(minPlayerIndex, playerIndex); maxPlayerIndex = Math.Max(maxPlayerIndex, playerIndex); } @@ -1633,23 +1656,23 @@ private void OnEnable() } // Split-screen index defaults to player index. - if (s_InitSplitScreenIndex >= 0) + if (s_GlobalState.initSplitScreenIndex >= 0) m_SplitScreenIndex = splitScreenIndex; else m_SplitScreenIndex = playerIndex; // Add to global list and sort it by player index. - ArrayHelpers.AppendWithCapacity(ref s_AllActivePlayers, ref s_AllActivePlayersCount, this); - for (var i = 1; i < s_AllActivePlayersCount; ++i) - for (var j = i; j > 0 && s_AllActivePlayers[j - 1].playerIndex > s_AllActivePlayers[j].playerIndex; --j) - s_AllActivePlayers.SwapElements(j, j - 1); + ArrayHelpers.AppendWithCapacity(ref s_GlobalState.allActivePlayers, ref s_GlobalState.allActivePlayersCount, this); + for (var i = 1; i < s_GlobalState.allActivePlayersCount; ++i) + for (var j = i; j > 0 && s_GlobalState.allActivePlayers[j - 1].playerIndex > s_GlobalState.allActivePlayers[j].playerIndex; --j) + s_GlobalState.allActivePlayers.SwapElements(j, j - 1); // If it's the first player, hook into user change notifications. - if (s_AllActivePlayersCount == 1) + if (s_GlobalState.allActivePlayersCount == 1) { - if (s_UserChangeDelegate == null) - s_UserChangeDelegate = OnUserChange; - InputUser.onChange += s_UserChangeDelegate; + if (s_GlobalState.userChangeDelegate == null) + s_GlobalState.userChangeDelegate = OnUserChange; + InputUser.onChange += s_GlobalState.userChangeDelegate; } // In single player, set up for automatic device switching. @@ -1722,13 +1745,13 @@ private void OnDisable() m_Enabled = false; // Remove from global list. - var index = ArrayHelpers.IndexOfReference(s_AllActivePlayers, this, s_AllActivePlayersCount); + var index = ArrayHelpers.IndexOfReference(s_GlobalState.allActivePlayers, this, s_GlobalState.allActivePlayersCount); if (index != -1) - ArrayHelpers.EraseAtWithCapacity(s_AllActivePlayers, ref s_AllActivePlayersCount, index); + ArrayHelpers.EraseAtWithCapacity(s_GlobalState.allActivePlayers, ref s_GlobalState.allActivePlayersCount, index); // Unhook from change notifications if we're the last player. - if (s_AllActivePlayersCount == 0 && s_UserChangeDelegate != null) - InputUser.onChange -= s_UserChangeDelegate; + if (s_GlobalState.allActivePlayersCount == 0 && s_GlobalState.userChangeDelegate != null) + InputUser.onChange -= s_GlobalState.userChangeDelegate; StopListeningForUnpairedDeviceActivity(); StopListeningForDeviceChanges(); @@ -1830,9 +1853,9 @@ private static void OnUserChange(InputUser user, InputUserChange change, InputDe { case InputUserChange.DeviceLost: case InputUserChange.DeviceRegained: - for (var i = 0; i < s_AllActivePlayersCount; ++i) + for (var i = 0; i < s_GlobalState.allActivePlayersCount; ++i) { - var player = s_AllActivePlayers[i]; + var player = s_GlobalState.allActivePlayers[i]; if (player.m_InputUser == user) { if (change == InputUserChange.DeviceLost) @@ -1844,9 +1867,9 @@ private static void OnUserChange(InputUser user, InputUserChange change, InputDe break; case InputUserChange.ControlsChanged: - for (var i = 0; i < s_AllActivePlayersCount; ++i) + for (var i = 0; i < s_GlobalState.allActivePlayersCount; ++i) { - var player = s_AllActivePlayers[i]; + var player = s_GlobalState.allActivePlayers[i]; if (player.m_InputUser == user) player.HandleControlsChanged(); } diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/PlayerInputManager.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/PlayerInputManager.cs index 2c2c557766..196c1f1de9 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/PlayerInputManager.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/PlayerInputManager.cs @@ -129,7 +129,7 @@ public bool splitScreen /// /// This count corresponds to all instances that are currently enabled. /// - public int playerCount => PlayerInput.s_AllActivePlayersCount; + public int playerCount => PlayerInput.allActivePlayersCount; ////FIXME: this needs to be settable /// @@ -432,7 +432,7 @@ public PlayerInput JoinPlayer(int playerIndex = -1, int splitScreenIndex = -1, s if (!CheckIfPlayerCanJoin(playerIndex)) return null; - PlayerInput.s_DestroyIfDeviceSetupUnsuccessful = true; + PlayerInput.destroyIfDeviceSetupUnsuccessful = true; return PlayerInput.Instantiate(m_PlayerPrefab, playerIndex: playerIndex, splitScreenIndex: splitScreenIndex, controlScheme: controlScheme, pairWithDevice: pairWithDevice); } @@ -458,7 +458,7 @@ public PlayerInput JoinPlayer(int playerIndex = -1, int splitScreenIndex = -1, s if (!CheckIfPlayerCanJoin(playerIndex)) return null; - PlayerInput.s_DestroyIfDeviceSetupUnsuccessful = true; + PlayerInput.destroyIfDeviceSetupUnsuccessful = true; return PlayerInput.Instantiate(m_PlayerPrefab, playerIndex: playerIndex, splitScreenIndex: splitScreenIndex, controlScheme: controlScheme, pairWithDevices: pairWithDevices); } @@ -508,12 +508,12 @@ private bool CheckIfPlayerCanJoin(int playerIndex = -1) // If we have a player index, make sure it's unique. if (playerIndex != -1) { - for (var i = 0; i < PlayerInput.s_AllActivePlayersCount; ++i) - if (PlayerInput.s_AllActivePlayers[i].playerIndex == playerIndex) + for (var i = 0; i < PlayerInput.allActivePlayersCount; ++i) + if (PlayerInput.allActivePlayers[i].playerIndex == playerIndex) { Debug.LogError( - $"Player index #{playerIndex} is already taken by player {PlayerInput.s_AllActivePlayers[i]}", - PlayerInput.s_AllActivePlayers[i]); + $"Player index #{playerIndex} is already taken by player {PlayerInput.allActivePlayers[i]}", + PlayerInput.allActivePlayers[i]); return false; } } @@ -565,8 +565,8 @@ private void OnEnable() } // Join all players already in the game. - for (var i = 0; i < PlayerInput.s_AllActivePlayersCount; ++i) - NotifyPlayerJoined(PlayerInput.s_AllActivePlayers[i]); + for (var i = 0; i < PlayerInput.allActivePlayersCount; ++i) + NotifyPlayerJoined(PlayerInput.allActivePlayers[i]); if (m_AllowJoining) EnableJoining(); diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/Steam/SteamSupport.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/Steam/SteamSupport.cs index 357b65676e..108e0fa988 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/Steam/SteamSupport.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/Steam/SteamSupport.cs @@ -27,7 +27,7 @@ public static ISteamControllerAPI api set { s_API = value; - InstallHooks(s_API != null); + InstallControllerUpdateHooks(s_API != null); } } @@ -38,11 +38,19 @@ internal static ISteamControllerAPI GetAPIAndRequireItToBeSet() return s_API; } - internal static SteamHandle[] s_ConnectedControllers; - internal static SteamController[] s_InputDevices; - internal static int s_InputDeviceCount; - internal static bool s_HooksInstalled; - internal static ISteamControllerAPI s_API; + /// + /// Returns if the controller Update event handlers have been set or not. + /// + /// + /// The s_ConnectedControllers array is allocated in response to setting event handlers and + /// so it can double as our "is installed" flag. + /// + private static bool updateHooksInstalled => s_ConnectedControllers != null; + + private static SteamHandle[] s_ConnectedControllers; + private static SteamController[] s_InputDevices; + private static int s_InputDeviceCount; + private static ISteamControllerAPI s_API; private const int STEAM_CONTROLLER_MAX_COUNT = 16; @@ -54,22 +62,35 @@ public static void Initialize() // We use this as a base layout. InputSystem.RegisterLayout(); - if (api != null) - InstallHooks(true); + InstallControllerUpdateHooks(s_API != null); } - private static void InstallHooks(bool state) + /// + /// Disable Steam controller API support and reset the state. + /// + internal static void Shutdown() { - Debug.Assert(api != null); - if (state && !s_HooksInstalled) + InstallControllerUpdateHooks(false); + + s_API = null; + s_InputDevices = null; + s_InputDeviceCount = 0; + } + + private static void InstallControllerUpdateHooks(bool state) + { + Debug.Assert(api != null || !state); + if (state && !updateHooksInstalled) { + s_ConnectedControllers = new SteamHandle[STEAM_CONTROLLER_MAX_COUNT]; InputSystem.onBeforeUpdate += OnUpdate; InputSystem.onActionChange += OnActionChange; } - else if (!state && s_HooksInstalled) + else if (!state && updateHooksInstalled) { InputSystem.onBeforeUpdate -= OnUpdate; InputSystem.onActionChange -= OnActionChange; + s_ConnectedControllers = null; } } @@ -124,9 +145,7 @@ private static void OnUpdate() // Update controller state. api.RunFrame(); - // Check if we have any new controllers have appeared. - if (s_ConnectedControllers == null) - s_ConnectedControllers = new SteamHandle[STEAM_CONTROLLER_MAX_COUNT]; + // Check if we have any new controllers have appeared. var numConnectedControllers = api.GetConnectedControllers(s_ConnectedControllers); for (var i = 0; i < numConnectedControllers; ++i) { diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/InputSystemUIInputModuleEditor.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/InputSystemUIInputModuleEditor.cs index 86caf47eb7..22cdfe3368 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/InputSystemUIInputModuleEditor.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/InputSystemUIInputModuleEditor.cs @@ -13,6 +13,7 @@ namespace UnityEngine.InputSystem.UI.Editor [InitializeOnLoad] internal class InputSystemUIInputModuleEditor : UnityEditor.Editor { + // ISX-1966 - It's unclear if this initializer will work correctly with CoreCLR and needs to be investigated. static InputSystemUIInputModuleEditor() { #if UNITY_6000_0_OR_NEWER && ENABLE_INPUT_SYSTEM From bc2d23e08fda97ec6ae136bbbcb8519a1d2b25a4 Mon Sep 17 00:00:00 2001 From: Tim Keosababian <36088732+timkeo@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:36:51 -0700 Subject: [PATCH 09/10] Misc refactoring to clean up static fields and other small improvements (ISX-1840) - Consolidate Touchscreen's cached settings into a separate struct - Rework NativeInputRuntime initialization to (fully) employ Singleton pattern - Refactor Actions_CanHandleModification TestCase generator to work without Domain Reloads - Fix Device static fields not getting reset during SimulateDomainReload() --- Assets/Tests/InputSystem/CoreTests_Actions.cs | 86 +++++++++++-------- Assets/Tests/InputSystem/CoreTests_Editor.cs | 2 + .../Controls/InputControlLayout.cs | 2 +- .../InputSystem/Devices/Touchscreen.cs | 30 +++++-- .../InputSystem/InputManager.cs | 38 ++++++-- .../InputSystem/InputSystemTestHooks.cs | 5 ++ .../InputSystem/NativeInputRuntime.cs | 21 ++++- .../Plugins/EnhancedTouch/TouchSimulation.cs | 4 +- .../Plugins/UI/InputSystemUIInputModule.cs | 3 +- 9 files changed, 137 insertions(+), 54 deletions(-) diff --git a/Assets/Tests/InputSystem/CoreTests_Actions.cs b/Assets/Tests/InputSystem/CoreTests_Actions.cs index 02e2b33c13..971297a1ae 100644 --- a/Assets/Tests/InputSystem/CoreTests_Actions.cs +++ b/Assets/Tests/InputSystem/CoreTests_Actions.cs @@ -5263,56 +5263,68 @@ public class ModificationCases : IEnumerable [Preserve] public ModificationCases() {} + private static readonly Modification[] ModificationAppliesToSingleActionMap = + { + Modification.AddBinding, + Modification.RemoveBinding, + Modification.ModifyBinding, + Modification.ApplyBindingOverride, + Modification.AddAction, + Modification.RemoveAction, + Modification.ChangeBindingMask, + Modification.AddDevice, + Modification.RemoveDevice, + Modification.AddDeviceGlobally, + Modification.RemoveDeviceGlobally, + // Excludes: AddMap, RemoveMap + }; + + private static readonly Modification[] ModificationAppliesToSingletonAction = + { + Modification.AddBinding, + Modification.RemoveBinding, + Modification.ModifyBinding, + Modification.ApplyBindingOverride, + Modification.AddDeviceGlobally, + Modification.RemoveDeviceGlobally, + }; + public IEnumerator GetEnumerator() { - bool ModificationAppliesToSingletonAction(Modification modification) + // NOTE: This executes *outside* of our test fixture during test discovery. + + // We cannot directly create the InputAction objects within GetEnumerator() because the underlying + // asset object might be invalid by the time the tests are actually run. + // + // That is, NUnit TestCases are generated once when the Assembly is loaded and will persist until it's unloaded, + // meaning they'll never be recreated without a Domain Reload. However, since InputActionAsset is a ScriptableObject, + // it could be deleted or otherwise invalidated between test case creation and actual test execution. + // + // So, instead we'll create a delegate to create the Actions object as the parameter for each test case, allowing + // the test case to create an Actions object itself when it actually runs. { - switch (modification) + var actionsFromAsset = new Func(() => new DefaultInputActions().asset); + foreach (var value in Enum.GetValues(typeof(Modification))) { - case Modification.AddBinding: - case Modification.RemoveBinding: - case Modification.ModifyBinding: - case Modification.ApplyBindingOverride: - case Modification.AddDeviceGlobally: - case Modification.RemoveDeviceGlobally: - return true; + yield return new TestCaseData(value, actionsFromAsset); } - return false; } - bool ModificationAppliesToSingleActionMap(Modification modification) { - switch (modification) + var actionMap = new Func(CreateMap); + foreach (var value in Enum.GetValues(typeof(Modification))) { - case Modification.AddMap: - case Modification.RemoveMap: - return false; + if (ModificationAppliesToSingleActionMap.Contains((Modification)value)) + yield return new TestCaseData(value, actionMap); } - return true; } - // NOTE: This executes *outside* of our test fixture during test discovery. - - // Creates a matrix of all permutations of Modifications combined with assets, maps, and singleton actions. - foreach (var func in new Func[] { () => new DefaultInputActions().asset, CreateMap, CreateSingletonAction }) { + var singletonMap = new Func(CreateSingletonAction); foreach (var value in Enum.GetValues(typeof(Modification))) { - var actions = func(); - if (actions is InputActionMap map) - { - if (map.m_SingletonAction != null) - { - if (!ModificationAppliesToSingletonAction((Modification)value)) - continue; - } - else if (!ModificationAppliesToSingleActionMap((Modification)value)) - { - continue; - } - } - - yield return new TestCaseData(value, actions); + if (ModificationAppliesToSingletonAction.Contains((Modification)value)) + yield return new TestCaseData(value, singletonMap); } } } @@ -5343,14 +5355,14 @@ private InputActionMap CreateSingletonAction() [Test] [Category("Actions")] [TestCaseSource(typeof(ModificationCases))] - public void Actions_CanHandleModification(Modification modification, IInputActionCollection2 actions) + public void Actions_CanHandleModification(Modification modification, Func getActions) { #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS // Exclude project-wide actions from this test InputSystem.actions?.Disable(); InputActionState.DestroyAllActionMapStates(); // Required for `onActionChange` to report correct number of changes #endif - + var actions = getActions(); var gamepad = InputSystem.AddDevice(); if (modification == Modification.AddDevice || modification == Modification.RemoveDevice) diff --git a/Assets/Tests/InputSystem/CoreTests_Editor.cs b/Assets/Tests/InputSystem/CoreTests_Editor.cs index 88ba90641c..c08b9cb650 100644 --- a/Assets/Tests/InputSystem/CoreTests_Editor.cs +++ b/Assets/Tests/InputSystem/CoreTests_Editor.cs @@ -165,6 +165,7 @@ public void Editor_CanSaveAndRestoreState() Assert.That(unsupportedDevices[0].interfaceName, Is.EqualTo("Test")); } +#if !ENABLE_CORECLR // onFindLayoutForDevice allows dynamically injecting new layouts into the system that // are custom-tailored at runtime for the discovered device. Make sure that our domain // reload can restore these. @@ -345,6 +346,7 @@ public void Editor_DomainReload_CanRemoveDevicesDuringDomainReload() Assert.That(InputSystem.devices, Has.Count.EqualTo(1)); Assert.That(InputSystem.devices[0], Is.AssignableTo()); } +#endif // !ENABLE_CORECLR [Test] [Category("Editor")] diff --git a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlLayout.cs b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlLayout.cs index 501378afdc..ad8923044f 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlLayout.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Controls/InputControlLayout.cs @@ -109,7 +109,7 @@ public delegate string InputDeviceFindControlLayoutDelegate(ref InputDeviceDescr /// public class InputControlLayout { - private static InternedString s_DefaultVariant = new InternedString("Default"); + private static readonly InternedString s_DefaultVariant = new InternedString("Default"); public static InternedString DefaultVariant => s_DefaultVariant; public const string VariantSeparator = ";"; diff --git a/Packages/com.unity.inputsystem/InputSystem/Devices/Touchscreen.cs b/Packages/com.unity.inputsystem/InputSystem/Devices/Touchscreen.cs index c119cb05c6..748198f11b 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Devices/Touchscreen.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Devices/Touchscreen.cs @@ -534,6 +534,14 @@ protected TouchControl[] touchControlArray /// Current touch screen. public new static Touchscreen current { get; internal set; } + /// + /// The current global settings for Touchscreen devices. + /// + /// + /// These are cached values taken from . + /// + internal static TouchscreenSettings settings { get; set; } + /// public override void MakeCurrent() { @@ -624,14 +632,14 @@ protected override void FinishSetup() // that to do so we would have to add another record to keep track of timestamps for each touch. And // since we know the maximum time that a tap can take, we have a reasonable estimate for when a prior // tap must have ended. - if (touchStatePtr->tapCount > 0 && InputState.currentTime >= touchStatePtr->startTime + s_TapTime + s_TapDelayTime) + if (touchStatePtr->tapCount > 0 && InputState.currentTime >= touchStatePtr->startTime + settings.tapTime + settings.tapDelayTime) InputState.Change(touches[i].tapCount, (byte)0); } var primaryTouchState = (TouchState*)((byte*)statePtr + stateBlock.byteOffset); if (primaryTouchState->delta != default) InputState.Change(primaryTouch.delta, Vector2.zero); - if (primaryTouchState->tapCount > 0 && InputState.currentTime >= primaryTouchState->startTime + s_TapTime + s_TapDelayTime) + if (primaryTouchState->tapCount > 0 && InputState.currentTime >= primaryTouchState->startTime + settings.tapTime + settings.tapDelayTime) InputState.Change(primaryTouch.tapCount, (byte)0); Profiler.EndSample(); @@ -720,11 +728,11 @@ protected override void FinishSetup() // Detect taps. var isTap = newTouchState.isNoneEndedOrCanceled && - (eventPtr.time - newTouchState.startTime) <= s_TapTime && + (eventPtr.time - newTouchState.startTime) <= settings.tapTime && ////REVIEW: this only takes the final delta to start position into account, not the delta over the lifetime of the //// touch; is this robust enough or do we need to make sure that we never move more than the tap radius //// over the entire lifetime of the touch? - (newTouchState.position - newTouchState.startPosition).sqrMagnitude <= s_TapRadiusSquared; + (newTouchState.position - newTouchState.startPosition).sqrMagnitude <= settings.tapRadiusSquared; if (isTap) newTouchState.tapCount = (byte)(currentTouchState[i].tapCount + 1); else @@ -1044,8 +1052,16 @@ private static void TriggerTap(TouchControl control, ref TouchState state, Input state.isTapRelease = false; } - internal static float s_TapTime; - internal static float s_TapDelayTime; - internal static float s_TapRadiusSquared; + private static TouchscreenSettings s_Settings; + } + + /// + /// Cached settings retrieved from . + /// + internal struct TouchscreenSettings + { + public float tapTime; + public float tapDelayTime; + public float tapRadiusSquared; } } diff --git a/Packages/com.unity.inputsystem/InputSystem/InputManager.cs b/Packages/com.unity.inputsystem/InputSystem/InputManager.cs index 8391d32a89..e8268f9502 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputManager.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputManager.cs @@ -2520,7 +2520,7 @@ private void InstallBeforeUpdateHookIfNecessary() private void RestoreDevicesAfterDomainReloadIfNecessary() { - #if UNITY_EDITOR + #if UNITY_EDITOR && !ENABLE_CORECLR if (m_SavedDeviceStates != null) RestoreDevicesAfterDomainReload(); #endif @@ -2714,10 +2714,14 @@ internal void ApplySettings() } } - // Cache some values. - Touchscreen.s_TapTime = settings.defaultTapTime; - Touchscreen.s_TapDelayTime = settings.multiTapDelayTime; - Touchscreen.s_TapRadiusSquared = settings.tapRadius * settings.tapRadius; + // Cache Touch specific settings to Touchscreen + Touchscreen.settings = new TouchscreenSettings + { + tapTime = settings.defaultTapTime, + tapDelayTime = settings.multiTapDelayTime, + tapRadiusSquared = settings.tapRadius * settings.tapRadius + }; + // Extra clamp here as we can't tell what we're getting from serialized data. ButtonControl.s_GlobalDefaultButtonPressPoint = Mathf.Clamp(settings.defaultButtonPressPoint, ButtonControl.kMinButtonPressPoint, float.MaxValue); ButtonControl.s_GlobalDefaultButtonReleaseThreshold = settings.buttonReleaseThreshold; @@ -4009,6 +4013,7 @@ internal void RestoreStateWithoutDevices(SerializedState state) internal DeviceState[] m_SavedDeviceStates; internal AvailableDevice[] m_SavedAvailableDevices; +#if !ENABLE_CORECLR /// /// Recreate devices based on the devices we had before a domain reload. /// @@ -4092,6 +4097,29 @@ internal void RestoreDevicesAfterDomainReload() Profiler.EndSample(); } + /// + /// Notifies all devices of removal to better cleanup data when using SimulateDomainReload test hook + /// + /// + /// Devices maintain their own list of Devices within static fields, updated via NotifyAdded and NotifyRemoved overrides. + /// These fields are reset during a real DR, but not so when we "simulate" causing them to report incorrect values when + /// queried via direct APIs, e.g. Gamepad.all. So, to mitigate this we'll call NotifyRemove during this scenario. + /// + internal void TestHook_RemoveDevicesForSimulatedDomainReload() + { + if (m_Devices == null) + return; + + foreach (var device in m_Devices) + { + if (device == null) + break; + + device.NotifyRemoved(); + } + } +#endif // !ENABLE_CORECLR + // We have two general types of devices we need to care about when recreating devices // after domain reloads: // diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSystemTestHooks.cs b/Packages/com.unity.inputsystem/InputSystem/InputSystemTestHooks.cs index 1ac0be8b62..3f2ed56f46 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputSystemTestHooks.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputSystemTestHooks.cs @@ -34,6 +34,7 @@ internal static void TestHook_InitializeForPlayModeTests(bool enableRemoting, II #endif } +#if !ENABLE_CORECLR internal static void TestHook_SimulateDomainReload(IInputRuntime runtime) { // This quite invasive goes into InputSystem internals. Unfortunately, we @@ -41,12 +42,16 @@ internal static void TestHook_SimulateDomainReload(IInputRuntime runtime) // internal methods here in a sequence similar to what we'd get during a domain reload. // Since we're faking it, pass 'true' for calledFromCtor param. + // Need to notify devices of removal so their static fields are cleaned up + InputSystem.s_Manager.TestHook_RemoveDevicesForSimulatedDomainReload(); + InputSystem.s_DomainStateManager.OnBeforeSerialize(); InputSystem.s_DomainStateManager = null; InputSystem.s_Manager = null; // Do NOT Dispose()! The native memory cannot be freed as it's reference by saved state InputSystem.s_PluginsInitialized = false; InputSystem.InitializeInEditor(true, runtime); } +#endif #endif // UNITY_EDITOR /// diff --git a/Packages/com.unity.inputsystem/InputSystem/NativeInputRuntime.cs b/Packages/com.unity.inputsystem/InputSystem/NativeInputRuntime.cs index 6bba685046..217df9bf88 100644 --- a/Packages/com.unity.inputsystem/InputSystem/NativeInputRuntime.cs +++ b/Packages/com.unity.inputsystem/InputSystem/NativeInputRuntime.cs @@ -20,7 +20,26 @@ namespace UnityEngine.InputSystem.LowLevel /// internal class NativeInputRuntime : IInputRuntime { - public static readonly NativeInputRuntime instance = new NativeInputRuntime(); + private static NativeInputRuntime s_Instance; + + // Private ctor exists to enforce Singleton pattern + private NativeInputRuntime() { } + + /// + /// Employ the Singleton pattern for this class and initialize a new instance on first use. + /// + /// + /// This property is typically used to initialize InputManager and isn't used afterwards, i.e. there's + /// no perf impact to the null check. + /// + public static NativeInputRuntime instance + { + get + { + s_Instance ??= new NativeInputRuntime(); + return s_Instance; + } + } public int AllocateDeviceId() { diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/EnhancedTouch/TouchSimulation.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/EnhancedTouch/TouchSimulation.cs index f8de763e49..d25132fc43 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/EnhancedTouch/TouchSimulation.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/EnhancedTouch/TouchSimulation.cs @@ -325,8 +325,8 @@ private unsafe void UpdateTouch(int touchIndex, int pointerIndex, TouchPhase pha if (phase == TouchPhase.Ended) { - touch.isTap = time - oldTouchState->startTime <= Touchscreen.s_TapTime && - (position - oldTouchState->startPosition).sqrMagnitude <= Touchscreen.s_TapRadiusSquared; + touch.isTap = time - oldTouchState->startTime <= Touchscreen.settings.tapTime && + (position - oldTouchState->startPosition).sqrMagnitude <= Touchscreen.settings.tapRadiusSquared; if (touch.isTap) ++touch.tapCount; } diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/InputSystemUIInputModule.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/InputSystemUIInputModule.cs index 66c4f95bb1..c19026e397 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/InputSystemUIInputModule.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/InputSystemUIInputModule.cs @@ -1378,7 +1378,8 @@ public InputActionReference trackedDevicePosition public void AssignDefaultActions() { - if (defaultActions == null) + // Without Domain Reloads, the InputActionAsset could be "null" even if defaultActions is valid + if (defaultActions == null || defaultActions.asset == null) { defaultActions = new DefaultInputActions(); } From bef951567961d3cfaaee8ca3497938d0b568e6a6 Mon Sep 17 00:00:00 2001 From: Tim Keosababian <36088732+timkeo@users.noreply.github.com> Date: Thu, 23 May 2024 14:00:29 -0700 Subject: [PATCH 10/10] Address InputSystem refactor PR feedback - Rename some variables - Add update some comments - Other small tweaks. --- .../Actions/InputActionRebindingExtensions.cs | 9 +++--- .../InputSystem/InputManager.cs | 29 +++++++++--------- .../InputSystem/InputSystem.cs | 21 ++++++------- .../InputSystem/InputSystemStateManager.cs | 30 ++++++++++++++++++- .../Plugins/PlayerInput/PlayerInput.cs | 2 +- 5 files changed, 58 insertions(+), 33 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionRebindingExtensions.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionRebindingExtensions.cs index 6a9af20747..62a02b5cbe 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionRebindingExtensions.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionRebindingExtensions.cs @@ -2782,7 +2782,7 @@ internal static DeferBindingResolutionContext DeferBindingResolution() } } - internal class DeferBindingResolutionContext : IDisposable + internal sealed class DeferBindingResolutionContext : IDisposable { public int deferredCount => m_DeferredCount; @@ -2793,14 +2793,13 @@ public void Acquire() public void Release() { - if (m_DeferredCount > 0) - --m_DeferredCount; - if (m_DeferredCount == 0) + if (m_DeferredCount > 0 && --m_DeferredCount == 0) ExecuteDeferredResolutionOfBindings(); } /// - /// Allows usage within using() blocks. + /// Allows usage within using() blocks, i.e. we need a "Release" method to match "Acquire", but we also want + /// to implement IDisposable so instance are automatically cleaned up when exiting a using() block. /// public void Dispose() { diff --git a/Packages/com.unity.inputsystem/InputSystem/InputManager.cs b/Packages/com.unity.inputsystem/InputSystem/InputManager.cs index e8268f9502..5ec57297ff 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputManager.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputManager.cs @@ -61,12 +61,12 @@ internal partial class InputManager : IDisposable { private InputManager() { } - public static InputManager CreateAndInitialize(IInputRuntime runtime, InputSettings settings, bool fakeRemove = false) + public static InputManager CreateAndInitialize(IInputRuntime runtime, InputSettings settings, bool fakeManagerForRemotingTests = false) { - var newInst = new InputManager(); + var newInstance = new InputManager(); // Not directly used by InputManager, but we need a single instance that's used in a variety of places without a static field - newInst.m_DeferBindingResolutionContext = new DeferBindingResolutionContext(); + newInstance.m_DeferBindingResolutionContext = new DeferBindingResolutionContext(); // If settings object wasn't provided, create a temporary settings object for now if (settings == null) @@ -74,25 +74,26 @@ public static InputManager CreateAndInitialize(IInputRuntime runtime, InputSetti settings = ScriptableObject.CreateInstance(); settings.hideFlags = HideFlags.HideAndDontSave; } - newInst.m_Settings = settings; + newInstance.m_Settings = settings; #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS - newInst.InitializeActions(); + newInstance.InitializeActions(); #endif // UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS - newInst.InitializeData(); - newInst.InstallRuntime(runtime); + newInstance.InitializeData(); + newInstance.InstallRuntime(runtime); - // Skip if initializing for "Fake Remove" manager (in tests) - if (!fakeRemove) - newInst.InstallGlobals(); + // For remoting tests, we need to create a "fake manager" that simulates a remote endpoint. + // In this case don't install globals as this will corrupt the "local" manager state. + if (!fakeManagerForRemotingTests) + newInstance.InstallGlobals(); - newInst.ApplySettings(); + newInstance.ApplySettings(); #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS - newInst.ApplyActions(); + newInstance.ApplyActions(); #endif - return newInst; + return newInstance; } #region Dispose implementation @@ -3985,7 +3986,7 @@ internal void RestoreStateWithoutDevices(SerializedState state) if (state.settings == null) { state.settings = ScriptableObject.CreateInstance(); - state.settings.hideFlags = HideFlags.HideAndDontSave; + state.settings.hideFlags = HideFlags.HideAndDontSave; // Hide from the project Hierarchy and Scene } if (m_Settings != null && m_Settings != state.settings) diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs b/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs index 634a3516a1..f87b4ce54a 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs @@ -89,7 +89,7 @@ static InputSystem() [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] private static void RuntimeInitialize() - { + { GlobalInitialize(false); } @@ -3483,12 +3483,9 @@ private static void SetUpRemotingInternal() private static bool ShouldEnableRemoting() { #if UNITY_INCLUDE_TESTS - var isRunningTests = true; - #else - var isRunningTests = false; + return false; // Don't remote while running tests. #endif - if (isRunningTests) - return false; // Don't remote while running tests. + return true; } #endif //!UNITY_EDITOR @@ -3517,7 +3514,7 @@ private static void GlobalInitialize(bool calledFromCtor) // must initialize via the Runtime call. if (calledFromCtor || IsDomainReloadDisabledForPlayMode()) - { + { InitializeInEditor(calledFromCtor); } #else @@ -3544,7 +3541,7 @@ internal static void EnsureInitialized() #if UNITY_EDITOR - // TODO: ISX-1860 + // ISX-1860 - #ifdef out Domain Reload specific functionality from CoreCLR private static InputSystemStateManager s_DomainStateManager; internal static InputSystemStateManager domainStateManager => s_DomainStateManager; @@ -3573,8 +3570,8 @@ internal static void InitializeInEditor(bool calledFromCtor, IInputRuntime runti #endif } - var existingSystemObjects = Resources.FindObjectsOfTypeAll(); - if (existingSystemObjects != null && existingSystemObjects.Length > 0) + var existingSystemStateManagers = Resources.FindObjectsOfTypeAll(); + if (existingSystemStateManagers != null && existingSystemStateManagers.Length > 0) { if (globalReset) { @@ -3584,7 +3581,7 @@ internal static void InitializeInEditor(bool calledFromCtor, IInputRuntime runti // InputManager state here but we're still waiting from layout registrations // that happen during domain initialization. - s_DomainStateManager = existingSystemObjects[0]; + s_DomainStateManager = existingSystemStateManagers[0]; s_Manager.RestoreStateWithoutDevices(s_DomainStateManager.systemState.managerState); InputDebuggerWindow.ReviveAfterDomainReload(); @@ -3765,7 +3762,7 @@ internal static void InitializeInPlayer(IInputRuntime runtime, bool loadSettings #if UNITY_INCLUDE_TESTS // - // We cannot define UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONSw with the Test-Framework assembly, and + // We cannot define UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS within the Test-Framework assembly, and // so this hook is needed; it's called from InputTestStateManager.Reset(). // internal static void TestHook_DisableActions() diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSystemStateManager.cs b/Packages/com.unity.inputsystem/InputSystem/InputSystemStateManager.cs index cab2b50071..0c2decc009 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputSystemStateManager.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputSystemStateManager.cs @@ -33,7 +33,7 @@ internal struct InputSystemState [NonSerialized] public ISavedState inputUserState; } - // TODO: ISX-1860 + // ISX-1860 - #ifdef out Domain Reload specific functionality from CoreCLR #if UNITY_EDITOR /// /// A hidden, internal object we put in the editor to bundle input system state @@ -45,10 +45,38 @@ internal struct InputSystemState /// internal class InputSystemStateManager : ScriptableObject, ISerializationCallbackReceiver { + /// + /// References the "core" input state that must survive domain reloads. + /// [SerializeField] public InputSystemState systemState; + + /// + /// Triggers Editor restart when enabling NewInput back-ends. + /// [SerializeField] public bool newInputBackendsCheckedAsEnabled; + + /// + /// Saves and restores InputSettings across domain reloads + /// + /// + /// InputSettings are serialized to JSON which this string holds. + /// [SerializeField] public string settings; + + /// + /// Timestamp retrieved from InputRuntime.currentTime when exiting EditMode. + /// + /// + /// All input events occurring between exiting EditMode and entering PlayMode are discarded. + /// [SerializeField] public double exitEditModeTime; + + /// + /// Timestamp retrieved from InputRuntime.currentTime when entering PlayMode. + /// + /// + /// All input events occurring between exiting EditMode and entering PlayMode are discarded. + /// [SerializeField] public double enterPlayModeTime; public void OnBeforeSerialize() diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/PlayerInput.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/PlayerInput.cs index 8a511591bb..d3d522aa59 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/PlayerInput.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/PlayerInput/PlayerInput.cs @@ -1209,7 +1209,7 @@ internal struct GlobalState private static void InitializeGlobalPlayerState() { // Touch GlobalState doesn't require Dispose operations - s_GlobalState = new GlobalState + s_GlobalState = new PlayerInput.GlobalState { initPlayerIndex = -1, initSplitScreenIndex = -1