Skip to content

FIX: Add hopeful fix for mouse polling causing frame-rate issues on windows.#2441

Open
Darren-Kelly-Unity wants to merge 2 commits into
developfrom
bugfix/UUM-142550-high-polling-mouse-framerate-drop
Open

FIX: Add hopeful fix for mouse polling causing frame-rate issues on windows.#2441
Darren-Kelly-Unity wants to merge 2 commits into
developfrom
bugfix/UUM-142550-high-polling-mouse-framerate-drop

Conversation

@Darren-Kelly-Unity

@Darren-Kelly-Unity Darren-Kelly-Unity commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Purpose of this PR

DISCLAIMER:
This was very heavily done by overconfident Claude and may be the completely wrong place to do this. He seemed to know what he was talking about..

Fixes a performance regression (UUM-142550) where high-polling-rate mice (1000–8000 Hz) cause severe FPS drops on Windows. The FastMouse event merger only handled StateEvent; this PR extends it to also coalesce DeltaStateEvent messages that carry a full MouseState payload, closing the path by which Windows Raw Input events could flood the managed event loop unmerged.

Release Notes

  • [Fix] FastMouse event merger now handles DeltaStateEvent with full MouseState payload, preventing high-polling-rate mice from flooding the event loop on Windows. (UUM-142550)

Functional Testing status

  • New automated tests in CoreTests_FastMouseMerger.cs covering:
    • Unit: MergeForward accepts/rejects DeltaStateEvent by type and size
    • Unit: mixed StateEvent + DeltaStateEvent merge
    • Integration: 100+ events collapse to 1 through the full ProcessEventBuffer pipeline
    • Integration: button press mid-stream preserves event and merges surrounding moves
    • Integration: high-polling-rate simulation (130 events/frame, two mice, scroll accumulation, merging on/off control case)
  • Manual repro requires a Windows machine with a ≥1000 Hz polling rate mouse; not verified manually.

Performance Testing Status

This change reduces the number of events processed per frame for high-polling-rate mice on Windows from O(polling rate / fps) to O(1) for pure movement. No performance regression expected on other platforms or lower polling rates.

Overall Product Risks

  • Technical Risk: 1 — Change is confined to FastMouse.partial.cs; only affects devices instantiated as FastMouse (layout "Mouse"). All four MergeForward type combinations are guarded by format and size checks; non-qualifying events fall through to the existing base class path unchanged.
  • Halo Effect: 1 — Could affect any project using a mouse device on Windows. The IsFullMouseStateDeltaEvent gate (stateOffset=0, size≥sizeof(MouseState)) is conservative and only activates on full-device delta events, so partial control delta events are unaffected.

Class diagram

Class diagram for origin/develop...HEAD

Class diagram
classDiagram
    IInputStateCallbackReceiver <|.. FastMouse
    IEventMerger <|.. FastMouse

    class FastMouse {
        +OnStateEvent(InputEventPtr eventPtr) void
        +MergeForward(InputEventPtr currentEventPtr, InputEventPtr nextEventPtr) bool
        -IsFullMouseStateDeltaEvent(DeltaStateEvent* deltaEvent) bool
    }

    class CoreTests {
        +FastMouseMerger_Unit_MergesDeltaStateEventsWithSameButtonState()
        +FastMouseMerger_Unit_DoesNotMergeDeltaStateEventsWithDifferentButtonState()
        +FastMouseMerger_Unit_RejectsPartialDeltaStateEvent()
        +FastMouseMerger_Unit_MergesMixedStateAndDeltaStateEvent()
        +FastMouseMerger_Integration_ManyDeltaStateEvents_CoalesceToOne()
        +FastMouseMerger_Integration_ButtonPressMidStream_PreservesButtonEventAndMergesSurroundingMoves()
        +FastMouseMerger_Integration_MixedStateAndDeltaStateEvents_Merge()
        +FastMouseMerger_Integration_PartialDeltaStateEvent_IsAppliedButNotMerged()
        +FastMouseMerger_Integration_HighPollingRateSimulation(bool mergeEvents)
    }
Loading

@u-pr u-pr Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Great

Focused fix for Windows mouse event merging; centralizing the event parsing logic would improve the maintainability of the new functionality.

🤖 Helpful? 👍/👎


var currentEvent = StateEvent.FromUnchecked(currentEventPtr);
var nextEvent = StateEvent.FromUnchecked(nextEventPtr);
MouseState* currentState;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Have you considered centralizing the logic for extracting a MouseState* pointer from an InputEventPtr into a private helper method?

Currently, both OnStateEvent and MergeForward contain highly repetitive conditional checks (StateEvent vs DeltaStateEvent), format/size validations, and unsafe pointer casting. Centralizing this significantly improves readability and maintainability without introducing any performance or GC overhead (since it deals entirely with unmanaged pointers).

For example, we could define the helper:

private static unsafe MouseState* TryGetMouseState(InputEventPtr eventPtr)
{
    if (eventPtr.type == StateEvent.Type)
    {
        var stateEvent = StateEvent.FromUnchecked(eventPtr);
        if (stateEvent->stateFormat == MouseState.Format)
            return (MouseState*)stateEvent->state;
    }
    else if (eventPtr.type == DeltaStateEvent.Type)
    {
        var deltaEvent = DeltaStateEvent.FromUnchecked(eventPtr);
        if (IsFullMouseStateDeltaEvent(deltaEvent))
            return (MouseState*)deltaEvent->deltaState;
    }
    return null;
}

With this helper, MergeForward simplifies dramatically to:

internal static unsafe bool MergeForward(InputEventPtr currentEventPtr, InputEventPtr nextEventPtr)
{
    var currentState = TryGetMouseState(currentEventPtr);
    if (currentState == null)
        return false;

    var nextState = TryGetMouseState(nextEventPtr);
    if (nextState == null)
        return false;

    if (currentState->buttons != nextState->buttons || currentState->clickCount != nextState->clickCount)
        return false;

    nextState->delta += currentState->delta;
    nextState->scroll += currentState->scroll;
    return true;
}

And OnStateEvent becomes:

protected new unsafe void OnStateEvent(InputEventPtr eventPtr)
{
    var newStatePtr = TryGetMouseState(eventPtr);
    if (newStatePtr == null)
    {
        base.OnStateEvent(eventPtr);
        return;
    }

    var newState = *newStatePtr;
    var stateFromDevice = (MouseState*)((byte*)currentStatePtr + m_StateBlock.byteOffset);

    newState.delta += stateFromDevice->delta;
    newState.scroll += stateFromDevice->scroll;

    InputState.Change(this, ref newState, InputState.currentUpdateType, eventPtr: eventPtr);
}

🤖 Helpful? 👍/👎 by quality_agent

@codecov-github-com

codecov-github-com Bot commented Jun 23, 2026

Copy link
Copy Markdown

Codecov Report

Attention: Patch coverage is 96.44670% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...m/Runtime/Devices/Precompiled/FastMouse.partial.cs 89.28% 6 Missing ⚠️
...ets/Tests/InputSystem/CoreTests_FastMouseMerger.cs 99.29% 1 Missing ⚠️
@@             Coverage Diff              @@
##           develop    #2441       +/-   ##
============================================
+ Coverage    58.58%   79.05%   +20.47%     
============================================
  Files          738      766       +28     
  Lines       135831   140558     +4727     
============================================
+ Hits         79570   111121    +31551     
+ Misses       56261    29437    -26824     
Flag Coverage Δ
inputsystem_MacOS_6000.0 5.31% <0.00%> (-0.01%) ⬇️
inputsystem_MacOS_6000.0_project 77.51% <96.44%> (+0.25%) ⬆️
inputsystem_MacOS_6000.3 5.31% <0.00%> (-0.01%) ⬇️
inputsystem_MacOS_6000.3_project 77.50% <96.44%> (+0.28%) ⬆️
inputsystem_MacOS_6000.4 5.31% <0.00%> (-0.01%) ⬇️
inputsystem_MacOS_6000.4_project 77.51% <96.44%> (+0.28%) ⬆️
inputsystem_MacOS_6000.5 5.30% <0.00%> (-0.01%) ⬇️
inputsystem_MacOS_6000.5_project 77.55% <96.44%> (+0.28%) ⬆️
inputsystem_MacOS_6000.6 5.30% <0.00%> (-0.01%) ⬇️
inputsystem_Ubuntu_6000.0 5.31% <0.00%> (-0.01%) ⬇️
inputsystem_Ubuntu_6000.0_project 77.42% <96.44%> (+0.26%) ⬆️
inputsystem_Ubuntu_6000.3 5.31% <0.00%> (-0.01%) ⬇️
inputsystem_Ubuntu_6000.3_project 77.41% <96.44%> (+0.28%) ⬆️
inputsystem_Ubuntu_6000.4 5.32% <0.00%> (-0.01%) ⬇️
inputsystem_Ubuntu_6000.4_project 77.42% <96.44%> (+0.28%) ⬆️
inputsystem_Ubuntu_6000.5 5.31% <0.00%> (-0.01%) ⬇️
inputsystem_Ubuntu_6000.5_project 77.46% <96.44%> (+0.29%) ⬆️
inputsystem_Ubuntu_6000.6 5.31% <0.00%> (-0.01%) ⬇️
inputsystem_Windows_6000.0 5.31% <0.00%> (-0.01%) ⬇️
inputsystem_Windows_6000.0_project 77.63% <96.44%> (+0.25%) ⬆️
inputsystem_Windows_6000.3 5.31% <0.00%> (-0.01%) ⬇️
inputsystem_Windows_6000.3_project 77.63% <96.44%> (+0.28%) ⬆️
inputsystem_Windows_6000.4 5.31% <0.00%> (-0.01%) ⬇️
inputsystem_Windows_6000.4_project 77.63% <96.44%> (+0.28%) ⬆️
inputsystem_Windows_6000.5 5.30% <0.00%> (-0.01%) ⬇️
inputsystem_Windows_6000.5_project 77.68% <96.44%> (+0.28%) ⬆️
inputsystem_Windows_6000.6 5.30% <0.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...ets/Tests/InputSystem/CoreTests_FastMouseMerger.cs 99.29% <99.29%> (ø)
...m/Runtime/Devices/Precompiled/FastMouse.partial.cs 88.63% <89.28%> (+88.63%) ⬆️

... and 272 files with indirect coverage changes

ℹ️ Need help interpreting these results?

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.

1 participant