-
Notifications
You must be signed in to change notification settings - Fork 317
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FIX: Changed to frame based comparison for tracking UI click/navigation events #2112
base: develop
Are you sure you want to change the base?
Conversation
…d approach - track frame of interest in action
WIP - Adding tests - coming soon |
Packages/com.unity.inputsystem/InputSystem/Actions/InputActionState.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes look like a good approach to fixing the problems with dynamic/fixed and manual update.
I'm a little concerned about having two functions to check the state. In summary as a user I find the naming confusing and hard to tell if I when to use WasPressed vs WasPressedThisFrame Adding a another reviewer for input. |
@@ -3640,7 +3657,10 @@ public struct TriggerState | |||
[FieldOffset(40)] private uint m_PressedInUpdate; | |||
[FieldOffset(44)] private uint m_ReleasedInUpdate; | |||
[FieldOffset(48)] private uint m_LastCompletedInUpdate; | |||
[FieldOffset(52)] private int m_Frame; | |||
[FieldOffset(52)] private int m_FramePerformed; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Am wondering how big an impact this increase in size is for states.
(How many do we typically have active)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be linear to the number of actions if I am not mistaken?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes
/// <seealso cref="CallbackContext.ReadValueAsButton"/> | ||
/// <seealso cref="WasCompletedThisFrame"/> | ||
/// <seealso cref="InputSettings.updateMode"/> | ||
public unsafe bool WasReleased() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am a little confused of the intent here. wasXXXXThisFrame was intended to be evaluation whether an event occurrent at least once in the associated frame (which is a time window related to the driving update setting if I am not wrong on this). When the system was configured with Dynamic Update "ThisFrame" corresponds to a dynamic update frame. When set to e.g. FixedUpdate "ThisFrame" corresponds to a fixed update frame. Regardless of setting both frames coexist as overlapping time windows in parallel. Hence, wouldn't it make sense to attempt keeping frame comparison and tracking but in parallel for each time context? This would in turn require the same "tracking data" to coexist per update perspective but this cannot be avoided I am afraid if the requirement is to be able to evaluate multiple timelines. Based on such an approach there could e.g. be
public unsafe bool WasReleasedThisFrame(InputUpdateType updateType = InputUpdateType.UseCurrentSetting) and let it default to current configuration unless specified. If specified update type could be used to offset into array of parallel timeline information. This way API contract could remain intact, but one could be explicit, e.g. UI could always pass InputUpdateType.Dynamic?
With the proposed e.g. WasReleased() it's not clear to me what frame this maps to? The comment says Update cycle which would imply the frame given by setting right? Could you help me understand what time window this maps to?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am a little suprised that ThisFrame should map to FixedUpdate, FixedUpdate can happen multiple times between frames.
I will schedule a call to explain this in more detail.
But the basic idea maps down to the solution to the fact that InputSystem.Update() can be called to any time (every millisecond if desired) which increases the update step.
So there is the desire to check for input events on input update steps (which are not necesarilly related to dynamic update at all) and the desire to check against the current frame (eg for UI, maybe users need some events only once per frame).
The previous logic was solely relying on a frame based approach and would fail for FixedUpdate and any other manual input update setup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In short:
WasReleased()- input update step (eg in fixed update, fixed update & dynamic update or at any millisecond in the game)
WasReleasedThisFrame() - dynamic update cycle
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes a frame is a time window, as you say it gets problematic if the consumer of the input do not get the answer in the time window (frame) of interest the consuming context is assuming. For UI update its rendering frames as you say. Let's discuss in the call. Also added an ASCII image comment to this PR to aid the discussion.
Another option would be to just at a boolean parameter to each API piece wheather to check the dynamic Update cycle (which could default to true).
I understand the concerns, I couldn't make up a better name for it since WasPressedThisUpdate() would be misleading since Update could be wrongly interpreted as dynamic Update again. |
@@ -135,6 +137,8 @@ public void Samples_RebindingUI_SuppressingEventsDoesNotInterfereWithUIInput() | |||
// UI should be fine with that. | |||
PressAndRelease(keyboard.enterKey); | |||
eventSystem.InvokeUpdate(); | |||
yield return null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this test update to step a frame?
Was it failing without this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes it was failing before, since this is relying on a frame based (dynamic update) comparison and InvokeUpdate() does not set the frameCount like in a real playmode (Update) scenario.
@@ -87,9 +87,9 @@ public void Actions_WhenShortcutsDisabled_AllConflictingActionsTrigger() | |||
Press(keyboard.wKey); | |||
|
|||
// All Actions were triggered | |||
Assert.That(action1.WasPerformedThisFrame()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a lot of tests update to make this pass?
Were all these failing without the changes?
Make me worry about how much customer code would be impacted by the change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test run without mimiking the dynamic update, which make this call wrong in it self (ThisFrame). Either they need to use Coroutines (see comment above) or rely on InputSystem.Update manual updates like here. In real settings (non test environments) there are no such requirements, so I don't think the users will be impacted. Especially because most users rely on update, which will work just like before.
What I meant with previous question/comment is that, considering e.g. this sequence, the results vary depending on what frame corresponds to right? Mainly adding this comment to support discussion.
|
Description
During investigating ISXB-1313 it got clear that catching navigation and click events does not work properly once InputSystem is set to a custom / Fixed Update mode. The main reason for this is that the InputSystemUIInputModule (Process -> ProcessNavigation -> WasPerformedThisFrame) is automatically called in dynamic Update, this calls in
WasPressedThisFrame()
WasReleasedThisFrame()
WasCompletedThisFrame()
WasPerformedThisFrame()
to check weather UI actions were triggered. Internally those methods use
pressedInUpdate
releasedInUpdate
lastPerformedInUpdate
lastCompletedInUpdate
which are properties to track at which update cycle of the InputSystem the events triggered. All above are just used for this checks.
Problem:
The update step is not comparable at the time of the dynamic Update since it is not clear how many times InputSystem.Update() (and with that the current update step was incremented by 1) was called in between two dynamic Updates. It can be incremented by 1 customly every millisecond or switched to be called in FixedUpdate.
The solution is to have two different methods for the (WasPressedThisFrame/WasPerformedThisFrame ...)checks, where one checks for the current frame only and the other for the input update cycle.
They will be equal if the update mode is set to dynamic Update, if set to FixedUpdate or Manual update eg WasPressed will return only true if the action was pressed this InputSystem update cycle (eg in fixed update). This was missing since the old approach was assuming a frame based input updating.
Testing status & QA
testing in InputSystem repo, bug project (attached to ISXB-1313) and against https://jira.unity3d.com/browse/ISXB-1160
https://jira.unity3d.com/browse/ISXB-1141.
Overall Product Risks
Comments to reviewers
For further explanation also check the comments in the ticket.
Checklist
Before review:
Changed
,Fixed
,Added
sections.Area_CanDoX
,Area_CanDoX_EvenIfYIsTheCase
,Area_WhenIDoX_AndYHappens_ThisIsTheResult
.During merge:
NEW: ___
.FIX: ___
.DOCS: ___
.CHANGE: ___
.RELEASE: 1.1.0-preview.3
.After merge: