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

NEW: Input System Profiler Module #2038

Draft
wants to merge 8 commits into
base: develop
Choose a base branch
from
Draft

NEW: Input System Profiler Module #2038

wants to merge 8 commits into from

Conversation

ekcoh
Copy link
Collaborator

@ekcoh ekcoh commented Oct 25, 2024

Description

Made a draft implementation of a Profiler Module for the Input System based on lack of observability to work as an aid in understanding system mechanics. Hopefully could work as an embryo of something more refined if considered a good idea.

Screenshot 2024-10-25 at 13 36 23

Testing status & QA

Just a prototype at this point. Try it out and suggest or contribute to this PR to make it more useful.

Overall Product Risks

Some more work likely need to be done to InputManager.OnUpdate to make that method cleaner and this work more reliably.

Might not be possible to merge unless minimum Unity version requirement is changed to 2020.1. (Profiler package dependency)

  • Complexity: Small
  • Halo Effect: Medium

Comments to reviewers

At this point mainly looking for feedback and suggestions since this was unplanned work.

Checklist

Before review:

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

var controlCount = ProfilerDriver.GetFormattedCounterValue(selectedFrameIndex,
InputStatistics.Category.Name, InputStatistics.ControlCountName);

m_UpdateCountLabel.text = $"{InputStatistics.UpdateCountName}: {updateCount}";
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is just a basic label list at the moment. Is it possible to pass any extra information via the profiler API? Or extract sample data to e.g. create a distribution within detail window?

Copy link
Collaborator

Choose a reason for hiding this comment

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

There is a ProfilerRecorder that you can use to gather samples, which then allows you to do more statistical computations.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@ekcoh it seems to be possible to add more complex UITK elements. I think a list can be fine for now, and then we could add a nicer UI using UITK (there's more info here , which you probably have seen; and a more more complex UI example in this package where this is the result: image

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes it's possible, but IMHO it would make most sense to align on the data first and keep it simple (as most other modules do). It could be improved after that point if there is value in it.

{
try
{
DoUpdate(updateType, ref eventBuffer);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This needs more work to extract some stuff from DoUpdate below

InputStatistics.EventSize.Value += totalEventSizeBytes;
InputStatistics.AverageLatency.Value += ((totalEventLag / totalEventCount) * 1e9);
InputStatistics.MaxLatency.Value += (maxEventLag * 1e9);
InputStatistics.EventProcessingTime.Value += eventProcessingTime * 1e9; // TODO Possible to replace Stopwatch with marker somehow?
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 guess this last line here is an anti pattern, ideally I would like to include the Update profiler marker into the profiler module, is this possible?

Copy link
Collaborator

Choose a reason for hiding this comment

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

During hackweek I used StopWatch. It was preallocated and reset and reused every frame and accumulated in a ProfilerCounter

Copy link
Collaborator

Choose a reason for hiding this comment

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

which is what it looks like you're doing here :)

@surfnerd
Copy link
Collaborator

surfnerd commented Oct 25, 2024

My first thought looking at the window is the mix of types being presented. I see Counters, Memory Size, and Times. It looks a little bit busy and hard to know what I'm looking at exactly. Overall I think this is a great idea. I wonder if there's a way we can group like values together within one window.

@ekcoh
Copy link
Collaborator Author

ekcoh commented Oct 28, 2024

My first thought looking at the window is the mix of types being presented. I see Counters, Memory Size, and Times. It looks a little bit busy and hard to know what I'm looking at exactly. Overall I think this is a great idea. I wonder if there's a way we can group like values together within one window.

Agreed, I just saw other modules were doing that and replicated it. I think there is opportunity to slip things into separate modules to handle it, e.g. Latency and Input System Counters. I wonder if execution time should even be there since its covered by the ProfilerMarkers anyway.....

Copy link
Collaborator

@jfreire-unity jfreire-unity left a comment

Choose a reason for hiding this comment

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

Great work in adding this functionality.
I think this could easily land in an experimental kind of namespace, almost as is. Just with small tweaks IMO.

The only issue I have is from a UI perspective. Similar to what @surfnerd mentioned, the Module UI is a bit confusing with all the Counts, Bytes, Timings.

I think we could put the Bytes and Timings statistics only in the panel label for now. In my opinion the count statistics are better suited for the "line" UI type.

Ideally, the best would be to have some kind of "Timeline" similar to what CPU has (see img below). But we don't need to be perfect for this to land as I don't think it adds a big risk. I think this would already provide benefit and we could just improve the parts that are confusing a little bit. But of course, we should ask ourselves what are trying to solve with this and see if this
image

I will try to play around a bit with this this sprint and see if I can improve anything.

/// <summary>
/// Input Statistics for Unity Profiler integration.
/// </summary>
internal static class InputStatistics
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd create a Editor/Internal/Profiler folder and but everything in there. I find odd to have this class next to a bunch of "Utilities" classes.

var controlCount = ProfilerDriver.GetFormattedCounterValue(selectedFrameIndex,
InputStatistics.Category.Name, InputStatistics.ControlCountName);

m_UpdateCountLabel.text = $"{InputStatistics.UpdateCountName}: {updateCount}";
Copy link
Collaborator

Choose a reason for hiding this comment

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

@ekcoh it seems to be possible to add more complex UITK elements. I think a list can be fine for now, and then we could add a nicer UI using UITK (there's more info here , which you probably have seen; and a more more complex UI example in this package where this is the result: image

@@ -2,7 +2,8 @@
"name": "Unity.InputSystem",
"rootNamespace": "",
"references": [
"Unity.ugui"
"Unity.ugui",
"Unity.Profiling.Core"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Wondering if this might create some overhead? Probably we could set a define like INPUTSYSTEM_USE_CUSTOMPROFILER or something similar, in case users don't want to have this code in their release builds.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not sure I see how a define helps you omit a reference?

@@ -3055,6 +3058,10 @@ internal bool ShouldRunUpdate(InputUpdateType updateType)
return (updateType & mask) != 0;
}

struct UpdateMetrics
Copy link
Collaborator

Choose a reason for hiding this comment

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

To remove?

Comment on lines 3494 to +3502
if (currentEventTimeInternal <= currentTime)
totalEventLag += currentTime - currentEventTimeInternal;
++m_Metrics.totalEventCount;
m_Metrics.totalEventBytes += (int)currentEventReadPtr->sizeInBytes;
{
var lag = currentTime - currentEventTimeInternal;
totalEventLag += lag;
if (lag > maxEventLag)
maxEventLag = lag;
}
++totalEventCount;
totalEventSizeBytes += (int)currentEventReadPtr->sizeInBytes;
Copy link
Collaborator

Choose a reason for hiding this comment

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

This a bit more complex so I would extract this to a method, as OnUpdate is already big enoug.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Makes sense

m_Metrics.totalEventLagTime += totalEventLag;

ResetCurrentProcessedEventBytesForDevices();
// Profiler counters
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd add a comment that these counters are PerFrame, as we can call update more than once?
Or Maybe a prefix of something like InputStatistics.EventCountPerFrame.Value or PerUpdate, something like that.

Also if could we use ProfileRecorder for this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

makes sense, I haven't looked into the ProfileRecorder yet so I am not sure

"com.unity.settings-manager": "1.0.3",
"com.unity.test-framework": "1.4.5",
"com.unity.test-framework.build": "0.0.1-preview.12",
"com.unity.test-framework.performance": "3.0.3",
"com.unity.test-framework.utp-reporter": "1.1.0-preview",
"com.unity.textmeshpro": "2.1.4",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Any reason why this is removed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Can't recall so probably a mistake

Copy link
Collaborator

@ritamerkl ritamerkl left a comment

Choose a reason for hiding this comment

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

I tested the Profiler in the editor, and I think this is a great initiative! Thanks for setting it up Håkan.

First of all I think the values in the InputSystem profiler make a lot of sense. I agree that some of them could leave the Highlights window and the graph and just show up in the detailed view underneath (I lack the terms - but I mean the window where all values appear in text form with their current values) since they don't really give more insights in a graph form but are more confusing than helpful.

Thinking about some debugging cases I had already I think it would be interesting to be able to get more insights in the kind of events fired and more information on the connected devices and controls.
Something like a Hierarchy view for all fired events (see picture).
Screenshot 2025-01-03 at 15 38 34

For the devices/contorls I could imagine a table like this:
Screenshot 2025-01-03 at 15 39 33
To visualize all devices with the controls (maybe even which ones were acuated).

Last but not least: A general variable which indicates the total storage needed and used by the inputsystem would be great, I am not sure if Total state buffer size represents this completely - maybe it is possible to split this up in several aspects below in the detail area as well.

I did not take a look at the code yet, but only tried it in the editor.

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.

4 participants