Skip to content
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

Open
wants to merge 33 commits into
base: develop
Choose a base branch
from

Conversation

ritamerkl
Copy link
Collaborator

@ritamerkl ritamerkl commented Jan 22, 2025

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.

Analysis:
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.

Solution:
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 WasPressedThisRenderingFrame will return only true if the action was pressed this dynamic update cycle (no matter in which cycle the InputSystem is updated). This was missing since the old approach was assuming a frame based input updating and would not return valid values if updating the InputSystem would happen in any other time cycle.
InputSystemUIInputModule calls in the WasPerformedThisRenderingFrame() now, which returns valid results independent of the InputSetting during the dynamic Update.
Decoupling the rendering frame count checking from the input update count checking is one essential part of this PR. Those two values are only related if the input update mode is set to dynamic Update (in fact they are the same in this case). In any other case they don't have anything in common. Checking when an action state was changed can happen in the same time cycle like updating the InputSystem (which can now be decoupled from the dynamic Update).

See more details in this technical discussion document.

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

  • Complexity: Medium
  • Halo Effect: High

Comments to reviewers

For further explanation also check the comments in the ticket.

Checklist

Before review:

  • [x ] Changelog entry added.
    • Explains the change in Changed, Fixed, Added sections.
    • For API change contains an example snippet and/or migration example.
    • JIRA ticket linked, example (case %%). If it is a private issue, just add the case ID without a link.
    • Jira port for the next release set as "Resolved".
  • Tests added/changed, if applicable.
    • Functional tests Area_CanDoX, Area_CanDoX_EvenIfYIsTheCase, Area_WhenIDoX_AndYHappens_ThisIsTheResult.
    • Performance tests.
    • Integration tests.
  • Docs for new/changed API's.
    • Xmldoc cross references are set correctly.
    • Added explanation how the API works.
    • Usage code examples added.
    • The manual is updated, if needed.

During merge:

  • Commit message for squash-merge is prefixed with one of the list:
    • NEW: ___.
    • FIX: ___.
    • DOCS: ___.
    • CHANGE: ___.
    • RELEASE: 1.1.0-preview.3.

After merge:

  • Create forward/backward port if needed. If you are blocked from creating a forward port now please add a task to ISX-1444.

…d approach - track frame of interest in action
@ritamerkl ritamerkl requested a review from AlexTyrer January 22, 2025 14:23
@ritamerkl
Copy link
Collaborator Author

WIP - Adding tests - coming soon

Copy link
Collaborator

@AlexTyrer AlexTyrer left a 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.

@lyndon-unity lyndon-unity requested review from ekcoh and removed request for lyndon-unity January 24, 2025 17:12
@lyndon-unity
Copy link
Collaborator

I'm a little concerned about having two functions to check the state.
I'm also unsure if the naming is clear enough as WasPressed still checks part of the input update step.
actionStatePtr->pressedInUpdate == currentUpdateStep && currentUpdateStep != default

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.

@ritamerkl
Copy link
Collaborator Author

ritamerkl commented Jan 27, 2025

I'm a little concerned about having two functions to check the state. I'm also unsure if the naming is clear enough as WasPressed still checks part of the input update step. actionStatePtr->pressedInUpdate == currentUpdateStep && currentUpdateStep != default
I think separating those two is necessary because we have two cases:

  1. Input updates happening between frames on custom times or fixed update (checking against input update step) and
  2. Input updates happening between frames / custom times / fixed update and the need of knowing at the time of dynamic update (checking against frame)
    The need to have the 2. option sets the requirement for the second API piece, this is made by two conditions: the desire to check frame based by components like UI (can also be handy for users who still want to check input in dynamic update despite updating input in another time cycle) and the fact that the input system can be updated manually.
    The API surface could be reduced to only allow WasPerformed / WasPerformedThisFrame in the two different dynamic/input update modes. Since it is the only one now used internally to identify input events in dynamic updates. The reason that I applied it for all 4 API pieces is consistency at one hand and to allow users with custom input update cycles accessing input events between frames on the other hand (which would require another workaround otherwise).

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).

In summary as a user I find the naming confusing and hard to tell if I when to use WasPressed vs WasPressedThisFrame

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.

@@ -87,9 +87,9 @@ public void Actions_WhenShortcutsDisabled_AllConflictingActionsTrigger()
Press(keyboard.wKey);

// All Actions were triggered
Assert.That(action1.WasPerformedThisFrame());
Copy link
Collaborator

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.

Copy link
Collaborator Author

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.

@ekcoh
Copy link
Collaborator

ekcoh commented Jan 27, 2025

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.

Expected wasPressedThisFrame for different kinds of frames (Difference time perspectives)
--------------------------------------------------> time
0   0   1   1   1   0   1   0   1   0   0   0       Button State
|  Yes     |  No       | Yes      |  No       |     Dynamic Update
|  Yes         |  Yes         |  Yes         |      Fixed Update
|  No  | Yes   | No   | Yes      | No    | No   |   Manual Update

@ritamerkl ritamerkl requested a review from ekcoh February 4, 2025 14:21
Pauliusd01

This comment was marked as resolved.

@Pauliusd01 Pauliusd01 self-requested a review February 10, 2025 11:21
Copy link
Collaborator

@Pauliusd01 Pauliusd01 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Things checked (playmode, player, mac/win, 21.3; 22.3; 6.0; 6.2):

  • Changing Input Settings update modes
  • Different FPS settings (1-120)
  • Mouse clicks, keyboard clicks, gamepad clicks for the UI. Checked that both events shows up
  • Similar, older bug fixes are still fixed (ISXB-1141)
  • General input samples, main test project

Copy link
Collaborator

@lyndon-unity lyndon-unity left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With QA sign off on the changes I'm happy to approve. Thank you.

Copy link
Collaborator

@ekcoh ekcoh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes generally makes sense, would have been great to see some test coverage of the FixedUpdate and ManualUpdate modes. Additionally I wonder whether the corresponding fixes for wasPressedThisFrame/wasReleasedThisFrame are missing (to provide consistency) for:

https://github.com/Unity-Technologies/InputSystem/blob/develop/Packages/com.unity.inputsystem/InputSystem/Controls/ButtonControl.cs#L278-L298

https://github.com/Unity-Technologies/InputSystem/blob/develop/Packages/com.unity.inputsystem/InputSystem/Controls/ButtonControl.cs#L323-L343

https://github.com/Unity-Technologies/InputSystem/blob/develop/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/PointerModel.cs#L245-L248

Or would this make them behave differently compared to this if evaluated in tandem with each other? Which would be a great test to have for the different modes.

[Category("Samples")]
public void Samples_RebindingUI_SuppressingEventsDoesNotInterfereWithUIInput()
public IEnumerator Samples_RebindingUI_SuppressingEventsDoesNotInterfereWithUIInput()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ritamerkl Do we have a test that verifies behavior when configured to run input processing on fixed update?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will add tests now

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added 8332195

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

especially tested the update and frame based comparing in co-dependency to another

@@ -3796,10 +3816,25 @@ public uint lastPerformedInUpdate
set => m_LastPerformedInUpdate = value;
}

internal int frame
internal int framePerformed
Copy link
Collaborator

@ekcoh ekcoh Feb 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Minor) If these are anyway internal and just plain getters/setters, why not skip the properties and access the fields directly here? If keeping it like this potentially mark with aggressive inlining hint

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, they were kinda decorational only - fixed here 7114e6f

@ekcoh
Copy link
Collaborator

ekcoh commented Feb 11, 2025

Seems to be a conflict in InputSystemUIInputModule that needs to be resolved.

@ritamerkl
Copy link
Collaborator Author

Changes generally makes sense, would have been great to see some test coverage of the FixedUpdate and ManualUpdate modes. Additionally I wonder whether the corresponding fixes for wasPressedThisFrame/wasReleasedThisFrame are missing (to provide consistency) for:

https://github.com/Unity-Technologies/InputSystem/blob/develop/Packages/com.unity.inputsystem/InputSystem/Controls/ButtonControl.cs#L278-L298

https://github.com/Unity-Technologies/InputSystem/blob/develop/Packages/com.unity.inputsystem/InputSystem/Controls/ButtonControl.cs#L323-L343

Those two actually just check the Update step, like desired.

https://github.com/Unity-Technologies/InputSystem/blob/develop/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/PointerModel.cs#L245-L248

This checks on the rendering frame, and is exclusively changed by the InputSystemUIInputModule, which looks right then, since this operates in the dynamic update cycle.

@ekcoh
Copy link
Collaborator

ekcoh commented Feb 12, 2025

Changes generally makes sense, would have been great to see some test coverage of the FixedUpdate and ManualUpdate modes. Additionally I wonder whether the corresponding fixes for wasPressedThisFrame/wasReleasedThisFrame are missing (to provide consistency) for:
https://github.com/Unity-Technologies/InputSystem/blob/develop/Packages/com.unity.inputsystem/InputSystem/Controls/ButtonControl.cs#L278-L298
https://github.com/Unity-Technologies/InputSystem/blob/develop/Packages/com.unity.inputsystem/InputSystem/Controls/ButtonControl.cs#L323-L343

Those two actually just check the Update step, like desired.

https://github.com/Unity-Technologies/InputSystem/blob/develop/Packages/com.unity.inputsystem/InputSystem/Plugins/UI/PointerModel.cs#L245-L248

This checks on the rendering frame, and is exclusively changed by the InputSystemUIInputModule, which looks right then, since this operates in the dynamic update cycle.

Sounds good! Thanks for checking in detail

@ekcoh
Copy link
Collaborator

ekcoh commented Feb 12, 2025

Seems the remaining CI issues you are facing Rita is due to this PR having version 1.13.1 when it needs to be 1.14.0 to not violate package verification due to 4 added APIs

@ekcoh
Copy link
Collaborator

ekcoh commented Feb 12, 2025

Seems the remaining CI issues you are facing Rita is due to this PR having version 1.13.1 when it needs to be 1.14.0 to not violate package verification due to 4 added APIs

Will approve when this have been fixed

@ritamerkl ritamerkl requested a review from ekcoh February 13, 2025 16:17
Copy link
Collaborator

@ekcoh ekcoh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems version is correct now given API additions which made CI green, great.
The only two remaining things I can spot or think of now is:

  • Why isn't this called out also under #Added in changelog since the PR is adding functionality not previously available?
  • This risk on this PR is quite high so curious whether UI or XR QA or devs have been involved at any point since it may likely also have halo effects into e.g. XRI and UI related solutions?

Will approve now but consider extending CHANGELOG and/or reach out to UI and/or XR maybe before landing it?

…r-update-non-dynamic

# Conflicts:
#	Assets/Samples/InGameHints/InGameHintsActions.cs
#	Assets/Samples/SimpleDemo/SimpleControls.cs
#	Assets/Tests/InputSystem/InputActionCodeGeneratorActions.cs
#	Packages/com.unity.inputsystem/InputSystem/AssemblyInfo.cs
#	Packages/com.unity.inputsystem/InputSystem/Devices/Precompiled/FastKeyboard.cs
#	Packages/com.unity.inputsystem/InputSystem/Devices/Precompiled/FastMouse.cs
#	Packages/com.unity.inputsystem/InputSystem/Devices/Precompiled/FastTouchscreen.cs
#	Packages/com.unity.inputsystem/Tests/TestFixture/AssemblyInfo.cs
#	Packages/com.unity.inputsystem/package.json
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants