FIX: ISXB-1319 Input recorder game view focus#2407
FIX: ISXB-1319 Input recorder game view focus#2407MorganHoarau wants to merge 9 commits intodevelopfrom
Conversation
Bypasses several game focused related logic gates to let replay input event reach playmode session.
Bit of logics to track replay controllers and bridge with InputManager.
Uses InputManager.isReplayActive flag
| if (!m_ReplayBypassActive) | ||
| { | ||
| m_ReplayBypassActive = true; | ||
| ++InputSystem.s_Manager.m_ActiveReplayCount; |
|
|
||
| if (m_ClearReplayBypassCallback == null) | ||
| m_ClearReplayBypassCallback = EndReplayBypass; | ||
| InputSystem.onAfterUpdate += m_ClearReplayBypassCallback; |
There was a problem hiding this comment.
This logic has two issues that can lead to memory leaks and incorrect behavior:
-
Subscription Leak:
ScheduleEndReplayBypassdoes not check if a callback is already scheduled before calling+=. IfFinished()is called multiple times (e.g., if a user triggersPlayAllEventsrepeatedly in the same frame), multiple subscriptions are added toInputSystem.onAfterUpdate. SinceEndReplayBypass(line 1130) nullifiesm_ClearReplayBypassCallbackafter removing only one subscription, any additional subscriptions will remain in the globalonAfterUpdateevent forever. This will also keep theReplayControllerinstance alive in memory indefinitely. -
Premature Termination: If a playback is restarted (e.g., calling
PlayAllFramesOneByOne) on the same controller after it has finished but before theonAfterUpdatecleanup has fired,BeginReplayBypasswill seem_ReplayBypassActiveastrueand skip the increment. However, the pendingEndReplayBypasswill then fire, settingm_ReplayBypassActivetofalseand decrementing the count, leaving the newly started replay without its bypass.
Suggested Fix:
- In
ScheduleEndReplayBypass, return early ifm_ClearReplayBypassCallback != null. - In
BeginReplayBypass, ifm_ClearReplayBypassCallbackis not null, unsubscribe it fromonAfterUpdateand nullify it to cancel the pending cleanup before checkingm_ReplayBypassActive.
🤖 Helpful? 👍/👎
| Dispose event traces after use, so that they do not leak memory on the unmanaged (C++) memory heap. | ||
|
|
||
| > [!NOTE] | ||
| > **Keyboard text input is not replayed to UI text fields.** Keyboard state (key presses) is captured and replayed correctly and remains accessible via `Keyboard.current`. However there is a known limitation with character delivery to UI Framework components (uGUI `InputField` or UIToolkit `TextField`). These components receive text through a separate native pipeline that is not fed by event replay. As a result, text typed into UI text fields during recording will not appear during playback. |
WinPlayer build made to ensure compilation
Codecov ReportAttention: Patch coverage is
@@ Coverage Diff @@
## develop #2407 +/- ##
========================================
Coverage 78.13% 78.13%
========================================
Files 483 483
Lines 98770 98805 +35
========================================
+ Hits 77169 77201 +32
- Misses 21601 21604 +3 Flags with carried forward coverage won't be shown. Click here to find out more.
|
Description
PR addresses https://jira.unity3d.com/browse/ISXB-1319
Tech Roundtable Slide-deck: https://docs.google.com/presentation/d/18zL10TRcnVfBiSSBe_YT5EYVkJLyt7gFvD3s7EL0JKY/edit?usp=drive_link
Issue 1: replay requires Game View focus
Root cause: three independent focus-gating layers that block replayed events when Game View is unfocused (event deferral, device-disabled discard, UI module skip).
The fix implemented uses an InputManager-level
isReplayActiveflag that bypasses all three (Editor-only).Issues 1 Details
When Game View loses focus ->
applicationHasFocus=false->gameHasFocus=false. Replay events are blocked at three independent layers:sequenceDiagram participant IM as InputManager participant RC as ReplayController participant Dev as Mouse/Keyboard participant UIIM as InputSystemUIInputModule participant ES as EventSystem Note over IM,ES: Game View loses focus IM->>IM: gameHasFocus = false IM->>Dev: DisableDevice(DisabledWhileInBackground) ES->>ES: OnApplicationFocus(false) → m_HasFocus = false Note over IM,ES: Replay starts RC->>IM: QueueEvent(mouseStateEvent) IM->>IM: ❌ A — event deferred (ShouldDefer) IM->>IM: ❌ B — event discarded (device disabled) ES->>UIIM: Process() UIIM->>UIIM: ❌ C — !isFocused → skip ProcessPointer/ProcessNavigationKnown trade-off: The bypass doesn't distinguish replayed events from real hardware input, so real keyboard/mouse input also reaches the game during replay even with Game View unfocused. This is acceptable for typical usage (replay in isolation in Editor).
Please also note that the fix essentially add a new tightly coupled flag for a feature from a sample. It's not the best, but is a small patch to get this feature working.
Issue 2: TextField text not replayed
Architectural limitation. UIToolkit TextField receives text through the native IMGUI event queue (
Event.PopEvent), which is a separate pipeline from InputSystem. Replayed TextEvents fire correctly throughKeyboard.OnTextInputbut have zero subscribers. The IMGUI queue is only populated by native OS input. Keyboard state (key presses) does replay correctly; only character delivery to TextFields is broken. Fixing this would require engine-side changes (InputForUI / IMGUI bridge), outside scope of this ticket.This is a consistent pattern across Unity's UI framework: IMGUI, uGUI, and UIToolkit all use
Event.PopEvent()for text processing.Testing status & QA
Please describe the testing already done by you and what testing you request/recommend QA to execute. If you used or created any testing project please link them here too for QA.
input-recorder-half-fix.mp4
Overall Product Risks
Please rate the potential complexity and halo effect from low to high for the reviewers. Note down potential risks to specific Editor branches if any.
Comments to reviewers
Please describe any additional information such as what to focus on, or historical info for the reviewers.
Checklist
Before review:
Changed,Fixed,Addedsections.Area_CanDoX,Area_CanDoX_EvenIfYIsTheCase,Area_WhenIDoX_AndYHappens_ThisIsTheResult.During merge:
NEW: ___.FIX: ___.DOCS: ___.CHANGE: ___.RELEASE: 1.1.0-preview.3.