using System;
using UnityEngine.InputSystem.Layouts;
////TODO: ManualThreaded
namespace UnityEngine.InputSystem.LowLevel
{
///
/// Enum of different player loop positions where the input system can invoke its update mechanism.
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1714:FlagsEnumsShouldHavePluralNames", Justification = "Not consistently used as flags, many using APIs expect only one type to be passed.")]
[Flags]
public enum InputUpdateType
{
///
/// Performs no actual update but still allows devices to reset themselves. Usually occurs immediately after domain reload.
///
None = 0,
///
/// Update corresponding to Update.
///
/// Every frame has exactly one dynamic update. If not reconfigured using ,
/// the dynamic update happens after all the fixed updates for the frame have run (which can be
/// zero or more).
///
/// Input updates run before script callbacks on MonoBehaviours are fired.
///
Dynamic = 1 << 0,
///
/// Update corresponding to FixedUpdate.
///
/// Every frame has zero or more fixed updates. These are run before the dynamic update for the
/// frame.
///
/// Input updates run before script callbacks on MonoBehaviours are fired.
///
Fixed = 1 << 1,
////REVIEW: Axe this update type from the public API?
///
/// Input update that happens right before rendering.
///
/// The BeforeRender update affects only devices that have before-render updates enabled. This
/// has to be done through a device's layout (
/// and is visible through .
///
/// BeforeRender updates are useful to minimize lag of transform data that is used in rendering
/// but is coming from external tracking devices. An example are HMDs. If the head transform used
/// for the render camera is not synchronized right before rendering, it can result in a noticeable
/// lag between head and camera movement.
///
BeforeRender = 1 << 2,
///
/// Input update that happens right before s are updated.
///
/// This update only occurs in the editor. It is triggered right before .
///
Editor = 1 << 3,
///
/// Input updates do not happen automatically but have to be triggered manually by calling .
///
Manual = 1 << 4,
///
/// Default update mask. Combines , , and .
///
Default = Dynamic | Fixed | Editor,
}
internal static class InputUpdate
{
public static uint s_UpdateStepCount; // read only, but kept as a variable for performance reasons
public static InputUpdateType s_LatestUpdateType;
public static UpdateStepCount s_PlayerUpdateStepCount;
#if UNITY_EDITOR
public static InputUpdateType s_LatestNonEditorUpdateType;
public static UpdateStepCount s_EditorUpdateStepCount;
#endif
[Serializable]
public struct UpdateStepCount
{
private bool m_WasUpdated;
public uint value { get; private set; }
public void OnBeforeUpdate()
{
m_WasUpdated = true;
value++;
}
public void OnUpdate()
{
// only increment if OnBeforeUpdate was not called
if (!m_WasUpdated)
value++;
m_WasUpdated = false;
}
};
[Serializable]
public struct SerializedState
{
public InputUpdateType lastUpdateType;
public UpdateStepCount playerUpdateStepCount;
#if UNITY_EDITOR
public InputUpdateType lastNonEditorUpdateType;
public UpdateStepCount editorUpdateStepCount;
#endif
}
internal static void OnBeforeUpdate(InputUpdateType type)
{
s_LatestUpdateType = type;
switch (type)
{
case InputUpdateType.Dynamic:
case InputUpdateType.Manual:
case InputUpdateType.Fixed:
s_PlayerUpdateStepCount.OnBeforeUpdate();
s_UpdateStepCount = s_PlayerUpdateStepCount.value;
#if UNITY_EDITOR
s_LatestNonEditorUpdateType = type;
#endif
break;
#if UNITY_EDITOR
case InputUpdateType.Editor:
s_EditorUpdateStepCount.OnBeforeUpdate();
s_UpdateStepCount = s_EditorUpdateStepCount.value;
break;
#endif
}
}
internal static void OnUpdate(InputUpdateType type)
{
s_LatestUpdateType = type;
switch (type)
{
case InputUpdateType.Dynamic:
case InputUpdateType.Manual:
case InputUpdateType.Fixed:
s_PlayerUpdateStepCount.OnUpdate();
s_UpdateStepCount = s_PlayerUpdateStepCount.value;
#if UNITY_EDITOR
s_LatestNonEditorUpdateType = type;
#endif
break;
#if UNITY_EDITOR
case InputUpdateType.Editor:
s_EditorUpdateStepCount.OnUpdate();
s_UpdateStepCount = s_EditorUpdateStepCount.value;
break;
#endif
}
}
#if UNITY_EDITOR
internal static void RestoreStateAfterEditorUpdate()
{
s_LatestUpdateType = s_LatestNonEditorUpdateType;
s_UpdateStepCount = s_PlayerUpdateStepCount.value;
}
#endif
public static SerializedState Save()
{
return new SerializedState
{
lastUpdateType = s_LatestUpdateType,
playerUpdateStepCount = s_PlayerUpdateStepCount,
#if UNITY_EDITOR
lastNonEditorUpdateType = s_LatestNonEditorUpdateType,
editorUpdateStepCount = s_EditorUpdateStepCount
#endif
};
}
public static void Restore(SerializedState state)
{
s_LatestUpdateType = state.lastUpdateType;
s_PlayerUpdateStepCount = state.playerUpdateStepCount;
#if UNITY_EDITOR
s_LatestNonEditorUpdateType = state.lastNonEditorUpdateType;
s_EditorUpdateStepCount = state.editorUpdateStepCount;
#endif
switch (s_LatestUpdateType)
{
case InputUpdateType.Dynamic:
case InputUpdateType.Manual:
case InputUpdateType.Fixed:
s_UpdateStepCount = s_PlayerUpdateStepCount.value;
break;
#if UNITY_EDITOR
case InputUpdateType.Editor:
s_UpdateStepCount = s_EditorUpdateStepCount.value;
break;
#endif
default:
// if there was no previous update type, reset the counter
s_UpdateStepCount = 0;
break;
}
}
public static InputUpdateType GetUpdateTypeForPlayer(this InputUpdateType mask)
{
if ((mask & InputUpdateType.Manual) != 0)
return InputUpdateType.Manual;
if ((mask & InputUpdateType.Dynamic) != 0)
return InputUpdateType.Dynamic;
if ((mask & InputUpdateType.Fixed) != 0)
return InputUpdateType.Fixed;
return InputUpdateType.None;
}
public static bool IsPlayerUpdate(this InputUpdateType updateType)
{
if (updateType == InputUpdateType.Editor)
return false;
return updateType != default;
}
#if UNITY_EDITOR
public static bool IsEditorUpdate(this InputUpdateType updateType)
{
return updateType == InputUpdateType.Editor;
}
#endif
}
}