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

Add initial axis input proposal draft #2415

Open
wants to merge 21 commits into
base: main
Choose a base branch
from

Conversation

domportera
Copy link

Summary of the PR

Adds the axis-based input proposal's first draft

Related issues, Discord discussions, or proposals

Several I've lost track of and I am sorry to report I am feeling particularly lazy after spewing so many words from my fingertips in a single afternoon

Further Comments

Any and all feedback ahead of our discussion this sunday is welcome - even if just for clarity of writing, and especially re: the contents of the proposal

@domportera domportera requested a review from a team as a code owner January 24, 2025 20:10
@domportera
Copy link
Author

@dotnet-policy-service agree

Copy link
Member

@Perksey Perksey left a comment

Choose a reason for hiding this comment

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

I love this proposal!

documentation/proposals/Proposal - Axis Input Devices.md Outdated Show resolved Hide resolved
documentation/proposals/Proposal - Axis Input Devices.md Outdated Show resolved Hide resolved
public class DualsenseDevice : IAxisDevice
{
IReadOnlyList<AxisDescription>
private static readonly IReadOnlyList<AxisDescription> Axes =
Copy link
Member

Choose a reason for hiding this comment

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

I know this is INFORMATIVE TEXT but... triggers? Only reason I mention it is because it's quite pertinent for DualSense specifically. Feel free to ignore this comment though as it may open irrelevant discussions now I mention it (namely application-controlled axes like the haptic feedback)

Copy link
Author

@domportera domportera Jan 26, 2025

Choose a reason for hiding this comment

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

I haven't yet thought of any sort of API for axis output yet. my thoughts were outputs could be a non-breaking extension to this API as you mightve guessed - lots of things to consider there (and potentially lots of input-related expansions too e.g. microphone/audio out, LEDs, etc). i have no problem re-visiting this to properly complete the axis list though

Copy link
Member

Choose a reason for hiding this comment

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

I do think it would be a shame to accept the proposal not having a path to implementing the base proposal in terms of this proposal, as that is ultimately one of the main goals.

A lot of the benefits we get from this axis model still exist without that mind e.g. it makes developing an input action model a lot easier, but we should have a clear strategy for getting to the future we want. And if we accept this proposal without everything, we open up opportunities to have fragmented implementations given that we'd have to care about backwards compatibility if it does get implemented without the other proposals being approved.

Copy link
Author

Choose a reason for hiding this comment

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

lemme stew a bit. i just dont want to paint ourselves into a corner with device outputs (since some may not be very simple - e.g. streaming outputs that arent abstractable to a floating point value)

Copy link
Author

Choose a reason for hiding this comment

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

I added a draft of an idea to the end of the proposal

documentation/proposals/Proposal - Axis Input Devices.md Outdated Show resolved Hide resolved
documentation/proposals/Proposal - Axis Input Devices.md Outdated Show resolved Hide resolved
/// <summary>
/// An interface for handling deadzones in different ways
/// </summary>
public interface IDeadzoneHandler
Copy link
Member

Choose a reason for hiding this comment

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

Sceptical about calling this IDeadzoneHandler given that in our input API "handlers" usually refer to our actor-like model for handling input events.

Maybe IDeadzoneDeterminant, or maybe call this IDeadzone and rename Deadzone to DeadzoneBounds? Or something else?

Copy link
Author

Choose a reason for hiding this comment

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

the naming was an attempt to keep the API consistent with how people will be used to handling input events, though if this doesn't actually follow the model of the others then I'm totally down to change the name. Though not crazy about those names in particular bc i think it's less clear

we can shop it with The Gang? ¯_(ツ)_/¯

Copy link
Member

Choose a reason for hiding this comment

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

Will discuss in meeting.

documentation/proposals/Proposal - Axis Input Devices.md Outdated Show resolved Hide resolved
@Perksey Perksey added this to the Next Working Group Meeting milestone Jan 26, 2025
/// Represents the state of a single axis
/// </summary>
/// <param name="IsActive">True if the axis is currently in use / actively updated (required to be true if axis is not <see cref="AxisTrait.Dynamic"/>)
public readonly record struct AxisState(IAxisDevice Device, AxisDescription Description, float Value, bool IsActive = true);
Copy link
Member

Choose a reason for hiding this comment

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

Should IsActive be in AxisDescription? Or more specifically, should this be queryable outside of a handler method call?

Copy link
Member

Choose a reason for hiding this comment

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

Or perhaps RawValues should be replaced with an AxisState IReadOnlyList that produces these structs on the fly?

Copy link
Author

Choose a reason for hiding this comment

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

I like the idea of putting it in AxisDescription, though I'd really like to avoid the scenario where a "fixed" axis is marked as "IsActive = false" post-validation. currently theres no defense against this anyway, but it could create ideas.

though it could be a nice way to say "hey my X button is broken, can we not?" so in that case im actually gonna update this to be in the AxisDefinition and allow IsActive (w/ potential rename) to be used universally. we're probably never going to do that, but ¯_(ツ)_/¯

/// </summary>
/// <param name="index">The index of the <see cref="OutputDescription"/> in <see cref="Outputs"/></param>
/// <param name="values">A pointer to the value(s) to be set</param>
public void SetOutput<T>(int index, T* values, int count = 1) where T : unmanaged => throw new NotImplementedException();
Copy link
Member

Choose a reason for hiding this comment

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

Can we change this to be ReadOnlySpan, perhaps with a DIM that implicitly creates a 1-element span from a single ref readonly parameter?

Copy link
Author

Choose a reason for hiding this comment

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

funny i actually changed it from a span to a pointer when writing this - happy to change it back to appease my paranoid ass

Copy link
Member

Choose a reason for hiding this comment

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

I think if we're splitting this up into outputs vs output groups then we should likely have a single output be a single float or a single T (with the T path being try-based to enable extension detection, again we should reserve the right to define these extensions as we see fit given that they're not the happy path, and that not everything is cleanly representable as a float.

/// specific trigger, an LED for a specific button, etc.</param>
/// <param name="Name">An optional name for the output</param>
/// <param name="CustomDataType">Valid (and required) only for outputs marked as <see cref="OutputAxisTrait.RawDataOnly"/></param>
public readonly record struct OutputDescription(int Index, OutputAxisTrait Traits, int? AssociatedAxisIndex = null, string? Name = null, Type? CustomDataType = null);
Copy link
Member

Choose a reason for hiding this comment

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

AssociatedAxisIndex should probably be AssociatedAxes and be a nullable IReadOnlyList<int> for consistency with the other structs. CustomDataType I don't think should be included in the description, and we should instead make this inferable from the traits.

Copy link
Author

@domportera domportera Jan 26, 2025

Choose a reason for hiding this comment

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

should probably be [plural]

wouldnt that only apply to "OutputAxisGroup"s? this is more like TwinIndex in AxisDescription

Copy link
Member

Choose a reason for hiding this comment

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

Yes now that we have decided that axis groups are going to exist. These comments did not have knowledge of eachother seemingly 😅

Copy link
Author

Choose a reason for hiding this comment

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

CustomDataType I don't think should be included in the description, and we should instead make this inferable from the traits

I also don't like it, but it was just off-the-dome in the last few minutes. all in all im not sure how to handle the typing for this sort of thing in a graceful way atm

Copy link
Member

@Perksey Perksey Jan 26, 2025

Choose a reason for hiding this comment

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

Would recommend replacing with my other comment i.e. the try get info. Axis/output description needn't specify this as it is likely going to be a one to many relationship from axes/outputs to additional metadata available.

/// <param name="index">The index of the <see cref="OutputDescription"/> in <see cref="Outputs"/></param>
/// <param name="values">A pointer to the value(s) to be set</param>
public void SetOutput<T>(int index, T* values, int count = 1) where T : unmanaged => throw new NotImplementedException();
}
Copy link
Member

Choose a reason for hiding this comment

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

Come to think of it, we should probably add a TryGetAxisInfo<T> and a TryGetOutputInfo<T> so that we can have extensions in the future.

Copy link
Author

@domportera domportera Jan 26, 2025

Choose a reason for hiding this comment

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

this could be a nicer way to handle the typing - kind of enforces the idea that axis devices should strive to use similar output types where possible. I wonder how this would extend to the incoming "OutputGroup"

Copy link
Author

Choose a reason for hiding this comment

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

that being said, i think that sort of api needs to be more thoroughly thought out than the next 30 minutes would allow us

Copy link
Member

Choose a reason for hiding this comment

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

I think I'd be happy defining these two methods and just having no acceptable types for T atm i.e. Silk.NET reserves the right to add device-type-specific information to aid in axis identification, mapping, configuration, etc. Use of these APIs shouldn't be encouraged as the happy path, but it's sometimes useful to get more info than our intentionally over-generalised API provides.

Comment on lines 691 to 695
LEDBrightness = 1 << 4 | LED,
LEDRed = 1u << 5 | LED,
LEDGreen = 1u << 6 | LED,
LEDBlue = 1u << 7 | LED,
LEDWhite = 1u << 8 | LED,
Copy link
Member

Choose a reason for hiding this comment

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

Kind of feels like we need output groups as well. Which I guess makes sense from a consistency standpoint.

Copy link
Author

Choose a reason for hiding this comment

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

it does feel that way huh. could get rid of all these different sub-types of LED in favor of an LED OutputAxisGroupTypeNamePending

Copy link
Member

Choose a reason for hiding this comment

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

Basically I think we should just have a very similar API surface for outputs as we do axes - we worked hard on making that generalised abstraction and I like the trait system that you came up with e.g. if it's an LED and it has 3 axes you can assume they're RGB etc

@Perksey
Copy link
Member

Perksey commented Jan 26, 2025

  • We need to discuss this further another time (primarily because of the last minute additions like outputs that naturally need more thought), but we reviewed the majority and were happy with most of what we saw, with the following comments...
  • AsAvailable isn't needed as C# has with
  • Use radians instead of degrees for EulerAngleComponents
  • Use sequential bits for sanity, or otherwise use a more defined bit usage scheme for the bitmasks ("enum sandwich")
  • Use pitch, yaw, roll instead of ypw
  • Some consideration has to be done for the diamond layouts and region-specific mappings of the buttons therein.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: In Progress
Development

Successfully merging this pull request may close these issues.

2 participants